2021-04-14 07:54:19 +08:00
|
|
|
use zero_ui::core::app::HeadlessApp;
|
2021-04-24 05:41:28 +08:00
|
|
|
use zero_ui::core::focus::{FocusChangedArgs, FocusChangedCause, ReturnFocusChangedArgs};
|
2021-04-15 08:52:19 +08:00
|
|
|
use zero_ui::core::gesture::HeadlessAppGestureExt;
|
2021-04-14 07:54:19 +08:00
|
|
|
use zero_ui::core::keyboard::HeadlessAppKeyboardExt;
|
2021-05-13 06:00:43 +08:00
|
|
|
use zero_ui::core::window::{HeadlessAppWindowExt, WindowId};
|
2021-04-14 07:54:19 +08:00
|
|
|
use zero_ui::prelude::*;
|
2021-06-01 08:47:00 +08:00
|
|
|
use zero_ui_core::event::EventBuffer;
|
2021-06-04 11:31:21 +08:00
|
|
|
use zero_ui_core::focus::{FocusChangedEvent, ReturnFocusChangedEvent};
|
2021-04-24 05:41:28 +08:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
pub fn first_and_last_window_events() {
|
|
|
|
let buttons = widgets![button! { content = text("Button 0") }, button! { content = text("Button 1") },];
|
|
|
|
|
|
|
|
let root_id = WidgetId::new_unique();
|
|
|
|
let stack_id = WidgetId::new_unique();
|
|
|
|
let button_0_id = buttons.widget_id(0);
|
|
|
|
|
|
|
|
let mut app = TestApp::new_w(window! {
|
|
|
|
content = v_stack!(id = stack_id; items = buttons);
|
|
|
|
root_id;
|
|
|
|
});
|
|
|
|
let root_path = WidgetPath::new(app.window_id, [root_id]);
|
|
|
|
let button_0_path = WidgetPath::new(app.window_id, [root_id, stack_id, button_0_id]);
|
|
|
|
|
|
|
|
let events = app.take_focus_changed();
|
|
|
|
assert_eq!(2, events.len());
|
|
|
|
|
2021-05-18 09:24:48 +08:00
|
|
|
// "recover" focus to the focused window root.
|
2021-04-24 05:41:28 +08:00
|
|
|
assert!(events[0].prev_focus.is_none());
|
|
|
|
assert_eq!(Some(root_path.clone()), events[0].new_focus);
|
|
|
|
assert_eq!(FocusChangedCause::Recovery, events[0].cause);
|
|
|
|
assert!(!events[0].highlight);
|
|
|
|
|
|
|
|
// root is a scope that auto-advanced focus to first focusable child.
|
|
|
|
assert_eq!(Some(root_path), events[1].prev_focus);
|
|
|
|
assert_eq!(Some(button_0_path.clone()), events[1].new_focus);
|
|
|
|
assert_eq!(FocusChangedCause::ScopeGotFocus(false), events[1].cause);
|
|
|
|
assert!(!events[1].highlight);
|
|
|
|
|
|
|
|
let events = app.take_return_focus_changed();
|
|
|
|
assert_eq!(1, events.len());
|
|
|
|
|
|
|
|
// the window remembers is previous focused descendant.
|
|
|
|
assert!(events[0].prev_return.is_none());
|
|
|
|
assert_eq!(root_id, events[0].scope_id);
|
|
|
|
assert_eq!(Some(button_0_path.clone()), events[0].new_return);
|
|
|
|
|
|
|
|
/*
|
|
|
|
Last Events
|
|
|
|
*/
|
|
|
|
|
|
|
|
app.set_shutdown_on_last_close(false);
|
2021-05-14 12:12:14 +08:00
|
|
|
app.close_main_window();
|
2021-04-24 05:41:28 +08:00
|
|
|
|
|
|
|
let events = app.take_focus_changed();
|
|
|
|
assert_eq!(1, events.len());
|
|
|
|
|
|
|
|
// "recover" to focus nothing.
|
|
|
|
assert_eq!(Some(button_0_path.clone()), events[0].prev_focus);
|
|
|
|
assert!(events[0].new_focus.is_none());
|
|
|
|
assert_eq!(FocusChangedCause::Recovery, events[0].cause);
|
|
|
|
assert!(!events[0].highlight);
|
|
|
|
|
|
|
|
let events = app.take_return_focus_changed();
|
|
|
|
assert_eq!(1, events.len());
|
|
|
|
|
|
|
|
// cleanup return focus.
|
|
|
|
assert_eq!(Some(button_0_path), events[0].prev_return);
|
|
|
|
assert!(events[0].new_return.is_none());
|
|
|
|
}
|
2021-04-14 07:54:19 +08:00
|
|
|
|
|
|
|
#[test]
|
2021-04-16 07:08:12 +08:00
|
|
|
pub fn window_tab_cycle_index_auto() {
|
2021-04-16 09:38:04 +08:00
|
|
|
// default window! cycles TAB navigation.
|
2021-04-16 07:08:12 +08:00
|
|
|
t(|_| TabIndex::AUTO);
|
|
|
|
t(TabIndex);
|
|
|
|
t(|i| TabIndex(TabIndex::AUTO.0 - i - 1));
|
2021-04-15 08:52:19 +08:00
|
|
|
|
2021-04-16 07:08:12 +08:00
|
|
|
fn t(make_index: impl FnMut(u32) -> TabIndex) {
|
|
|
|
// all TAB navigation must respect the `tab_index` value
|
|
|
|
// that by default is AUTO, but can be not in the same order
|
|
|
|
// as the widgets are declared.
|
|
|
|
let tab_ids: Vec<_> = (0..3).map(make_index).collect();
|
|
|
|
|
|
|
|
let buttons = widgets![
|
|
|
|
button! { content = text("Button 0"); tab_index = tab_ids[0] },
|
|
|
|
button! { content = text("Button 1"); tab_index = tab_ids[1] },
|
|
|
|
button! { content = text("Button 2"); tab_index = tab_ids[2] },
|
|
|
|
];
|
|
|
|
// we collect the widget_id values in the TAB navigation order.
|
|
|
|
let mut ids: Vec<_> = (0..3).map(|i| (buttons.widget_id(i), tab_ids[i])).collect();
|
|
|
|
ids.sort_by_key(|(_, ti)| *ti);
|
|
|
|
let ids: Vec<_> = ids.into_iter().map(|(id, _)| id).collect();
|
|
|
|
|
|
|
|
let mut app = TestApp::new(v_stack(buttons));
|
|
|
|
|
2021-04-16 09:38:04 +08:00
|
|
|
// advance normally.
|
2021-04-16 07:08:12 +08:00
|
|
|
assert_eq!(Some(ids[0]), app.focused());
|
|
|
|
app.press_tab();
|
|
|
|
assert_eq!(Some(ids[1]), app.focused());
|
|
|
|
app.press_tab();
|
|
|
|
assert_eq!(Some(ids[2]), app.focused());
|
|
|
|
// then cycles back.
|
|
|
|
app.press_tab();
|
|
|
|
assert_eq!(Some(ids[0]), app.focused());
|
|
|
|
|
|
|
|
// same backwards.
|
|
|
|
app.press_shift_tab();
|
|
|
|
assert_eq!(Some(ids[2]), app.focused());
|
|
|
|
app.press_shift_tab();
|
|
|
|
assert_eq!(Some(ids[1]), app.focused());
|
|
|
|
app.press_shift_tab();
|
|
|
|
assert_eq!(Some(ids[0]), app.focused());
|
|
|
|
// cycles back.
|
|
|
|
app.press_shift_tab();
|
|
|
|
assert_eq!(Some(ids[2]), app.focused());
|
|
|
|
}
|
2021-04-15 08:52:19 +08:00
|
|
|
}
|
2021-04-14 07:54:19 +08:00
|
|
|
|
2021-04-14 11:38:26 +08:00
|
|
|
#[test]
|
2021-04-16 03:28:30 +08:00
|
|
|
pub fn window_tab_cycle_and_alt_scope() {
|
2021-04-15 08:52:19 +08:00
|
|
|
// default window! with an ALT scope, TAB navigation cycles
|
|
|
|
// by default in the ALT scope too.
|
|
|
|
|
2021-04-16 09:38:04 +08:00
|
|
|
t(|_| TabIndex::AUTO);
|
|
|
|
t(TabIndex);
|
|
|
|
t(|i| TabIndex(TabIndex::AUTO.0 - i - 1));
|
|
|
|
|
|
|
|
fn t(make_index: impl FnMut(u32) -> TabIndex) {
|
|
|
|
let tab_ids: Vec<_> = (0..5).map(make_index).collect();
|
|
|
|
|
|
|
|
let buttons = widgets![
|
|
|
|
button! { content = text("Button 0"); tab_index = tab_ids[0] },
|
|
|
|
button! { content = text("Button 1"); tab_index = tab_ids[1] },
|
|
|
|
];
|
|
|
|
let mut ids: Vec<_> = (0..2).map(|i| (buttons.widget_id(i), tab_ids[i])).collect();
|
|
|
|
ids.sort_by_key(|(_, ti)| *ti);
|
|
|
|
let ids: Vec<_> = ids.into_iter().map(|(id, _)| id).collect();
|
|
|
|
|
|
|
|
let alt_buttons = widgets![
|
|
|
|
button! { content = text("Alt 0"); tab_index = tab_ids[2] },
|
|
|
|
button! { content = text("Alt 1"); tab_index = tab_ids[3] },
|
|
|
|
button! { content = text("Alt 2"); tab_index = tab_ids[4] },
|
|
|
|
];
|
|
|
|
let mut alt_ids: Vec<_> = (0..3).map(|i| (alt_buttons.widget_id(i), tab_ids[i + 2])).collect();
|
|
|
|
alt_ids.sort_by_key(|(_, ti)| *ti);
|
|
|
|
let alt_ids: Vec<_> = alt_ids.into_iter().map(|(id, _)| id).collect();
|
|
|
|
|
|
|
|
let mut app = TestApp::new(v_stack(widgets![
|
|
|
|
h_stack! {
|
|
|
|
alt_focus_scope = true;
|
|
|
|
items = alt_buttons;
|
|
|
|
},
|
|
|
|
v_stack(buttons)
|
|
|
|
]));
|
|
|
|
|
|
|
|
// cycle in the window scope does not enter the ALT scope.
|
|
|
|
assert_eq!(Some(ids[0]), app.focused());
|
|
|
|
app.press_tab();
|
|
|
|
assert_eq!(Some(ids[1]), app.focused());
|
|
|
|
app.press_tab();
|
|
|
|
assert_eq!(Some(ids[0]), app.focused());
|
|
|
|
// and back.
|
|
|
|
app.press_shift_tab();
|
|
|
|
assert_eq!(Some(ids[1]), app.focused());
|
|
|
|
app.press_shift_tab();
|
|
|
|
assert_eq!(Some(ids[0]), app.focused());
|
|
|
|
|
|
|
|
// make the "Button 1" be the return focus from the ALT scope.
|
|
|
|
app.press_tab();
|
|
|
|
assert_eq!(Some(ids[1]), app.focused());
|
|
|
|
|
|
|
|
// goes to the ALT scope.
|
|
|
|
app.press_alt();
|
|
|
|
assert_eq!(Some(alt_ids[0]), app.focused());
|
|
|
|
|
|
|
|
// cycle in the ALT scope.
|
|
|
|
app.press_tab();
|
|
|
|
assert_eq!(Some(alt_ids[1]), app.focused());
|
|
|
|
app.press_tab();
|
|
|
|
assert_eq!(Some(alt_ids[2]), app.focused());
|
|
|
|
app.press_tab();
|
|
|
|
assert_eq!(Some(alt_ids[0]), app.focused());
|
|
|
|
// and back.
|
|
|
|
app.press_shift_tab();
|
|
|
|
assert_eq!(Some(alt_ids[2]), app.focused());
|
|
|
|
app.press_shift_tab();
|
|
|
|
assert_eq!(Some(alt_ids[1]), app.focused());
|
|
|
|
app.press_shift_tab();
|
|
|
|
assert_eq!(Some(alt_ids[0]), app.focused());
|
|
|
|
|
|
|
|
// return to the window scope that focus on the "Button 1".
|
|
|
|
app.press_esc();
|
|
|
|
assert_eq!(Some(ids[1]), app.focused());
|
|
|
|
|
|
|
|
// we are back to cycling the window scope.
|
|
|
|
app.press_tab();
|
|
|
|
assert_eq!(Some(ids[0]), app.focused());
|
|
|
|
app.press_shift_tab();
|
|
|
|
assert_eq!(Some(ids[1]), app.focused());
|
|
|
|
|
|
|
|
// also can return from ALT scope by pressing ALT again.
|
|
|
|
app.press_alt();
|
|
|
|
assert_eq!(Some(alt_ids[0]), app.focused());
|
|
|
|
app.press_alt();
|
|
|
|
assert_eq!(Some(ids[1]), app.focused());
|
|
|
|
}
|
2021-04-15 08:52:19 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2021-04-16 03:28:30 +08:00
|
|
|
pub fn window_tab_contained() {
|
2021-04-16 05:32:51 +08:00
|
|
|
// TabNav::Contained stops at the last item and back
|
|
|
|
// the root scope behaves just like any other Contained scope.
|
|
|
|
window_tab_contained_and_continue(TabNav::Contained);
|
2021-04-15 08:52:19 +08:00
|
|
|
}
|
|
|
|
#[test]
|
2021-04-16 03:28:30 +08:00
|
|
|
pub fn window_tab_continue() {
|
2021-04-16 05:32:51 +08:00
|
|
|
// TabNav::Continue in the root scope behaves like a Contained
|
|
|
|
// scope because there is no outer-scope to continue to.
|
|
|
|
window_tab_contained_and_continue(TabNav::Continue);
|
2021-04-15 08:52:19 +08:00
|
|
|
}
|
2021-04-16 05:32:51 +08:00
|
|
|
fn window_tab_contained_and_continue(tab_nav: TabNav) {
|
2021-04-16 09:38:04 +08:00
|
|
|
t(tab_nav, |_| TabIndex::AUTO);
|
|
|
|
t(tab_nav, TabIndex);
|
|
|
|
t(tab_nav, |i| TabIndex(TabIndex::AUTO.0 - i - 1));
|
|
|
|
|
|
|
|
fn t(tab_nav: TabNav, make_index: impl FnMut(u32) -> TabIndex) {
|
|
|
|
let tab_ids: Vec<_> = (0..3).map(make_index).collect();
|
|
|
|
|
|
|
|
let buttons = widgets![
|
|
|
|
button! { content = text("Button 0"); tab_index = tab_ids[0] },
|
|
|
|
button! { content = text("Button 1"); tab_index = tab_ids[1] },
|
|
|
|
button! { content = text("Button 2"); tab_index = tab_ids[2] },
|
|
|
|
];
|
|
|
|
// we collect the widget_id values in the TAB navigation order.
|
|
|
|
let mut ids: Vec<_> = (0..3).map(|i| (buttons.widget_id(i), tab_ids[i])).collect();
|
|
|
|
ids.sort_by_key(|(_, ti)| *ti);
|
|
|
|
let ids: Vec<_> = ids.into_iter().map(|(id, _)| id).collect();
|
|
|
|
|
|
|
|
let mut app = TestApp::new_w(window! {
|
|
|
|
tab_nav;
|
|
|
|
content = v_stack(buttons);
|
|
|
|
});
|
|
|
|
|
|
|
|
// navigates normally forward.
|
|
|
|
assert_eq!(Some(ids[0]), app.focused());
|
|
|
|
app.press_tab();
|
|
|
|
assert_eq!(Some(ids[1]), app.focused());
|
|
|
|
app.press_tab();
|
|
|
|
assert_eq!(Some(ids[2]), app.focused());
|
|
|
|
// but after reaching the end does not move.
|
|
|
|
app.press_tab();
|
|
|
|
assert_eq!(Some(ids[2]), app.focused());
|
|
|
|
|
|
|
|
// same backwards.
|
|
|
|
app.press_shift_tab();
|
|
|
|
assert_eq!(Some(ids[1]), app.focused());
|
|
|
|
app.press_shift_tab();
|
|
|
|
assert_eq!(Some(ids[0]), app.focused());
|
|
|
|
app.press_shift_tab();
|
|
|
|
assert_eq!(Some(ids[0]), app.focused());
|
|
|
|
}
|
2021-04-15 08:52:19 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2021-04-16 03:28:30 +08:00
|
|
|
pub fn window_tab_once() {
|
2021-04-15 08:52:19 +08:00
|
|
|
// we already start focused inside so Once==None in root widgets.
|
2021-04-16 05:32:51 +08:00
|
|
|
window_tab_once_and_none(TabNav::Once);
|
2021-04-15 08:52:19 +08:00
|
|
|
}
|
|
|
|
#[test]
|
2021-04-16 03:28:30 +08:00
|
|
|
pub fn window_tab_none() {
|
2021-04-15 08:52:19 +08:00
|
|
|
// we already start focused inside so Once==None in root widgets.
|
2021-04-16 05:32:51 +08:00
|
|
|
window_tab_once_and_none(TabNav::None);
|
2021-04-15 08:52:19 +08:00
|
|
|
}
|
2021-04-16 05:32:51 +08:00
|
|
|
fn window_tab_once_and_none(tab_nav: TabNav) {
|
2021-04-16 09:38:04 +08:00
|
|
|
t(tab_nav, |_| TabIndex::AUTO);
|
|
|
|
t(tab_nav, TabIndex);
|
|
|
|
t(tab_nav, |i| TabIndex(TabIndex::AUTO.0 - i - 1));
|
|
|
|
|
|
|
|
fn t(tab_nav: TabNav, make_index: impl FnMut(u32) -> TabIndex) {
|
|
|
|
let tab_ids: Vec<_> = (0..3).map(make_index).collect();
|
|
|
|
|
|
|
|
let buttons = widgets![
|
|
|
|
button! { content = text("Button 0"); tab_index = tab_ids[0] },
|
|
|
|
button! { content = text("Button 1"); tab_index = tab_ids[1] },
|
|
|
|
button! { content = text("Button 2"); tab_index = tab_ids[2] },
|
|
|
|
];
|
|
|
|
// we collect the widget_id values in the TAB navigation order.
|
|
|
|
let mut ids: Vec<_> = (0..3).map(|i| (buttons.widget_id(i), tab_ids[i])).collect();
|
|
|
|
ids.sort_by_key(|(_, ti)| *ti);
|
|
|
|
let ids: Vec<_> = ids.into_iter().map(|(id, _)| id).collect();
|
|
|
|
|
|
|
|
let mut app = TestApp::new_w(window! {
|
|
|
|
content = v_stack(buttons);
|
|
|
|
tab_nav;
|
|
|
|
});
|
|
|
|
|
|
|
|
assert_eq!(Some(ids[0]), app.focused());
|
|
|
|
app.press_tab();
|
|
|
|
assert_eq!(Some(ids[0]), app.focused());
|
|
|
|
app.press_shift_tab();
|
|
|
|
assert_eq!(Some(ids[0]), app.focused());
|
|
|
|
|
|
|
|
app.focus(ids[1]);
|
|
|
|
|
|
|
|
app.press_tab();
|
|
|
|
assert_eq!(Some(ids[1]), app.focused());
|
|
|
|
app.press_shift_tab();
|
|
|
|
assert_eq!(Some(ids[1]), app.focused());
|
|
|
|
}
|
2021-04-15 08:52:19 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2021-04-16 03:28:30 +08:00
|
|
|
pub fn two_continue_scopes_in_tab_cycle_window() {
|
2021-04-15 08:52:19 +08:00
|
|
|
// TabNav::Continue in non-root widget scopes that are
|
2021-04-15 11:14:37 +08:00
|
|
|
// FocusScopeOnFocus::FirstDescendant just behaves like normal containers.
|
2021-04-16 05:32:51 +08:00
|
|
|
two_continue_scopes_or_containers_in_tab_cycle_window(true);
|
2021-04-15 08:52:19 +08:00
|
|
|
}
|
|
|
|
#[test]
|
2021-04-16 03:28:30 +08:00
|
|
|
pub fn two_containers_in_tab_cycle_window() {
|
|
|
|
// the containers are not focus scopes, but they naturally
|
2021-04-16 10:40:56 +08:00
|
|
|
// behave like one with TabNav::Continue, as long as the tab-indexes
|
|
|
|
// are linear or AUTO.
|
2021-04-16 05:32:51 +08:00
|
|
|
two_continue_scopes_or_containers_in_tab_cycle_window(false);
|
2021-04-15 08:52:19 +08:00
|
|
|
}
|
2021-04-16 05:32:51 +08:00
|
|
|
fn two_continue_scopes_or_containers_in_tab_cycle_window(focus_scope: bool) {
|
2021-04-15 08:52:19 +08:00
|
|
|
let buttons_a = widgets![
|
|
|
|
button! { content = text("Button 0") },
|
|
|
|
button! { content = text("Button 1") },
|
|
|
|
button! { content = text("Button 2") },
|
|
|
|
];
|
|
|
|
let ids_a: Vec<_> = (0..3).map(|i| buttons_a.widget_id(i)).collect();
|
|
|
|
|
|
|
|
let buttons_b = widgets![
|
|
|
|
button! { content = text("Button 0") },
|
|
|
|
button! { content = text("Button 1") },
|
|
|
|
button! { content = text("Button 2") },
|
|
|
|
];
|
|
|
|
let ids_b: Vec<_> = (0..3).map(|i| buttons_b.widget_id(i)).collect();
|
|
|
|
|
|
|
|
let a = v_stack! {
|
|
|
|
items = buttons_a;
|
|
|
|
focus_scope;
|
|
|
|
tab_nav = TabNav::Continue;
|
|
|
|
};
|
|
|
|
let b = v_stack! {
|
|
|
|
items = buttons_b;
|
|
|
|
focus_scope;
|
|
|
|
tab_nav = TabNav::Continue;
|
|
|
|
};
|
|
|
|
let mut app = TestApp::new(h_stack(widgets![a, b]));
|
|
|
|
|
2021-04-16 05:32:51 +08:00
|
|
|
// forward nav goes through the first stack.
|
2021-04-15 08:52:19 +08:00
|
|
|
assert_eq!(Some(ids_a[0]), app.focused());
|
|
|
|
app.press_tab();
|
|
|
|
assert_eq!(Some(ids_a[1]), app.focused());
|
|
|
|
app.press_tab();
|
|
|
|
assert_eq!(Some(ids_a[2]), app.focused());
|
|
|
|
app.press_tab();
|
2021-04-16 05:32:51 +08:00
|
|
|
// and then the second stack.
|
2021-04-15 08:52:19 +08:00
|
|
|
assert_eq!(Some(ids_b[0]), app.focused());
|
|
|
|
app.press_tab();
|
|
|
|
assert_eq!(Some(ids_b[1]), app.focused());
|
|
|
|
app.press_tab();
|
|
|
|
assert_eq!(Some(ids_b[2]), app.focused());
|
|
|
|
|
2021-04-16 05:32:51 +08:00
|
|
|
// and then cycles back to the first item in the first stack.
|
2021-04-15 08:52:19 +08:00
|
|
|
app.press_tab();
|
2021-04-16 05:32:51 +08:00
|
|
|
assert_eq!(Some(ids_a[0]), app.focused());
|
2021-04-15 08:52:19 +08:00
|
|
|
|
2021-04-16 05:32:51 +08:00
|
|
|
// backward nav does the same in reverse.
|
2021-04-15 08:52:19 +08:00
|
|
|
|
2021-04-16 05:32:51 +08:00
|
|
|
// cycles back to the last item of the last stack.
|
2021-04-15 08:52:19 +08:00
|
|
|
app.press_shift_tab();
|
|
|
|
assert_eq!(Some(ids_b[2]), app.focused());
|
|
|
|
app.press_shift_tab();
|
|
|
|
assert_eq!(Some(ids_b[1]), app.focused());
|
|
|
|
app.press_shift_tab();
|
|
|
|
assert_eq!(Some(ids_b[0]), app.focused());
|
|
|
|
|
2021-04-16 05:32:51 +08:00
|
|
|
// then moves back to the last item of the first stack.
|
2021-04-15 08:52:19 +08:00
|
|
|
app.press_shift_tab();
|
|
|
|
assert_eq!(Some(ids_a[2]), app.focused());
|
|
|
|
app.press_shift_tab();
|
|
|
|
assert_eq!(Some(ids_a[1]), app.focused());
|
|
|
|
app.press_shift_tab();
|
|
|
|
assert_eq!(Some(ids_a[0]), app.focused());
|
2021-04-16 05:32:51 +08:00
|
|
|
// then cycles again.
|
2021-04-15 08:52:19 +08:00
|
|
|
app.press_shift_tab();
|
|
|
|
assert_eq!(Some(ids_b[2]), app.focused());
|
|
|
|
}
|
2021-04-14 11:38:26 +08:00
|
|
|
|
2021-04-16 10:40:56 +08:00
|
|
|
#[test]
|
|
|
|
pub fn two_continue_scopes_with_mixed_indexes() {
|
|
|
|
// the tab_index sequence goes back and forth, but
|
|
|
|
// because the containers are scopes they do each container
|
|
|
|
// at a time.
|
|
|
|
//
|
|
|
|
// we are testing that scopes contain their navigation here,
|
|
|
|
// and because scopes exclude their branch when navigating out
|
|
|
|
// it all works here. But you can break this by adding another
|
|
|
|
// scope or other widgets with weird tab indexes in the root scope,
|
|
|
|
// because the navigation goes back to the root momentarily, it can
|
|
|
|
// jump back to a higher priority index without visiting all indexes.
|
|
|
|
//
|
|
|
|
// TODO review if this is a problem to be solved, or we mixing indexes is a user error?
|
|
|
|
|
|
|
|
let buttons_a = widgets![
|
|
|
|
button! { content = text("Button 0"); tab_index = 0; },
|
|
|
|
button! { content = text("Button 2"); tab_index = 5; },
|
|
|
|
button! { content = text("Button 1"); tab_index = 3; },
|
|
|
|
];
|
|
|
|
let ids_a: Vec<_> = (0..3).map(|i| buttons_a.widget_id(i)).collect();
|
|
|
|
|
|
|
|
let buttons_b = widgets![
|
|
|
|
button! { content = text("Button 3"); tab_index = 2; },
|
|
|
|
button! { content = text("Button 4"); tab_index = 4; },
|
|
|
|
button! { content = text("Button 5"); tab_index = 6; },
|
|
|
|
];
|
|
|
|
let ids_b: Vec<_> = (0..3).map(|i| buttons_b.widget_id(i)).collect();
|
|
|
|
|
|
|
|
let a = v_stack! {
|
|
|
|
items = buttons_a;
|
|
|
|
focus_scope = true;
|
|
|
|
tab_nav = TabNav::Continue;
|
|
|
|
};
|
|
|
|
let b = v_stack! {
|
|
|
|
items = buttons_b;
|
|
|
|
focus_scope = true;
|
|
|
|
tab_nav = TabNav::Continue;
|
|
|
|
};
|
|
|
|
let mut app = TestApp::new(h_stack(widgets![a, b]));
|
|
|
|
|
|
|
|
// window starts at (0), that is also inside `a`.
|
|
|
|
assert_eq!(Some(ids_a[0]), app.focused());
|
|
|
|
|
|
|
|
// goes to next index in the same scope (3), does not goes to (2)
|
|
|
|
app.press_tab();
|
|
|
|
assert_eq!(Some(ids_a[2]), app.focused());
|
|
|
|
|
|
|
|
// goes to next index in the same scope (5), again did not go to (4)
|
|
|
|
app.press_tab();
|
|
|
|
assert_eq!(Some(ids_a[1]), app.focused());
|
|
|
|
|
|
|
|
// goes to (2) in the `b` scope now.
|
|
|
|
app.press_tab();
|
|
|
|
assert_eq!(Some(ids_b[0]), app.focused());
|
|
|
|
// goes next to (4)
|
|
|
|
app.press_tab();
|
|
|
|
assert_eq!(Some(ids_b[1]), app.focused());
|
|
|
|
// goes next to (6)
|
|
|
|
app.press_tab();
|
|
|
|
assert_eq!(Some(ids_b[2]), app.focused());
|
|
|
|
|
|
|
|
// cycle back to (0)
|
|
|
|
app.press_tab();
|
|
|
|
assert_eq!(Some(ids_a[0]), app.focused());
|
|
|
|
|
|
|
|
// the same backwards.
|
|
|
|
app.press_shift_tab();
|
|
|
|
assert_eq!(Some(ids_b[2]), app.focused());
|
|
|
|
app.press_shift_tab();
|
|
|
|
assert_eq!(Some(ids_b[1]), app.focused());
|
|
|
|
app.press_shift_tab();
|
|
|
|
assert_eq!(Some(ids_b[0]), app.focused());
|
|
|
|
app.press_shift_tab();
|
|
|
|
assert_eq!(Some(ids_a[1]), app.focused());
|
|
|
|
app.press_shift_tab();
|
|
|
|
assert_eq!(Some(ids_a[2]), app.focused());
|
|
|
|
app.press_shift_tab();
|
|
|
|
assert_eq!(Some(ids_a[0]), app.focused());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
pub fn two_containers_with_mixed_indexes() {
|
|
|
|
// the tab-indexes go back and forth in the containers
|
|
|
|
// and because they are not each a scope the focus jumps
|
|
|
|
// from on container to another.
|
|
|
|
|
|
|
|
let buttons_a = widgets![
|
|
|
|
button! { content = text("Button 0"); tab_index = 0; },
|
|
|
|
button! { content = text("Button 2"); tab_index = 5; },
|
|
|
|
button! { content = text("Button 1"); tab_index = 3; },
|
|
|
|
];
|
|
|
|
let ids_a: Vec<_> = (0..3).map(|i| buttons_a.widget_id(i)).collect();
|
|
|
|
|
|
|
|
let buttons_b = widgets![
|
|
|
|
button! { content = text("Button 3"); tab_index = 2; },
|
|
|
|
button! { content = text("Button 4"); tab_index = 4; },
|
|
|
|
button! { content = text("Button 5"); tab_index = 6; },
|
|
|
|
];
|
|
|
|
let ids_b: Vec<_> = (0..3).map(|i| buttons_b.widget_id(i)).collect();
|
|
|
|
|
|
|
|
let a = v_stack(buttons_a);
|
|
|
|
let b = v_stack(buttons_b);
|
|
|
|
let mut app = TestApp::new(h_stack(widgets![a, b]));
|
|
|
|
|
|
|
|
// forward.
|
|
|
|
|
|
|
|
// starts at `0`
|
|
|
|
assert_eq!(Some(ids_a[0]), app.focused());
|
|
|
|
// goes to `2` in `b`
|
|
|
|
app.press_tab();
|
|
|
|
assert_eq!(Some(ids_b[0]), app.focused());
|
|
|
|
// goes to `3` back in `a`
|
|
|
|
app.press_tab();
|
|
|
|
assert_eq!(Some(ids_a[2]), app.focused());
|
|
|
|
// goes to `4` back in `b`
|
|
|
|
app.press_tab();
|
|
|
|
assert_eq!(Some(ids_b[1]), app.focused());
|
|
|
|
// goes to `5` back in `a`
|
|
|
|
app.press_tab();
|
|
|
|
assert_eq!(Some(ids_a[1]), app.focused());
|
|
|
|
// goes to `6` back in `b`
|
|
|
|
app.press_tab();
|
|
|
|
assert_eq!(Some(ids_b[2]), app.focused());
|
|
|
|
// cycle back to `0` in `a`
|
|
|
|
app.press_tab();
|
|
|
|
assert_eq!(Some(ids_a[0]), app.focused());
|
|
|
|
|
|
|
|
// backward.
|
|
|
|
app.press_shift_tab();
|
|
|
|
assert_eq!(Some(ids_b[2]), app.focused());
|
|
|
|
app.press_shift_tab();
|
|
|
|
assert_eq!(Some(ids_a[1]), app.focused());
|
|
|
|
app.press_shift_tab();
|
|
|
|
assert_eq!(Some(ids_b[1]), app.focused());
|
|
|
|
app.press_shift_tab();
|
|
|
|
assert_eq!(Some(ids_a[2]), app.focused());
|
|
|
|
app.press_shift_tab();
|
|
|
|
assert_eq!(Some(ids_b[0]), app.focused());
|
|
|
|
app.press_shift_tab();
|
|
|
|
assert_eq!(Some(ids_a[0]), app.focused());
|
|
|
|
}
|
|
|
|
|
2021-04-16 03:28:30 +08:00
|
|
|
#[test]
|
|
|
|
pub fn tab_index_skip() {
|
|
|
|
let buttons = widgets![
|
|
|
|
button! { content = text("Button 0") },
|
|
|
|
button! { content = text("Button 1"); tab_index = TabIndex::SKIP; },
|
|
|
|
button! { content = text("Button 2") },
|
|
|
|
];
|
|
|
|
let ids: Vec<_> = (0..3).map(|i| buttons.widget_id(i)).collect();
|
|
|
|
|
|
|
|
let mut app = TestApp::new(v_stack(buttons));
|
|
|
|
|
|
|
|
assert_eq!(Some(ids[0]), app.focused());
|
|
|
|
app.press_tab();
|
|
|
|
assert_eq!(Some(ids[2]), app.focused());
|
|
|
|
app.press_tab();
|
|
|
|
assert_eq!(Some(ids[0]), app.focused());
|
|
|
|
|
|
|
|
app.press_shift_tab();
|
|
|
|
assert_eq!(Some(ids[2]), app.focused());
|
|
|
|
app.press_shift_tab();
|
|
|
|
assert_eq!(Some(ids[0]), app.focused());
|
|
|
|
app.press_shift_tab();
|
|
|
|
assert_eq!(Some(ids[2]), app.focused());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
pub fn tab_inner_container() {
|
|
|
|
// sanity check for `tab_skip_inner_container`.
|
|
|
|
|
|
|
|
let inner_buttons = widgets![button! { content = text("Button 1") }, button! { content = text("Button 2") },];
|
|
|
|
let inner_ids: Vec<_> = (0..2).map(|i| inner_buttons.widget_id(i)).collect();
|
|
|
|
let items = widgets![
|
|
|
|
button! { content = text("Button 0") },
|
|
|
|
v_stack(inner_buttons),
|
|
|
|
button! { content = text("Button 3") },
|
|
|
|
];
|
|
|
|
let item_ids: Vec<_> = (0..3).map(|i| items.widget_id(i)).collect();
|
|
|
|
|
|
|
|
let mut app = TestApp::new(v_stack(items));
|
|
|
|
|
|
|
|
assert_eq!(Some(item_ids[0]), app.focused());
|
|
|
|
app.press_tab();
|
|
|
|
assert_eq!(Some(inner_ids[0]), app.focused());
|
|
|
|
app.press_tab();
|
|
|
|
assert_eq!(Some(inner_ids[1]), app.focused());
|
|
|
|
app.press_tab();
|
|
|
|
assert_eq!(Some(item_ids[2]), app.focused());
|
|
|
|
|
|
|
|
app.press_shift_tab();
|
|
|
|
assert_eq!(Some(inner_ids[1]), app.focused());
|
|
|
|
app.press_shift_tab();
|
|
|
|
assert_eq!(Some(inner_ids[0]), app.focused());
|
|
|
|
app.press_shift_tab();
|
|
|
|
assert_eq!(Some(item_ids[0]), app.focused());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
pub fn tab_skip_inner_container() {
|
|
|
|
// we expect that TabIndex::SKIP skips the full widget branch
|
|
|
|
// but that the items inside will still tab navigate if focused
|
|
|
|
// directly.
|
|
|
|
|
|
|
|
let inner_buttons = widgets![button! { content = text("Button 1") }, button! { content = text("Button 2") },];
|
|
|
|
let inner_ids: Vec<_> = (0..2).map(|i| inner_buttons.widget_id(i)).collect();
|
|
|
|
let items = widgets![
|
|
|
|
button! { content = text("Button 0") },
|
|
|
|
v_stack! {
|
|
|
|
items = inner_buttons;
|
|
|
|
tab_index = TabIndex::SKIP;
|
|
|
|
},
|
|
|
|
button! { content = text("Button 3") },
|
|
|
|
];
|
|
|
|
let item_ids: Vec<_> = (0..3).map(|i| items.widget_id(i)).collect();
|
|
|
|
|
|
|
|
let mut app = TestApp::new(v_stack(items));
|
|
|
|
|
|
|
|
// assert skipped inner.
|
|
|
|
assert_eq!(Some(item_ids[0]), app.focused());
|
|
|
|
app.press_tab();
|
|
|
|
assert_eq!(Some(item_ids[2]), app.focused());
|
|
|
|
app.press_shift_tab();
|
|
|
|
assert_eq!(Some(item_ids[0]), app.focused());
|
|
|
|
|
|
|
|
// assert that focused directly it still works.
|
|
|
|
app.focus(inner_ids[0]);
|
|
|
|
assert_eq!(Some(inner_ids[0]), app.focused());
|
|
|
|
app.press_tab();
|
|
|
|
assert_eq!(Some(inner_ids[1]), app.focused());
|
|
|
|
// and continues normally.
|
|
|
|
app.press_tab();
|
|
|
|
assert_eq!(Some(item_ids[2]), app.focused());
|
|
|
|
// but is skipped from the outside.
|
|
|
|
app.press_shift_tab();
|
|
|
|
assert_eq!(Some(item_ids[0]), app.focused());
|
|
|
|
|
|
|
|
// and the same in reverse.
|
|
|
|
app.focus(inner_ids[1]);
|
|
|
|
assert_eq!(Some(inner_ids[1]), app.focused());
|
|
|
|
app.press_shift_tab();
|
|
|
|
assert_eq!(Some(inner_ids[0]), app.focused());
|
|
|
|
app.press_shift_tab();
|
|
|
|
assert_eq!(Some(item_ids[0]), app.focused());
|
|
|
|
app.press_tab();
|
|
|
|
assert_eq!(Some(item_ids[2]), app.focused());
|
|
|
|
}
|
|
|
|
|
2021-04-16 03:33:35 +08:00
|
|
|
#[test]
|
|
|
|
pub fn tab_inner_scope_continue() {
|
|
|
|
// sanity check for `tab_skip_inner_scope_continue`.
|
|
|
|
|
|
|
|
let inner_buttons = widgets![button! { content = text("Button 1") }, button! { content = text("Button 2") },];
|
|
|
|
let inner_ids: Vec<_> = (0..2).map(|i| inner_buttons.widget_id(i)).collect();
|
|
|
|
let items = widgets![
|
|
|
|
button! { content = text("Button 0") },
|
|
|
|
v_stack! {
|
|
|
|
items = inner_buttons;
|
|
|
|
focus_scope = true;
|
|
|
|
tab_nav = TabNav::Continue;
|
|
|
|
},
|
|
|
|
button! { content = text("Button 3") },
|
|
|
|
];
|
|
|
|
let item_ids: Vec<_> = (0..3).map(|i| items.widget_id(i)).collect();
|
|
|
|
|
|
|
|
let mut app = TestApp::new(v_stack(items));
|
|
|
|
|
|
|
|
assert_eq!(Some(item_ids[0]), app.focused());
|
|
|
|
app.press_tab();
|
|
|
|
assert_eq!(Some(inner_ids[0]), app.focused());
|
|
|
|
app.press_tab();
|
|
|
|
assert_eq!(Some(inner_ids[1]), app.focused());
|
|
|
|
app.press_tab();
|
|
|
|
assert_eq!(Some(item_ids[2]), app.focused());
|
|
|
|
|
|
|
|
app.press_shift_tab();
|
|
|
|
assert_eq!(Some(inner_ids[1]), app.focused());
|
|
|
|
app.press_shift_tab();
|
|
|
|
assert_eq!(Some(inner_ids[0]), app.focused());
|
|
|
|
app.press_shift_tab();
|
|
|
|
assert_eq!(Some(item_ids[0]), app.focused());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
pub fn tab_skip_inner_scope_continue() {
|
|
|
|
// we expect that TabIndex::SKIP skips the full widget branch
|
|
|
|
// but that the items inside will still tab navigate if focused
|
|
|
|
// directly.
|
|
|
|
|
|
|
|
let inner_buttons = widgets![button! { content = text("Button 1") }, button! { content = text("Button 2") },];
|
|
|
|
let inner_ids: Vec<_> = (0..2).map(|i| inner_buttons.widget_id(i)).collect();
|
|
|
|
let items = widgets![
|
|
|
|
button! { content = text("Button 0") },
|
|
|
|
v_stack! {
|
|
|
|
items = inner_buttons;
|
|
|
|
focus_scope = true;
|
|
|
|
tab_nav = TabNav::Continue;
|
|
|
|
tab_index = TabIndex::SKIP;
|
|
|
|
},
|
|
|
|
button! { content = text("Button 3") },
|
|
|
|
];
|
|
|
|
let item_ids: Vec<_> = (0..3).map(|i| items.widget_id(i)).collect();
|
|
|
|
|
|
|
|
let mut app = TestApp::new(v_stack(items));
|
|
|
|
|
|
|
|
// assert skipped inner.
|
|
|
|
assert_eq!(Some(item_ids[0]), app.focused());
|
|
|
|
app.press_tab();
|
|
|
|
assert_eq!(Some(item_ids[2]), app.focused());
|
|
|
|
app.press_shift_tab();
|
|
|
|
assert_eq!(Some(item_ids[0]), app.focused());
|
|
|
|
|
|
|
|
// assert that focused directly it still works.
|
|
|
|
app.focus(inner_ids[0]);
|
|
|
|
assert_eq!(Some(inner_ids[0]), app.focused());
|
|
|
|
app.press_tab();
|
|
|
|
assert_eq!(Some(inner_ids[1]), app.focused());
|
|
|
|
// and continues normally.
|
|
|
|
app.press_tab();
|
|
|
|
assert_eq!(Some(item_ids[2]), app.focused());
|
|
|
|
// but is skipped from the outside.
|
|
|
|
app.press_shift_tab();
|
|
|
|
assert_eq!(Some(item_ids[0]), app.focused());
|
|
|
|
|
|
|
|
// and the same in reverse.
|
|
|
|
app.focus(inner_ids[1]);
|
|
|
|
assert_eq!(Some(inner_ids[1]), app.focused());
|
|
|
|
app.press_shift_tab();
|
|
|
|
assert_eq!(Some(inner_ids[0]), app.focused());
|
|
|
|
app.press_shift_tab();
|
|
|
|
assert_eq!(Some(item_ids[0]), app.focused());
|
|
|
|
app.press_tab();
|
|
|
|
assert_eq!(Some(item_ids[2]), app.focused());
|
|
|
|
}
|
|
|
|
|
2021-04-16 05:32:51 +08:00
|
|
|
#[test]
|
|
|
|
pub fn tab_inner_scope_cycle() {
|
|
|
|
// we expect tab navigation to enter the inner scope and get trapped in there.
|
|
|
|
|
|
|
|
let inner_buttons = widgets![button! { content = text("Button 1") }, button! { content = text("Button 2") },];
|
|
|
|
let inner_ids: Vec<_> = (0..2).map(|i| inner_buttons.widget_id(i)).collect();
|
|
|
|
let items = widgets![
|
|
|
|
button! { content = text("Button 0") },
|
|
|
|
v_stack! {
|
|
|
|
items = inner_buttons;
|
|
|
|
focus_scope = true;
|
|
|
|
tab_nav = TabNav::Cycle;
|
|
|
|
},
|
|
|
|
button! { content = text("Button 3") },
|
|
|
|
];
|
|
|
|
let item_ids: Vec<_> = (0..3).map(|i| items.widget_id(i)).collect();
|
|
|
|
|
|
|
|
let mut app = TestApp::new(v_stack(items));
|
|
|
|
|
|
|
|
// focus starts outside of inner cycle.
|
|
|
|
assert_eq!(Some(item_ids[0]), app.focused());
|
|
|
|
app.press_tab();
|
|
|
|
|
|
|
|
// focus enters the inner cycle.
|
|
|
|
assert_eq!(Some(inner_ids[0]), app.focused());
|
|
|
|
app.press_tab();
|
|
|
|
assert_eq!(Some(inner_ids[1]), app.focused());
|
|
|
|
// we still are in the inner cycle.
|
|
|
|
app.press_tab();
|
|
|
|
assert_eq!(Some(inner_ids[0]), app.focused());
|
|
|
|
|
|
|
|
// same in reverse.
|
|
|
|
app.focus(item_ids[2]);
|
|
|
|
assert_eq!(Some(item_ids[2]), app.focused());
|
|
|
|
app.press_shift_tab();
|
|
|
|
assert_eq!(Some(inner_ids[1]), app.focused());
|
|
|
|
app.press_shift_tab();
|
|
|
|
assert_eq!(Some(inner_ids[0]), app.focused());
|
|
|
|
app.press_shift_tab();
|
|
|
|
assert_eq!(Some(inner_ids[1]), app.focused());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
pub fn tab_inner_scope_contained() {
|
|
|
|
// we expect tab navigation to enter the inner scope and get trapped in there.
|
2021-04-16 03:33:35 +08:00
|
|
|
|
2021-04-16 05:32:51 +08:00
|
|
|
let inner_buttons = widgets![button! { content = text("Button 1") }, button! { content = text("Button 2") },];
|
|
|
|
let inner_ids: Vec<_> = (0..2).map(|i| inner_buttons.widget_id(i)).collect();
|
|
|
|
let items = widgets![
|
|
|
|
button! { content = text("Button 0") },
|
|
|
|
v_stack! {
|
|
|
|
items = inner_buttons;
|
|
|
|
focus_scope = true;
|
|
|
|
tab_nav = TabNav::Contained;
|
|
|
|
},
|
|
|
|
button! { content = text("Button 3") },
|
|
|
|
];
|
|
|
|
let item_ids: Vec<_> = (0..3).map(|i| items.widget_id(i)).collect();
|
|
|
|
|
|
|
|
let mut app = TestApp::new(v_stack(items));
|
|
|
|
|
|
|
|
// focus starts outside of inner container.
|
|
|
|
assert_eq!(Some(item_ids[0]), app.focused());
|
|
|
|
app.press_tab();
|
|
|
|
|
|
|
|
// focus enters the inner container.
|
|
|
|
assert_eq!(Some(inner_ids[0]), app.focused());
|
|
|
|
app.press_tab();
|
|
|
|
assert_eq!(Some(inner_ids[1]), app.focused());
|
|
|
|
// we still are in the inner container.
|
|
|
|
app.press_tab();
|
|
|
|
assert_eq!(Some(inner_ids[1]), app.focused());
|
|
|
|
|
|
|
|
// same in reverse.
|
|
|
|
app.focus(item_ids[2]);
|
|
|
|
assert_eq!(Some(item_ids[2]), app.focused());
|
|
|
|
app.press_shift_tab();
|
|
|
|
assert_eq!(Some(inner_ids[1]), app.focused());
|
|
|
|
app.press_shift_tab();
|
|
|
|
assert_eq!(Some(inner_ids[0]), app.focused());
|
|
|
|
app.press_shift_tab();
|
|
|
|
assert_eq!(Some(inner_ids[0]), app.focused());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
pub fn tab_inner_scope_once() {
|
|
|
|
// we expect tab navigation to enter the inner scope but then leave it.
|
|
|
|
|
|
|
|
let inner_buttons = widgets![button! { content = text("Button 1") }, button! { content = text("Button 2") },];
|
|
|
|
let inner_ids: Vec<_> = (0..2).map(|i| inner_buttons.widget_id(i)).collect();
|
|
|
|
let items = widgets![
|
|
|
|
button! { content = text("Button 0") },
|
|
|
|
v_stack! {
|
|
|
|
items = inner_buttons;
|
|
|
|
focus_scope = true;
|
|
|
|
tab_nav = TabNav::Once;
|
|
|
|
},
|
|
|
|
button! { content = text("Button 3") },
|
|
|
|
];
|
|
|
|
let item_ids: Vec<_> = (0..3).map(|i| items.widget_id(i)).collect();
|
|
|
|
|
|
|
|
let mut app = TestApp::new(v_stack(items));
|
|
|
|
|
|
|
|
// focus starts outside of inner scope.
|
|
|
|
assert_eq!(Some(item_ids[0]), app.focused());
|
|
|
|
app.press_tab();
|
|
|
|
|
|
|
|
// focus enters the inner scope.
|
|
|
|
assert_eq!(Some(inner_ids[0]), app.focused());
|
|
|
|
app.press_tab();
|
|
|
|
// and we leave it already.
|
|
|
|
assert_eq!(Some(item_ids[2]), app.focused());
|
|
|
|
|
|
|
|
// same in reverse.
|
|
|
|
app.press_shift_tab();
|
|
|
|
assert_eq!(Some(inner_ids[1]), app.focused());
|
|
|
|
app.press_shift_tab();
|
|
|
|
assert_eq!(Some(item_ids[0]), app.focused());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
pub fn tab_inner_scope_none() {
|
|
|
|
// we expect tab navigation to enter the inner scope and then not move.
|
|
|
|
|
|
|
|
let inner_buttons = widgets![button! { content = text("Button 1") }, button! { content = text("Button 2") },];
|
|
|
|
let inner_ids: Vec<_> = (0..2).map(|i| inner_buttons.widget_id(i)).collect();
|
|
|
|
let items = widgets![
|
|
|
|
button! { content = text("Button 0") },
|
|
|
|
v_stack! {
|
|
|
|
items = inner_buttons;
|
|
|
|
focus_scope = true;
|
|
|
|
tab_nav = TabNav::None;
|
|
|
|
},
|
|
|
|
button! { content = text("Button 3") },
|
|
|
|
];
|
|
|
|
let item_ids: Vec<_> = (0..3).map(|i| items.widget_id(i)).collect();
|
|
|
|
|
|
|
|
let mut app = TestApp::new(v_stack(items));
|
|
|
|
|
|
|
|
// focus starts outside of inner scope.
|
|
|
|
assert_eq!(Some(item_ids[0]), app.focused());
|
|
|
|
app.press_tab();
|
|
|
|
|
|
|
|
// focus enters the inner scope.
|
|
|
|
assert_eq!(Some(inner_ids[0]), app.focused());
|
|
|
|
app.press_tab();
|
|
|
|
// and we did not move.
|
|
|
|
assert_eq!(Some(inner_ids[0]), app.focused());
|
|
|
|
|
|
|
|
// same in reverse.
|
|
|
|
app.focus(item_ids[2]);
|
|
|
|
assert_eq!(Some(item_ids[2]), app.focused());
|
|
|
|
app.press_shift_tab();
|
|
|
|
assert_eq!(Some(inner_ids[1]), app.focused());
|
|
|
|
app.press_shift_tab();
|
|
|
|
assert_eq!(Some(inner_ids[1]), app.focused());
|
|
|
|
}
|
2021-04-16 03:33:35 +08:00
|
|
|
|
2021-05-18 07:19:01 +08:00
|
|
|
#[test]
|
|
|
|
pub fn tab_inner_scope_continue_to_non_focusable_siblings_focusable_child() {
|
|
|
|
let btn1 = WidgetId::new_unique();
|
|
|
|
let btn2 = WidgetId::new_unique();
|
|
|
|
let mut app = TestApp::new(h_stack(widgets![
|
|
|
|
v_stack! {
|
|
|
|
focus_scope = true;
|
|
|
|
tab_nav = TabNav::Continue;
|
|
|
|
items = widgets![button! { id = btn1; content = text("Btn 1"); }];
|
|
|
|
},
|
|
|
|
v_stack(widgets![button! { id = btn2; content = text("Btn 2"); }])
|
|
|
|
]));
|
|
|
|
|
|
|
|
assert_eq!(Some(btn1), app.focused());
|
|
|
|
app.press_tab();
|
|
|
|
assert_eq!(Some(btn2), app.focused());
|
|
|
|
}
|
|
|
|
|
2021-04-27 07:13:38 +08:00
|
|
|
#[test]
|
|
|
|
pub fn dont_focus_alt_when_alt_pressed_before_focusing_window() {
|
|
|
|
let start_focus_id = WidgetId::new_unique();
|
|
|
|
let buttons = widgets![
|
|
|
|
button! { content = text("Button 0"); id = start_focus_id; },
|
|
|
|
button! { content = text("Button 1"); },
|
|
|
|
];
|
2021-04-27 09:44:17 +08:00
|
|
|
let alt_buttons = widgets![button! { content = text("Alt 0"); }, button! { content = text("Alt 1"); },];
|
2021-04-27 07:13:38 +08:00
|
|
|
|
|
|
|
let mut app = TestApp::new(v_stack(widgets![
|
|
|
|
h_stack! {
|
|
|
|
alt_focus_scope = true;
|
|
|
|
items = alt_buttons;
|
|
|
|
},
|
|
|
|
v_stack(buttons)
|
|
|
|
]));
|
|
|
|
|
|
|
|
// clear
|
|
|
|
app.take_focus_changed();
|
|
|
|
app.take_return_focus_changed();
|
|
|
|
assert_eq!(Some(start_focus_id), app.focused());
|
|
|
|
|
|
|
|
// just an ALT release, no press:
|
|
|
|
app.just_release_alt();
|
|
|
|
assert!(app.take_focus_changed().is_empty());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2021-05-18 09:24:48 +08:00
|
|
|
pub fn window_blur_focus() {
|
2021-04-27 07:13:38 +08:00
|
|
|
let expected_id = WidgetId::new_unique();
|
|
|
|
let buttons = widgets![
|
|
|
|
button! { content = text("Button 0"); },
|
|
|
|
button! { content = text("Button 1"); id = expected_id; },
|
|
|
|
];
|
2021-04-27 09:44:17 +08:00
|
|
|
let alt_buttons = widgets![button! { content = text("Alt 0"); }, button! { content = text("Alt 1"); },];
|
2021-04-27 07:13:38 +08:00
|
|
|
|
|
|
|
let mut app = TestApp::new(v_stack(widgets![
|
|
|
|
h_stack! {
|
|
|
|
alt_focus_scope = true;
|
|
|
|
items = alt_buttons;
|
|
|
|
},
|
|
|
|
v_stack(buttons)
|
|
|
|
]));
|
|
|
|
|
|
|
|
app.press_tab();
|
|
|
|
assert_eq!(Some(expected_id), app.focused());
|
|
|
|
|
2021-05-18 09:24:48 +08:00
|
|
|
app.blur_window();
|
2021-04-27 07:13:38 +08:00
|
|
|
assert_eq!(None, app.focused());
|
2021-05-18 09:24:48 +08:00
|
|
|
app.focus_window();
|
2021-04-27 07:13:38 +08:00
|
|
|
assert_eq!(Some(expected_id), app.focused());
|
|
|
|
|
|
|
|
app.press_alt();
|
|
|
|
assert_ne!(Some(expected_id), app.focused());
|
|
|
|
|
2021-05-18 09:24:48 +08:00
|
|
|
app.blur_window();
|
2021-04-27 07:13:38 +08:00
|
|
|
assert_eq!(None, app.focused());
|
2021-05-18 09:24:48 +08:00
|
|
|
app.focus_window();
|
2021-04-27 07:13:38 +08:00
|
|
|
assert_eq!(Some(expected_id), app.focused());
|
|
|
|
}
|
|
|
|
|
2021-05-14 09:52:37 +08:00
|
|
|
#[test]
|
|
|
|
pub fn focused_removed_by_disabling() {
|
|
|
|
let enabled = var(true);
|
|
|
|
focused_removed_test(button! { content = text("Button 1"); enabled = enabled.clone() }, |vars| {
|
|
|
|
enabled.set(vars, false)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
#[test]
|
|
|
|
pub fn focused_removed_by_hidding() {
|
|
|
|
let visibility = var(Visibility::Visible);
|
|
|
|
focused_removed_test(button! { content = text("Button 1"); visibility = visibility.clone() }, |vars| {
|
|
|
|
visibility.set(vars, Visibility::Hidden)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
#[test]
|
|
|
|
pub fn focused_removed_by_collapsing() {
|
|
|
|
let visibility = var(Visibility::Visible);
|
|
|
|
focused_removed_test(button! { content = text("Button 1"); visibility = visibility.clone() }, |vars| {
|
|
|
|
visibility.set(vars, Visibility::Collapsed)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
#[test]
|
|
|
|
pub fn focused_removed_by_making_not_focusable() {
|
|
|
|
let focusable = var(true);
|
|
|
|
focused_removed_test(button! { content = text("Button 1"); focusable = focusable.clone() }, |vars| {
|
|
|
|
focusable.set(vars, false)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
fn focused_removed_test(button1: impl Widget, set_var: impl FnOnce(&Vars)) {
|
|
|
|
let buttons = widgets![
|
|
|
|
button! { content = text("Button 0") },
|
|
|
|
button1,
|
|
|
|
button! { content = text("Button 2") },
|
|
|
|
];
|
|
|
|
let ids: Vec<_> = (0..3).map(|i| buttons.widget_id(i)).collect();
|
|
|
|
|
|
|
|
let mut app = TestApp::new(v_stack(buttons));
|
|
|
|
|
|
|
|
app.focus(ids[1]);
|
|
|
|
|
|
|
|
assert_eq!(Some(ids[1]), app.focused());
|
|
|
|
|
|
|
|
app.set_vars(set_var);
|
|
|
|
|
|
|
|
assert_ne!(Some(ids[1]), app.focused());
|
|
|
|
}
|
|
|
|
#[test]
|
|
|
|
pub fn focused_removed_by_deleting() {
|
|
|
|
let index = var(0);
|
|
|
|
let button1_id = WidgetId::new_unique();
|
|
|
|
let buttons = widgets! {
|
|
|
|
button! { content = text("Button 0") },
|
|
|
|
switch(
|
|
|
|
index.clone(),
|
|
|
|
widgets![button! { id = button1_id; content = text("Button 1") }, button! { content = text("Button Other") },],
|
|
|
|
),
|
|
|
|
button! { content = text("Button 2") ,}
|
|
|
|
};
|
|
|
|
|
|
|
|
let mut app = TestApp::new(v_stack(buttons));
|
|
|
|
|
|
|
|
app.focus(button1_id);
|
|
|
|
assert_eq!(Some(button1_id), app.focused());
|
|
|
|
|
|
|
|
app.set_vars(|vars| {
|
|
|
|
index.set(vars, 1);
|
|
|
|
});
|
|
|
|
|
|
|
|
assert_ne!(Some(button1_id), app.focused());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
pub fn focus_widget_or_parent_goes_to_parent() {
|
|
|
|
let first_focus_id = WidgetId::new_unique();
|
|
|
|
let parent_id = WidgetId::new_unique();
|
|
|
|
let child_id = WidgetId::new_unique();
|
|
|
|
|
|
|
|
let mut app = TestApp::new(v_stack(widgets![
|
|
|
|
button! {
|
|
|
|
id = first_focus_id;
|
|
|
|
content = text("initial focus")
|
|
|
|
},
|
|
|
|
container! {
|
|
|
|
id = parent_id;
|
|
|
|
focusable = true;
|
|
|
|
content = text! {
|
|
|
|
id = child_id;
|
|
|
|
focusable = false;
|
|
|
|
text = "not focusable"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
]));
|
|
|
|
|
|
|
|
assert_eq!(Some(first_focus_id), app.focused());
|
|
|
|
app.focus(child_id); // not focusable, does nothing.
|
|
|
|
assert_eq!(Some(first_focus_id), app.focused());
|
|
|
|
|
|
|
|
app.focus_or_parent(child_id);
|
|
|
|
assert_eq!(Some(parent_id), app.focused());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
pub fn focus_widget_or_child_goes_to_child() {
|
|
|
|
let first_focus_id = WidgetId::new_unique();
|
|
|
|
let parent_id = WidgetId::new_unique();
|
|
|
|
let child_id = WidgetId::new_unique();
|
|
|
|
|
|
|
|
let mut app = TestApp::new(v_stack(widgets![
|
|
|
|
button! {
|
|
|
|
id = first_focus_id;
|
|
|
|
content = text("initial focus")
|
|
|
|
},
|
|
|
|
container! {
|
|
|
|
id = parent_id;
|
|
|
|
focusable = false;
|
|
|
|
content = text! {
|
|
|
|
id = child_id;
|
|
|
|
focusable = true;
|
|
|
|
text = "focusable focusable"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
]));
|
|
|
|
|
|
|
|
assert_eq!(Some(first_focus_id), app.focused());
|
|
|
|
app.focus(parent_id); // not focusable, does nothing.
|
|
|
|
assert_eq!(Some(first_focus_id), app.focused());
|
|
|
|
|
|
|
|
app.focus_or_child(parent_id);
|
|
|
|
assert_eq!(Some(child_id), app.focused());
|
|
|
|
}
|
2021-05-14 12:12:14 +08:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
pub fn focus_continued_after_widget_id_move() {
|
|
|
|
let id = WidgetId::new_unique();
|
|
|
|
let index = var(0);
|
|
|
|
let button = switch(
|
|
|
|
index.clone(),
|
|
|
|
widgets![
|
|
|
|
button! { id; content = text("Button A") },
|
|
|
|
container! {
|
|
|
|
content = button! { id; content = text("Button B") }
|
|
|
|
},
|
|
|
|
],
|
|
|
|
);
|
|
|
|
let mut app = TestApp::new(button);
|
|
|
|
assert_eq!(Some(id), app.focused());
|
|
|
|
app.take_focus_changed();
|
|
|
|
|
|
|
|
app.set_vars(|vars| {
|
|
|
|
index.set(vars, 1);
|
|
|
|
});
|
|
|
|
assert_eq!(Some(id), app.focused());
|
|
|
|
let evs = app.take_focus_changed();
|
|
|
|
assert_eq!(1, evs.len());
|
|
|
|
assert!(evs[0].is_widget_move());
|
|
|
|
assert_eq!(FocusChangedCause::Recovery, evs[0].cause);
|
|
|
|
}
|
|
|
|
|
2021-05-14 09:52:37 +08:00
|
|
|
#[test]
|
2021-05-14 12:12:14 +08:00
|
|
|
pub fn focus_continued_after_widget_move_same_window() {
|
2021-05-14 09:52:37 +08:00
|
|
|
let id = WidgetId::new_unique();
|
2021-05-16 02:31:55 +08:00
|
|
|
let button = RcNode::new(button! {
|
2021-05-14 09:52:37 +08:00
|
|
|
id;
|
|
|
|
content = text("Click Me!");
|
|
|
|
});
|
|
|
|
let do_move = var(false);
|
|
|
|
|
|
|
|
let mut app = TestApp::new(v_stack(widgets![
|
|
|
|
container! {
|
2021-05-15 09:45:49 +08:00
|
|
|
content = button.slot(take_on_init())
|
2021-05-14 09:52:37 +08:00
|
|
|
},
|
|
|
|
container! {
|
2021-05-15 09:45:49 +08:00
|
|
|
content = button.slot(do_move.clone())
|
2021-05-14 09:52:37 +08:00
|
|
|
}
|
|
|
|
]));
|
|
|
|
assert_eq!(Some(id), app.focused());
|
|
|
|
app.take_focus_changed();
|
|
|
|
|
|
|
|
app.set_vars(|vars| do_move.set(vars, true));
|
|
|
|
|
|
|
|
assert_eq!(Some(id), app.focused());
|
|
|
|
let evs = app.take_focus_changed();
|
|
|
|
assert_eq!(1, evs.len());
|
|
|
|
assert!(evs[0].is_widget_move());
|
|
|
|
assert_eq!(FocusChangedCause::Recovery, evs[0].cause);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2021-05-14 12:12:14 +08:00
|
|
|
pub fn focus_moves_to_new_window() {
|
|
|
|
let main_id = WidgetId::new_unique();
|
2021-05-18 12:27:27 +08:00
|
|
|
let win2_id = WidgetId::new_unique();
|
|
|
|
let win3_id = WidgetId::new_unique();
|
2021-05-14 12:12:14 +08:00
|
|
|
|
|
|
|
let mut app = TestApp::new(button! {
|
|
|
|
id = main_id;
|
|
|
|
content = text("Button in main window");
|
|
|
|
});
|
|
|
|
assert_eq!(Some(main_id), app.focused());
|
|
|
|
|
|
|
|
app.open_window(button! {
|
2021-05-18 12:27:27 +08:00
|
|
|
id = win2_id;
|
|
|
|
content = text("Button in second window");
|
2021-05-14 12:12:14 +08:00
|
|
|
});
|
2021-05-18 12:27:27 +08:00
|
|
|
assert_eq!(Some(win2_id), app.focused());
|
|
|
|
|
|
|
|
app.open_window(button! {
|
|
|
|
id = win3_id;
|
|
|
|
content = text("Button in third window");
|
|
|
|
});
|
|
|
|
assert_eq!(Some(win3_id), app.focused());
|
2021-05-14 09:52:37 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
pub fn focus_goes_to_parent_after_remove() {
|
|
|
|
let parent_id = WidgetId::new_unique();
|
|
|
|
let child_id = WidgetId::new_unique();
|
|
|
|
|
|
|
|
let enabled = var(true);
|
|
|
|
|
|
|
|
let mut app = TestApp::new(v_stack(widgets![container! {
|
|
|
|
id = parent_id;
|
|
|
|
focusable = true;
|
|
|
|
content = button! {
|
|
|
|
id = child_id;
|
|
|
|
enabled = enabled.clone();
|
|
|
|
content = text( "item 'removed'")
|
|
|
|
}
|
|
|
|
}]));
|
|
|
|
|
|
|
|
app.focus(child_id);
|
|
|
|
assert_eq!(Some(child_id), app.focused());
|
|
|
|
app.take_focus_changed();
|
|
|
|
|
|
|
|
app.set_vars(|vars| {
|
|
|
|
enabled.set(vars, false);
|
|
|
|
});
|
|
|
|
assert_eq!(Some(parent_id), app.focused());
|
|
|
|
let evs = app.take_focus_changed();
|
|
|
|
assert_eq!(1, evs.len());
|
|
|
|
assert_eq!(FocusChangedCause::Recovery, evs[0].cause);
|
|
|
|
}
|
|
|
|
|
2021-05-27 05:09:33 +08:00
|
|
|
#[test]
|
|
|
|
pub fn directional_focus_up() {
|
|
|
|
let buttons = widgets![
|
|
|
|
button! { content = text("Button 0") },
|
|
|
|
button! { content = text("Button 1") },
|
|
|
|
button! { content = text("Button 2") },
|
|
|
|
];
|
|
|
|
let ids: Vec<_> = (0..3).map(|i| buttons.widget_id(i)).collect();
|
|
|
|
|
|
|
|
let mut app = TestApp::new(v_stack(buttons));
|
|
|
|
|
|
|
|
app.focus(ids[2]);
|
|
|
|
assert_eq!(Some(ids[2]), app.focused());
|
|
|
|
|
|
|
|
app.press_up();
|
|
|
|
assert_eq!(Some(ids[1]), app.focused());
|
|
|
|
|
|
|
|
app.press_up();
|
|
|
|
assert_eq!(Some(ids[0]), app.focused());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
pub fn directional_focus_down() {
|
|
|
|
let buttons = widgets![
|
|
|
|
button! { content = text("Button 0") },
|
|
|
|
button! { content = text("Button 1") },
|
|
|
|
button! { content = text("Button 2") },
|
|
|
|
];
|
|
|
|
let ids: Vec<_> = (0..3).map(|i| buttons.widget_id(i)).collect();
|
|
|
|
|
|
|
|
let mut app = TestApp::new(v_stack(buttons));
|
|
|
|
|
|
|
|
assert_eq!(Some(ids[0]), app.focused());
|
|
|
|
|
|
|
|
app.press_down();
|
|
|
|
assert_eq!(Some(ids[1]), app.focused());
|
|
|
|
|
|
|
|
app.press_down();
|
|
|
|
assert_eq!(Some(ids[2]), app.focused());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
pub fn directional_focus_left() {
|
|
|
|
let buttons = widgets![
|
|
|
|
button! { content = text("Button 0") },
|
|
|
|
button! { content = text("Button 1") },
|
|
|
|
button! { content = text("Button 2") },
|
|
|
|
];
|
|
|
|
let ids: Vec<_> = (0..3).map(|i| buttons.widget_id(i)).collect();
|
|
|
|
|
|
|
|
let mut app = TestApp::new(h_stack(buttons));
|
|
|
|
|
|
|
|
app.focus(ids[2]);
|
|
|
|
assert_eq!(Some(ids[2]), app.focused());
|
|
|
|
|
|
|
|
app.press_left();
|
|
|
|
assert_eq!(Some(ids[1]), app.focused());
|
|
|
|
|
|
|
|
app.press_left();
|
|
|
|
assert_eq!(Some(ids[0]), app.focused());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
pub fn directional_focus_right() {
|
|
|
|
let buttons = widgets![
|
|
|
|
button! { content = text("Button 0") },
|
|
|
|
button! { content = text("Button 1") },
|
|
|
|
button! { content = text("Button 2") },
|
|
|
|
];
|
|
|
|
let ids: Vec<_> = (0..3).map(|i| buttons.widget_id(i)).collect();
|
|
|
|
|
|
|
|
let mut app = TestApp::new(h_stack(buttons));
|
|
|
|
|
|
|
|
assert_eq!(Some(ids[0]), app.focused());
|
|
|
|
|
|
|
|
app.press_right();
|
|
|
|
assert_eq!(Some(ids[1]), app.focused());
|
|
|
|
|
|
|
|
app.press_right();
|
|
|
|
assert_eq!(Some(ids[2]), app.focused());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
pub fn directional_cycle_vertical() {
|
|
|
|
let buttons = widgets![
|
|
|
|
button! { content = text("Button 0") },
|
|
|
|
button! { content = text("Button 1") },
|
|
|
|
button! { content = text("Button 2") },
|
|
|
|
];
|
|
|
|
let ids: Vec<_> = (0..3).map(|i| buttons.widget_id(i)).collect();
|
|
|
|
|
|
|
|
let mut app = TestApp::new_w(window! {
|
|
|
|
directional_nav = DirectionalNav::Cycle;
|
|
|
|
content = v_stack(buttons);
|
|
|
|
});
|
|
|
|
assert_eq!(Some(ids[0]), app.focused());
|
|
|
|
|
|
|
|
app.press_up();
|
|
|
|
assert_eq!(Some(ids[2]), app.focused());
|
|
|
|
|
|
|
|
app.press_down();
|
|
|
|
assert_eq!(Some(ids[0]), app.focused());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
pub fn directional_cycle_horizontal() {
|
|
|
|
let buttons = widgets![
|
|
|
|
button! { content = text("Button 0") },
|
|
|
|
button! { content = text("Button 1") },
|
|
|
|
button! { content = text("Button 2") },
|
|
|
|
];
|
|
|
|
let ids: Vec<_> = (0..3).map(|i| buttons.widget_id(i)).collect();
|
|
|
|
|
|
|
|
let mut app = TestApp::new_w(window! {
|
|
|
|
directional_nav = DirectionalNav::Cycle;
|
|
|
|
content = h_stack(buttons);
|
|
|
|
});
|
|
|
|
assert_eq!(Some(ids[0]), app.focused());
|
|
|
|
|
|
|
|
app.press_left();
|
|
|
|
assert_eq!(Some(ids[2]), app.focused());
|
|
|
|
|
|
|
|
app.press_right();
|
|
|
|
assert_eq!(Some(ids[0]), app.focused());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
pub fn directional_contained_vertical() {
|
|
|
|
let buttons = widgets![
|
|
|
|
button! { content = text("Button 0") },
|
|
|
|
button! { content = text("Button 1") },
|
|
|
|
button! { content = text("Button 2") },
|
|
|
|
];
|
|
|
|
let ids: Vec<_> = (0..3).map(|i| buttons.widget_id(i)).collect();
|
|
|
|
|
|
|
|
let mut app = TestApp::new_w(window! {
|
|
|
|
directional_nav = DirectionalNav::Contained;
|
|
|
|
content = v_stack(buttons);
|
|
|
|
});
|
|
|
|
assert_eq!(Some(ids[0]), app.focused());
|
|
|
|
|
|
|
|
app.press_up();
|
|
|
|
assert_eq!(Some(ids[0]), app.focused());
|
|
|
|
|
|
|
|
app.press_down();
|
|
|
|
assert_eq!(Some(ids[1]), app.focused());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
pub fn directional_contained_horizontal() {
|
|
|
|
let buttons = widgets![
|
|
|
|
button! { content = text("Button 0") },
|
|
|
|
button! { content = text("Button 1") },
|
|
|
|
button! { content = text("Button 2") },
|
|
|
|
];
|
|
|
|
let ids: Vec<_> = (0..3).map(|i| buttons.widget_id(i)).collect();
|
|
|
|
|
|
|
|
let mut app = TestApp::new_w(window! {
|
|
|
|
directional_nav = DirectionalNav::Contained;
|
|
|
|
content = h_stack(buttons);
|
|
|
|
});
|
|
|
|
assert_eq!(Some(ids[0]), app.focused());
|
|
|
|
|
|
|
|
app.press_left();
|
|
|
|
assert_eq!(Some(ids[0]), app.focused());
|
|
|
|
|
|
|
|
app.press_right();
|
|
|
|
assert_eq!(Some(ids[1]), app.focused());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
pub fn directional_none() {
|
|
|
|
fn test(press: impl Fn(&mut TestApp)) {
|
|
|
|
let buttons = widgets![
|
|
|
|
button! { content = text("Button 0") },
|
|
|
|
button! { content = text("Button 1") },
|
|
|
|
button! { content = text("Button 2") },
|
|
|
|
];
|
|
|
|
let ids: Vec<_> = (0..3).map(|i| buttons.widget_id(i)).collect();
|
|
|
|
|
|
|
|
let mut app = TestApp::new_w(window! {
|
|
|
|
directional_nav = DirectionalNav::None;
|
|
|
|
content = h_stack(buttons);
|
|
|
|
});
|
|
|
|
|
|
|
|
app.focus(ids[1]);
|
|
|
|
assert_eq!(Some(ids[1]), app.focused());
|
|
|
|
|
|
|
|
press(&mut app);
|
|
|
|
assert_eq!(Some(ids[1]), app.focused());
|
|
|
|
}
|
|
|
|
|
|
|
|
test(|a| a.press_up());
|
|
|
|
test(|a| a.press_down());
|
|
|
|
test(|a| a.press_left());
|
|
|
|
test(|a| a.press_right());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
pub fn directional_continue_up() {
|
|
|
|
let start_id = WidgetId::new_unique();
|
|
|
|
let buttons = widgets![
|
|
|
|
button! { content = text("Button 0") },
|
|
|
|
v_stack! {
|
|
|
|
focus_scope = true;
|
|
|
|
directional_nav = DirectionalNav::Continue;
|
|
|
|
items = widgets![
|
|
|
|
button! { content = text("Button 1"); id = start_id; },
|
|
|
|
];
|
|
|
|
},
|
|
|
|
button! { content = text("Button 2") },
|
|
|
|
];
|
|
|
|
let ids: Vec<_> = (0..3).map(|i| buttons.widget_id(i)).collect();
|
|
|
|
|
|
|
|
let mut app = TestApp::new(v_stack(buttons));
|
|
|
|
|
|
|
|
app.focus(start_id);
|
|
|
|
assert_eq!(Some(start_id), app.focused());
|
|
|
|
|
|
|
|
app.press_up();
|
|
|
|
assert_eq!(Some(ids[0]), app.focused());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
pub fn directional_continue_down() {
|
|
|
|
let start_id = WidgetId::new_unique();
|
|
|
|
let buttons = widgets![
|
|
|
|
button! { content = text("Button 0") },
|
|
|
|
v_stack! {
|
|
|
|
focus_scope = true;
|
|
|
|
directional_nav = DirectionalNav::Continue;
|
|
|
|
items = widgets![
|
|
|
|
button! { content = text("Button 1"); id = start_id; },
|
|
|
|
];
|
|
|
|
},
|
|
|
|
button! { content = text("Button 2") },
|
|
|
|
];
|
|
|
|
let ids: Vec<_> = (0..3).map(|i| buttons.widget_id(i)).collect();
|
|
|
|
|
|
|
|
let mut app = TestApp::new(v_stack(buttons));
|
|
|
|
|
|
|
|
app.focus(start_id);
|
|
|
|
assert_eq!(Some(start_id), app.focused());
|
|
|
|
|
|
|
|
app.press_down();
|
|
|
|
assert_eq!(Some(ids[2]), app.focused());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
pub fn directional_continue_left() {
|
|
|
|
let start_id = WidgetId::new_unique();
|
|
|
|
let buttons = widgets![
|
|
|
|
button! { content = text("Button 0") },
|
|
|
|
v_stack! {
|
|
|
|
focus_scope = true;
|
|
|
|
directional_nav = DirectionalNav::Continue;
|
|
|
|
items = widgets![
|
|
|
|
button! { content = text("Button 1"); id = start_id; },
|
|
|
|
];
|
|
|
|
},
|
|
|
|
button! { content = text("Button 2") },
|
|
|
|
];
|
|
|
|
let ids: Vec<_> = (0..3).map(|i| buttons.widget_id(i)).collect();
|
|
|
|
|
|
|
|
let mut app = TestApp::new(h_stack(buttons));
|
|
|
|
|
|
|
|
app.focus(start_id);
|
|
|
|
assert_eq!(Some(start_id), app.focused());
|
|
|
|
|
|
|
|
app.press_left();
|
|
|
|
assert_eq!(Some(ids[0]), app.focused());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
pub fn directional_continue_right() {
|
|
|
|
let start_id = WidgetId::new_unique();
|
|
|
|
let buttons = widgets![
|
|
|
|
button! { content = text("Button 0") },
|
|
|
|
v_stack! {
|
|
|
|
focus_scope = true;
|
|
|
|
directional_nav = DirectionalNav::Continue;
|
|
|
|
items = widgets![
|
|
|
|
button! { content = text("Button 1"); id = start_id; },
|
|
|
|
];
|
|
|
|
},
|
|
|
|
button! { content = text("Button 2") },
|
|
|
|
];
|
|
|
|
let ids: Vec<_> = (0..3).map(|i| buttons.widget_id(i)).collect();
|
|
|
|
|
|
|
|
let mut app = TestApp::new(h_stack(buttons));
|
|
|
|
|
|
|
|
app.focus(start_id);
|
|
|
|
assert_eq!(Some(start_id), app.focused());
|
|
|
|
|
|
|
|
app.press_right();
|
|
|
|
assert_eq!(Some(ids[2]), app.focused());
|
|
|
|
}
|
|
|
|
|
2021-04-14 07:54:19 +08:00
|
|
|
struct TestApp {
|
|
|
|
app: HeadlessApp,
|
2021-04-24 05:41:28 +08:00
|
|
|
pub window_id: WindowId,
|
|
|
|
|
2021-06-04 11:31:21 +08:00
|
|
|
focus_changed: EventBuffer<FocusChangedEvent>,
|
|
|
|
return_focus_changed: EventBuffer<ReturnFocusChangedEvent>,
|
2021-04-14 07:54:19 +08:00
|
|
|
}
|
|
|
|
impl TestApp {
|
|
|
|
pub fn new(content: impl UiNode) -> Self {
|
2021-04-15 08:52:19 +08:00
|
|
|
Self::new_w(window!(content))
|
|
|
|
}
|
|
|
|
pub fn new_w(window: Window) -> Self {
|
2021-04-14 07:54:19 +08:00
|
|
|
let mut app = App::default().run_headless();
|
2021-04-24 05:41:28 +08:00
|
|
|
|
2021-06-03 08:37:37 +08:00
|
|
|
let (focus_changed, return_focus_changed) = {
|
|
|
|
let ctx = app.ctx();
|
2021-06-01 08:47:00 +08:00
|
|
|
let fc = ctx.events.buffer::<zero_ui::core::focus::FocusChangedEvent>();
|
|
|
|
let rfc = ctx.events.buffer::<zero_ui::core::focus::ReturnFocusChangedEvent>();
|
2021-04-24 05:41:28 +08:00
|
|
|
(fc, rfc)
|
2021-06-03 08:37:37 +08:00
|
|
|
};
|
2021-04-24 05:41:28 +08:00
|
|
|
|
2021-04-15 08:52:19 +08:00
|
|
|
let window_id = app.open_window(move |_| window);
|
2021-04-24 05:41:28 +08:00
|
|
|
TestApp {
|
|
|
|
app,
|
|
|
|
window_id,
|
|
|
|
focus_changed,
|
|
|
|
return_focus_changed,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-14 09:52:37 +08:00
|
|
|
pub fn set_vars(&mut self, set: impl FnOnce(&Vars)) {
|
2021-06-03 08:37:37 +08:00
|
|
|
set(self.app.ctx().vars);
|
2021-05-19 11:17:59 +08:00
|
|
|
self.app.update(false); // notify vars.
|
|
|
|
self.app.update(false); // respond to new frame.
|
2021-05-14 09:52:37 +08:00
|
|
|
}
|
|
|
|
|
2021-04-24 05:41:28 +08:00
|
|
|
pub fn set_shutdown_on_last_close(&mut self, shutdown: bool) {
|
2021-06-03 08:37:37 +08:00
|
|
|
let w = self.app.ctx().services.req::<zero_ui::core::window::Windows>();
|
|
|
|
w.shutdown_on_last_close = shutdown;
|
2021-04-24 05:41:28 +08:00
|
|
|
}
|
|
|
|
|
2021-05-14 12:12:14 +08:00
|
|
|
pub fn close_main_window(&mut self) {
|
2021-04-24 05:41:28 +08:00
|
|
|
let closed = self.app.close_window(self.window_id);
|
|
|
|
assert!(closed);
|
|
|
|
}
|
|
|
|
|
2021-05-14 12:12:14 +08:00
|
|
|
pub fn open_window(&mut self, content: impl UiNode) -> WindowId {
|
|
|
|
let id = self.app.open_window(|_| {
|
|
|
|
window! {
|
|
|
|
content
|
|
|
|
}
|
|
|
|
});
|
2021-05-19 11:17:59 +08:00
|
|
|
self.app.update(false);
|
2021-05-14 12:12:14 +08:00
|
|
|
id
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
pub fn close_window(&mut self, window_id: WindowId) {
|
|
|
|
let closed = self.app.close_window(window_id);
|
|
|
|
assert!(closed);
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
|
2021-04-24 05:41:28 +08:00
|
|
|
pub fn take_focus_changed(&mut self) -> Vec<FocusChangedArgs> {
|
|
|
|
self.focus_changed.pop_all()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn take_return_focus_changed(&mut self) -> Vec<ReturnFocusChangedArgs> {
|
|
|
|
self.return_focus_changed.pop_all()
|
2021-04-14 07:54:19 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn focused(&mut self) -> Option<WidgetId> {
|
2021-06-12 08:00:08 +08:00
|
|
|
self.app.ctx().services.focus().focused().map(|w| w.widget_id())
|
2021-04-14 07:54:19 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn press_tab(&mut self) {
|
|
|
|
self.app.press_key(self.window_id, Key::Tab)
|
|
|
|
}
|
2021-04-15 08:52:19 +08:00
|
|
|
pub fn press_shift_tab(&mut self) {
|
|
|
|
self.app.press_shortcut(self.window_id, shortcut!(SHIFT + Tab));
|
|
|
|
}
|
|
|
|
|
2021-04-14 11:38:26 +08:00
|
|
|
pub fn press_alt(&mut self) {
|
|
|
|
self.app.press_key(self.window_id, Key::LAlt);
|
|
|
|
}
|
|
|
|
pub fn press_esc(&mut self) {
|
|
|
|
self.app.press_key(self.window_id, Key::Escape);
|
|
|
|
}
|
2021-04-15 08:52:19 +08:00
|
|
|
|
2021-05-27 05:09:33 +08:00
|
|
|
pub fn press_up(&mut self) {
|
|
|
|
self.app.press_key(self.window_id, Key::Up);
|
|
|
|
}
|
|
|
|
pub fn press_down(&mut self) {
|
|
|
|
self.app.press_key(self.window_id, Key::Down);
|
|
|
|
}
|
|
|
|
pub fn press_left(&mut self) {
|
|
|
|
self.app.press_key(self.window_id, Key::Left);
|
|
|
|
}
|
|
|
|
pub fn press_right(&mut self) {
|
|
|
|
self.app.press_key(self.window_id, Key::Right);
|
|
|
|
}
|
|
|
|
|
2021-04-27 07:13:38 +08:00
|
|
|
pub fn just_release_alt(&mut self) {
|
|
|
|
self.app.on_keyboard_input(self.window_id, Key::LAlt, ElementState::Released);
|
2021-05-19 11:17:59 +08:00
|
|
|
self.app.update(false);
|
2021-04-27 07:13:38 +08:00
|
|
|
}
|
|
|
|
|
2021-04-15 08:52:19 +08:00
|
|
|
pub fn focus(&mut self, widget_id: WidgetId) {
|
2021-06-12 08:00:08 +08:00
|
|
|
self.app.ctx().services.focus().focus_widget(widget_id, true);
|
2021-05-19 11:17:59 +08:00
|
|
|
self.app.update(false);
|
2021-04-15 08:52:19 +08:00
|
|
|
}
|
2021-04-27 07:13:38 +08:00
|
|
|
|
2021-05-14 09:52:37 +08:00
|
|
|
pub fn focus_or_parent(&mut self, widget_id: WidgetId) {
|
2021-06-12 08:00:08 +08:00
|
|
|
self.app.ctx().services.focus().focus_widget_or_exit(widget_id, true);
|
2021-05-19 11:17:59 +08:00
|
|
|
self.app.update(false);
|
2021-05-14 09:52:37 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn focus_or_child(&mut self, widget_id: WidgetId) {
|
2021-06-12 08:00:08 +08:00
|
|
|
self.app.ctx().services.focus().focus_widget_or_enter(widget_id, true);
|
2021-05-19 11:17:59 +08:00
|
|
|
self.app.update(false);
|
2021-05-14 09:52:37 +08:00
|
|
|
}
|
|
|
|
|
2021-05-18 09:24:48 +08:00
|
|
|
pub fn focus_window(&mut self) {
|
|
|
|
self.app.focus_window(self.window_id)
|
2021-04-27 07:13:38 +08:00
|
|
|
}
|
|
|
|
|
2021-05-18 09:24:48 +08:00
|
|
|
pub fn blur_window(&mut self) {
|
|
|
|
self.app.blur_window(self.window_id)
|
2021-04-27 07:13:38 +08:00
|
|
|
}
|
2021-04-14 07:54:19 +08:00
|
|
|
}
|