live preview: Implement drop marker mapping for elements

... so we know what to do when dropping near tghe border of elements that
accept drops in a layout.
This commit is contained in:
Tobias Hunger 2024-03-27 20:55:47 +01:00 committed by Tobias Hunger
parent 1bb0a8638c
commit 69b8e8d57c
4 changed files with 824 additions and 51 deletions

View file

@ -75,6 +75,10 @@ impl ElementRcNode {
(n.source_file.path().to_owned(), u32::from(n.text_range().start()))
})
}
pub fn as_element(&self) -> &ElementRc {
&self.element
}
}
pub fn create_workspace_edit(

View file

@ -220,10 +220,8 @@ fn change_geometry_of_selected_element(x: f32, y: f32, width: f32, height: f32)
return;
};
let Some(geometry) = component_instance
.element_positions(&selected_element_node.element)
.get(selected.instance_index)
.cloned()
let Some(geometry) =
selected_element_node.geometries(&component_instance).get(selected.instance_index).cloned()
else {
return;
};

View file

@ -1,8 +1,8 @@
// Copyright © SixtyFPS GmbH <info@slint.dev>
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.2 OR LicenseRef-Slint-commercial
use i_slint_compiler::parser::SyntaxKind;
use i_slint_core::lengths::{LogicalLength, LogicalPoint};
use i_slint_compiler::parser::{SyntaxKind, SyntaxNode};
use i_slint_core::lengths::{LogicalPoint, LogicalRect};
use slint_interpreter::ComponentInstance;
use crate::common;
@ -51,6 +51,139 @@ impl TextOffsetAdjustment {
}
}
#[derive(Debug, Eq, PartialEq)]
enum DropMarkDirection {
None,
N,
E,
S,
W,
}
impl DropMarkDirection {
/// Create `DropMarkSetup` for a 'live' element (accpting the drop)
pub fn for_element(
element_geometry: &LogicalRect,
position: LogicalPoint,
element_is_in_layout: ui::LayoutKind,
) -> Self {
if Self::element_accepts(element_geometry, position, element_is_in_layout) {
DropMarkDirection::None
} else {
Self::element_direction(element_geometry, position, element_is_in_layout)
}
}
fn border_size(dimension: f32) -> f32 {
match dimension {
d if d >= 16.0 => 4.0,
d if d >= 14.0 => 3.0,
d if d >= 12.0 => 2.0,
d if d >= 6.0 => 1.0,
_ => 0.0,
}
}
// Does this hit the center of an element that accepty drops?
fn element_accepts(
element_geometry: &LogicalRect,
position: LogicalPoint,
element_is_in_layout: ui::LayoutKind,
) -> bool {
if !element_geometry.contains(position) {
return false;
}
let mut inner = element_geometry.clone();
let bs_h = Self::border_size(element_geometry.size.width);
let bs_v = Self::border_size(element_geometry.size.height);
if bs_h < 0.9 || bs_v < 0.9 {
// To small to sub-divide into individual drop-zones:-/
return true;
}
match element_is_in_layout {
ui::LayoutKind::None => {}
ui::LayoutKind::Horizontal => {
inner.origin.x += bs_h;
inner.size.width -= 2.0 * bs_h;
}
ui::LayoutKind::Vertical => {
inner.origin.y += bs_v;
inner.size.height -= 2.0 * bs_v;
}
ui::LayoutKind::Grid => {
inner.origin.x += bs_h;
inner.size.width -= 2.0 * bs_h;
inner.origin.y += bs_v;
inner.size.height -= 2.0 * bs_v;
}
};
inner.contains(position)
}
/// Create `DropMarkSetup` for an element just based on layout info and ignoring
/// the element itself as a drop target (which is handled by `element_accepts`).
fn element_direction(
element_geometry: &LogicalRect,
position: LogicalPoint,
element_is_in_layout: ui::LayoutKind,
) -> Self {
if !element_geometry.contains(position) {
return DropMarkDirection::None;
}
if Self::border_size(element_geometry.size.width) <= 0.9
|| Self::border_size(element_geometry.size.height) <= 0.9
{
// Geometry is too small to sub-divide:
return match element_is_in_layout {
ui::LayoutKind::None => DropMarkDirection::None,
ui::LayoutKind::Horizontal => DropMarkDirection::E,
ui::LayoutKind::Vertical => DropMarkDirection::S,
ui::LayoutKind::Grid => DropMarkDirection::E,
};
}
match element_is_in_layout {
ui::LayoutKind::None => DropMarkDirection::None,
ui::LayoutKind::Horizontal => {
if position.x <= element_geometry.origin.x + (element_geometry.size.width / 2.0) {
DropMarkDirection::W
} else {
DropMarkDirection::E
}
}
ui::LayoutKind::Vertical => {
if position.y <= element_geometry.origin.y + (element_geometry.size.height / 2.0) {
DropMarkDirection::N
} else {
DropMarkDirection::S
}
}
ui::LayoutKind::Grid => {
let x = position.x - element_geometry.origin.x;
let y = position.y - element_geometry.origin.y;
let ascend = element_geometry.size.height / element_geometry.size.width;
let ne_half = y <= ascend * x;
let nw_half = y <= (x * -ascend) + element_geometry.size.height;
eprintln!("GRID: {position:?} in {element_geometry:?}: x: {x}, y: {y}, ascend: {ascend} => NE-half: {} => {ne_half}, NW-half: {} => {nw_half}", -ascend * x, (x * ascend) - element_geometry.size.height);
match (ne_half, nw_half) {
(false, false) => DropMarkDirection::S,
(false, true) => DropMarkDirection::W,
(true, false) => DropMarkDirection::E,
(true, true) => DropMarkDirection::N,
}
}
}
}
}
pub struct DropInformation {
pub target_element_node: common::ElementRcNode,
pub insert_info: InsertInformation,
@ -71,7 +204,7 @@ pub struct DropMark {
pub end: i_slint_core::lengths::LogicalPoint,
}
fn find_insert_position_at_end(
fn insert_position_at_end(
target_element_node: &common::ElementRcNode,
) -> Option<InsertInformation> {
target_element_node.with_element_node(|node| {
@ -125,14 +258,14 @@ fn find_insert_position_at_end(
})
}
fn find_drop_location(
fn drop_target_element_node(
component_instance: &ComponentInstance,
x: f32,
y: f32,
component_type: &str,
) -> Option<DropInformation> {
let target_element_node = {
let mut result = None;
) -> (Option<common::ElementRcNode>, Option<common::ElementRcNode>) {
let mut self_node = None;
let mut surrounding_node = None;
let tl = component_instance.definition().type_loader();
for sc in &element_selection::collect_all_element_nodes_covering(x, y, component_instance) {
let Some(en) = sc.as_element_node() else {
@ -151,9 +284,7 @@ fn find_drop_location(
util::lookup_current_element_type((node.clone()).into(), &doc.local_registry)
}) {
if en.layout_kind() == ui::LayoutKind::None
&& element_type
.accepts_child_element(component_type, &doc.local_registry)
.is_err()
&& element_type.accepts_child_element(component_type, &doc.local_registry).is_err()
{
break;
}
@ -163,16 +294,96 @@ fn find_drop_location(
continue;
}
result = Some(en);
if self_node.is_some() {
surrounding_node = Some(en);
break;
} else {
self_node = Some(en);
}
result
}?;
}
(self_node, surrounding_node)
}
let insert_info = find_insert_position_at_end(&target_element_node)?;
fn extract_element(node: SyntaxNode) -> Option<i_slint_compiler::parser::syntax_nodes::Element> {
match node.kind() {
SyntaxKind::Element => Some(node.into()),
SyntaxKind::SubElement => extract_element(node.child_node(SyntaxKind::Element)?),
SyntaxKind::ConditionalElement | SyntaxKind::RepeatedElement => {
extract_element(node.child_node(SyntaxKind::SubElement)?)
}
_ => None,
}
}
fn examine_target_element_node(
context: &str,
component_instance: &ComponentInstance,
x: f32,
y: f32,
target_element_node: &common::ElementRcNode,
) {
let geometry = target_element_node.geometry_at(component_instance, x, y);
eprintln!(
"{context} :: {target_element_node:?}<{:?}> [{:?}] @ {x}, {y}",
target_element_node.with_element_node(|n| n.kind()),
target_element_node.layout_kind()
);
// let mut result = Vec::new();
if let Some(geometry) = geometry {
eprintln!(
" Element : children count: {}, Geometry: {geometry:?}",
target_element_node.element.borrow().children.len()
);
for (i, c) in target_element_node.element.borrow().children.iter().enumerate() {
let c = common::ElementRcNode::new(c.clone(), 0).unwrap();
eprintln!(
" {i}: {c:?}<{:?}>, Geometry: {:?}",
c.with_element_node(|n| n.kind()),
c.geometry_in(component_instance, &geometry)
);
}
eprintln!(
" Node : children count: {}",
target_element_node.with_element_node(|node| node.children().count())
);
target_element_node.with_element_node(|node| {
for (i, c) in node.children().enumerate() {
let element_data = if let Some(c_element) = extract_element(c.clone()) {
format!(
"\n {:?}:{:?}",
c_element.source_file.path(),
c_element.text_range().start()
)
} else {
String::new()
};
eprintln!(" {i}: {:?}<{:?}>{element_data}", c.text_range(), c.kind());
}
});
}
}
fn find_drop_location(
component_instance: &ComponentInstance,
x: f32,
y: f32,
component_type: &str,
) -> Option<DropInformation> {
let (drop_element_node, drop_surrounding_element_node) =
drop_target_element_node(component_instance, x, y, component_type);
let drop_element_node = drop_element_node?;
examine_target_element_node("Drop target", component_instance, x, y, &drop_element_node);
if let Some(sn) = &drop_surrounding_element_node {
examine_target_element_node("Surrounding", component_instance, x, y, sn);
}
let insert_info = insert_position_at_end(&drop_element_node)?;
Some(DropInformation {
target_element_node,
target_element_node: drop_element_node,
insert_info,
drop_mark: Some(DropMark {
start: LogicalPoint::new(x - 10.0, y - 10.0),
@ -221,16 +432,10 @@ pub fn drop_at(
let properties = {
let mut props = component.default_properties.clone();
let click_position =
LogicalPoint::from_lengths(LogicalLength::new(x), LogicalLength::new(y));
if drop_info.target_element_node.layout_kind() == ui::LayoutKind::None
&& !component.fills_parent
{
if let Some(area) = component_instance
.element_positions(&drop_info.target_element_node.element)
.iter()
.find(|p| p.contains(click_position))
if let Some(area) = drop_info.target_element_node.geometry_at(&component_instance, x, y)
{
props.push(common::PropertyChange::new("x", format!("{}px", x - area.origin.x)));
props.push(common::PropertyChange::new("y", format!("{}px", y - area.origin.y)));
@ -288,3 +493,516 @@ pub fn drop_at(
DropData { selection_offset, path },
))
}
#[cfg(test)]
mod tests {
use i_slint_core::lengths::{LogicalPoint, LogicalRect, LogicalSize};
use crate::preview::{drop_location::DropMarkDirection, ui};
#[test]
fn test_drop_mark_direction_border_size() {
// The border size starts at 0.0 and grows by 1px from there
// Two borders always fit into the dimension passed into border_size
let mut expected = 0.0;
for i in 0_u16..100 {
let dimension = f32::from(i) / 10.0;
let bs = DropMarkDirection::border_size(dimension);
assert!(bs >= (expected - 0.05));
assert!(bs <= (expected + 1.05));
assert!((bs * 2.0) <= dimension); // this makes sure the first bs is 0.0
expected = bs;
}
// The maximum border size is 4px:
assert!(expected <= 4.05);
}
#[test]
fn test_drop_mark_direction_no_area() {
let rect = LogicalRect::new(LogicalPoint::new(50.0, 50.0), LogicalSize::new(0.0, 0.0));
let position = LogicalPoint::new(50.0, 50.0);
assert_eq!(
DropMarkDirection::for_element(&rect, position, ui::LayoutKind::None),
DropMarkDirection::None
);
assert_eq!(
DropMarkDirection::for_element(&rect, position, ui::LayoutKind::Horizontal),
DropMarkDirection::None
);
assert_eq!(
DropMarkDirection::for_element(&rect, position, ui::LayoutKind::Vertical),
DropMarkDirection::None
);
assert_eq!(
DropMarkDirection::for_element(&rect, position, ui::LayoutKind::Grid),
DropMarkDirection::None
);
let rect = LogicalRect::new(LogicalPoint::new(50.0, 50.0), LogicalSize::new(100.0, 0.0));
let position = LogicalPoint::new(50.0, 50.0);
assert_eq!(
DropMarkDirection::for_element(&rect, position, ui::LayoutKind::None),
DropMarkDirection::None
);
assert_eq!(
DropMarkDirection::for_element(&rect, position, ui::LayoutKind::Horizontal),
DropMarkDirection::None
);
assert_eq!(
DropMarkDirection::for_element(&rect, position, ui::LayoutKind::Vertical),
DropMarkDirection::None
);
assert_eq!(
DropMarkDirection::for_element(&rect, position, ui::LayoutKind::Grid),
DropMarkDirection::None
);
let rect = LogicalRect::new(LogicalPoint::new(50.0, 50.0), LogicalSize::new(0.0, 100.0));
let position = LogicalPoint::new(50.0, 50.0);
assert_eq!(
DropMarkDirection::for_element(&rect, position, ui::LayoutKind::None),
DropMarkDirection::None
);
assert_eq!(
DropMarkDirection::for_element(&rect, position, ui::LayoutKind::Horizontal),
DropMarkDirection::None
);
assert_eq!(
DropMarkDirection::for_element(&rect, position, ui::LayoutKind::Vertical),
DropMarkDirection::None
);
assert_eq!(
DropMarkDirection::for_element(&rect, position, ui::LayoutKind::Grid),
DropMarkDirection::None
);
}
#[test]
fn test_drop_mark_direction_outside_position() {
let rect = LogicalRect::new(LogicalPoint::new(50.0, 50.0), LogicalSize::new(50.0, 50.0));
let position = LogicalPoint::new(45.0, 75.0);
assert_eq!(
DropMarkDirection::for_element(&rect, position, ui::LayoutKind::None),
DropMarkDirection::None
);
assert_eq!(
DropMarkDirection::for_element(&rect, position, ui::LayoutKind::Horizontal),
DropMarkDirection::None
);
assert_eq!(
DropMarkDirection::for_element(&rect, position, ui::LayoutKind::Vertical),
DropMarkDirection::None
);
assert_eq!(
DropMarkDirection::for_element(&rect, position, ui::LayoutKind::Grid),
DropMarkDirection::None
);
let position = LogicalPoint::new(105.0, 75.0);
assert_eq!(
DropMarkDirection::for_element(&rect, position, ui::LayoutKind::None),
DropMarkDirection::None
);
assert_eq!(
DropMarkDirection::for_element(&rect, position, ui::LayoutKind::Horizontal),
DropMarkDirection::None
);
assert_eq!(
DropMarkDirection::for_element(&rect, position, ui::LayoutKind::Vertical),
DropMarkDirection::None
);
assert_eq!(
DropMarkDirection::for_element(&rect, position, ui::LayoutKind::Grid),
DropMarkDirection::None
);
let position = LogicalPoint::new(75.0, 45.0);
assert_eq!(
DropMarkDirection::for_element(&rect, position, ui::LayoutKind::None),
DropMarkDirection::None
);
assert_eq!(
DropMarkDirection::for_element(&rect, position, ui::LayoutKind::Horizontal),
DropMarkDirection::None
);
assert_eq!(
DropMarkDirection::for_element(&rect, position, ui::LayoutKind::Vertical),
DropMarkDirection::None
);
assert_eq!(
DropMarkDirection::for_element(&rect, position, ui::LayoutKind::Grid),
DropMarkDirection::None
);
let position = LogicalPoint::new(75.0, 105.0);
assert_eq!(
DropMarkDirection::for_element(&rect, position, ui::LayoutKind::None),
DropMarkDirection::None
);
assert_eq!(
DropMarkDirection::for_element(&rect, position, ui::LayoutKind::Horizontal),
DropMarkDirection::None
);
assert_eq!(
DropMarkDirection::for_element(&rect, position, ui::LayoutKind::Vertical),
DropMarkDirection::None
);
assert_eq!(
DropMarkDirection::for_element(&rect, position, ui::LayoutKind::Grid),
DropMarkDirection::None
);
}
#[test]
fn test_drop_mark_direction_valid_position() {
for width in 1_u16..50 {
for height in 1_u16..50 {
let width = f32::from(width);
let height = f32::from(height);
let rect = LogicalRect::new(
LogicalPoint::new(50.0, 50.0),
LogicalSize::new(width, height),
);
let bs_h = DropMarkDirection::border_size(rect.size.width);
let bs_v = DropMarkDirection::border_size(rect.size.height);
eprintln!("width: {width}, height: {height} => border-h: {bs_h}, border-v: {bs_v}");
// Center: Drop into self, no drop mark ever:
let pos =
LogicalPoint::new(50.0 + (width / 2.0), 50.0 + (height / 2.0));
eprintln!(" Testing: {pos:?}");
assert_eq!(
DropMarkDirection::for_element(
&rect,
pos,
ui::LayoutKind::None
),
DropMarkDirection::None
);
assert_eq!(
DropMarkDirection::for_element(
&rect,
pos,
ui::LayoutKind::Horizontal
),
DropMarkDirection::None
);
assert_eq!(
DropMarkDirection::for_element(
&rect,
pos,
ui::LayoutKind::Vertical
),
DropMarkDirection::None
);
assert_eq!(
DropMarkDirection::for_element(
&rect,
pos,
ui::LayoutKind::Grid
),
DropMarkDirection::None
);
// N-side (in border):
let pos = LogicalPoint::new(50.0 + (width / 2.0), 49.0 + bs_v);
eprintln!(" Testing: {pos:?}");
assert_eq!(
DropMarkDirection::for_element(
&rect,
pos,
ui::LayoutKind::None
),
DropMarkDirection::None
);
assert_eq!(
DropMarkDirection::for_element(
&rect,
pos,
ui::LayoutKind::Horizontal
),
DropMarkDirection::None
);
assert_eq!(
DropMarkDirection::for_element(
&rect,
pos,
ui::LayoutKind::Vertical
),
if bs_h > 0.9 && bs_v > 0.9 { DropMarkDirection::N } else { DropMarkDirection::None }
);
assert_eq!(
DropMarkDirection::for_element(
&rect,
pos,
ui::LayoutKind::Grid
),
if bs_h > 0.9 && bs_v > 0.9 { DropMarkDirection::N } else { DropMarkDirection::None }
);
// N-side (outside border):
let pos = LogicalPoint::new(50.0 + (width / 2.0), 50.0 + bs_v);
eprintln!(" Testing: {pos:?}");
assert_eq!(
DropMarkDirection::for_element(
&rect,
pos,
ui::LayoutKind::None
),
DropMarkDirection::None
);
assert_eq!(
DropMarkDirection::for_element(
&rect,
pos,
ui::LayoutKind::Horizontal
),
DropMarkDirection::None
);
assert_eq!(
DropMarkDirection::for_element(
&rect,
pos,
ui::LayoutKind::Vertical
),
DropMarkDirection::None
);
assert_eq!(
DropMarkDirection::for_element(
&rect,
pos,
ui::LayoutKind::Grid
),
DropMarkDirection::None
);
// E-side (inside border):
let pos = LogicalPoint::new(50.0 + width - bs_h, 50.0 + (height / 2.0));
eprintln!(" Testing: {pos:?}");
assert_eq!(
DropMarkDirection::for_element(
&rect,
pos,
ui::LayoutKind::None
),
DropMarkDirection::None
);
assert_eq!(
DropMarkDirection::for_element(
&rect,
pos,
ui::LayoutKind::Horizontal
),
if bs_h > 0.9 && bs_v > 0.9 { DropMarkDirection::E } else { DropMarkDirection::None }
);
assert_eq!(
DropMarkDirection::for_element(
&rect,
pos,
ui::LayoutKind::Vertical
),
DropMarkDirection::None
);
assert_eq!(
DropMarkDirection::for_element(
&rect,
pos,
ui::LayoutKind::Grid
),
if bs_h > 0.9 && bs_v > 0.9 { DropMarkDirection::E } else { DropMarkDirection::None }
);
// E-side (outside border):
let pos = LogicalPoint::new(49.0 + width - bs_h, 50.0 + (height / 2.0));
eprintln!(" Testing: {pos:?}");
assert_eq!(
DropMarkDirection::for_element(
&rect,
pos,
ui::LayoutKind::None
),
DropMarkDirection::None
);
assert_eq!(
DropMarkDirection::for_element(
&rect,
pos,
ui::LayoutKind::Horizontal
),
DropMarkDirection::None
);
assert_eq!(
DropMarkDirection::for_element(
&rect,
pos,
ui::LayoutKind::Vertical
),
DropMarkDirection::None
);
assert_eq!(
DropMarkDirection::for_element(
&rect,
pos,
ui::LayoutKind::Grid
),
DropMarkDirection::None
);
// S-side (in border):
let pos = LogicalPoint::new(50.0 + (width / 2.0), 50.0 + height - bs_v);
eprintln!(" Testing: {pos:?}");
assert_eq!(
DropMarkDirection::for_element(
&rect,
pos,
ui::LayoutKind::None
),
DropMarkDirection::None
);
assert_eq!(
DropMarkDirection::for_element(
&rect,
pos,
ui::LayoutKind::Horizontal
),
DropMarkDirection::None
);
assert_eq!(
DropMarkDirection::for_element(
&rect,
pos,
ui::LayoutKind::Vertical
),
if bs_h > 0.9 && bs_v > 0.9 { DropMarkDirection::S } else { DropMarkDirection::None }
);
assert_eq!(
DropMarkDirection::for_element(
&rect,
pos,
ui::LayoutKind::Grid
),
if bs_h > 0.9 && bs_v > 0.9 { DropMarkDirection::S } else { DropMarkDirection::None }
);
// S-side (outside border):
let pos = LogicalPoint::new(50.0 + (width / 2.0), 49.0 + height - bs_v);
eprintln!(" Testing: {pos:?}");
assert_eq!(
DropMarkDirection::for_element(
&rect,
pos,
ui::LayoutKind::None
),
DropMarkDirection::None
);
assert_eq!(
DropMarkDirection::for_element(
&rect,
pos,
ui::LayoutKind::Horizontal
),
DropMarkDirection::None
);
assert_eq!(
DropMarkDirection::for_element(
&rect,
pos,
ui::LayoutKind::Vertical
),
DropMarkDirection::None
);
assert_eq!(
DropMarkDirection::for_element(
&rect,
pos,
ui::LayoutKind::Grid
),
DropMarkDirection::None
);
// W-side (inside border):
let pos = LogicalPoint::new(49.0 + bs_h, 50.0 + (height / 2.0));
eprintln!(" Testing: {pos:?}");
assert_eq!(
DropMarkDirection::for_element(
&rect,
pos,
ui::LayoutKind::None
),
DropMarkDirection::None
);
assert_eq!(
DropMarkDirection::for_element(
&rect,
pos,
ui::LayoutKind::Horizontal
),
if bs_h > 0.9 && bs_v > 0.9 { DropMarkDirection::W } else { DropMarkDirection::None }
);
assert_eq!(
DropMarkDirection::for_element(
&rect,
pos,
ui::LayoutKind::Vertical
),
DropMarkDirection::None
);
assert_eq!(
DropMarkDirection::for_element(
&rect,
pos,
ui::LayoutKind::Grid
),
if bs_h > 0.9 && bs_v > 0.9 { DropMarkDirection::W } else { DropMarkDirection::None }
);
// W-side (outside border):
let pos = LogicalPoint::new(50.0 + bs_h, 50.0 + (height / 2.0));
eprintln!(" Testing: {pos:?}");
assert_eq!(
DropMarkDirection::for_element(
&rect,
pos,
ui::LayoutKind::None
),
DropMarkDirection::None
);
assert_eq!(
DropMarkDirection::for_element(
&rect,
pos,
ui::LayoutKind::Horizontal
),
DropMarkDirection::None
);
assert_eq!(
DropMarkDirection::for_element(
&rect,
pos,
ui::LayoutKind::Vertical
),
DropMarkDirection::None
);
assert_eq!(
DropMarkDirection::for_element(
&rect,
pos,
ui::LayoutKind::Grid
),
DropMarkDirection::None
);
}
}
}
}

View file

@ -2,12 +2,36 @@
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.2 OR LicenseRef-Slint-commercial
use i_slint_compiler::layout;
use i_slint_core::lengths::{LogicalPoint, LogicalRect};
use crate::common;
use crate::preview::ui;
use slint_interpreter::ComponentInstance;
pub trait ElementRcNodeExt {
fn layout_kind(&self) -> crate::preview::ui::LayoutKind;
/// Find all geometries for the given `ElementRcNode`
fn geometries(
&self,
component_instance: &ComponentInstance,
) -> Vec<i_slint_core::lengths::LogicalRect>;
/// Find the first geometry of `ElementRcNode` that includes the point `x`, `y`
fn geometry_at(
&self,
component_instance: &ComponentInstance,
x: f32,
y: f32,
) -> Option<i_slint_core::lengths::LogicalRect>;
/// Find the first geometry of ElementRcNode in `rect`
fn geometry_in(
&self,
component_instance: &ComponentInstance,
rect: &LogicalRect,
) -> Vec<i_slint_core::lengths::LogicalRect>;
}
impl ElementRcNodeExt for common::ElementRcNode {
@ -25,4 +49,33 @@ impl ElementRcNodeExt for common::ElementRcNode {
_ => ui::LayoutKind::None,
})
}
fn geometries(
&self,
component_instance: &ComponentInstance,
) -> Vec<i_slint_core::lengths::LogicalRect> {
component_instance.element_positions(&self.as_element())
}
fn geometry_at(
&self,
component_instance: &ComponentInstance,
x: f32,
y: f32,
) -> Option<i_slint_core::lengths::LogicalRect> {
let click_position = LogicalPoint::new(x, y);
self.geometries(component_instance).iter().find(|g| g.contains(click_position)).cloned()
}
fn geometry_in(
&self,
component_instance: &ComponentInstance,
rect: &LogicalRect,
) -> Vec<i_slint_core::lengths::LogicalRect> {
self.geometries(component_instance)
.iter()
.filter(|g| rect.contains_rect(g))
.cloned()
.collect()
}
}