Add FocusEventReason to FocusEvent and add a select all on keyboard focus for TextInput (#8523)

Closes #5992

Adds the enum FocusEventReason and makes it an argument for FocusEvent. This reason could eventually be exposed in Slint to solve #8387.

Using the focus reason tracking, I also added a select all on keyboard focus for TextInputs (except on macOS), which should close #5992.

ChangeLog: TextInput selects its content when focused with the keyboard on Windows and Linux
This commit is contained in:
Avery Townsend 2025-05-27 01:56:13 -04:00 committed by GitHub
parent 370f72a15d
commit c41d4a4df3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 225 additions and 86 deletions

View file

@ -135,10 +135,11 @@ public:
items, &inner);
}
void set_focus_item(const ItemTreeRc &component_rc, uint32_t item_index, bool set_focus)
void set_focus_item(const ItemTreeRc &component_rc, uint32_t item_index, bool set_focus,
cbindgen_private::FocusEventReason reason)
{
cbindgen_private::ItemRc item_rc { component_rc, item_index };
cbindgen_private::slint_windowrc_set_focus_item(&inner, &item_rc, set_focus);
cbindgen_private::slint_windowrc_set_focus_item(&inner, &item_rc, set_focus, reason);
}
void set_component(const cbindgen_private::ItemTreeWeak &weak) const

View file

@ -193,8 +193,8 @@ pub mod re_exports {
pub use i_slint_core::detect_operating_system;
pub use i_slint_core::graphics::*;
pub use i_slint_core::input::{
key_codes::Key, FocusEvent, InputEventResult, KeyEvent, KeyEventResult, KeyboardModifiers,
MouseEvent,
key_codes::Key, FocusEvent, FocusEventReason, InputEventResult, KeyEvent, KeyEventResult,
KeyboardModifiers, MouseEvent,
};
pub use i_slint_core::item_tree::{
register_item_tree, unregister_item_tree, IndexRange, ItemTree, ItemTreeRefPin,

View file

@ -318,7 +318,7 @@ impl Item for NativeButton {
Self::FIELD_OFFSETS
.has_focus
.apply_pin(self)
.set(event == &FocusEvent::FocusIn || event == &FocusEvent::WindowReceivedFocus);
.set(matches!(event, FocusEvent::FocusIn(_)));
FocusEventResult::FocusAccepted
} else {
FocusEventResult::FocusIgnored

View file

@ -129,7 +129,7 @@ impl Item for NativeCheckBox {
Self::FIELD_OFFSETS
.has_focus
.apply_pin(self)
.set(event == &FocusEvent::FocusIn || event == &FocusEvent::WindowReceivedFocus);
.set(matches!(event, FocusEvent::FocusIn(_)));
FocusEventResult::FocusAccepted
} else {
FocusEventResult::FocusIgnored

View file

@ -2,8 +2,7 @@
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
use i_slint_core::{
input::key_codes,
input::{FocusEventResult, KeyEventType},
input::{key_codes, FocusEventReason, FocusEventResult, KeyEventType},
items::PointerEventButton,
};
@ -211,7 +210,11 @@ impl Item for NativeSlider {
click_count: _,
} => {
if !self.has_focus() {
WindowInner::from_pub(window_adapter.window()).set_focus_item(self_rc, true);
WindowInner::from_pub(window_adapter.window()).set_focus_item(
self_rc,
true,
FocusEventReason::Mouse,
);
}
data.pressed_x = if vertical { pos.y as f32 } else { pos.x as f32 };
data.pressed = 1;
@ -317,7 +320,7 @@ impl Item for NativeSlider {
Self::FIELD_OFFSETS
.has_focus
.apply_pin(self)
.set(event == &FocusEvent::FocusIn || event == &FocusEvent::WindowReceivedFocus);
.set(matches!(event, FocusEvent::FocusIn(_)));
FocusEventResult::FocusAccepted
} else {
FocusEventResult::FocusIgnored

View file

@ -3,7 +3,7 @@
use crate::key_generated;
use i_slint_core::{
input::{FocusEventResult, KeyEventType},
input::{FocusEventReason, FocusEventResult, KeyEventType},
items::TextHorizontalAlignment,
platform::PointerEventButton,
};
@ -232,7 +232,11 @@ impl Item for NativeSpinBox {
if let MouseEvent::Pressed { .. } = event {
if !self.has_focus() {
WindowInner::from_pub(window_adapter.window()).set_focus_item(self_rc, true);
WindowInner::from_pub(window_adapter.window()).set_focus_item(
self_rc,
true,
FocusEventReason::Mouse,
);
}
}
InputEventResult::EventAccepted
@ -273,15 +277,14 @@ impl Item for NativeSpinBox {
_self_rc: &ItemRc,
) -> FocusEventResult {
match event {
FocusEvent::FocusIn => {
FocusEvent::FocusIn(_) => {
if self.enabled() {
self.has_focus.set(true);
}
}
FocusEvent::FocusOut | FocusEvent::WindowLostFocus => {
FocusEvent::FocusOut(_) => {
self.has_focus.set(false);
}
FocusEvent::WindowReceivedFocus => self.has_focus.set(true),
}
FocusEventResult::FocusAccepted
}

View file

@ -3,7 +3,10 @@
// cSpell: ignore hframe qreal tabbar vframe
use i_slint_core::{input::FocusEventResult, platform::PointerEventButton};
use i_slint_core::{
input::{FocusEventReason, FocusEventResult},
platform::PointerEventButton,
};
use super::*;
@ -455,7 +458,11 @@ impl Item for NativeTab {
if matches!(event, MouseEvent::Released { button, .. } if !click_on_press && button == PointerEventButton::Left)
|| matches!(event, MouseEvent::Pressed { button, .. } if click_on_press && button == PointerEventButton::Left)
{
WindowInner::from_pub(window_adapter.window()).set_focus_item(self_rc, true);
WindowInner::from_pub(window_adapter.window()).set_focus_item(
self_rc,
true,
FocusEventReason::Mouse,
);
self.current.set(self.tab_index());
InputEventResult::EventAccepted
} else {

View file

@ -11,6 +11,7 @@ use i_slint_core::accessibility::{
AccessibilityAction, AccessibleStringProperty, SupportedAccessibilityAction,
};
use i_slint_core::api::Window;
use i_slint_core::input::FocusEventReason;
use i_slint_core::item_tree::{ItemTreeRc, ItemTreeRef, ItemTreeWeak, ParentItemTraversalMode};
use i_slint_core::items::{ItemRc, WindowItem};
use i_slint_core::lengths::{LogicalPoint, ScaleFactor};
@ -740,7 +741,11 @@ impl DeferredAccessKitAction {
pub fn invoke(&self, window: &Window) {
match self {
DeferredAccessKitAction::SetFocus(item) => {
WindowInner::from_pub(window).set_focus_item(item, true);
WindowInner::from_pub(window).set_focus_item(
item,
true,
FocusEventReason::AccessKit,
);
}
DeferredAccessKitAction::InvokeAccessibleAction(item, accessibility_action) => {
item.accessible_action(accessibility_action);

View file

@ -3611,7 +3611,7 @@ fn compile_builtin_function_call(
if let [llr::Expression::PropertyReference(pr)] = arguments {
let window = access_window_field(ctx);
let focus_item = access_item_rc(pr, ctx);
format!("{window}.set_focus_item({focus_item}, true);")
format!("{window}.set_focus_item({focus_item}, true, slint::cbindgen_private::FocusEventReason::BuiltinFunction);")
} else {
panic!("internal error: invalid args to SetFocusItem {arguments:?}")
}
@ -3620,7 +3620,7 @@ fn compile_builtin_function_call(
if let [llr::Expression::PropertyReference(pr)] = arguments {
let window = access_window_field(ctx);
let focus_item = access_item_rc(pr, ctx);
format!("{window}.set_focus_item({focus_item}, false);")
format!("{window}.set_focus_item({focus_item}, false, slint::cbindgen_private::FocusEventReason::BuiltinFunction);")
} else {
panic!("internal error: invalid args to ClearFocusItem {arguments:?}")
}

View file

@ -2672,7 +2672,7 @@ fn compile_builtin_function_call(
let window_tokens = access_window_adapter_field(ctx);
let focus_item = access_item_rc(pr, ctx);
quote!(
sp::WindowInner::from_pub(#window_tokens.window()).set_focus_item(#focus_item, true)
sp::WindowInner::from_pub(#window_tokens.window()).set_focus_item(#focus_item, true, sp::FocusEventReason::BuiltinFunction)
)
} else {
panic!("internal error: invalid args to SetFocusItem {arguments:?}")
@ -2683,7 +2683,7 @@ fn compile_builtin_function_call(
let window_tokens = access_window_adapter_field(ctx);
let focus_item = access_item_rc(pr, ctx);
quote!(
sp::WindowInner::from_pub(#window_tokens.window()).set_focus_item(#focus_item, false)
sp::WindowInner::from_pub(#window_tokens.window()).set_focus_item(#focus_item, false, sp::FocusEventReason::BuiltinFunction)
)
} else {
panic!("internal error: invalid args to ClearFocusItem {arguments:?}")

View file

@ -455,19 +455,33 @@ pub enum FocusEventResult {
FocusIgnored,
}
/// This enum describes the different reasons for a FocusEvent
#[derive(Debug, Clone, Copy, PartialEq)]
#[repr(u8)]
pub enum FocusEventReason {
/// The keyboard caused the event (tabbing)
Keyboard,
/// The mouse caused the event
Mouse,
/// A popup caused the event
Popup,
/// A built-in function caused the event (set-focus-item, clear-focus-item)
BuiltinFunction,
/// AccessKit caused the event
AccessKit,
/// The window manager changed the active window and caused the event
ActiveWindow,
}
/// This event is sent to a component and items when they receive or loose
/// the keyboard focus.
#[derive(Debug, Clone, Copy, PartialEq)]
#[repr(u8)]
pub enum FocusEvent {
/// This event is sent when an item receives the focus.
FocusIn,
FocusIn(FocusEventReason),
/// This event is sent when an item looses the focus.
FocusOut,
/// This event is sent when the window receives the keyboard focus.
WindowReceivedFocus,
/// This event is sent when the window looses the keyboard focus. (including if this is because of a popup)
WindowLostFocus,
FocusOut(FocusEventReason),
}
/// This state is used to count the clicks separated by [`crate::platform::Platform::click_interval`]

View file

@ -8,8 +8,8 @@ use super::{
};
use crate::api::LogicalPosition;
use crate::input::{
FocusEvent, FocusEventResult, InputEventFilterResult, InputEventResult, KeyEvent,
KeyEventResult, KeyEventType, MouseEvent,
FocusEvent, FocusEventReason, FocusEventResult, InputEventFilterResult, InputEventResult,
KeyEvent, KeyEventResult, KeyEventType, MouseEvent,
};
use crate::item_rendering::CachedRenderingData;
use crate::layout::{LayoutInfo, Orientation};
@ -294,7 +294,11 @@ impl Item for FocusScope {
self_rc: &ItemRc,
) -> InputEventResult {
if self.enabled() && matches!(event, MouseEvent::Pressed { .. }) && !self.has_focus() {
WindowInner::from_pub(window_adapter.window()).set_focus_item(self_rc, true);
WindowInner::from_pub(window_adapter.window()).set_focus_item(
self_rc,
true,
FocusEventReason::Mouse,
);
InputEventResult::EventAccepted
} else {
InputEventResult::EventIgnored
@ -335,11 +339,11 @@ impl Item for FocusScope {
}
match event {
FocusEvent::FocusIn | FocusEvent::WindowReceivedFocus => {
FocusEvent::FocusIn(_) => {
self.has_focus.set(true);
Self::FIELD_OFFSETS.focus_changed_event.apply_pin(self).call(&());
}
FocusEvent::FocusOut | FocusEvent::WindowLostFocus => {
FocusEvent::FocusOut(_) => {
self.has_focus.set(false);
Self::FIELD_OFFSETS.focus_changed_event.apply_pin(self).call(&());
}

View file

@ -15,8 +15,8 @@ use super::{
};
use crate::graphics::{Brush, Color, FontRequest};
use crate::input::{
key_codes, FocusEvent, FocusEventResult, InputEventFilterResult, InputEventResult, KeyEvent,
KeyboardModifiers, MouseEvent, StandardShortcut, TextShortcut,
key_codes, FocusEvent, FocusEventReason, FocusEventResult, InputEventFilterResult,
InputEventResult, KeyEvent, KeyboardModifiers, MouseEvent, StandardShortcut, TextShortcut,
};
use crate::item_rendering::{CachedRenderingData, ItemRenderer, RenderText};
use crate::layout::{LayoutInfo, Orientation};
@ -910,7 +910,7 @@ impl Item for TextInput {
self_rc: &ItemRc,
) -> FocusEventResult {
match event {
FocusEvent::FocusIn | FocusEvent::WindowReceivedFocus => {
FocusEvent::FocusIn(_reason) => {
self.has_focus.set(true);
self.show_cursor(window_adapter);
WindowInner::from_pub(window_adapter.window()).set_text_input_focused(true);
@ -921,12 +921,20 @@ impl Item for TextInput {
self.ime_properties(window_adapter, self_rc),
));
}
#[cfg(not(target_vendor = "apple"))]
{
// check self.enabled() to make sure it doesn't select disabled (greyed-out) inputs
if *_reason == FocusEventReason::Keyboard && self.enabled() {
self.select_all(window_adapter, self_rc);
}
}
FocusEvent::FocusOut | FocusEvent::WindowLostFocus => {
}
}
FocusEvent::FocusOut(reason) => {
self.has_focus.set(false);
self.hide_cursor();
if matches!(event, FocusEvent::FocusOut) {
if !matches!(reason, FocusEventReason::ActiveWindow | FocusEventReason::Popup) {
self.as_ref()
.anchor_position_byte_offset
.set(self.as_ref().cursor_position_byte_offset());
@ -1777,7 +1785,11 @@ impl TextInput {
self_rc: &ItemRc,
) {
if !self.has_focus() {
WindowInner::from_pub(window_adapter.window()).set_focus_item(self_rc, true);
WindowInner::from_pub(window_adapter.window()).set_focus_item(
self_rc,
true,
FocusEventReason::Mouse,
);
} else if !self.read_only() {
if let Some(w) = window_adapter.internal(crate::InternalToken) {
w.input_method_request(InputMethodRequest::Enable(

View file

@ -11,8 +11,8 @@ use crate::api::{
WindowPosition, WindowSize,
};
use crate::input::{
key_codes, ClickState, FocusEvent, InternalKeyboardModifierState, KeyEvent, KeyEventType,
MouseEvent, MouseInputState, TextCursorBlinker,
key_codes, ClickState, FocusEvent, FocusEventReason, InternalKeyboardModifierState, KeyEvent,
KeyEventType, MouseEvent, MouseInputState, TextCursorBlinker,
};
use crate::item_tree::{
ItemRc, ItemTreeRc, ItemTreeRef, ItemTreeVTable, ItemTreeWeak, ItemWeak,
@ -762,7 +762,7 @@ impl WindowInner {
if item.as_ref().is_some_and(|i| !i.is_visible()) {
// Reset the focus... not great, but better than keeping it.
self.take_focus_item(&FocusEvent::FocusOut);
self.take_focus_item(&FocusEvent::FocusOut(FocusEventReason::Keyboard));
item = None;
}
@ -835,7 +835,12 @@ impl WindowInner {
/// Sets the focus to the item pointed to by item_ptr. This will remove the focus from any
/// currently focused item. If set_focus is false, the focus is cleared.
pub fn set_focus_item(&self, new_focus_item: &ItemRc, set_focus: bool) {
pub fn set_focus_item(
&self,
new_focus_item: &ItemRc,
set_focus: bool,
reason: FocusEventReason,
) {
if self.prevent_focus_change.get() {
return;
}
@ -846,7 +851,7 @@ impl WindowInner {
});
if let Some(popup_wa) = popup_wa {
// Set the focus item on the popup's Window instead
popup_wa.window().0.set_focus_item(new_focus_item, set_focus);
popup_wa.window().0.set_focus_item(new_focus_item, set_focus, reason);
return;
}
@ -863,9 +868,12 @@ impl WindowInner {
}
}
let old = self.take_focus_item(&FocusEvent::FocusOut);
let new =
if set_focus { self.move_focus(new_focus_item.clone(), next_focus_item) } else { None };
let old = self.take_focus_item(&FocusEvent::FocusOut(reason));
let new = if set_focus {
self.move_focus(new_focus_item.clone(), next_focus_item, reason)
} else {
None
};
let window_adapter = self.window_adapter();
if let Some(window_adapter) = window_adapter.internal(crate::InternalToken) {
window_adapter.handle_focus_change(old, new);
@ -877,7 +885,7 @@ impl WindowInner {
/// This sends the event whiwh must be either FocusOut or WindowLostFocus for popups
fn take_focus_item(&self, event: &FocusEvent) -> Option<ItemRc> {
let focus_item = self.focus_item.take();
assert!(matches!(event, FocusEvent::FocusOut | FocusEvent::WindowLostFocus));
assert!(matches!(event, FocusEvent::FocusOut(_)));
if let Some(focus_item_rc) = focus_item.upgrade() {
focus_item_rc.borrow().as_ref().focus_event(
@ -894,12 +902,16 @@ impl WindowInner {
/// Publish the new focus_item to this Window and return the FocusEventResult
///
/// This sends a FocusIn event!
fn publish_focus_item(&self, item: &Option<ItemRc>) -> crate::input::FocusEventResult {
fn publish_focus_item(
&self,
item: &Option<ItemRc>,
reason: FocusEventReason,
) -> crate::input::FocusEventResult {
match item {
Some(item) => {
*self.focus_item.borrow_mut() = item.downgrade();
item.borrow().as_ref().focus_event(
&FocusEvent::FocusIn,
&FocusEvent::FocusIn(reason),
&self.window_adapter(),
item,
)
@ -911,13 +923,18 @@ impl WindowInner {
}
}
fn move_focus(&self, start_item: ItemRc, forward: impl Fn(ItemRc) -> ItemRc) -> Option<ItemRc> {
fn move_focus(
&self,
start_item: ItemRc,
forward: impl Fn(ItemRc) -> ItemRc,
reason: FocusEventReason,
) -> Option<ItemRc> {
let mut current_item = start_item;
let mut visited = Vec::new();
loop {
if current_item.is_visible()
&& self.publish_focus_item(&Some(current_item.clone()))
&& self.publish_focus_item(&Some(current_item.clone()), reason)
== crate::input::FocusEventResult::FocusAccepted
{
return Some(current_item); // Item was just published.
@ -933,8 +950,10 @@ impl WindowInner {
/// Move keyboard focus to the next item
pub fn focus_next_item(&self) {
let start_item =
self.take_focus_item(&FocusEvent::FocusOut).map(next_focus_item).unwrap_or_else(|| {
let start_item = self
.take_focus_item(&FocusEvent::FocusOut(FocusEventReason::Keyboard))
.map(next_focus_item)
.unwrap_or_else(|| {
ItemRc::new(
self.active_popups
.borrow()
@ -943,7 +962,8 @@ impl WindowInner {
0,
)
});
let end_item = self.move_focus(start_item.clone(), next_focus_item);
let end_item =
self.move_focus(start_item.clone(), next_focus_item, FocusEventReason::Keyboard);
let window_adapter = self.window_adapter();
if let Some(window_adapter) = window_adapter.internal(crate::InternalToken) {
window_adapter.handle_focus_change(Some(start_item), end_item);
@ -952,8 +972,9 @@ impl WindowInner {
/// Move keyboard focus to the previous item.
pub fn focus_previous_item(&self) {
let start_item =
previous_focus_item(self.take_focus_item(&FocusEvent::FocusOut).unwrap_or_else(|| {
let start_item = previous_focus_item(
self.take_focus_item(&FocusEvent::FocusOut(FocusEventReason::Keyboard)).unwrap_or_else(
|| {
ItemRc::new(
self.active_popups
.borrow()
@ -961,8 +982,11 @@ impl WindowInner {
.map_or_else(|| self.component(), |p| p.component.clone()),
0,
)
}));
let end_item = self.move_focus(start_item.clone(), previous_focus_item);
},
),
);
let end_item =
self.move_focus(start_item.clone(), previous_focus_item, FocusEventReason::Keyboard);
let window_adapter = self.window_adapter();
if let Some(window_adapter) = window_adapter.internal(crate::InternalToken) {
window_adapter.handle_focus_change(Some(start_item), end_item);
@ -977,8 +1001,11 @@ impl WindowInner {
pub fn set_active(&self, have_focus: bool) {
self.pinned_fields.as_ref().project_ref().active.set(have_focus);
let event =
if have_focus { FocusEvent::WindowReceivedFocus } else { FocusEvent::WindowLostFocus };
let event = if have_focus {
FocusEvent::FocusIn(FocusEventReason::ActiveWindow)
} else {
FocusEvent::FocusOut(FocusEventReason::ActiveWindow)
};
if let Some(focus_item) = self.focus_item.borrow().upgrade() {
focus_item.borrow().as_ref().focus_event(&event, &self.window_adapter(), &focus_item);
@ -1207,7 +1234,7 @@ impl WindowInner {
};
let focus_item = self
.take_focus_item(&FocusEvent::WindowLostFocus)
.take_focus_item(&FocusEvent::FocusOut(FocusEventReason::Popup))
.map(|item| item.downgrade())
.unwrap_or_default();
@ -1260,7 +1287,7 @@ impl WindowInner {
}
}
if let Some(focus) = current_popup.focus_item_in_parent.upgrade() {
self.set_focus_item(&focus, true);
self.set_focus_item(&focus, true, FocusEventReason::Popup);
}
}
@ -1572,9 +1599,10 @@ pub mod ffi {
handle: *const WindowAdapterRcOpaque,
focus_item: &ItemRc,
set_focus: bool,
reason: FocusEventReason,
) {
let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
WindowInner::from_pub(window_adapter.window()).set_focus_item(focus_item, set_focus)
WindowInner::from_pub(window_adapter.window()).set_focus_item(focus_item, set_focus, reason)
}
/// Associates the window with the given component.

View file

@ -19,6 +19,7 @@ use i_slint_compiler::langtype::Type;
use i_slint_compiler::namedreference::NamedReference;
use i_slint_compiler::object_tree::ElementRc;
use i_slint_core as corelib;
use i_slint_core::input::FocusEventReason;
use i_slint_core::items::ItemRc;
use smol_str::SmolStr;
use std::collections::HashMap;
@ -532,6 +533,7 @@ fn call_builtin_function(
item_info.item_index(),
),
true,
FocusEventReason::BuiltinFunction,
)
});
Value::Void
@ -564,6 +566,7 @@ fn call_builtin_function(
item_info.item_index(),
),
false,
FocusEventReason::BuiltinFunction,
)
});
Value::Void

View file

@ -66,6 +66,8 @@ export component TestCase inherits Window {
/*
```rust
// some of these tests vary per platform because tabbing for
// focus on platforms other than macOS causes a select all
let instance = TestCase::new().unwrap();
slint_testing::send_keyboard_string_sequence(&instance, "X");
assert_eq!(instance.get_le1_text(), "X");
@ -79,39 +81,96 @@ assert_eq!(instance.get_inner_text(), "");
assert_eq!(instance.get_inner2_text(), "");
slint_testing::send_keyboard_string_sequence(&instance, "\t\tZ");
assert_eq!(instance.get_le1_text(), "X");
assert_eq!(instance.get_le2_text(), "YZ");
#[cfg(not(target_vendor = "apple"))]
{
assert_eq!(instance.get_le2_text(), "Z");
}
#[cfg(target_vendor = "apple")]
{
assert_eq!(instance.get_le2_text(), "YZ");
}
assert_eq!(instance.get_inner_text(), "");
assert_eq!(instance.get_inner2_text(), "");
slint_testing::send_keyboard_string_sequence(&instance, "\nA");
assert_eq!(instance.get_le1_text(), "X");
assert_eq!(instance.get_le2_text(), "YZ");
#[cfg(not(target_vendor = "apple"))]
{
assert_eq!(instance.get_le2_text(), "Z");
}
#[cfg(target_vendor = "apple")]
{
assert_eq!(instance.get_le2_text(), "YZ");
}
assert_eq!(instance.get_inner_text(), "A");
assert_eq!(instance.get_inner2_text(), "");
slint_testing::send_keyboard_string_sequence(&instance, "\tB");
assert_eq!(instance.get_le1_text(), "X");
assert_eq!(instance.get_le2_text(), "YZ");
#[cfg(not(target_vendor = "apple"))]
{
assert_eq!(instance.get_le2_text(), "Z");
}
#[cfg(target_vendor = "apple")]
{
assert_eq!(instance.get_le2_text(), "YZ");
}
assert_eq!(instance.get_inner_text(), "A");
assert_eq!(instance.get_inner2_text(), "B");
slint_testing::send_keyboard_string_sequence(&instance, "\tC");
assert_eq!(instance.get_le1_text(), "X");
assert_eq!(instance.get_le2_text(), "YZ");
assert_eq!(instance.get_inner_text(), "AC");
#[cfg(not(target_vendor = "apple"))]
{
assert_eq!(instance.get_le2_text(), "Z");
assert_eq!(instance.get_inner_text(), "C");
}
#[cfg(target_vendor = "apple")]
{
assert_eq!(instance.get_le2_text(), "YZ");
assert_eq!(instance.get_inner_text(), "AC");
}
assert_eq!(instance.get_inner2_text(), "B");
// backwards tab
slint_testing::send_keyboard_string_sequence(&instance, "\u{0019}D\u{0019}E");
assert_eq!(instance.get_le1_text(), "X");
assert_eq!(instance.get_le2_text(), "YZ");
assert_eq!(instance.get_inner_text(), "ACE");
assert_eq!(instance.get_inner2_text(), "BD");
#[cfg(not(target_vendor = "apple"))]
{
assert_eq!(instance.get_le2_text(), "Z");
assert_eq!(instance.get_inner_text(), "E");
assert_eq!(instance.get_inner2_text(), "D");
}
#[cfg(target_vendor = "apple")]
{
assert_eq!(instance.get_le2_text(), "YZ");
assert_eq!(instance.get_inner_text(), "ACE");
assert_eq!(instance.get_inner2_text(), "BD");
}
slint_testing::send_keyboard_string_sequence(&instance, "\nQ");
assert_eq!(instance.get_le1_text(), "X");
assert_eq!(instance.get_le2_text(), "YZQ");
assert_eq!(instance.get_inner_text(), "ACE");
assert_eq!(instance.get_inner2_text(), "BD");
#[cfg(not(target_vendor = "apple"))]
{
assert_eq!(instance.get_le2_text(), "ZQ");
assert_eq!(instance.get_inner_text(), "E");
assert_eq!(instance.get_inner2_text(), "D");
}
#[cfg(target_vendor = "apple")]
{
assert_eq!(instance.get_le2_text(), "YZQ");
assert_eq!(instance.get_inner_text(), "ACE");
assert_eq!(instance.get_inner2_text(), "BD");
}
slint_testing::send_keyboard_string_sequence(&instance, "\tR");
assert_eq!(instance.get_le1_text(), "XR");
assert_eq!(instance.get_le2_text(), "YZQ");
assert_eq!(instance.get_inner_text(), "ACE");
assert_eq!(instance.get_inner2_text(), "BD");
#[cfg(not(target_vendor = "apple"))]
{
assert_eq!(instance.get_le1_text(), "R");
assert_eq!(instance.get_le2_text(), "ZQ");
assert_eq!(instance.get_inner_text(), "E");
assert_eq!(instance.get_inner2_text(), "D");
}
#[cfg(target_vendor = "apple")]
{
assert_eq!(instance.get_le1_text(), "XR");
assert_eq!(instance.get_le2_text(), "YZQ");
assert_eq!(instance.get_inner_text(), "ACE");
assert_eq!(instance.get_inner2_text(), "BD");
}
```
*/