Allow FocusScope to reject the events

This commit is contained in:
Olivier Goffart 2021-01-26 17:04:43 +01:00
parent 2e23b5bbdd
commit 9de5af75ce
10 changed files with 140 additions and 38 deletions

View file

@ -77,6 +77,7 @@ using cbindgen_private::TextVerticalAlignment;
using cbindgen_private::TraversalOrder; using cbindgen_private::TraversalOrder;
using cbindgen_private::ImageFit; using cbindgen_private::ImageFit;
using cbindgen_private::KeyEvent; using cbindgen_private::KeyEvent;
using cbindgen_private::EventResult;
using cbindgen_private::KeyboardModifiers; using cbindgen_private::KeyboardModifiers;
namespace private_api { namespace private_api {

View file

@ -104,8 +104,8 @@ export FocusScope := _ {
property <length> width; property <length> width;
property <length> height; property <length> height;
property <bool> has_focus; property <bool> has_focus;
callback key_pressed(KeyEvent); callback key_pressed(KeyEvent) -> EventResult;
callback key_released(KeyEvent); callback key_released(KeyEvent) -> EventResult;
//-default_size_binding:expands_to_parent_geometry //-default_size_binding:expands_to_parent_geometry
//-accepts_focus //-accepts_focus
} }

View file

@ -160,6 +160,14 @@ impl<'a> LookupCtx<'a> {
type_loader: None, type_loader: None,
} }
} }
fn return_type(&self) -> &Type {
if let Type::Callback { return_type, .. } = &self.property_type {
return_type.as_ref().map_or(&Type::Void, |b| &(**b))
} else {
&self.property_type
}
}
} }
fn find_element_by_id(roots: &[ElementRc], name: &str) -> Option<ElementRc> { fn find_element_by_id(roots: &[ElementRc], name: &str) -> Option<ElementRc> {
@ -310,11 +318,7 @@ impl Expression {
node: syntax_nodes::ReturnStatement, node: syntax_nodes::ReturnStatement,
ctx: &mut LookupCtx, ctx: &mut LookupCtx,
) -> Expression { ) -> Expression {
let return_type = if let Type::Callback { return_type, .. } = &ctx.property_type { let return_type = ctx.return_type().clone();
return_type.as_ref().map_or(Type::Void, |b| (**b).clone())
} else {
ctx.property_type.clone()
};
Expression::ReturnStatement(node.Expression().map(|n| { Expression::ReturnStatement(node.Expression().map(|n| {
Box::new(Self::from_expression_node(n.into(), ctx).maybe_convert_to( Box::new(Self::from_expression_node(n.into(), ctx).maybe_convert_to(
return_type, return_type,
@ -555,7 +559,7 @@ impl Expression {
return Expression::Invalid; return Expression::Invalid;
} }
match &ctx.property_type { match ctx.return_type() {
Type::Color => { Type::Color => {
if let Some(c) = css_color_parser2::NAMED_COLORS.get(first_str.as_str()) { if let Some(c) = css_color_parser2::NAMED_COLORS.get(first_str.as_str()) {
let value = ((c.a as u32 * 255) << 24) let value = ((c.a as u32 * 255) << 24)

View file

@ -122,6 +122,7 @@ impl TypeRegister {
&["stretch", "center", "start", "end", "space_between", "space_around"], &["stretch", "center", "start", "end", "space_between", "space_around"],
); );
declare_enum("ImageFit", &["fill", "contain"]); declare_enum("ImageFit", &["fill", "contain"]);
declare_enum("EventResult", &["reject", "accept"]);
register.supported_property_animation_types.insert(Type::Float32.to_string()); register.supported_property_animation_types.insert(Type::Float32.to_string());
register.supported_property_animation_types.insert(Type::Int32.to_string()); register.supported_property_animation_types.insert(Type::Int32.to_string());

View file

@ -386,6 +386,21 @@ ItemVTable_static! {
pub static TouchAreaVTable for TouchArea pub static TouchAreaVTable for TouchArea
} }
#[derive(Copy, Clone, Debug, PartialEq, strum_macros::EnumString, strum_macros::Display)]
#[repr(C)]
#[allow(non_camel_case_types)]
/// What is returned from the event handler
pub enum EventResult {
reject,
accept,
}
impl Default for EventResult {
fn default() -> Self {
Self::reject
}
}
/// A runtime item that exposes key /// A runtime item that exposes key
#[repr(C)] #[repr(C)]
#[derive(FieldOffsets, Default, SixtyFPSElement)] #[derive(FieldOffsets, Default, SixtyFPSElement)]
@ -396,8 +411,8 @@ pub struct FocusScope {
pub width: Property<f32>, pub width: Property<f32>,
pub height: Property<f32>, pub height: Property<f32>,
pub has_focus: Property<bool>, pub has_focus: Property<bool>,
pub key_pressed: Callback<KeyEventArg>, pub key_pressed: Callback<KeyEventArg, EventResult>,
pub key_released: Callback<KeyEventArg>, pub key_released: Callback<KeyEventArg, EventResult>,
/// FIXME: remove this /// FIXME: remove this
pub cached_rendering_data: CachedRenderingData, pub cached_rendering_data: CachedRenderingData,
} }
@ -431,19 +446,22 @@ impl Item for FocusScope {
window.set_focus_item(self_rc); window.set_focus_item(self_rc);
} }
} }
InputEventResult::EventAccepted InputEventResult::EventIgnored
} }
fn key_event(self: Pin<&Self>, event: &KeyEvent, _window: &ComponentWindow) -> KeyEventResult { fn key_event(self: Pin<&Self>, event: &KeyEvent, _window: &ComponentWindow) -> KeyEventResult {
match event.event_type { let r = match event.event_type {
KeyEventType::KeyPressed => { KeyEventType::KeyPressed => {
Self::FIELD_OFFSETS.key_pressed.apply_pin(self).call(&(event.clone(),)); Self::FIELD_OFFSETS.key_pressed.apply_pin(self).call(&(event.clone(),))
} }
KeyEventType::KeyReleased => { KeyEventType::KeyReleased => {
Self::FIELD_OFFSETS.key_released.apply_pin(self).call(&(event.clone(),)); Self::FIELD_OFFSETS.key_released.apply_pin(self).call(&(event.clone(),))
} }
}; };
KeyEventResult::EventAccepted match r {
EventResult::accept => KeyEventResult::EventAccepted,
EventResult::reject => KeyEventResult::EventIgnored,
}
} }
fn focus_event(self: Pin<&Self>, event: &FocusEvent, _window: &ComponentWindow) { fn focus_event(self: Pin<&Self>, event: &FocusEvent, _window: &ComponentWindow) {

View file

@ -41,6 +41,7 @@ declare_ValueType![
crate::model::StandardListViewItem, crate::model::StandardListViewItem,
crate::items::ImageFit, crate::items::ImageFit,
crate::input::KeyEvent, crate::input::KeyEvent,
crate::items::EventResult,
]; ];
/// What kind of animation is on a binding /// What kind of animation is on a binding

View file

@ -247,6 +247,7 @@ declare_value_enum_conversion!(corelib::items::TextVerticalAlignment, TextVertic
declare_value_enum_conversion!(corelib::layout::LayoutAlignment, LayoutAlignment); declare_value_enum_conversion!(corelib::layout::LayoutAlignment, LayoutAlignment);
declare_value_enum_conversion!(corelib::items::ImageFit, ImageFit); declare_value_enum_conversion!(corelib::items::ImageFit, ImageFit);
declare_value_enum_conversion!(corelib::input::KeyEventType, KeyEventType); declare_value_enum_conversion!(corelib::input::KeyEventType, KeyEventType);
declare_value_enum_conversion!(corelib::items::EventResult, EventResult);
impl TryFrom<corelib::animations::Instant> for Value { impl TryFrom<corelib::animations::Instant> for Value {
type Error = (); type Error = ();

View file

@ -28,6 +28,7 @@ W := Window {
} }
debug(event.text); debug(event.text);
t.text += event.text; t.text += event.text;
accept
} }
Rectangle { color: yellow; } Rectangle { color: yellow; }
} }

View file

@ -19,7 +19,8 @@ TestCase := Rectangle {
FocusScope { FocusScope {
width: 75%; width: 75%;
key-pressed(event) => { key-pressed(event) => {
recieved += event.text recieved += event.text;
accept
} }
input2 := TextInput { input2 := TextInput {
@ -81,26 +82,4 @@ assert_eq(instance.get_input2_text(), "Hello");
assert_eq(instance.get_input1_text(), ""); assert_eq(instance.get_input1_text(), "");
assert_eq(instance.get_recieved(), "ß"); 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");
```
*/ */

View file

@ -0,0 +1,96 @@
/* 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 := Window {
width: 100phx;
height: 100phx;
FocusScope {
key-pressed(event) => {
r1 += event.text;
return event.text == "a" ? accept : reject;
}
FocusScope {
key-pressed(event) => {
r2 += event.text;
return event.text == "b" ? accept : reject;
}
Rectangle {
FocusScope {
key-pressed(event) => {
r3 += event.text;
return event.text == "c" ? accept : reject;
}
if (toggle) : FocusScope {
key-pressed(event) => {
r4 += event.text;
return event.text == "d" ? accept : reject;
}
FocusScope {
key-pressed(event) => {
r5 += event.text;
return event.text == "e" ? accept : reject;
}
TouchArea {
clicked => {
parent.focus();
}
}
}
}
}
}
}
}
property<bool> toggle: true;
property<string> r1;
property<string> r2;
property<string> r3;
property<string> r4;
property<string> r5;
}
/*
```rust
let instance = TestCase::new();
sixtyfps::testing::send_mouse_click(&instance, 50., 50.);
sixtyfps::testing::send_keyboard_string_sequence(&instance, "__abcdefghij__");
assert_eq!(instance.get_r1(), "__afghij__");
assert_eq!(instance.get_r2(), "__abfghij__");
assert_eq!(instance.get_r3(), "__abcfghij__");
assert_eq!(instance.get_r4(), "__abcdfghij__");
assert_eq!(instance.get_r5(), "__abcdefghij__");
```
```cpp
auto handle = TestCase::create();
const TestCase &instance = *handle;
sixtyfps::testing::send_mouse_click(&handle, 50., 50.);
sixtyfps::testing::send_keyboard_string_sequence(&instance, "__abcdefghij__");
assert_eq(instance.get_r1(), "__afghij__");
assert_eq(instance.get_r2(), "__abfghij__");
assert_eq(instance.get_r3(), "__abcfghij__");
assert_eq(instance.get_r4(), "__abcdfghij__");
assert_eq(instance.get_r5(), "__abcdefghij__");
```
```js
var instance = new sixtyfps.TestCase();
instance.send_mouse_click(50., 50.);
instance.send_keyboard_string_sequence("__abcdefghij__");
assert.equal(instance.r1, "__afghij__");
assert.equal(instance.r2, "__abfghij__");
assert.equal(instance.r3, "__abcfghij__");
assert.equal(instance.r4, "__abcdfghij__");
assert.equal(instance.r5, "__abcdefghij__");
```
*/