mirror of
https://github.com/slint-ui/slint.git
synced 2025-10-02 06:41:14 +00:00
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:
parent
1bb0a8638c
commit
69b8e8d57c
4 changed files with 824 additions and 51 deletions
|
@ -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(
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
(self_node, surrounding_node)
|
||||
}
|
||||
result
|
||||
}?;
|
||||
|
||||
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
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue