mirror of
https://github.com/slint-ui/slint.git
synced 2025-10-01 06:11:16 +00:00
Add a FocusScope
C++ part not working because the Callback with arguments are not yet supported with C++
This commit is contained in:
parent
340e8f0d5a
commit
094287697a
8 changed files with 159 additions and 12 deletions
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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>(),
|
||||||
|
|
|
@ -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
|
||||||
|
|
27
tests/cases/examples/key_press.60
Normal file
27
tests/cases/examples/key_press.60
Normal 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: "> ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue