mirror of
https://github.com/slint-ui/slint.git
synced 2025-10-03 07:04:34 +00:00
Mouse grab in rust
This commit is contained in:
parent
5aa7ee86fe
commit
0a56912d0f
9 changed files with 263 additions and 139 deletions
|
@ -73,6 +73,7 @@ pub(crate) mod repeater;
|
||||||
pub mod re_exports {
|
pub mod re_exports {
|
||||||
pub use crate::repeater::*;
|
pub use crate::repeater::*;
|
||||||
pub use const_field_offset::{self, FieldOffsets};
|
pub use const_field_offset::{self, FieldOffsets};
|
||||||
|
pub use once_cell::sync::Lazy;
|
||||||
pub use once_cell::unsync::OnceCell;
|
pub use once_cell::unsync::OnceCell;
|
||||||
pub use pin_weak::rc::*;
|
pub use pin_weak::rc::*;
|
||||||
pub use sixtyfps_corelib::abi::datastructures::*;
|
pub use sixtyfps_corelib::abi::datastructures::*;
|
||||||
|
@ -81,9 +82,12 @@ pub mod re_exports {
|
||||||
pub use sixtyfps_corelib::graphics::{
|
pub use sixtyfps_corelib::graphics::{
|
||||||
PathArcTo, PathData, PathElement, PathEvent, PathLineTo, Point, Rect, Size,
|
PathArcTo, PathData, PathElement, PathEvent, PathLineTo, Point, Rect, Size,
|
||||||
};
|
};
|
||||||
pub use sixtyfps_corelib::input::{InputEventResult, MouseEvent};
|
pub use sixtyfps_corelib::input::{
|
||||||
|
process_ungrabbed_mouse_event, InputEventResult, MouseEvent,
|
||||||
|
};
|
||||||
pub use sixtyfps_corelib::item_tree::{
|
pub use sixtyfps_corelib::item_tree::{
|
||||||
visit_item_tree, ItemTreeNode, ItemVisitorRefMut, ItemVisitorVTable, VisitChildrenResult,
|
item_offset, visit_item_tree, ItemTreeNode, ItemVisitorRefMut, ItemVisitorVTable,
|
||||||
|
VisitChildrenResult,
|
||||||
};
|
};
|
||||||
pub use sixtyfps_corelib::items::*;
|
pub use sixtyfps_corelib::items::*;
|
||||||
pub use sixtyfps_corelib::layout::LayoutInfo;
|
pub use sixtyfps_corelib::layout::LayoutInfo;
|
||||||
|
|
|
@ -53,6 +53,15 @@ where
|
||||||
sixtyfps_corelib::item_tree::VisitChildrenResult::CONTINUE
|
sixtyfps_corelib::item_tree::VisitChildrenResult::CONTINUE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Forward an input event to a particular item
|
||||||
|
pub fn input_event(
|
||||||
|
&self,
|
||||||
|
idx: usize,
|
||||||
|
event: sixtyfps_corelib::input::MouseEvent,
|
||||||
|
) -> sixtyfps_corelib::input::InputEventResult {
|
||||||
|
self.components.borrow()[idx].as_ref().input_event(event)
|
||||||
|
}
|
||||||
|
|
||||||
/// Return the amount of item currently in the component
|
/// Return the amount of item currently in the component
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
self.components.borrow().len()
|
self.components.borrow().len()
|
||||||
|
|
|
@ -146,6 +146,8 @@ MainWindow := Rectangle {
|
||||||
clicked => {
|
clicked => {
|
||||||
if (root.active_page == 0) {
|
if (root.active_page == 0) {
|
||||||
root.active_page = idx + 1;
|
root.active_page = idx + 1;
|
||||||
|
} else {
|
||||||
|
root.active_page = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,7 +62,7 @@ pub struct FieldOffset<T, U, PinFlag = NotPinnedFlag>(
|
||||||
/// of.apply(foo)
|
/// of.apply(foo)
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
PhantomData<(PhantomContra<T>, *const U, PinFlag)>,
|
PhantomData<(PhantomContra<T>, U, PinFlag)>,
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Type that can be used in the `Flag` parameter of `FieldOffset` to specify that
|
/// Type that can be used in the `Flag` parameter of `FieldOffset` to specify that
|
||||||
|
|
|
@ -460,6 +460,7 @@ impl<Base, T: ?Sized + VTableMeta, Flag> VOffset<Base, T, Flag> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn new<X: HasStaticVTable<T>>(o: FieldOffset<Base, X, Flag>) -> Self {
|
pub fn new<X: HasStaticVTable<T>>(o: FieldOffset<Base, X, Flag>) -> Self {
|
||||||
Self { vtable: X::static_vtable(), offset: o.get_byte_offset(), phantom: PhantomData }
|
Self { vtable: X::static_vtable(), offset: o.get_byte_offset(), phantom: PhantomData }
|
||||||
}
|
}
|
||||||
|
@ -467,12 +468,14 @@ impl<Base, T: ?Sized + VTableMeta, Flag> VOffset<Base, T, Flag> {
|
||||||
/// Create a new VOffset from raw data
|
/// Create a new VOffset from raw data
|
||||||
///
|
///
|
||||||
/// Safety: there must be a field that matches the vtable at offset T in base
|
/// Safety: there must be a field that matches the vtable at offset T in base
|
||||||
|
#[inline]
|
||||||
pub unsafe fn from_raw(vtable: &'static T::VTable, offset: usize) -> Self {
|
pub unsafe fn from_raw(vtable: &'static T::VTable, offset: usize) -> Self {
|
||||||
Self { vtable, offset, phantom: PhantomData }
|
Self { vtable, offset, phantom: PhantomData }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Base, T: ?Sized + VTableMeta> VOffset<Base, T, PinnedFlag> {
|
impl<Base, T: ?Sized + VTableMeta> VOffset<Base, T, PinnedFlag> {
|
||||||
|
#[inline]
|
||||||
pub fn apply_pin<'a>(self, x: Pin<&'a Base>) -> Pin<VRef<'a, T>> {
|
pub fn apply_pin<'a>(self, x: Pin<&'a Base>) -> Pin<VRef<'a, T>> {
|
||||||
let ptr = x.get_ref() as *const Base as *mut u8;
|
let ptr = x.get_ref() as *const Base as *mut u8;
|
||||||
unsafe {
|
unsafe {
|
||||||
|
|
|
@ -162,6 +162,7 @@ fn generate_component(
|
||||||
let mut repeated_element_components = Vec::new();
|
let mut repeated_element_components = Vec::new();
|
||||||
let mut repeated_dynmodel_names = Vec::new();
|
let mut repeated_dynmodel_names = Vec::new();
|
||||||
let mut repeated_visit_branch = Vec::new();
|
let mut repeated_visit_branch = Vec::new();
|
||||||
|
let mut repeated_input_branch = Vec::new();
|
||||||
let mut init = Vec::new();
|
let mut init = Vec::new();
|
||||||
super::build_array_helper(component, |item_rc, children_index, is_flickable_rect| {
|
super::build_array_helper(component, |item_rc, children_index, is_flickable_rect| {
|
||||||
let item = item_rc.borrow();
|
let item = item_rc.borrow();
|
||||||
|
@ -249,6 +250,10 @@ fn generate_component(
|
||||||
repeated_dynmodel_names.push(model_name);
|
repeated_dynmodel_names.push(model_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
repeated_input_branch.push(quote!(
|
||||||
|
#repeater_index => self.#repeater_id.input_event(rep_index, event),
|
||||||
|
));
|
||||||
|
|
||||||
item_tree_array.push(quote!(
|
item_tree_array.push(quote!(
|
||||||
sixtyfps::re_exports::ItemTreeNode::DynamicTree {
|
sixtyfps::re_exports::ItemTreeNode::DynamicTree {
|
||||||
index: #repeater_index,
|
index: #repeater_index,
|
||||||
|
@ -408,6 +413,8 @@ fn generate_component(
|
||||||
// Trick so we can use `#()` as a `if let Some` in `quote!`
|
// Trick so we can use `#()` as a `if let Some` in `quote!`
|
||||||
let parent_component_type = parent_component_type.iter().collect::<Vec<_>>();
|
let parent_component_type = parent_component_type.iter().collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let item_tree_array_len = item_tree_array.len();
|
||||||
|
|
||||||
if diag.has_error() {
|
if diag.has_error() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
@ -427,6 +434,7 @@ fn generate_component(
|
||||||
#(#repeated_dynmodel_names : sixtyfps::re_exports::PropertyListenerScope,)*
|
#(#repeated_dynmodel_names : sixtyfps::re_exports::PropertyListenerScope,)*
|
||||||
self_weak: sixtyfps::re_exports::OnceCell<sixtyfps::re_exports::PinWeak<#component_id>>,
|
self_weak: sixtyfps::re_exports::OnceCell<sixtyfps::re_exports::PinWeak<#component_id>>,
|
||||||
#(parent : sixtyfps::re_exports::PinWeak<#parent_component_type>,)*
|
#(parent : sixtyfps::re_exports::PinWeak<#parent_component_type>,)*
|
||||||
|
mouse_grabber: ::core::cell::Cell<sixtyfps::re_exports::VisitChildrenResult>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl sixtyfps::re_exports::Component for #component_id {
|
impl sixtyfps::re_exports::Component for #component_id {
|
||||||
|
@ -434,8 +442,7 @@ fn generate_component(
|
||||||
-> sixtyfps::re_exports::VisitChildrenResult
|
-> sixtyfps::re_exports::VisitChildrenResult
|
||||||
{
|
{
|
||||||
use sixtyfps::re_exports::*;
|
use sixtyfps::re_exports::*;
|
||||||
let tree = &[#(#item_tree_array),*];
|
return sixtyfps::re_exports::visit_item_tree(self, VRef::new_pin(self), Self::item_tree(), index, visitor, visit_dynamic);
|
||||||
return sixtyfps::re_exports::visit_item_tree(self, VRef::new_pin(self), tree, index, visitor, visit_dynamic);
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
fn visit_dynamic(self_pinned: ::core::pin::Pin<&#component_id>, visitor: ItemVisitorRefMut, dyn_index: usize) -> VisitChildrenResult {
|
fn visit_dynamic(self_pinned: ::core::pin::Pin<&#component_id>, visitor: ItemVisitorRefMut, dyn_index: usize) -> VisitChildrenResult {
|
||||||
match dyn_index {
|
match dyn_index {
|
||||||
|
@ -445,8 +452,35 @@ fn generate_component(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn input_event(self: ::core::pin::Pin<&Self>, _ : sixtyfps::re_exports::MouseEvent) -> sixtyfps::re_exports::InputEventResult {
|
fn input_event(self: ::core::pin::Pin<&Self>, mouse_event : sixtyfps::re_exports::MouseEvent) -> sixtyfps::re_exports::InputEventResult {
|
||||||
todo!()
|
use sixtyfps::re_exports::*;
|
||||||
|
let mouse_grabber = self.mouse_grabber.get();
|
||||||
|
#[allow(unused)]
|
||||||
|
let (status, new_grab) = if let Some((item_index, rep_index)) = mouse_grabber.aborted_indexes() {
|
||||||
|
let tree = Self::item_tree();
|
||||||
|
let offset = item_offset(self, tree, item_index);
|
||||||
|
let mut event = mouse_event.clone();
|
||||||
|
event.pos -= offset.to_vector();
|
||||||
|
let res = match tree[item_index] {
|
||||||
|
ItemTreeNode::Item { item, .. } => {
|
||||||
|
item.apply_pin(self).as_ref().input_event(event)
|
||||||
|
}
|
||||||
|
ItemTreeNode::DynamicTree { index } => {
|
||||||
|
match index {
|
||||||
|
#(#repeated_input_branch)*
|
||||||
|
_ => panic!("invalid index {}", index),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
match res {
|
||||||
|
InputEventResult::GrabMouse => (res, mouse_grabber),
|
||||||
|
_ => (res, VisitChildrenResult::CONTINUE),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
process_ungrabbed_mouse_event(VRef::new_pin(self), mouse_event)
|
||||||
|
};
|
||||||
|
self.mouse_grabber.set(new_grab);
|
||||||
|
status
|
||||||
}
|
}
|
||||||
|
|
||||||
#layouts
|
#layouts
|
||||||
|
@ -467,6 +501,7 @@ fn generate_component(
|
||||||
#(#repeated_dynmodel_names : ::core::default::Default::default(),)*
|
#(#repeated_dynmodel_names : ::core::default::Default::default(),)*
|
||||||
self_weak : ::core::default::Default::default(),
|
self_weak : ::core::default::Default::default(),
|
||||||
#(parent : parent as sixtyfps::re_exports::PinWeak::<#parent_component_type>,)*
|
#(parent : parent as sixtyfps::re_exports::PinWeak::<#parent_component_type>,)*
|
||||||
|
mouse_grabber: ::core::cell::Cell::new(sixtyfps::re_exports::VisitChildrenResult::CONTINUE),
|
||||||
};
|
};
|
||||||
let self_pinned = std::rc::Rc::pin(self_);
|
let self_pinned = std::rc::Rc::pin(self_);
|
||||||
self_pinned.self_weak.set(PinWeak::downgrade(self_pinned.clone())).map_err(|_|())
|
self_pinned.self_weak.set(PinWeak::downgrade(self_pinned.clone())).map_err(|_|())
|
||||||
|
@ -475,6 +510,14 @@ fn generate_component(
|
||||||
self_pinned
|
self_pinned
|
||||||
}
|
}
|
||||||
#(#property_and_signal_accessors)*
|
#(#property_and_signal_accessors)*
|
||||||
|
|
||||||
|
fn item_tree() -> &'static [sixtyfps::re_exports::ItemTreeNode<Self>] {
|
||||||
|
use sixtyfps::re_exports::*;
|
||||||
|
// FIXME: ideally this should be a const
|
||||||
|
static ITEM_TREE : Lazy<[sixtyfps::re_exports::ItemTreeNode<#component_id>; #item_tree_array_len]> =
|
||||||
|
Lazy::new(|| [#(#item_tree_array),*]);
|
||||||
|
&*ITEM_TREE
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#(#extra_components)*
|
#(#extra_components)*
|
||||||
|
@ -1236,27 +1279,3 @@ fn compile_path(path: &Path, component: &Rc<Component>) -> TokenStream {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
quote! {
|
|
||||||
|
|
||||||
fn process_input_event(self: ::core::pin::Pin<&Self>, mouse_event) {
|
|
||||||
if self.grab == -1 {
|
|
||||||
sixtyfps::re_exports::process_ungrabbed_input_event(mouse_event)
|
|
||||||
} else {
|
|
||||||
let inx =self.grab & 0xffff;
|
|
||||||
match child_array[inx] {
|
|
||||||
DynamicItem(repeater_offset) => {
|
|
||||||
let repeater_index = self.grab >> 16;
|
|
||||||
match repeater_offset => {
|
|
||||||
#(repeater_id => {
|
|
||||||
self.#repeater_name.component[repeater_index].proccess_input_event()
|
|
||||||
|
|
||||||
} )*
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ TODO: Keyboard events
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use crate::graphics::Point;
|
use crate::graphics::Point;
|
||||||
use crate::item_tree::ItemVisitorResult;
|
use crate::item_tree::{ItemVisitorResult, VisitChildrenResult};
|
||||||
use crate::ComponentRefPin;
|
use crate::ComponentRefPin;
|
||||||
use euclid::default::Vector2D;
|
use euclid::default::Vector2D;
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ pub struct MouseEvent {
|
||||||
/// to notify the run-time about how the event was handled and
|
/// to notify the run-time about how the event was handled and
|
||||||
/// what the next steps are.
|
/// what the next steps are.
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
pub enum InputEventResult {
|
pub enum InputEventResult {
|
||||||
/// The event was accepted. This may result in additional events, for example
|
/// The event was accepted. This may result in additional events, for example
|
||||||
/// accepting a mouse move will result in a MouseExit event later.
|
/// accepting a mouse move will result in a MouseExit event later.
|
||||||
|
@ -60,7 +60,7 @@ pub enum InputEventResult {
|
||||||
pub fn process_ungrabbed_mouse_event(
|
pub fn process_ungrabbed_mouse_event(
|
||||||
component: ComponentRefPin,
|
component: ComponentRefPin,
|
||||||
event: MouseEvent,
|
event: MouseEvent,
|
||||||
) -> (InputEventResult, isize) {
|
) -> (InputEventResult, VisitChildrenResult) {
|
||||||
let offset = Vector2D::new(0., 0.);
|
let offset = Vector2D::new(0., 0.);
|
||||||
|
|
||||||
let mut result = InputEventResult::EventIgnored;
|
let mut result = InputEventResult::EventIgnored;
|
||||||
|
@ -90,8 +90,39 @@ pub fn process_ungrabbed_mouse_event(
|
||||||
},
|
},
|
||||||
offset,
|
offset,
|
||||||
);
|
);
|
||||||
(result, item_index)
|
|
||||||
|
(
|
||||||
|
result,
|
||||||
|
if result == InputEventResult::GrabMouse {
|
||||||
|
item_index
|
||||||
|
} else {
|
||||||
|
VisitChildrenResult::CONTINUE
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
/// The event must be in the component coordinate
|
||||||
|
/// Returns the new grabber.
|
||||||
|
pub fn process_grabbed_mouse_event(
|
||||||
|
component: ComponentRefPin,
|
||||||
|
item: core::pin::Pin<ItemRef>,
|
||||||
|
offset: Point,
|
||||||
|
event: MouseEvent,
|
||||||
|
old_grab: VisitChildrenResult,
|
||||||
|
) -> (InputEventResult, VisitChildrenResult) {
|
||||||
|
let mut event2 = event.clone();
|
||||||
|
event2.pos -= offset.to_vector();
|
||||||
|
|
||||||
|
let res = item.as_ref().input_event(event2);
|
||||||
|
match res {
|
||||||
|
InputEventResult::EventIgnored => {
|
||||||
|
// We need then to forward to another event
|
||||||
|
process_ungrabbed_mouse_event(component, event)
|
||||||
|
}
|
||||||
|
InputEventResult::GrabMouse => (res, old_grab),
|
||||||
|
InputEventResult::EventAccepted => (res, VisitChildrenResult::CONTINUE),
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
pub(crate) mod ffi {
|
pub(crate) mod ffi {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -101,7 +132,18 @@ pub(crate) mod ffi {
|
||||||
pub extern "C" fn sixtyfps_process_ungrabbed_mouse_event(
|
pub extern "C" fn sixtyfps_process_ungrabbed_mouse_event(
|
||||||
component: ComponentRefPin,
|
component: ComponentRefPin,
|
||||||
event: MouseEvent,
|
event: MouseEvent,
|
||||||
) -> (InputEventResult, isize) {
|
) -> (InputEventResult, crate::item_tree::VisitChildrenResult) {
|
||||||
process_ungrabbed_mouse_event(component, event)
|
process_ungrabbed_mouse_event(component, event)
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn sixtyfps_process_grabbed_mouse_event(
|
||||||
|
component: ComponentRefPin,
|
||||||
|
item: core::pin::Pin<ItemRef>,
|
||||||
|
offset: Point,
|
||||||
|
event: MouseEvent,
|
||||||
|
old_grab: VisitChildrenResult,
|
||||||
|
) -> (InputEventResult, crate::item_tree::VisitChildrenResult) {
|
||||||
|
process_grabbed_mouse_event(component, item, offset, event, old_grab)
|
||||||
|
}*/
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,13 @@ impl VisitChildrenResult {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn aborted_indexes(&self) -> Option<(usize, usize)> {
|
||||||
|
if self.0 != -1 {
|
||||||
|
Some(((self.0 & 0xffff_ffff) as usize, (self.0 >> 32) as usize))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The item tree is an array of ItemTreeNode representing a static tree of items
|
/// The item tree is an array of ItemTreeNode representing a static tree of items
|
||||||
|
@ -70,14 +77,12 @@ pub struct ItemVisitorVTable {
|
||||||
/// as the parent's component.
|
/// as the parent's component.
|
||||||
/// `index` is to be used again in the visit_item_children function of the Component (the one passed as parameter)
|
/// `index` is to be used again in the visit_item_children function of the Component (the one passed as parameter)
|
||||||
/// and `item` is a reference to the item itself
|
/// and `item` is a reference to the item itself
|
||||||
///
|
|
||||||
/// returns true to continue, or false to abort the visit
|
|
||||||
visit_item: fn(
|
visit_item: fn(
|
||||||
VRefMut<ItemVisitorVTable>,
|
VRefMut<ItemVisitorVTable>,
|
||||||
component: Pin<VRef<ComponentVTable>>,
|
component: Pin<VRef<ComponentVTable>>,
|
||||||
index: isize,
|
index: isize,
|
||||||
item: Pin<VRef<ItemVTable>>,
|
item: Pin<VRef<ItemVTable>>,
|
||||||
) -> bool,
|
) -> VisitChildrenResult,
|
||||||
/// Destructor
|
/// Destructor
|
||||||
drop: fn(VRefMut<ItemVisitorVTable>),
|
drop: fn(VRefMut<ItemVisitorVTable>),
|
||||||
}
|
}
|
||||||
|
@ -85,16 +90,129 @@ pub struct ItemVisitorVTable {
|
||||||
/// Type alias to `vtable::VRefMut<ItemVisitorVTable>`
|
/// Type alias to `vtable::VRefMut<ItemVisitorVTable>`
|
||||||
pub type ItemVisitorRefMut<'a> = vtable::VRefMut<'a, ItemVisitorVTable>;
|
pub type ItemVisitorRefMut<'a> = vtable::VRefMut<'a, ItemVisitorVTable>;
|
||||||
|
|
||||||
impl<T: FnMut(crate::ComponentRefPin, isize, Pin<ItemRef>) -> bool> ItemVisitor for T {
|
impl<T: FnMut(crate::ComponentRefPin, isize, Pin<ItemRef>) -> VisitChildrenResult> ItemVisitor
|
||||||
|
for T
|
||||||
|
{
|
||||||
fn visit_item(
|
fn visit_item(
|
||||||
&mut self,
|
&mut self,
|
||||||
component: crate::ComponentRefPin,
|
component: crate::ComponentRefPin,
|
||||||
index: isize,
|
index: isize,
|
||||||
item: Pin<ItemRef>,
|
item: Pin<ItemRef>,
|
||||||
) -> bool {
|
) -> VisitChildrenResult {
|
||||||
self(component, index, item)
|
self(component, index, item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub enum ItemVisitorResult<State> {
|
||||||
|
Continue(State),
|
||||||
|
Abort,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Visit each items recursively
|
||||||
|
///
|
||||||
|
/// The state parametter returned by the visitor is passed to each children.
|
||||||
|
///
|
||||||
|
/// Returns the index of the item that cancelled, or -1 if nobody cancelled
|
||||||
|
pub fn visit_items<State>(
|
||||||
|
component: ComponentRefPin,
|
||||||
|
mut visitor: impl FnMut(ComponentRefPin, Pin<ItemRef>, &State) -> ItemVisitorResult<State>,
|
||||||
|
state: State,
|
||||||
|
) -> VisitChildrenResult {
|
||||||
|
visit_internal(component, &mut visitor, -1, &state)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_internal<State>(
|
||||||
|
component: ComponentRefPin,
|
||||||
|
visitor: &mut impl FnMut(ComponentRefPin, Pin<ItemRef>, &State) -> ItemVisitorResult<State>,
|
||||||
|
index: isize,
|
||||||
|
state: &State,
|
||||||
|
) -> VisitChildrenResult {
|
||||||
|
let mut actual_visitor = |component: ComponentRefPin,
|
||||||
|
index: isize,
|
||||||
|
item: Pin<ItemRef>|
|
||||||
|
-> VisitChildrenResult {
|
||||||
|
match visitor(component, item, state) {
|
||||||
|
ItemVisitorResult::Continue(state) => visit_internal(component, visitor, index, &state),
|
||||||
|
ItemVisitorResult::Abort => VisitChildrenResult::abort(index as usize, 0),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
vtable::new_vref!(let mut actual_visitor : VRefMut<ItemVisitorVTable> for ItemVisitor = &mut actual_visitor);
|
||||||
|
component.as_ref().visit_children_item(index, actual_visitor)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Visit the children within an array of ItemTreeNode
|
||||||
|
///
|
||||||
|
/// The dynamic visitor is called for the dynamic nodes, its signature is
|
||||||
|
/// `fn(base: &Base, visitor: vtable::VRefMut<ItemVisitorVTable>, dyn_index: usize)`
|
||||||
|
///
|
||||||
|
/// FIXME: the design of this use lots of indirection and stack frame in recursive functions
|
||||||
|
/// Need to check if the compiler is able to optimize away some of it.
|
||||||
|
/// Possibly we should generate code that directly call the visitor instead
|
||||||
|
pub fn visit_item_tree<Base>(
|
||||||
|
base: Pin<&Base>,
|
||||||
|
component: ComponentRefPin,
|
||||||
|
item_tree: &[ItemTreeNode<Base>],
|
||||||
|
index: isize,
|
||||||
|
mut visitor: vtable::VRefMut<ItemVisitorVTable>,
|
||||||
|
visit_dynamic: impl Fn(Pin<&Base>, vtable::VRefMut<ItemVisitorVTable>, usize) -> VisitChildrenResult,
|
||||||
|
) -> VisitChildrenResult {
|
||||||
|
let mut visit_at_index = |idx: usize| -> VisitChildrenResult {
|
||||||
|
match &item_tree[idx] {
|
||||||
|
ItemTreeNode::Item { item, .. } => {
|
||||||
|
visitor.visit_item(component, idx as isize, item.apply_pin(base))
|
||||||
|
}
|
||||||
|
ItemTreeNode::DynamicTree { index } => {
|
||||||
|
if let Some(sub_idx) =
|
||||||
|
visit_dynamic(base, visitor.borrow_mut(), *index).aborted_index()
|
||||||
|
{
|
||||||
|
VisitChildrenResult::abort(idx, sub_idx)
|
||||||
|
} else {
|
||||||
|
VisitChildrenResult::CONTINUE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if index == -1 {
|
||||||
|
visit_at_index(0)
|
||||||
|
} else {
|
||||||
|
match &item_tree[index as usize] {
|
||||||
|
ItemTreeNode::Item { children_index, chilren_count, .. } => {
|
||||||
|
for c in *children_index..(*children_index + *chilren_count) {
|
||||||
|
let maybe_abort_index = visit_at_index(c as usize);
|
||||||
|
if maybe_abort_index.has_aborted() {
|
||||||
|
return maybe_abort_index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ItemTreeNode::DynamicTree { .. } => panic!("should not be called with dynamic items"),
|
||||||
|
};
|
||||||
|
VisitChildrenResult::CONTINUE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Attempt to find out the x, y position of the parent in the component's coordinate
|
||||||
|
pub fn item_offset<Base>(
|
||||||
|
base: Pin<&Base>,
|
||||||
|
item_tree: &[ItemTreeNode<Base>],
|
||||||
|
index: usize,
|
||||||
|
) -> crate::graphics::Point {
|
||||||
|
let index = index as u32;
|
||||||
|
// FIXME: This algorithm is shit
|
||||||
|
let parent = item_tree.iter().find_map(|n| match n {
|
||||||
|
ItemTreeNode::Item { item, chilren_count, children_index } => {
|
||||||
|
if *children_index > index && *children_index + *chilren_count < index {
|
||||||
|
Some(item)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ItemTreeNode::DynamicTree { .. } => None,
|
||||||
|
});
|
||||||
|
if let Some(parent) = parent {
|
||||||
|
parent.apply_pin(base).as_ref().geometry().origin
|
||||||
|
} else {
|
||||||
|
crate::graphics::Point::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) mod ffi {
|
pub(crate) mod ffi {
|
||||||
#![allow(unsafe_code)]
|
#![allow(unsafe_code)]
|
||||||
|
@ -126,101 +244,20 @@ pub(crate) mod ffi {
|
||||||
|a, b, c| visit_dynamic(a.get_ref(), b, c),
|
|a, b, c| visit_dynamic(a.get_ref(), b, c),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
pub enum ItemVisitorResult<State> {
|
/// Expose `crate::item_tree::item_offset` to C++
|
||||||
Continue(State),
|
|
||||||
Abort,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Visit each items recursively
|
|
||||||
///
|
///
|
||||||
/// The state parametter returned by the visitor is passed to each children.
|
/// Safety: Assume a correct implementation of the item_tree array
|
||||||
///
|
#[no_mangle]
|
||||||
/// Returns the index of the item that cancelled, or -1 if nobody cancelled
|
pub unsafe extern "C" fn sixtyfps_item_offset(
|
||||||
pub fn visit_items<State>(
|
component: Pin<VRef<ComponentVTable>>,
|
||||||
component: ComponentRefPin,
|
item_tree: Slice<ItemTreeNode<u8>>,
|
||||||
mut visitor: impl FnMut(ComponentRefPin, Pin<ItemRef>, &State) -> ItemVisitorResult<State>,
|
index: usize,
|
||||||
state: State,
|
) -> crate::graphics::Point {
|
||||||
) -> isize {
|
crate::item_tree::item_offset(
|
||||||
visit_internal(component, &mut visitor, -1, &state)
|
Pin::new_unchecked(&*(component.as_ptr() as *const u8)),
|
||||||
}
|
item_tree.as_slice(),
|
||||||
|
index,
|
||||||
fn visit_internal<State>(
|
)
|
||||||
component: ComponentRefPin,
|
|
||||||
visitor: &mut impl FnMut(ComponentRefPin, Pin<ItemRef>, &State) -> ItemVisitorResult<State>,
|
|
||||||
index: isize,
|
|
||||||
state: &State,
|
|
||||||
) -> isize {
|
|
||||||
let mut result = -1;
|
|
||||||
let mut actual_visitor =
|
|
||||||
|component: ComponentRefPin, index: isize, item: Pin<ItemRef>| -> bool {
|
|
||||||
match visitor(component, item, state) {
|
|
||||||
ItemVisitorResult::Continue(state) => {
|
|
||||||
result = visit_internal(component, visitor, index, &state);
|
|
||||||
result == -1
|
|
||||||
}
|
|
||||||
ItemVisitorResult::Abort => {
|
|
||||||
result = index;
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
vtable::new_vref!(let mut actual_visitor : VRefMut<ItemVisitorVTable> for ItemVisitor = &mut actual_visitor);
|
|
||||||
component.as_ref().visit_children_item(index, actual_visitor);
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Visit the children within an array of ItemTreeNode
|
|
||||||
///
|
|
||||||
/// The dynamic visitor is called for the dynamic nodes, its signature is
|
|
||||||
/// `fn(base: &Base, visitor: vtable::VRefMut<ItemVisitorVTable>, dyn_index: usize)`
|
|
||||||
///
|
|
||||||
/// FIXME: the design of this use lots of indirection and stack frame in recursive functions
|
|
||||||
/// Need to check if the compiler is able to optimize away some of it.
|
|
||||||
/// Possibly we should generate code that directly call the visitor instead
|
|
||||||
pub fn visit_item_tree<Base>(
|
|
||||||
base: Pin<&Base>,
|
|
||||||
component: ComponentRefPin,
|
|
||||||
item_tree: &[ItemTreeNode<Base>],
|
|
||||||
index: isize,
|
|
||||||
mut visitor: vtable::VRefMut<ItemVisitorVTable>,
|
|
||||||
visit_dynamic: impl Fn(Pin<&Base>, vtable::VRefMut<ItemVisitorVTable>, usize) -> VisitChildrenResult,
|
|
||||||
) -> VisitChildrenResult {
|
|
||||||
let mut visit_at_index = |idx: usize| -> VisitChildrenResult {
|
|
||||||
match &item_tree[idx] {
|
|
||||||
ItemTreeNode::Item { item, .. } => {
|
|
||||||
if visitor.visit_item(component, idx as isize, item.apply_pin(base)) {
|
|
||||||
VisitChildrenResult::CONTINUE
|
|
||||||
} else {
|
|
||||||
VisitChildrenResult::abort(idx, 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ItemTreeNode::DynamicTree { index } => {
|
|
||||||
if let Some(sub_idx) =
|
|
||||||
visit_dynamic(base, visitor.borrow_mut(), *index).aborted_index()
|
|
||||||
{
|
|
||||||
VisitChildrenResult::abort(idx, sub_idx)
|
|
||||||
} else {
|
|
||||||
VisitChildrenResult::CONTINUE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if index == -1 {
|
|
||||||
visit_at_index(0)
|
|
||||||
} else {
|
|
||||||
match &item_tree[index as usize] {
|
|
||||||
ItemTreeNode::Item { children_index, chilren_count, .. } => {
|
|
||||||
for c in *children_index..(*children_index + *chilren_count) {
|
|
||||||
let maybe_abort_index = visit_at_index(c as usize);
|
|
||||||
if maybe_abort_index.has_aborted() {
|
|
||||||
return maybe_abort_index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ItemTreeNode::DynamicTree { .. } => panic!("should not be called with dynamic items"),
|
|
||||||
};
|
|
||||||
VisitChildrenResult::CONTINUE
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -371,14 +371,22 @@ impl Item for TouchArea {
|
||||||
Self::FIELD_OFFSETS.pressed.apply_pin(self).set(match event.what {
|
Self::FIELD_OFFSETS.pressed.apply_pin(self).set(match event.what {
|
||||||
MouseEventType::MousePressed => true,
|
MouseEventType::MousePressed => true,
|
||||||
MouseEventType::MouseExit | MouseEventType::MouseReleased => false,
|
MouseEventType::MouseExit | MouseEventType::MouseReleased => false,
|
||||||
MouseEventType::MouseMoved => return InputEventResult::EventAccepted,
|
MouseEventType::MouseMoved => {
|
||||||
|
return if Self::FIELD_OFFSETS.pressed.apply_pin(self).get() {
|
||||||
|
InputEventResult::GrabMouse
|
||||||
|
} else {
|
||||||
|
InputEventResult::EventIgnored
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
if matches!(event.what, MouseEventType::MouseReleased) {
|
if matches!(event.what, MouseEventType::MouseReleased) {
|
||||||
Self::FIELD_OFFSETS.clicked.apply_pin(self).emit(())
|
Self::FIELD_OFFSETS.clicked.apply_pin(self).emit(());
|
||||||
}
|
InputEventResult::EventAccepted
|
||||||
|
} else {
|
||||||
InputEventResult::GrabMouse
|
InputEventResult::GrabMouse
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ItemConsts for TouchArea {
|
impl ItemConsts for TouchArea {
|
||||||
const cached_rendering_data_offset: const_field_offset::FieldOffset<
|
const cached_rendering_data_offset: const_field_offset::FieldOffset<
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue