mirror of
https://github.com/slint-ui/slint.git
synced 2025-08-04 18:58:36 +00:00
Improve keyboard navigation of StandardListView
(#6955)
This commit is contained in:
parent
e45e6996d5
commit
9b9a34561d
2 changed files with 160 additions and 5 deletions
|
@ -32,6 +32,7 @@ component StandardListViewBase inherits ListView {
|
|||
private property <length> into-view-item-y: root.item-y(root.into-view-item);
|
||||
private property <length> current-item-y: root.item-y(root.focus-item);
|
||||
private property <int> focus-item: 0;
|
||||
private property <bool> has-item-been-selected: false;
|
||||
|
||||
accessible-delegate-focus: root.focus-item;
|
||||
accessible-item-count: root.model.length;
|
||||
|
@ -40,10 +41,18 @@ component StandardListViewBase inherits ListView {
|
|||
return min(root.model.length - 1, max(0, round(-root.viewport-y / root.item-height)));
|
||||
}
|
||||
|
||||
pure function last-visible-item() -> int {
|
||||
return min(root.model.length - 1, max(0, round((-root.viewport-y + root.height - root.item-height) / root.item-height)));
|
||||
}
|
||||
|
||||
pure function item-y(index: int) -> length {
|
||||
return root.viewport-y + index * root.item-height;
|
||||
}
|
||||
|
||||
pure function item-at-y(y: length) -> int {
|
||||
return min(root.model.length - 1, max(0, round(y / root.item-height)));
|
||||
}
|
||||
|
||||
function bring-into-view(index: int) {
|
||||
if (index < 0 || index >= model.length) {
|
||||
return;
|
||||
|
@ -64,8 +73,32 @@ component StandardListViewBase inherits ListView {
|
|||
root.set-focus-item(root.focus-item - 1);
|
||||
}
|
||||
|
||||
protected function focus-page-up() {
|
||||
if root.focus-item != root.first-visible-item() {
|
||||
root.set-focus-item(root.first-visible-item())
|
||||
} else {
|
||||
root.set-focus-item(root.item-at-y(root.item-y(root.first-visible-item()) - root.height));
|
||||
}
|
||||
}
|
||||
|
||||
protected function focus-first() {
|
||||
root.set-focus-item(0);
|
||||
}
|
||||
|
||||
protected function focus-down() {
|
||||
root.set-focus-item(root.focus-item + 1);
|
||||
root.set-focus-item(root.focus-item + 1);
|
||||
}
|
||||
|
||||
protected function focus-page-down() {
|
||||
if root.focus-item != root.last-visible-item() {
|
||||
root.set-focus-item(root.last-visible-item())
|
||||
} else {
|
||||
root.set-focus-item(root.item-at-y(root.item-y(root.last-visible-item()) + root.height));
|
||||
}
|
||||
}
|
||||
|
||||
protected function focus-last() {
|
||||
root.set-focus-item(root.model.length - 1);
|
||||
}
|
||||
|
||||
protected function select-focus-item() {
|
||||
|
@ -73,7 +106,10 @@ component StandardListViewBase inherits ListView {
|
|||
}
|
||||
|
||||
protected function focus-current-item() {
|
||||
root.focus-item = max(0, root.current-item);
|
||||
if root.current-item == -1 && !root.has-item-been-selected && root.model.length > 0 {
|
||||
root.set-current-item(0);
|
||||
}
|
||||
root.has-item-been-selected = true;
|
||||
|
||||
if (root.current-item-y + root.item-height < 0
|
||||
|| root.current-item-y > root.height) {
|
||||
|
@ -81,6 +117,14 @@ component StandardListViewBase inherits ListView {
|
|||
}
|
||||
}
|
||||
|
||||
protected function toggle-focus-item-selection() {
|
||||
if (root.current-item == root.focus-item) {
|
||||
root.current-item = -1;
|
||||
} else {
|
||||
root.select-focus-item();
|
||||
}
|
||||
}
|
||||
|
||||
protected function set-focus-item(index: int) {
|
||||
root.focus-item = min(root.model.length - 1, max(0, index));
|
||||
root.bring-into-view(root.focus-item);
|
||||
|
@ -127,12 +171,46 @@ export component StandardListView inherits StandardListViewBase {
|
|||
key-pressed(event) => {
|
||||
if (event.text == Key.UpArrow) {
|
||||
root.focus-up();
|
||||
if (!event.modifiers.control) {
|
||||
root.select-focus-item();
|
||||
}
|
||||
return accept;
|
||||
} else if (event.text == Key.PageUp) {
|
||||
root.focus-page-up();
|
||||
if (!event.modifiers.control) {
|
||||
root.select-focus-item();
|
||||
}
|
||||
return accept;
|
||||
} else if (event.text == Key.Home) {
|
||||
root.focus-first();
|
||||
if (!event.modifiers.control) {
|
||||
root.select-focus-item();
|
||||
}
|
||||
return accept;
|
||||
} else if (event.text == Key.DownArrow) {
|
||||
root.focus-down();
|
||||
if (!event.modifiers.control) {
|
||||
root.select-focus-item();
|
||||
}
|
||||
return accept;
|
||||
}else if (event.text == Key.Return) {
|
||||
root.select-focus-item();
|
||||
} else if (event.text == Key.PageDown) {
|
||||
root.focus-page-down();
|
||||
if (!event.modifiers.control) {
|
||||
root.select-focus-item();
|
||||
}
|
||||
return accept;
|
||||
} else if (event.text == Key.End) {
|
||||
root.focus-last();
|
||||
if (!event.modifiers.control) {
|
||||
root.select-focus-item();
|
||||
}
|
||||
return accept;
|
||||
} else if (event.text == Key.Space) {
|
||||
if (event.modifiers.control) {
|
||||
root.toggle-focus-item-selection();
|
||||
} else {
|
||||
root.select-focus-item();
|
||||
}
|
||||
return accept;
|
||||
}
|
||||
reject
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
import { StandardListView } from "std-widgets.slint";
|
||||
|
||||
TestCase := Rectangle {
|
||||
export component TestCase inherits Window {
|
||||
callback set-current-item(int);
|
||||
|
||||
out property <int> count: list.model.length;
|
||||
|
@ -15,6 +15,7 @@ TestCase := Rectangle {
|
|||
{ text: "Item 3" },
|
||||
];
|
||||
in-out property <int> current-item <=> list.current-item;
|
||||
out property has-focus <=> list.has-focus;
|
||||
|
||||
list := StandardListView {
|
||||
model: root.model;
|
||||
|
@ -33,6 +34,9 @@ TestCase := Rectangle {
|
|||
/*
|
||||
|
||||
```rust
|
||||
use slint::platform::Key;
|
||||
use slint::SharedString;
|
||||
|
||||
let instance = TestCase::new().unwrap();
|
||||
assert_eq!(instance.get_count(), 3);
|
||||
assert_eq!(instance.get_current_item(), -1);
|
||||
|
@ -47,5 +51,78 @@ instance.invoke_set_current_item(1);
|
|||
assert_eq!(instance.get_callback_current_item(), -1);
|
||||
assert_eq!(instance.get_current_item(), 1);
|
||||
|
||||
instance.invoke_set_current_item(0);
|
||||
assert_eq!(instance.get_has_focus(), false);
|
||||
|
||||
// Focus the listview
|
||||
slint_testing::send_keyboard_string_sequence(&instance, &SharedString::from(Key::Tab));
|
||||
assert_eq!(instance.get_has_focus(), true);
|
||||
assert_eq!(instance.get_current_item(), 0);
|
||||
|
||||
// Up and Down arrow keys move the selection
|
||||
slint_testing::send_keyboard_string_sequence(&instance, &SharedString::from(Key::UpArrow));
|
||||
assert_eq!(instance.get_current_item(), 0);
|
||||
slint_testing::send_keyboard_string_sequence(&instance, &SharedString::from(Key::DownArrow));
|
||||
assert_eq!(instance.get_current_item(), 1);
|
||||
slint_testing::send_keyboard_string_sequence(&instance, &SharedString::from(Key::DownArrow));
|
||||
assert_eq!(instance.get_current_item(), 2);
|
||||
slint_testing::send_keyboard_string_sequence(&instance, &SharedString::from(Key::DownArrow));
|
||||
assert_eq!(instance.get_current_item(), 2);
|
||||
slint_testing::send_keyboard_string_sequence(&instance, &SharedString::from(Key::UpArrow));
|
||||
assert_eq!(instance.get_current_item(), 1);
|
||||
|
||||
// Pressing the Home key selects the first item
|
||||
instance.invoke_set_current_item(2);
|
||||
slint_testing::send_keyboard_string_sequence(&instance, &SharedString::from(Key::Home));
|
||||
assert_eq!(instance.get_current_item(), 0);
|
||||
|
||||
// Pressing the End key selects the last item
|
||||
slint_testing::send_keyboard_string_sequence(&instance, &SharedString::from(Key::End));
|
||||
assert_eq!(instance.get_current_item(), 2);
|
||||
|
||||
// Pressing the space bar on the selected item does not deselect it
|
||||
slint_testing::send_keyboard_string_sequence(&instance, &SharedString::from(Key::Space));
|
||||
assert_eq!(instance.get_current_item(), 2);
|
||||
|
||||
// Control+Space deselects the currently focused item
|
||||
slint_testing::send_keyboard_char(&instance, Key::Control.into(), true);
|
||||
slint_testing::send_keyboard_string_sequence(&instance, &SharedString::from(Key::Space));
|
||||
assert_eq!(instance.get_current_item(), -1);
|
||||
|
||||
// Control+Space selects the currently focused item
|
||||
slint_testing::send_keyboard_char(&instance, Key::Control.into(), true);
|
||||
slint_testing::send_keyboard_string_sequence(&instance, &SharedString::from(Key::Space));
|
||||
assert_eq!(instance.get_current_item(), 2);
|
||||
|
||||
// Control+UpArrow moves the focus up one item but not the selection
|
||||
slint_testing::send_keyboard_char(&instance, Key::Control.into(), true);
|
||||
slint_testing::send_keyboard_string_sequence(&instance, &SharedString::from(Key::UpArrow));
|
||||
assert_eq!(instance.get_current_item(), 2);
|
||||
|
||||
// Pressing the space bar selects the currently focused item if it is not
|
||||
slint_testing::send_keyboard_string_sequence(&instance, &SharedString::from(Key::Space));
|
||||
assert_eq!(instance.get_current_item(), 1);
|
||||
|
||||
// Control+DownArrow moves the focus down one item but not the selection
|
||||
slint_testing::send_keyboard_char(&instance, Key::Control.into(), true);
|
||||
slint_testing::send_keyboard_string_sequence(&instance, &SharedString::from(Key::DownArrow));
|
||||
assert_eq!(instance.get_current_item(), 1);
|
||||
slint_testing::send_keyboard_string_sequence(&instance, &SharedString::from(Key::Space));
|
||||
assert_eq!(instance.get_current_item(), 2);
|
||||
|
||||
// Control+Home moves the focus to the first item but not the selection
|
||||
slint_testing::send_keyboard_char(&instance, Key::Control.into(), true);
|
||||
slint_testing::send_keyboard_string_sequence(&instance, &SharedString::from(Key::Home));
|
||||
assert_eq!(instance.get_current_item(), 2);
|
||||
slint_testing::send_keyboard_string_sequence(&instance, &SharedString::from(Key::Space));
|
||||
assert_eq!(instance.get_current_item(), 0);
|
||||
|
||||
// Control+End moves the focus to the last item but not the selection
|
||||
slint_testing::send_keyboard_char(&instance, Key::Control.into(), true);
|
||||
slint_testing::send_keyboard_string_sequence(&instance, &SharedString::from(Key::End));
|
||||
assert_eq!(instance.get_current_item(), 0);
|
||||
slint_testing::send_keyboard_string_sequence(&instance, &SharedString::from(Key::Space));
|
||||
assert_eq!(instance.get_current_item(), 2);
|
||||
```
|
||||
|
||||
*/
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue