mirror of
https://github.com/slint-ui/slint.git
synced 2025-10-01 06:11:16 +00:00
Propagate key event to parent if the item don't handle it
Does not work for C++ because binary compatibility issue
This commit is contained in:
parent
c2982d9ab3
commit
802383cd6b
8 changed files with 133 additions and 15 deletions
|
@ -25,10 +25,11 @@ inline void send_mouse_click(const ComponentHandle<Component> *component, float
|
|||
}
|
||||
|
||||
template<typename Component>
|
||||
inline void send_keyboard_string_sequence(const Component &component,
|
||||
const sixtyfps::SharedString &str)
|
||||
inline void send_keyboard_string_sequence(const Component *component,
|
||||
const sixtyfps::SharedString &str,
|
||||
KeyboardModifiers modifiers = {})
|
||||
{
|
||||
cbindgen_private::send_keyboard_string_sequence(&str, {}, &component.window);
|
||||
cbindgen_private::send_keyboard_string_sequence(&str, modifiers, &component->window);
|
||||
}
|
||||
|
||||
#define assert_eq(A, B) \
|
||||
|
|
|
@ -194,6 +194,7 @@ pub struct KeyEvent {
|
|||
/// Represents how an item's key_event handler dealt with a key event.
|
||||
/// An accepted event results in no further event propagation.
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum KeyEventResult {
|
||||
/// The event was handled.
|
||||
EventAccepted,
|
||||
|
@ -203,7 +204,7 @@ pub enum KeyEventResult {
|
|||
|
||||
/// This event is sent to a component and items when they receive or loose
|
||||
/// the keyboard focus.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
#[repr(C)]
|
||||
pub enum FocusEvent {
|
||||
/// This event is sent when an item receives the focus.
|
||||
|
|
|
@ -128,6 +128,10 @@ impl ItemRc {
|
|||
pub fn downgrade(&self) -> ItemWeak {
|
||||
ItemWeak { component: VRc::downgrade(&self.component), index: self.index }
|
||||
}
|
||||
pub fn parent_item(&self) -> ItemWeak {
|
||||
let comp_ref_pin = vtable::VRc::borrow_pin(&self.component);
|
||||
comp_ref_pin.as_ref().parent_item(self.index)
|
||||
}
|
||||
}
|
||||
|
||||
/// A Weak reference to an item that can be constructed from an ItemRc.
|
||||
|
|
|
@ -292,12 +292,11 @@ impl Item for TextInput {
|
|||
return KeyEventResult::EventAccepted;
|
||||
}
|
||||
}
|
||||
KeyEventResult::EventIgnored
|
||||
}
|
||||
KeyEventType::KeyReleased
|
||||
|
||||
// Only insert/interpreter non-control character strings
|
||||
if !event.text.is_empty() && event.text.as_str().chars().all(|ch| !ch.is_control()) =>
|
||||
{
|
||||
if event.text.is_empty() || event.text.as_str().chars().any(|ch| ch.is_control()) {
|
||||
return KeyEventResult::EventIgnored;
|
||||
}
|
||||
if event.modifiers.control {
|
||||
if event.text == "c" {
|
||||
self.copy();
|
||||
|
@ -306,6 +305,7 @@ impl Item for TextInput {
|
|||
self.paste();
|
||||
return KeyEventResult::EventAccepted;
|
||||
}
|
||||
return KeyEventResult::EventIgnored;
|
||||
}
|
||||
self.delete_selection();
|
||||
|
||||
|
|
|
@ -135,9 +135,15 @@ impl Window {
|
|||
/// * `event`: The key event received by the windowing system.
|
||||
/// * `component`: The SixtyFPS compiled component that provides the tree of items.
|
||||
pub fn process_key_input(self: Rc<Self>, event: &KeyEvent) {
|
||||
if let Some(focus_item) = self.focus_item.borrow().upgrade() {
|
||||
let mut item = self.focus_item.borrow().clone();
|
||||
while let Some(focus_item) = item.upgrade() {
|
||||
let window = &ComponentWindow::new(self.clone());
|
||||
focus_item.borrow().as_ref().key_event(event, &window);
|
||||
if focus_item.borrow().as_ref().key_event(event, &window)
|
||||
== crate::input::KeyEventResult::EventAccepted
|
||||
{
|
||||
return;
|
||||
}
|
||||
item = focus_item.parent_item();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
106
tests/cases/focus/event_propagation.60
Normal file
106
tests/cases/focus/event_propagation.60
Normal file
|
@ -0,0 +1,106 @@
|
|||
/* LICENSE BEGIN
|
||||
This file is part of the SixtyFPS Project -- https://sixtyfps.io
|
||||
Copyright (c) 2020 Olivier Goffart <olivier.goffart@sixtyfps.io>
|
||||
Copyright (c) 2020 Simon Hausmann <simon.hausmann@sixtyfps.io>
|
||||
|
||||
SPDX-License-Identifier: GPL-3.0-only
|
||||
This file is also available under commercial licensing terms.
|
||||
Please contact info@sixtyfps.io for more information.
|
||||
LICENSE END */
|
||||
TestCase := Rectangle {
|
||||
width: 400phx;
|
||||
height: 400phx;
|
||||
forward-focus: input2;
|
||||
|
||||
input1 := TextInput {
|
||||
width: parent.width;
|
||||
height: 200phx;
|
||||
Rectangle {
|
||||
FocusScope {
|
||||
width: 75%;
|
||||
key-pressed(event) => {
|
||||
recieved += event.text
|
||||
}
|
||||
|
||||
input2 := TextInput {
|
||||
width: 75%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
property<bool> input1_focused: input1.has_focus;
|
||||
property<string> input1_text: input1.text;
|
||||
property<bool> input2_focused: input2.has_focus;
|
||||
property<string> input2_text: input2.text;
|
||||
property<string> recieved;
|
||||
}
|
||||
|
||||
/*
|
||||
```rust
|
||||
let ctrl_modifier = sixtyfps::re_exports::KeyboardModifiers {
|
||||
control: true,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let instance = TestCase::new();
|
||||
|
||||
assert!(!instance.get_input1_focused());
|
||||
assert!(instance.get_input2_focused());
|
||||
|
||||
sixtyfps::testing::send_keyboard_string_sequence(&instance, "Hello");
|
||||
assert_eq!(instance.get_input2_text(), "Hello");
|
||||
assert_eq!(instance.get_input1_text(), "");
|
||||
assert_eq!(instance.get_recieved(), "");
|
||||
|
||||
sixtyfps::testing::set_current_keyboard_modifiers(&instance, ctrl_modifier);
|
||||
sixtyfps::testing::send_keyboard_string_sequence(&instance, "ß");
|
||||
assert_eq!(instance.get_input2_text(), "Hello");
|
||||
assert_eq!(instance.get_input1_text(), "");
|
||||
assert_eq!(instance.get_recieved(), "ß");
|
||||
```
|
||||
|
||||
```cpp
|
||||
sixtyfps::cbindgen_private::KeyboardModifiers ctrl_modifier{};
|
||||
ctrl_modifier.control = true;
|
||||
|
||||
auto handle = TestCase::create();
|
||||
const TestCase &instance = *handle;
|
||||
|
||||
assert(!instance.get_input1_focused());
|
||||
assert(instance.get_input2_focused());
|
||||
|
||||
sixtyfps::testing::send_keyboard_string_sequence(&instance, "Hello");
|
||||
assert_eq(instance.get_input2_text(), "Hello");
|
||||
assert_eq(instance.get_input1_text(), "");
|
||||
assert_eq(instance.get_recieved(), "");
|
||||
|
||||
sixtyfps::testing::send_keyboard_string_sequence(&instance, "ß", ctrl_modifier);
|
||||
assert_eq(instance.get_input2_text(), "Hello");
|
||||
assert_eq(instance.get_input1_text(), "");
|
||||
assert_eq(instance.get_recieved(), "ß");
|
||||
```
|
||||
|
||||
```j*s
|
||||
var instance = new sixtyfps.TestCase();
|
||||
assert(!instance.input1_focused);
|
||||
assert(!instance.input2_focused);
|
||||
|
||||
instance.send_mouse_click(150., 100.);
|
||||
assert(instance.input1_focused);
|
||||
assert(!instance.input2_focused);
|
||||
|
||||
instance.send_keyboard_string_sequence("Only for field 1");
|
||||
assert.equal(instance.input1_text, "Only for field 1");
|
||||
assert.equal(instance.input2_text, "");
|
||||
|
||||
instance.send_mouse_click(150., 300.);
|
||||
assert(!instance.input1_focused);
|
||||
assert(instance.input2_focused);
|
||||
|
||||
instance.send_keyboard_string_sequence("Only for field 2");
|
||||
assert.equal(instance.input1_text, "Only for field 1");
|
||||
assert.equal(instance.input2_text, "Only for field 2");
|
||||
```
|
||||
*/
|
|
@ -61,7 +61,7 @@ sixtyfps::testing::send_mouse_click(&handle, 150., 100.);
|
|||
assert(instance.get_input1_focused());
|
||||
assert(!instance.get_input2_focused());
|
||||
|
||||
sixtyfps::testing::send_keyboard_string_sequence(instance, "Only for field 1");
|
||||
sixtyfps::testing::send_keyboard_string_sequence(&instance, "Only for field 1");
|
||||
assert_eq(instance.get_input1_text(), "Only for field 1");
|
||||
assert_eq(instance.get_input2_text(), "");
|
||||
|
||||
|
@ -69,7 +69,7 @@ sixtyfps::testing::send_mouse_click(&handle, 150., 300.);
|
|||
assert(!instance.get_input1_focused());
|
||||
assert(instance.get_input2_focused());
|
||||
|
||||
sixtyfps::testing::send_keyboard_string_sequence(instance, "Only for field 2");
|
||||
sixtyfps::testing::send_keyboard_string_sequence(&instance, "Only for field 2");
|
||||
assert_eq(instance.get_input1_text(), "Only for field 1");
|
||||
assert_eq(instance.get_input2_text(), "Only for field 2");
|
||||
```
|
||||
|
|
|
@ -54,7 +54,7 @@ const TestCase &instance = *handle;
|
|||
assert(instance.get_input1_focused());
|
||||
assert(!instance.get_input2_focused());
|
||||
|
||||
sixtyfps::testing::send_keyboard_string_sequence(instance, "Only for field 1");
|
||||
sixtyfps::testing::send_keyboard_string_sequence(&instance, "Only for field 1");
|
||||
assert_eq(instance.get_input1_text(), "Only for field 1");
|
||||
assert_eq(instance.get_input2_text(), "");
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue