Add a FocusScope

C++ part not working because the Callback with arguments are not yet supported with C++
This commit is contained in:
Olivier Goffart 2021-01-19 19:38:25 +01:00
parent 340e8f0d5a
commit 094287697a
8 changed files with 159 additions and 12 deletions

View file

@ -37,6 +37,7 @@ extern const cbindgen_private::ItemVTable RectangleVTable;
extern const cbindgen_private::ItemVTable BorderRectangleVTable; extern const cbindgen_private::ItemVTable BorderRectangleVTable;
extern const cbindgen_private::ItemVTable TextVTable; extern const cbindgen_private::ItemVTable TextVTable;
extern const cbindgen_private::ItemVTable TouchAreaVTable; extern const cbindgen_private::ItemVTable TouchAreaVTable;
extern const cbindgen_private::ItemVTable FocusScopeVTable;
extern const cbindgen_private::ItemVTable ImageVTable; extern const cbindgen_private::ItemVTable ImageVTable;
extern const cbindgen_private::ItemVTable ClippedImageVTable; extern const cbindgen_private::ItemVTable ClippedImageVTable;
extern const cbindgen_private::ItemVTable PathVTable; extern const cbindgen_private::ItemVTable PathVTable;
@ -139,6 +140,7 @@ using cbindgen_private::BorderRectangle;
using cbindgen_private::Clip; using cbindgen_private::Clip;
using cbindgen_private::ClippedImage; using cbindgen_private::ClippedImage;
using cbindgen_private::Flickable; using cbindgen_private::Flickable;
using cbindgen_private::FocusScope;
using cbindgen_private::Image; using cbindgen_private::Image;
using cbindgen_private::Path; using cbindgen_private::Path;
using cbindgen_private::Rectangle; using cbindgen_private::Rectangle;

View file

@ -84,6 +84,17 @@ export TouchArea := _ {
//-default_size_binding:expands_to_parent_geometry //-default_size_binding:expands_to_parent_geometry
} }
export FocusScope := _ {
property <length> x;
property <length> y;
property <length> width;
property <length> height;
property <bool> has_focus;
callback key_pressed(string);
callback key_released(string);
//-default_size_binding:expands_to_parent_geometry
}
export Flickable := _ { export Flickable := _ {
property <length> x; property <length> x;
property <length> y; property <length> y;

View file

@ -372,6 +372,97 @@ ItemVTable_static! {
pub static TouchAreaVTable for TouchArea pub static TouchAreaVTable for TouchArea
} }
/// A runtime item that exposes key
#[repr(C)]
#[derive(FieldOffsets, Default, SixtyFPSElement)]
#[pin]
pub struct FocusScope {
pub x: Property<f32>,
pub y: Property<f32>,
pub width: Property<f32>,
pub height: Property<f32>,
pub has_focus: Property<bool>,
pub key_pressed: Callback<(SharedString,)>,
pub key_released: Callback<(SharedString,)>,
/// FIXME: remove this
pub cached_rendering_data: CachedRenderingData,
}
impl Item for FocusScope {
fn init(self: Pin<&Self>, _window: &ComponentWindow) {}
fn geometry(self: Pin<&Self>) -> Rect {
euclid::rect(self.x(), self.y(), self.width(), self.height())
}
fn layouting_info(self: Pin<&Self>, _window: &ComponentWindow) -> LayoutInfo {
LayoutInfo::default()
}
fn implicit_size(self: Pin<&Self>, _window: &ComponentWindow) -> Size {
Default::default()
}
fn input_event(
self: Pin<&Self>,
event: MouseEvent,
window: &ComponentWindow,
self_rc: &ItemRc,
) -> InputEventResult {
/*if !self.enabled() {
return InputEventResult::EventIgnored;
}*/
if matches!(event.what, MouseEventType::MousePressed) {
if !self.has_focus() {
window.set_focus_item(self_rc);
}
}
InputEventResult::EventAccepted
}
fn key_event(self: Pin<&Self>, event: &KeyEvent, _window: &ComponentWindow) -> KeyEventResult {
match event {
KeyEvent::KeyPressed { .. } => {}
KeyEvent::KeyReleased { .. } => {}
KeyEvent::CharacterInput { unicode_scalar, .. } => {
if let Some(char) = std::char::from_u32(*unicode_scalar) {
let key = SharedString::from(char.to_string().as_str());
// FIXME: handle pressed and release in their event
Self::FIELD_OFFSETS.key_pressed.apply_pin(self).emit(&(key.clone(),));
Self::FIELD_OFFSETS.key_released.apply_pin(self).emit(&(key,));
}
}
};
KeyEventResult::EventAccepted
}
fn focus_event(self: Pin<&Self>, event: &FocusEvent, _window: &ComponentWindow) {
match event {
FocusEvent::FocusIn | FocusEvent::WindowReceivedFocus => {
self.has_focus.set(true);
}
FocusEvent::FocusOut | FocusEvent::WindowLostFocus => {
self.has_focus.set(false);
}
}
}
fn render(self: Pin<&Self>, _pos: Point, _backend: &mut ItemRendererRef) {}
}
impl ItemConsts for FocusScope {
const cached_rendering_data_offset: const_field_offset::FieldOffset<
FocusScope,
CachedRenderingData,
> = FocusScope::FIELD_OFFSETS.cached_rendering_data.as_unpinned_projection();
}
ItemVTable_static! {
/// The VTable for `FocusScope`
#[no_mangle]
pub static FocusScopeVTable for FocusScope
}
#[repr(C)] #[repr(C)]
#[derive(FieldOffsets, Default, SixtyFPSElement)] #[derive(FieldOffsets, Default, SixtyFPSElement)]
#[pin] #[pin]

View file

@ -19,7 +19,7 @@ use core::pin::Pin;
macro_rules! declare_ValueType { macro_rules! declare_ValueType {
($($ty:ty,)*) => { ($($ty:ty,)*) => {
pub trait ValueType: 'static + Default $(+ TryInto<$ty> + TryFrom<$ty>)* {} pub trait ValueType: 'static + Default + Clone $(+ TryInto<$ty> + TryFrom<$ty>)* {}
}; };
} }
declare_ValueType![ declare_ValueType![
@ -225,10 +225,6 @@ pub trait CallbackInfo<Item, Value> {
item: Pin<&Item>, item: Pin<&Item>,
handler: Box<dyn Fn(&[Value]) -> Value>, handler: Box<dyn Fn(&[Value]) -> Value>,
) -> Result<(), ()>; ) -> Result<(), ()>;
/// The offset of the Callback<> in the item.
/// The use of this is unsafe
fn offset(&self) -> usize;
} }
impl<Item, Value: Default + 'static> CallbackInfo<Item, Value> impl<Item, Value: Default + 'static> CallbackInfo<Item, Value>
@ -249,9 +245,32 @@ impl<Item, Value: Default + 'static> CallbackInfo<Item, Value>
}); });
Ok(()) Ok(())
} }
}
fn offset(&self) -> usize { impl<Item, Value: Clone + Default + 'static, T: Clone> CallbackInfo<Item, Value>
todo!() for FieldOffset<Item, crate::Callback<(T,)>>
where
Value: TryInto<T>,
T: TryInto<Value>,
{
fn emit(&self, item: Pin<&Item>, args: &[Value]) -> Result<Value, ()> {
let value = args.first().ok_or(())?;
let value = value.clone().try_into().map_err(|_| ())?;
self.apply_pin(item).emit(&(value,));
Ok(Value::default())
}
fn set_handler(
&self,
item: Pin<&Item>,
handler: Box<dyn Fn(&[Value]) -> Value>,
) -> Result<(), ()> {
self.apply_pin(item).set_handler(move |(val,)| {
let val: Value = val.clone().try_into().ok().unwrap();
handler(&[val]);
Value::default();
});
Ok(())
} }
} }

View file

@ -478,6 +478,7 @@ fn generate_component<'id>(
rtti_for::<Rectangle>(), rtti_for::<Rectangle>(),
rtti_for::<BorderRectangle>(), rtti_for::<BorderRectangle>(),
rtti_for::<TouchArea>(), rtti_for::<TouchArea>(),
rtti_for::<FocusScope>(),
rtti_for::<Path>(), rtti_for::<Path>(),
rtti_for_flickable(), rtti_for_flickable(),
rtti_for::<Window>(), rtti_for::<Window>(),

View file

@ -71,7 +71,6 @@ impl<Item: vtable::HasStaticVTable<corelib::items::ItemVTable>> ErasedPropertyIn
pub trait ErasedCallbackInfo { pub trait ErasedCallbackInfo {
fn emit(&self, item: Pin<ItemRef>, args: &[Value]) -> Value; fn emit(&self, item: Pin<ItemRef>, args: &[Value]) -> Value;
fn set_handler(&self, item: Pin<ItemRef>, handler: Box<dyn Fn(&[Value]) -> Value>); fn set_handler(&self, item: Pin<ItemRef>, handler: Box<dyn Fn(&[Value]) -> Value>);
fn offset(&self) -> usize;
} }
impl<Item: vtable::HasStaticVTable<corelib::items::ItemVTable>> ErasedCallbackInfo impl<Item: vtable::HasStaticVTable<corelib::items::ItemVTable>> ErasedCallbackInfo
@ -84,10 +83,6 @@ impl<Item: vtable::HasStaticVTable<corelib::items::ItemVTable>> ErasedCallbackIn
fn set_handler(&self, item: Pin<ItemRef>, handler: Box<dyn Fn(&[Value]) -> Value>) { fn set_handler(&self, item: Pin<ItemRef>, handler: Box<dyn Fn(&[Value]) -> Value>) {
(*self).set_handler(ItemRef::downcast_pin(item).unwrap(), handler).unwrap() (*self).set_handler(ItemRef::downcast_pin(item).unwrap(), handler).unwrap()
} }
fn offset(&self) -> usize {
(*self).offset()
}
} }
/// A Pointer to a model /// A Pointer to a model

View file

@ -0,0 +1,27 @@
/* 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 */
W := Window {
VerticalLayout {
Rectangle { color: field.has_focus ? blue: red; }
field := FocusScope {
vertical_stretch: 1;
key-pressed(key) => {
debug(key);
t.text += key;
}
Rectangle { color: yellow; }
}
t:= Text {
text: "> ";
}
}
}

View file

@ -47,6 +47,7 @@ fn gen_corelib(include_dir: &Path) -> anyhow::Result<()> {
"Image", "Image",
"ClippedImage", "ClippedImage",
"TouchArea", "TouchArea",
"FocusScope",
"Flickable", "Flickable",
"Text", "Text",
"Path", "Path",