feat(qt): Shift ranges in My QBZ collections
A collection keeps its selection in Rust by persisted position, and detailToggleItemSelect only toggled. It now takes `shift`: a plain or Ctrl click toggles and becomes the anchor, a Shift click adds every row between the anchor and the clicked one in the order the rows are DRAWN (filtered and sorted, not the position order), and the anchor is dropped wherever the selection is cleared. The list rows, their checkbox and the grid cards pass Shift through; the kiosk list passes false. In select mode a row's artwork still started playback and its title and subtitle still opened the item or the artist. They stand down now, so the click selects. cc
This commit is contained in:
8 files changed
+158
-24
No files matched your search
@@ -63,7 +63,7 @@ Rectangle {
|
|||||||
QbzMyQbz.kioskDetailArtwork(first, last, px)
|
QbzMyQbz.kioskDetailArtwork(first, last, px)
|
||||||
}
|
}
|
||||||
function openRow(r) {
|
function openRow(r) {
|
||||||
if (root.doc.selectMode) QbzMyQbz.detailToggleItemSelect(r.position)
|
if (root.doc.selectMode) QbzMyQbz.detailToggleItemSelect(r.position, false)
|
||||||
else QbzMyQbz.openItem(r.source, r.itemType, r.sourceItemId)
|
else QbzMyQbz.openItem(r.source, r.itemType, r.sourceItemId)
|
||||||
}
|
}
|
||||||
function entry(label, action) { return {label: root.t(label), action: action} }
|
function entry(label, action) { return {label: root.t(label), action: action} }
|
||||||
|
|||||||
@@ -171,7 +171,9 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
onClicked: function (mouse) {
|
onClicked: function (mouse) {
|
||||||
if (mouse.button !== Qt.LeftButton) return
|
if (mouse.button !== Qt.LeftButton) return
|
||||||
if (root.selectMode) QbzMyQbz.detailToggleItemSelect(root.itemPosition)
|
if (root.selectMode)
|
||||||
|
QbzMyQbz.detailToggleItemSelect(root.itemPosition,
|
||||||
|
(mouse.modifiers & Qt.ShiftModifier) !== 0)
|
||||||
else root.openThis()
|
else root.openThis()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -247,7 +249,10 @@ Rectangle {
|
|||||||
visible: root.selectMode
|
visible: root.selectMode
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
on: root.selected
|
on: root.selected
|
||||||
onToggled: QbzMyQbz.detailToggleItemSelect(root.itemPosition)
|
onToggled: function (mods) {
|
||||||
|
QbzMyQbz.detailToggleItemSelect(root.itemPosition,
|
||||||
|
(mods & Qt.ShiftModifier) !== 0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -295,6 +300,10 @@ Rectangle {
|
|||||||
MouseArea {
|
MouseArea {
|
||||||
id: artArea
|
id: artArea
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
// In select mode the whole row selects (Shift / Ctrl
|
||||||
|
// included): the artwork, title and artist stop playing
|
||||||
|
// or navigating so the click reaches the row body.
|
||||||
|
enabled: !root.selectMode
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: QbzMyQbz.playItem(root.sourceItemId)
|
onClicked: QbzMyQbz.playItem(root.sourceItemId)
|
||||||
@@ -321,6 +330,7 @@ Rectangle {
|
|||||||
MouseArea {
|
MouseArea {
|
||||||
id: titleArea
|
id: titleArea
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
enabled: !root.selectMode
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: root.openThis()
|
onClicked: root.openThis()
|
||||||
@@ -343,6 +353,7 @@ Rectangle {
|
|||||||
MouseArea {
|
MouseArea {
|
||||||
id: subArea
|
id: subArea
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
enabled: !root.selectMode
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: root.subtitleIsLink ? Qt.PointingHandCursor : Qt.ArrowCursor
|
cursorShape: root.subtitleIsLink ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||||
onClicked: {
|
onClicked: {
|
||||||
|
|||||||
@@ -1193,7 +1193,10 @@ Rectangle {
|
|||||||
// selection checkbox rather than bolt one on.
|
// selection checkbox rather than bolt one on.
|
||||||
selectMode: root.selectMode
|
selectMode: root.selectMode
|
||||||
selected: gcell.cSelected
|
selected: gcell.cSelected
|
||||||
onSelectToggled: QbzMyQbz.detailToggleItemSelect(gcell.cPosition)
|
onSelectToggled: function (mods) {
|
||||||
|
QbzMyQbz.detailToggleItemSelect(gcell.cPosition,
|
||||||
|
(mods & Qt.ShiftModifier) !== 0)
|
||||||
|
}
|
||||||
// Card body click, non-select mode only (the card
|
// Card body click, non-select mode only (the card
|
||||||
// routes a select-mode click to `selectToggled`).
|
// routes a select-mode click to `selectToggled`).
|
||||||
onOpenRequested: QbzMyQbz.openItem(gcell.cSource,
|
onOpenRequested: QbzMyQbz.openItem(gcell.cSource,
|
||||||
|
|||||||
@@ -162,9 +162,11 @@ pub mod qbz_myqbz_bridge {
|
|||||||
/// Leaving select mode clears the selection.
|
/// Leaving select mode clears the selection.
|
||||||
#[qinvokable]
|
#[qinvokable]
|
||||||
fn detail_toggle_select_mode(self: Pin<&mut QbzMyQbz>);
|
fn detail_toggle_select_mode(self: Pin<&mut QbzMyQbz>);
|
||||||
/// `position` is the 0-based persisted position (the stable key).
|
/// `position` is the 0-based persisted position (the stable key);
|
||||||
|
/// `shift` = the click held Shift, i.e. add the range from the last
|
||||||
|
/// plain click (myqbz_detail_qt::toggle_item_select).
|
||||||
#[qinvokable]
|
#[qinvokable]
|
||||||
fn detail_toggle_item_select(self: Pin<&mut QbzMyQbz>, position: i32);
|
fn detail_toggle_item_select(self: Pin<&mut QbzMyQbz>, position: i32, shift: bool);
|
||||||
/// Resets type/source/sort; the search text is left INTACT
|
/// Resets type/source/sort; the search text is left INTACT
|
||||||
/// (myqbz_detail.rs:540-541).
|
/// (myqbz_detail.rs:540-541).
|
||||||
#[qinvokable]
|
#[qinvokable]
|
||||||
@@ -472,8 +474,8 @@ impl qbz_myqbz_bridge::QbzMyQbz {
|
|||||||
crate::myqbz_detail_qt::toggle_select_mode();
|
crate::myqbz_detail_qt::toggle_select_mode();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn detail_toggle_item_select(self: Pin<&mut Self>, position: i32) {
|
pub fn detail_toggle_item_select(self: Pin<&mut Self>, position: i32, shift: bool) {
|
||||||
crate::myqbz_detail_qt::toggle_item_select(position);
|
crate::myqbz_detail_qt::toggle_item_select(position, shift);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn detail_reset_filters(self: Pin<&mut Self>) {
|
pub fn detail_reset_filters(self: Pin<&mut Self>) {
|
||||||
|
|||||||
@@ -463,6 +463,43 @@ static RESOLVE_CACHE: LazyLock<Mutex<HashMap<String, ResolvedItem>>> =
|
|||||||
/// the bulk actions in agreement with what the user sees (spec 02 §12 #13).
|
/// the bulk actions in agreement with what the user sees (spec 02 §12 #13).
|
||||||
static SELECTED: LazyLock<Mutex<HashSet<i32>>> = LazyLock::new(|| Mutex::new(HashSet::new()));
|
static SELECTED: LazyLock<Mutex<HashSet<i32>>> = LazyLock::new(|| Mutex::new(HashSet::new()));
|
||||||
|
|
||||||
|
/// The POSITION a Shift-click ranges from (the last plain or Ctrl click).
|
||||||
|
/// Dropped wherever `SELECTED` is cleared, so a range never measures from a
|
||||||
|
/// row of a previous derive. Lock order: DOC, then SELECTED, then this.
|
||||||
|
static SELECT_ANCHOR: Mutex<Option<i32>> = Mutex::new(None);
|
||||||
|
|
||||||
|
fn clear_select_anchor() {
|
||||||
|
*SELECT_ANCHOR.lock().unwrap() = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// One select-mode click over the rows as DRAWN (`order` = the rendered
|
||||||
|
/// positions, top to bottom). The rule every select-mode surface shares
|
||||||
|
/// (qml/controls/SelectionModel.qml): a plain or Ctrl click toggles `position`
|
||||||
|
/// and becomes the anchor; Shift ADDS every row from the anchor to `position`
|
||||||
|
/// and leaves the anchor where it is, so the range can be adjusted. Without a
|
||||||
|
/// drawn anchor a Shift-click is a plain toggle.
|
||||||
|
fn apply_item_click(
|
||||||
|
order: &[i32],
|
||||||
|
selected: &mut HashSet<i32>,
|
||||||
|
anchor: &mut Option<i32>,
|
||||||
|
position: i32,
|
||||||
|
shift: bool,
|
||||||
|
) {
|
||||||
|
if shift {
|
||||||
|
let from = anchor.and_then(|a| order.iter().position(|p| *p == a));
|
||||||
|
let to = order.iter().position(|p| *p == position);
|
||||||
|
if let (Some(from), Some(to)) = (from, to) {
|
||||||
|
let (lo, hi) = if from <= to { (from, to) } else { (to, from) };
|
||||||
|
selected.extend(order[lo..=hi].iter().copied());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !selected.remove(&position) {
|
||||||
|
selected.insert(position);
|
||||||
|
}
|
||||||
|
*anchor = Some(position);
|
||||||
|
}
|
||||||
|
|
||||||
/// Fingerprint of the last derive's filter/sort/search inputs — what decides
|
/// Fingerprint of the last derive's filter/sort/search inputs — what decides
|
||||||
/// whether a rebuild is a RE-DERIVE (clears the selection) or a row patch.
|
/// whether a rebuild is a RE-DERIVE (clears the selection) or a row patch.
|
||||||
static LAST_FILTER_KEY: Mutex<Option<String>> = Mutex::new(None);
|
static LAST_FILTER_KEY: Mutex<Option<String>> = Mutex::new(None);
|
||||||
@@ -1080,6 +1117,7 @@ fn rebuild(d: &mut DetailDoc) {
|
|||||||
// reference, the COUNT below is recomputed from the same set, so the
|
// reference, the COUNT below is recomputed from the same set, so the
|
||||||
// bulk bar can never claim a selection the rows do not show.
|
// bulk bar can never claim a selection the rows do not show.
|
||||||
SELECTED.lock().unwrap().clear();
|
SELECTED.lock().unwrap().clear();
|
||||||
|
clear_select_anchor();
|
||||||
}
|
}
|
||||||
|
|
||||||
let view = filtered_sorted(d);
|
let view = filtered_sorted(d);
|
||||||
@@ -1431,6 +1469,7 @@ fn reset() {
|
|||||||
// `apply` seeds this one back from it.
|
// `apply` seeds this one back from it.
|
||||||
OPEN_ROWS.lock().unwrap().clear();
|
OPEN_ROWS.lock().unwrap().clear();
|
||||||
SELECTED.lock().unwrap().clear();
|
SELECTED.lock().unwrap().clear();
|
||||||
|
clear_select_anchor();
|
||||||
*LAST_FILTER_KEY.lock().unwrap() = None;
|
*LAST_FILTER_KEY.lock().unwrap() = None;
|
||||||
// Close the persist gate until `apply` restores this collection's prefs.
|
// Close the persist gate until `apply` restores this collection's prefs.
|
||||||
PREFS_HYDRATED.store(false, Ordering::SeqCst);
|
PREFS_HYDRATED.store(false, Ordering::SeqCst);
|
||||||
@@ -2158,6 +2197,7 @@ pub(crate) fn toggle_select_mode() {
|
|||||||
let on = !d.select_mode;
|
let on = !d.select_mode;
|
||||||
if !on {
|
if !on {
|
||||||
SELECTED.lock().unwrap().clear();
|
SELECTED.lock().unwrap().clear();
|
||||||
|
clear_select_anchor();
|
||||||
for row in d.items.iter_mut() {
|
for row in d.items.iter_mut() {
|
||||||
row.selected = false;
|
row.selected = false;
|
||||||
}
|
}
|
||||||
@@ -2169,24 +2209,19 @@ pub(crate) fn toggle_select_mode() {
|
|||||||
publish(&doc);
|
publish(&doc);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Toggle one row's selection by its stable POSITION, then recount. A row patch
|
/// One select-mode click on a row, by its stable POSITION: a toggle, or with
|
||||||
/// — never a re-derive.
|
/// `shift` the range from the anchor (`apply_item_click`). Then every drawn
|
||||||
pub(crate) fn toggle_item_select(position: i32) {
|
/// row's tick and the count follow the set. A row patch — never a re-derive.
|
||||||
|
pub(crate) fn toggle_item_select(position: i32, shift: bool) {
|
||||||
let doc = with_doc(|d| {
|
let doc = with_doc(|d| {
|
||||||
let now_selected = {
|
let order: Vec<i32> = d.items.iter().map(|r| r.position).collect();
|
||||||
let mut set = SELECTED.lock().unwrap();
|
let mut set = SELECTED.lock().unwrap();
|
||||||
if set.contains(&position) {
|
let mut anchor = SELECT_ANCHOR.lock().unwrap();
|
||||||
set.remove(&position);
|
apply_item_click(&order, &mut set, &mut anchor, position, shift);
|
||||||
false
|
for row in d.items.iter_mut() {
|
||||||
} else {
|
row.selected = set.contains(&row.position);
|
||||||
set.insert(position);
|
|
||||||
true
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if let Some(row) = d.items.iter_mut().find(|r| r.position == position) {
|
|
||||||
row.selected = now_selected;
|
|
||||||
}
|
}
|
||||||
d.selected_count = SELECTED.lock().unwrap().len() as i32;
|
d.selected_count = set.len() as i32;
|
||||||
d.clone()
|
d.clone()
|
||||||
});
|
});
|
||||||
publish(&doc);
|
publish(&doc);
|
||||||
@@ -2197,6 +2232,7 @@ pub(crate) fn toggle_item_select(position: i32) {
|
|||||||
pub(crate) fn clear_selection() {
|
pub(crate) fn clear_selection() {
|
||||||
let doc = with_doc(|d| {
|
let doc = with_doc(|d| {
|
||||||
SELECTED.lock().unwrap().clear();
|
SELECTED.lock().unwrap().clear();
|
||||||
|
clear_select_anchor();
|
||||||
for row in d.items.iter_mut() {
|
for row in d.items.iter_mut() {
|
||||||
row.selected = false;
|
row.selected = false;
|
||||||
}
|
}
|
||||||
@@ -2374,6 +2410,7 @@ pub(crate) fn teardown() {
|
|||||||
RESOLVE_CACHE.lock().unwrap().clear();
|
RESOLVE_CACHE.lock().unwrap().clear();
|
||||||
OPEN_ROWS.lock().unwrap().clear();
|
OPEN_ROWS.lock().unwrap().clear();
|
||||||
SELECTED.lock().unwrap().clear();
|
SELECTED.lock().unwrap().clear();
|
||||||
|
clear_select_anchor();
|
||||||
*LAST_FILTER_KEY.lock().unwrap() = None;
|
*LAST_FILTER_KEY.lock().unwrap() = None;
|
||||||
PREFS_HYDRATED.store(false, Ordering::SeqCst);
|
PREFS_HYDRATED.store(false, Ordering::SeqCst);
|
||||||
let doc = with_doc(|d| {
|
let doc = with_doc(|d| {
|
||||||
@@ -2495,6 +2532,45 @@ pub(crate) fn bulk_action(id: String) {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
fn picked(set: &HashSet<i32>) -> Vec<i32> {
|
||||||
|
let mut out: Vec<i32> = set.iter().copied().collect();
|
||||||
|
out.sort_unstable();
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn select_click_ranges_over_the_drawn_order() {
|
||||||
|
// Rows drawn in a sorted order that is NOT the position order.
|
||||||
|
let order = [4, 0, 7, 2, 9, 5];
|
||||||
|
let mut set = HashSet::new();
|
||||||
|
let mut anchor = None;
|
||||||
|
apply_item_click(&order, &mut set, &mut anchor, 0, false);
|
||||||
|
assert_eq!(anchor, Some(0));
|
||||||
|
apply_item_click(&order, &mut set, &mut anchor, 9, true);
|
||||||
|
// 0, 7, 2, 9 are the drawn rows between them; 4 and 5 are outside.
|
||||||
|
assert_eq!(picked(&set), vec![0, 2, 7, 9]);
|
||||||
|
assert_eq!(anchor, Some(0), "a Shift-click keeps the anchor");
|
||||||
|
// Re-dragging the range upward only ever adds.
|
||||||
|
apply_item_click(&order, &mut set, &mut anchor, 4, true);
|
||||||
|
assert_eq!(picked(&set), vec![0, 2, 4, 7, 9]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn select_click_toggles_and_moves_the_anchor() {
|
||||||
|
let order = [1, 2, 3, 4];
|
||||||
|
let mut set = HashSet::new();
|
||||||
|
let mut anchor = None;
|
||||||
|
apply_item_click(&order, &mut set, &mut anchor, 3, false);
|
||||||
|
apply_item_click(&order, &mut set, &mut anchor, 3, false);
|
||||||
|
assert!(set.is_empty(), "a second plain click unticks the row");
|
||||||
|
assert_eq!(anchor, Some(3));
|
||||||
|
// Shift without a drawn anchor is a plain toggle that anchors there.
|
||||||
|
let mut gone = Some(42);
|
||||||
|
apply_item_click(&order, &mut set, &mut gone, 2, true);
|
||||||
|
assert_eq!(picked(&set), vec![2]);
|
||||||
|
assert_eq!(gone, Some(2));
|
||||||
|
}
|
||||||
|
|
||||||
fn item(
|
fn item(
|
||||||
position: i32,
|
position: i32,
|
||||||
title: &str,
|
title: &str,
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
pragma Singleton
|
||||||
|
import QtQuick
|
||||||
|
QtObject {
|
||||||
|
property var selectCalls: []
|
||||||
|
property var opened: []
|
||||||
|
property var played: []
|
||||||
|
function detailToggleItemSelect(position, shift) {
|
||||||
|
selectCalls = selectCalls.concat([{ "position": position, "shift": shift }])
|
||||||
|
}
|
||||||
|
function openItem(source, itemType, id) { opened = opened.concat([String(id)]) }
|
||||||
|
function openArtist(source, name, id) { opened = opened.concat(["artist:" + name]) }
|
||||||
|
function playItem(id) { played = played.concat([String(id)]) }
|
||||||
|
function detailToggleRowExpand(id) {}
|
||||||
|
}
|
||||||
@@ -13,3 +13,4 @@ singleton QbzArtist 1.0 QbzArtist.qml
|
|||||||
singleton QbzAlbum 1.0 QbzAlbum.qml
|
singleton QbzAlbum 1.0 QbzAlbum.qml
|
||||||
singleton QbzSearch 1.0 QbzSearch.qml
|
singleton QbzSearch 1.0 QbzSearch.qml
|
||||||
singleton QbzHome 1.0 QbzHome.qml
|
singleton QbzHome 1.0 QbzHome.qml
|
||||||
|
singleton QbzMyQbz 1.0 QbzMyQbz.qml
|
||||||
@@ -12,6 +12,7 @@ import "../../crates/qbz-qt/qml/rows"
|
|||||||
import "../../crates/qbz-qt/qml/cards"
|
import "../../crates/qbz-qt/qml/cards"
|
||||||
import "../../crates/qbz-qt/qml/views" as Views
|
import "../../crates/qbz-qt/qml/views" as Views
|
||||||
import "../../crates/qbz-qt/qml/views/local" as Local
|
import "../../crates/qbz-qt/qml/views/local" as Local
|
||||||
|
import "../../crates/qbz-qt/qml/views/myqbz" as MyQbz
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
@@ -119,6 +120,17 @@ Item {
|
|||||||
onToggled: function (mods) { root.selectCheckMods = root.selectCheckMods.concat([mods]) }
|
onToggled: function (mods) { root.selectCheckMods = root.selectCheckMods.concat([mods]) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---- a My QBZ collection row (selection lives in Rust) ----------------------
|
||||||
|
MyQbz.MyQbzDetailRow {
|
||||||
|
id: myqbzRow
|
||||||
|
x: 0; y: 820
|
||||||
|
width: 1000
|
||||||
|
selectMode: true
|
||||||
|
item: ({ "position": 3, "itemType": "album", "source": "qobuz",
|
||||||
|
"sourceItemId": "alb3", "title": "Collected record", "subtitle": "Collected band",
|
||||||
|
"subtitleIsLink": true, "artistId": "cb" })
|
||||||
|
}
|
||||||
|
|
||||||
TestCase {
|
TestCase {
|
||||||
name: "MultiSelect"
|
name: "MultiSelect"
|
||||||
when: windowShown
|
when: windowShown
|
||||||
@@ -182,6 +194,9 @@ Item {
|
|||||||
QbzArtist.opened = []
|
QbzArtist.opened = []
|
||||||
QbzAlbum.opened = []
|
QbzAlbum.opened = []
|
||||||
QbzHome.labels = []
|
QbzHome.labels = []
|
||||||
|
QbzMyQbz.selectCalls = []
|
||||||
|
QbzMyQbz.opened = []
|
||||||
|
QbzMyQbz.played = []
|
||||||
}
|
}
|
||||||
|
|
||||||
// A point of the row with nothing drawn over it but the body: the
|
// A point of the row with nothing drawn over it but the body: the
|
||||||
@@ -278,6 +293,18 @@ Item {
|
|||||||
compare(root.checkboxMods.length, 2)
|
compare(root.checkboxMods.length, 2)
|
||||||
compare(root.checkboxMods[1] & Qt.ShiftModifier, 0, "the keyboard toggle is a plain toggle")
|
compare(root.checkboxMods[1] & Qt.ShiftModifier, 0, "the keyboard toggle is a plain toggle")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function test_myqbz_row_targets_select_with_shift() {
|
||||||
|
var title = textItem(myqbzRow, "Collected record")
|
||||||
|
clickOn(title, 10, title.height / 2)
|
||||||
|
var subtitle = textItem(myqbzRow, "Collected band")
|
||||||
|
clickOn(subtitle, 10, subtitle.height / 2, Qt.ShiftModifier)
|
||||||
|
compare(QbzMyQbz.opened.length, 0, "nothing opens in select mode")
|
||||||
|
compare(QbzMyQbz.selectCalls.length, 2)
|
||||||
|
compare(QbzMyQbz.selectCalls[0].position, 3)
|
||||||
|
compare(QbzMyQbz.selectCalls[0].shift, false)
|
||||||
|
compare(QbzMyQbz.selectCalls[1].shift, true, "Shift reaches the Rust range")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- the selection rule itself ---------------------------------------------
|
// ---- the selection rule itself ---------------------------------------------
|
||||||
|
|||||||
Reference in new issue
Block a user