From 9b2a7fc6945e85d6c7501b88a6e20874b5f09cf9 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Fri, 29 Mar 2024 15:08:12 +0100 Subject: [PATCH] live preview: Prefer LogicalPoint over providing x and y --- tools/lsp/preview/drop_location.rs | 426 ++++++++++--------------- tools/lsp/preview/element_selection.rs | 208 ++++++------ tools/lsp/preview/ext.rs | 9 +- 3 files changed, 276 insertions(+), 367 deletions(-) diff --git a/tools/lsp/preview/drop_location.rs b/tools/lsp/preview/drop_location.rs index 49149038c..0fd728c0a 100644 --- a/tools/lsp/preview/drop_location.rs +++ b/tools/lsp/preview/drop_location.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.2 OR LicenseRef-Slint-commercial use i_slint_compiler::parser::{SyntaxKind, SyntaxNode}; -use i_slint_core::lengths::{LogicalPoint, LogicalRect}; +use i_slint_core::lengths::{LogicalPoint, LogicalRect, LogicalSize}; use slint_interpreter::ComponentInstance; use crate::common; @@ -75,16 +75,15 @@ impl DropMarkDirection { } 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, + let bs = (dimension / 4.0).floor(); + if bs > 8.0 { + 8.0 + } else { + bs } } - // Does this hit the center of an element that accepty drops? + // Does this hit the center area of an element that accept drops? fn element_accepts( element_geometry: &LogicalRect, position: LogicalPoint, @@ -171,7 +170,6 @@ impl DropMarkDirection { 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, @@ -260,14 +258,13 @@ fn insert_position_at_end( fn drop_target_element_node( component_instance: &ComponentInstance, - x: f32, - y: f32, + position: LogicalPoint, component_type: &str, ) -> (Option, Option) { 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) { + for sc in &element_selection::collect_all_element_nodes_covering(position, component_instance) { let Some(en) = sc.as_element_node() else { continue; }; @@ -318,34 +315,15 @@ fn extract_element(node: SyntaxNode) -> Option [{:?}] @ {x}, {y}", - target_element_node.with_element_node(|n| n.kind()), - target_element_node.layout_kind() - ); + let geometry = target_element_node.geometry_at(component_instance, position); // 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()) { @@ -357,46 +335,90 @@ fn examine_target_element_node( } else { String::new() }; - - eprintln!(" {i}: {:?}<{:?}>{element_data}", c.text_range(), c.kind()); } }); } } +fn drop_into_layout( + component_instance: &ComponentInstance, + element_node: common::ElementRcNode, + insert_position: Option<(DropMarkDirection, common::ElementRcNode)>, + position: LogicalPoint, +) -> Option { + let geometry = element_node.geometry_at(component_instance, position)?; + let insert_info = insert_position_at_end(&element_node)?; + + Some(DropInformation { + target_element_node: element_node, + insert_info, + drop_mark: Some(DropMark { + start: geometry.origin + LogicalSize::new(0.0, geometry.size.height - 1.0), + end: geometry.origin + geometry.size, + }), + }) +} + +fn drop_into_element( + component_instance: &ComponentInstance, + element_node: common::ElementRcNode, + surround_node: Option, + position: LogicalPoint, +) -> Option { + let geometry = element_node.geometry_at(component_instance, position)?; + let drop_mark_direction = DropMarkDirection::for_element( + &geometry, + position, + surround_node.as_ref().map(|n| n.layout_kind()).unwrap_or(ui::LayoutKind::None), + ); + + if drop_mark_direction == DropMarkDirection::None { + let insert_info = insert_position_at_end(&element_node)?; + + Some(DropInformation { target_element_node: element_node, insert_info, drop_mark: None }) + } else { + drop_into_layout( + component_instance, + surround_node.unwrap(), + Some((drop_mark_direction, element_node)), + position, + ) + } +} + fn find_drop_location( component_instance: &ComponentInstance, - x: f32, - y: f32, + position: LogicalPoint, component_type: &str, ) -> Option { let (drop_element_node, drop_surrounding_element_node) = - drop_target_element_node(component_instance, x, y, component_type); + drop_target_element_node(component_instance, position, component_type); let drop_element_node = drop_element_node?; - examine_target_element_node("Drop target", component_instance, x, y, &drop_element_node); + examine_target_element_node("Drop target", component_instance, position, &drop_element_node); if let Some(sn) = &drop_surrounding_element_node { - examine_target_element_node("Surrounding", component_instance, x, y, sn); + examine_target_element_node("Surrounding", component_instance, position, sn); } - let insert_info = insert_position_at_end(&drop_element_node)?; - - Some(DropInformation { - target_element_node: drop_element_node, - insert_info, - drop_mark: Some(DropMark { - start: LogicalPoint::new(x - 10.0, y - 10.0), - end: LogicalPoint::new(x + 10.0, y + 10.0), - }), - }) + if drop_element_node.layout_kind() != ui::LayoutKind::None { + drop_into_layout(component_instance, drop_element_node, None, position) + } else { + drop_into_element( + component_instance, + drop_element_node, + drop_surrounding_element_node, + position, + ) + } } /// Find the Element to insert into. None means we can not insert at this point. pub fn can_drop_at(x: f32, y: f32, component: &common::ComponentInformation) -> bool { let component_type = component.name.to_string(); - if let Some(dm) = - &super::component_instance().and_then(|ci| find_drop_location(&ci, x, y, &component_type)) + let position = LogicalPoint::new(x, y); + if let Some(dm) = &super::component_instance() + .and_then(|ci| find_drop_location(&ci, position, &component_type)) { super::set_drop_mark(&dm.drop_mark); true @@ -424,10 +446,11 @@ pub fn drop_at( y: f32, component: &common::ComponentInformation, ) -> Option<(lsp_types::WorkspaceEdit, DropData)> { + let position = LogicalPoint::new(x, y); let component_type = &component.name; let component_instance = preview::component_instance()?; let tl = component_instance.definition().type_loader(); - let drop_info = find_drop_location(&component_instance, x, y, component_type)?; + let drop_info = find_drop_location(&component_instance, position, component_type)?; let properties = { let mut props = component.default_properties.clone(); @@ -435,7 +458,8 @@ pub fn drop_at( if drop_info.target_element_node.layout_kind() == ui::LayoutKind::None && !component.fills_parent { - if let Some(area) = drop_info.target_element_node.geometry_at(&component_instance, x, y) + if let Some(area) = + drop_info.target_element_node.geometry_at(&component_instance, position) { props.push(common::PropertyChange::new("x", format!("{}px", x - area.origin.x))); props.push(common::PropertyChange::new("y", format!("{}px", y - area.origin.y))); @@ -508,13 +532,15 @@ mod tests { 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; + assert!( + bs >= (expected - 0.05) && bs < (expected + 0.05) + || (bs >= (expected + 0.95) && bs < (expected + 1.05)) + ); + assert!((bs * 3.0) <= dimension); // this makes sure the first bs is 0.0 + expected = bs.round(); } // The maximum border size is 4px: - assert!(expected <= 4.05); + assert!(expected <= 8.05); } #[test] @@ -666,342 +692,218 @@ mod tests { ); 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:?}"); + let pos = LogicalPoint::new(50.0 + (width / 2.0), 50.0 + (height / 2.0)); assert_eq!( - DropMarkDirection::for_element( - &rect, - pos, - ui::LayoutKind::None - ), + DropMarkDirection::for_element(&rect, pos, ui::LayoutKind::None), DropMarkDirection::None ); assert_eq!( - DropMarkDirection::for_element( - &rect, - pos, - ui::LayoutKind::Horizontal - ), + DropMarkDirection::for_element(&rect, pos, ui::LayoutKind::Horizontal), DropMarkDirection::None ); assert_eq!( - DropMarkDirection::for_element( - &rect, - pos, - ui::LayoutKind::Vertical - ), + DropMarkDirection::for_element(&rect, pos, ui::LayoutKind::Vertical), DropMarkDirection::None ); assert_eq!( - DropMarkDirection::for_element( - &rect, - pos, - ui::LayoutKind::Grid - ), + 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::for_element(&rect, pos, ui::LayoutKind::None), DropMarkDirection::None ); assert_eq!( - DropMarkDirection::for_element( - &rect, - pos, - ui::LayoutKind::Horizontal - ), + 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 } + 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 } + 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::for_element(&rect, pos, ui::LayoutKind::None), DropMarkDirection::None ); assert_eq!( - DropMarkDirection::for_element( - &rect, - pos, - ui::LayoutKind::Horizontal - ), + DropMarkDirection::for_element(&rect, pos, ui::LayoutKind::Horizontal), DropMarkDirection::None ); assert_eq!( - DropMarkDirection::for_element( - &rect, - pos, - ui::LayoutKind::Vertical - ), + DropMarkDirection::for_element(&rect, pos, ui::LayoutKind::Vertical), DropMarkDirection::None ); assert_eq!( - DropMarkDirection::for_element( - &rect, - pos, - ui::LayoutKind::Grid - ), + 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::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 } + 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::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 } + 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::for_element(&rect, pos, ui::LayoutKind::None), DropMarkDirection::None ); assert_eq!( - DropMarkDirection::for_element( - &rect, - pos, - ui::LayoutKind::Horizontal - ), + DropMarkDirection::for_element(&rect, pos, ui::LayoutKind::Horizontal), DropMarkDirection::None ); assert_eq!( - DropMarkDirection::for_element( - &rect, - pos, - ui::LayoutKind::Vertical - ), + DropMarkDirection::for_element(&rect, pos, ui::LayoutKind::Vertical), DropMarkDirection::None ); assert_eq!( - DropMarkDirection::for_element( - &rect, - pos, - ui::LayoutKind::Grid - ), + 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::for_element(&rect, pos, ui::LayoutKind::None), DropMarkDirection::None ); assert_eq!( - DropMarkDirection::for_element( - &rect, - pos, - ui::LayoutKind::Horizontal - ), + 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 } + 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 } + 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::for_element(&rect, pos, ui::LayoutKind::None), DropMarkDirection::None ); assert_eq!( - DropMarkDirection::for_element( - &rect, - pos, - ui::LayoutKind::Horizontal - ), + DropMarkDirection::for_element(&rect, pos, ui::LayoutKind::Horizontal), DropMarkDirection::None ); assert_eq!( - DropMarkDirection::for_element( - &rect, - pos, - ui::LayoutKind::Vertical - ), + DropMarkDirection::for_element(&rect, pos, ui::LayoutKind::Vertical), DropMarkDirection::None ); assert_eq!( - DropMarkDirection::for_element( - &rect, - pos, - ui::LayoutKind::Grid - ), + 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::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 } + 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::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 } + 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::for_element(&rect, pos, ui::LayoutKind::None), DropMarkDirection::None ); assert_eq!( - DropMarkDirection::for_element( - &rect, - pos, - ui::LayoutKind::Horizontal - ), + DropMarkDirection::for_element(&rect, pos, ui::LayoutKind::Horizontal), DropMarkDirection::None ); assert_eq!( - DropMarkDirection::for_element( - &rect, - pos, - ui::LayoutKind::Vertical - ), + DropMarkDirection::for_element(&rect, pos, ui::LayoutKind::Vertical), DropMarkDirection::None ); assert_eq!( - DropMarkDirection::for_element( - &rect, - pos, - ui::LayoutKind::Grid - ), + DropMarkDirection::for_element(&rect, pos, ui::LayoutKind::Grid), DropMarkDirection::None ); - } } } diff --git a/tools/lsp/preview/element_selection.rs b/tools/lsp/preview/element_selection.rs index 10f448b31..8f78bdb2a 100644 --- a/tools/lsp/preview/element_selection.rs +++ b/tools/lsp/preview/element_selection.rs @@ -5,7 +5,7 @@ use std::{path::PathBuf, rc::Rc}; use i_slint_compiler::diagnostics::SourceFile; use i_slint_compiler::object_tree::{Component, ElementRc}; -use i_slint_core::lengths::{LogicalLength, LogicalPoint}; +use i_slint_core::lengths::LogicalPoint; use rowan::TextRange; use slint_interpreter::ComponentInstance; @@ -73,17 +73,11 @@ fn lsp_element_node_position(element: &ElementRcNode) -> Option<(String, lsp_typ } fn element_covers_point( - x: f32, - y: f32, + position: LogicalPoint, component_instance: &ComponentInstance, selected_element: &ElementRc, ) -> bool { - let click_position = LogicalPoint::from_lengths(LogicalLength::new(x), LogicalLength::new(y)); - - component_instance - .element_positions(selected_element) - .iter() - .any(|p| p.contains(click_position)) + component_instance.element_positions(selected_element).iter().any(|p| p.contains(position)) } pub fn unselect_element() { @@ -197,8 +191,7 @@ impl std::fmt::Debug for SelectionCandidate { // Traverse the element tree in reverse render order and collect information on // all elements that "render" at the given x and y coordinates fn collect_all_element_nodes_covering_impl( - x: f32, - y: f32, + position: LogicalPoint, component_instance: &ComponentInstance, current_element: &ElementRc, component_stack: &Vec>, @@ -223,8 +216,7 @@ fn collect_all_element_nodes_covering_impl( for c in ce.borrow().children.iter().rev() { collect_all_element_nodes_covering_impl( - x, - y, + position, component_instance, c, children_component_stack, @@ -232,7 +224,7 @@ fn collect_all_element_nodes_covering_impl( ); } - if element_covers_point(x, y, component_instance, &ce) { + if element_covers_point(position, component_instance, &ce) { for (i, _) in ce.borrow().debug.iter().enumerate().rev() { // All nodes have the same geometry let text_range = element_node_source_range(&ce, i); @@ -247,15 +239,13 @@ fn collect_all_element_nodes_covering_impl( } pub fn collect_all_element_nodes_covering( - x: f32, - y: f32, + position: LogicalPoint, component_instance: &ComponentInstance, ) -> Vec { let root_element = root_element(component_instance); let mut elements = Vec::new(); collect_all_element_nodes_covering_impl( - x, - y, + position, component_instance, &root_element, &vec![], @@ -300,11 +290,10 @@ pub fn is_same_file_as_root_node( fn select_element_at_impl( component_instance: &ComponentInstance, - x: f32, - y: f32, + position: LogicalPoint, enter_component: bool, ) -> Option { - for sc in &collect_all_element_nodes_covering(x, y, component_instance) { + for sc in &collect_all_element_nodes_covering(position, component_instance) { if let Some(en) = filter_nodes_for_selection(component_instance, sc, enter_component) { return Some(en); } @@ -317,20 +306,22 @@ pub fn select_element_at(x: f32, y: f32, enter_component: bool) { return; }; + let position = LogicalPoint::new(x, y); + if let Some(se) = super::selected_element() { if let Some(element) = se.as_element() { - if element_covers_point(x, y, &component_instance, &element) { + if element_covers_point(position, &component_instance, &element) { // We clicked on the already selected element: Do nothing! return; } } } - let Some(en) = select_element_at_impl(&component_instance, x, y, enter_component) else { + let Some(en) = select_element_at_impl(&component_instance, position, enter_component) else { return; }; - select_element_node(&component_instance, &en, Some(LogicalPoint::new(x, y))); + select_element_node(&component_instance, &en, Some(position)); } pub fn is_element_node_in_layout(element: &ElementRcNode) -> bool { @@ -381,12 +372,11 @@ fn filter_nodes_for_selection( pub fn select_element_behind_impl( component_instance: &ComponentInstance, selected_element_node: &ElementRcNode, - x: f32, - y: f32, + position: LogicalPoint, enter_component: bool, reverse: bool, ) -> Option { - let elements = collect_all_element_nodes_covering(x, y, component_instance); + let elements = collect_all_element_nodes_covering(position, component_instance); let current_selection_position = elements.iter().position(|sc| sc.is_selected_element_node(selected_element_node))?; @@ -422,6 +412,7 @@ pub fn select_element_behind(x: f32, y: f32, enter_component: bool, reverse: boo let Some(component_instance) = super::component_instance() else { return; }; + let position = LogicalPoint::new(x, y); let Some(selected_element_node) = super::selected_element().and_then(|sel| sel.as_element_node()) else { @@ -431,15 +422,14 @@ pub fn select_element_behind(x: f32, y: f32, enter_component: bool, reverse: boo let Some(en) = select_element_behind_impl( &component_instance, &selected_element_node, - x, - y, + position, enter_component, reverse, ) else { return; }; - select_element_node(&component_instance, &en, Some(LogicalPoint::new(x, y))); + select_element_node(&component_instance, &en, Some(position)); } // Called from UI thread! @@ -459,6 +449,7 @@ pub fn reselect_element() { mod tests { use std::path::PathBuf; + use i_slint_core::lengths::LogicalPoint; use slint_interpreter::ComponentInstance; fn demo_app() -> ComponentInstance { @@ -494,8 +485,10 @@ export component Entry inherits Main { /* @lsp:ignore-node */ } // 401 fn test_find_covering_elements() { let component_instance = demo_app(); - let mut covers_center = - super::collect_all_element_nodes_covering(100.0, 100.0, &component_instance); + let mut covers_center = super::collect_all_element_nodes_covering( + LogicalPoint::new(100.0, 100.0), + &component_instance, + ); // Remove the "button" implenmentation details. They must be at the start: let button_path = PathBuf::from("builtin:/fluent-base/button.slint"); @@ -518,8 +511,10 @@ export component Entry inherits Main { /* @lsp:ignore-node */ } // 401 assert_eq!(offset, *expected_offset); } - let covers_below = - super::collect_all_element_nodes_covering(100.0, 180.0, &component_instance); + let covers_below = super::collect_all_element_nodes_covering( + LogicalPoint::new(100.0, 180.0), + &component_instance, + ); // All but the button itself as well as the SomeComponent (impl and use) assert_eq!(covers_below.len(), covers_center.len() - 3); @@ -537,26 +532,31 @@ export component Entry inherits Main { /* @lsp:ignore-node */ } // 401 let component_instance = demo_app(); let button_path = PathBuf::from("builtin:/fluent-base/button.slint"); - let mut covers_center = - super::collect_all_element_nodes_covering(100.0, 100.0, &component_instance) - .iter() - .flat_map(|sc| sc.as_element_node()) - .map(|en| en.path_and_offset()) - .collect::>(); + let mut covers_center = super::collect_all_element_nodes_covering( + LogicalPoint::new(100.0, 100.0), + &component_instance, + ) + .iter() + .flat_map(|sc| sc.as_element_node()) + .map(|en| en.path_and_offset()) + .collect::>(); let first_non_button = covers_center.iter().position(|(p, _)| p != &button_path).unwrap(); covers_center.drain(1..(first_non_button - 1)); // strip all but first/last of button // Select without crossing file boundries - let select = - super::select_element_at_impl(&component_instance, 100.0, 100.0, false).unwrap(); + let select = super::select_element_at_impl( + &component_instance, + LogicalPoint::new(100.0, 100.0), + false, + ) + .unwrap(); assert_eq!(&select.path_and_offset(), covers_center.get(2).unwrap()); // Move deeper into the image: let next = super::select_element_behind_impl( &component_instance, &select, - 100.0, - 100.0, + LogicalPoint::new(100.0, 100.0), false, false, ) @@ -565,8 +565,7 @@ export component Entry inherits Main { /* @lsp:ignore-node */ } // 401 let next = super::select_element_behind_impl( &component_instance, &next, - 100.0, - 100.0, + LogicalPoint::new(100.0, 100.0), false, false, ) @@ -575,8 +574,7 @@ export component Entry inherits Main { /* @lsp:ignore-node */ } // 401 let next = super::select_element_behind_impl( &component_instance, &next, - 100.0, - 100.0, + LogicalPoint::new(100.0, 100.0), false, false, ) @@ -585,8 +583,7 @@ export component Entry inherits Main { /* @lsp:ignore-node */ } // 401 let next = super::select_element_behind_impl( &component_instance, &next, - 100.0, - 100.0, + LogicalPoint::new(100.0, 100.0), false, false, ) @@ -595,8 +592,7 @@ export component Entry inherits Main { /* @lsp:ignore-node */ } // 401 assert!(super::select_element_behind_impl( &component_instance, &next, - 100.0, - 100.0, + LogicalPoint::new(100.0, 100.0), false, false ) @@ -606,8 +602,7 @@ export component Entry inherits Main { /* @lsp:ignore-node */ } // 401 let prev = super::select_element_behind_impl( &component_instance, &next, - 100.0, - 100.0, + LogicalPoint::new(100.0, 100.0), false, true, ) @@ -616,8 +611,7 @@ export component Entry inherits Main { /* @lsp:ignore-node */ } // 401 let prev = super::select_element_behind_impl( &component_instance, &prev, - 100.0, - 100.0, + LogicalPoint::new(100.0, 100.0), false, true, ) @@ -626,8 +620,7 @@ export component Entry inherits Main { /* @lsp:ignore-node */ } // 401 let prev = super::select_element_behind_impl( &component_instance, &prev, - 100.0, - 100.0, + LogicalPoint::new(100.0, 100.0), false, true, ) @@ -636,8 +629,7 @@ export component Entry inherits Main { /* @lsp:ignore-node */ } // 401 let prev = super::select_element_behind_impl( &component_instance, &prev, - 100.0, - 100.0, + LogicalPoint::new(100.0, 100.0), false, true, ) @@ -646,8 +638,7 @@ export component Entry inherits Main { /* @lsp:ignore-node */ } // 401 assert!(super::select_element_behind_impl( &component_instance, &prev, - 100.0, - 100.0, + LogicalPoint::new(100.0, 100.0), false, true ) @@ -657,8 +648,7 @@ export component Entry inherits Main { /* @lsp:ignore-node */ } // 401 super::select_element_behind_impl( &component_instance, &select, - 100.0, - 100.0, + LogicalPoint::new(100.0, 100.0), false, true ), @@ -666,8 +656,12 @@ export component Entry inherits Main { /* @lsp:ignore-node */ } // 401 ); // Select with crossing file boundries - let select = - super::select_element_at_impl(&component_instance, 100.0, 100.0, true).unwrap(); + let select = super::select_element_at_impl( + &component_instance, + LogicalPoint::new(100.0, 100.0), + true, + ) + .unwrap(); assert_eq!(&select.path_and_offset(), covers_center.get(0).unwrap()); // move to the last in the button definition: @@ -676,8 +670,7 @@ export component Entry inherits Main { /* @lsp:ignore-node */ } // 401 button = super::select_element_behind_impl( &component_instance, &button, - 100.0, - 100.0, + LogicalPoint::new(100.0, 100.0), true, false, ) @@ -691,8 +684,7 @@ export component Entry inherits Main { /* @lsp:ignore-node */ } // 401 let next = super::select_element_behind_impl( &component_instance, &button, - 100.0, - 100.0, + LogicalPoint::new(100.0, 100.0), true, false, ) @@ -701,8 +693,7 @@ export component Entry inherits Main { /* @lsp:ignore-node */ } // 401 let next = super::select_element_behind_impl( &component_instance, &next, - 100.0, - 100.0, + LogicalPoint::new(100.0, 100.0), true, false, ) @@ -711,8 +702,7 @@ export component Entry inherits Main { /* @lsp:ignore-node */ } // 401 let next = super::select_element_behind_impl( &component_instance, &next, - 100.0, - 100.0, + LogicalPoint::new(100.0, 100.0), true, false, ) @@ -721,8 +711,7 @@ export component Entry inherits Main { /* @lsp:ignore-node */ } // 401 let next = super::select_element_behind_impl( &component_instance, &next, - 100.0, - 100.0, + LogicalPoint::new(100.0, 100.0), true, false, ) @@ -731,8 +720,7 @@ export component Entry inherits Main { /* @lsp:ignore-node */ } // 401 let next = super::select_element_behind_impl( &component_instance, &next, - 100.0, - 100.0, + LogicalPoint::new(100.0, 100.0), true, false, ) @@ -741,33 +729,57 @@ export component Entry inherits Main { /* @lsp:ignore-node */ } // 401 assert!(super::select_element_behind_impl( &component_instance, &next, - 100.0, - 100.0, + LogicalPoint::new(100.0, 100.0), false, false ) .is_none()); // Move towards the viewer: - let prev = - super::select_element_behind_impl(&component_instance, &next, 100.0, 100.0, true, true) - .unwrap(); + let prev = super::select_element_behind_impl( + &component_instance, + &next, + LogicalPoint::new(100.0, 100.0), + true, + true, + ) + .unwrap(); assert_eq!(&prev.path_and_offset(), covers_center.get(5).unwrap()); - let prev = - super::select_element_behind_impl(&component_instance, &prev, 100.0, 100.0, true, true) - .unwrap(); + let prev = super::select_element_behind_impl( + &component_instance, + &prev, + LogicalPoint::new(100.0, 100.0), + true, + true, + ) + .unwrap(); assert_eq!(&prev.path_and_offset(), covers_center.get(4).unwrap()); - let prev = - super::select_element_behind_impl(&component_instance, &prev, 100.0, 100.0, true, true) - .unwrap(); + let prev = super::select_element_behind_impl( + &component_instance, + &prev, + LogicalPoint::new(100.0, 100.0), + true, + true, + ) + .unwrap(); assert_eq!(&prev.path_and_offset(), covers_center.get(3).unwrap()); - let prev = - super::select_element_behind_impl(&component_instance, &prev, 100.0, 100.0, true, true) - .unwrap(); + let prev = super::select_element_behind_impl( + &component_instance, + &prev, + LogicalPoint::new(100.0, 100.0), + true, + true, + ) + .unwrap(); assert_eq!(&prev.path_and_offset(), covers_center.get(2).unwrap()); - let prev = - super::select_element_behind_impl(&component_instance, &prev, 100.0, 100.0, true, true) - .unwrap(); + let prev = super::select_element_behind_impl( + &component_instance, + &prev, + LogicalPoint::new(100.0, 100.0), + true, + true, + ) + .unwrap(); assert_eq!(&prev.path_and_offset(), covers_center.get(1).unwrap()); button = prev; @@ -775,8 +787,7 @@ export component Entry inherits Main { /* @lsp:ignore-node */ } // 401 button = super::select_element_behind_impl( &component_instance, &button, - 100.0, - 100.0, + LogicalPoint::new(100.0, 100.0), true, true, ) @@ -789,8 +800,7 @@ export component Entry inherits Main { /* @lsp:ignore-node */ } // 401 assert!(super::select_element_behind_impl( &component_instance, &button, - 100.0, - 100.0, + LogicalPoint::new(100.0, 100.0), true, true ) diff --git a/tools/lsp/preview/ext.rs b/tools/lsp/preview/ext.rs index 196f7a6a8..07fa281f7 100644 --- a/tools/lsp/preview/ext.rs +++ b/tools/lsp/preview/ext.rs @@ -22,8 +22,7 @@ pub trait ElementRcNodeExt { fn geometry_at( &self, component_instance: &ComponentInstance, - x: f32, - y: f32, + position: LogicalPoint, ) -> Option; /// Find the first geometry of ElementRcNode in `rect` @@ -60,11 +59,9 @@ impl ElementRcNodeExt for common::ElementRcNode { fn geometry_at( &self, component_instance: &ComponentInstance, - x: f32, - y: f32, + position: LogicalPoint, ) -> Option { - let click_position = LogicalPoint::new(x, y); - self.geometries(component_instance).iter().find(|g| g.contains(click_position)).cloned() + self.geometries(component_instance).iter().find(|g| g.contains(position)).cloned() } fn geometry_in(