diff --git a/tools/lsp/preview/drop_location.rs b/tools/lsp/preview/drop_location.rs index 21ba6088b..4080dffcd 100644 --- a/tools/lsp/preview/drop_location.rs +++ b/tools/lsp/preview/drop_location.rs @@ -1,7 +1,6 @@ // Copyright © SixtyFPS GmbH // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.1 OR LicenseRef-Slint-commercial -use i_slint_compiler::object_tree::ElementRc; use i_slint_core::lengths::{LogicalLength, LogicalPoint}; use slint_interpreter::ComponentInstance; @@ -10,9 +9,10 @@ use crate::preview::element_selection::collect_all_element_nodes_covering; #[cfg(target_arch = "wasm32")] use crate::wasm_prelude::*; +use super::element_selection::ElementRcNode; + pub struct DropInformation { - pub target_element: ElementRc, - pub node_index: usize, + pub target_element_node: ElementRcNode, pub insertion_position: crate::common::VersionedPosition, } @@ -21,24 +21,27 @@ fn find_drop_location( x: f32, y: f32, ) -> Option { - let elements = collect_all_element_nodes_covering(x, y, &component_instance); - let (node_index, target_element) = elements.iter().find_map(|sc| { - sc.element - .borrow() - .debug - .iter() - .position(|d| !super::is_element_node_ignored(&d.0)) - .map(|i| (i, sc.element.clone())) - })?; + let target_element_node = { + let mut result = None; + for sc in &collect_all_element_nodes_covering(x, y, &component_instance) { + let Some(en) = sc.as_element_node() else { + continue; + }; + + if en.on_element_node(|n| super::is_element_node_ignored(n)) { + continue; + } + + result = Some(en); + break; + } + result + }?; let insertion_position = { - let elem = target_element.borrow(); + let elem = target_element_node.element.borrow(); - let (node, layout) = elem.debug.get(node_index)?; - - if layout.is_some() { - return None; - } + let (node, _) = elem.debug.get(target_element_node.debug_index)?; let last_token = crate::util::last_non_ws_token(node)?; @@ -53,7 +56,7 @@ fn find_drop_location( ) }; - Some(DropInformation { target_element, node_index, insertion_position }) + Some(DropInformation { target_element_node, insertion_position }) } /// Find the Element to insert into. None means we can not insert at this point. @@ -79,8 +82,10 @@ pub fn drop_at( let click_position = LogicalPoint::from_lengths(LogicalLength::new(x), LogicalLength::new(y)); - if let Some(area) = component_instance - .element_position(&drop_info.target_element) + if drop_info.target_element_node.is_layout() { + vec![] + } else if let Some(area) = component_instance + .element_position(&drop_info.target_element_node.element) .iter() .find(|p| p.contains(click_position)) { @@ -95,7 +100,11 @@ pub fn drop_at( let indentation = format!( "{} ", - crate::util::find_element_indent(&drop_info.target_element).unwrap_or_default() + crate::util::find_element_node_indent( + &drop_info.target_element_node.element, + drop_info.target_element_node.debug_index + ) + .unwrap_or_default() ); let component_text = if properties.is_empty() { diff --git a/tools/lsp/preview/element_selection.rs b/tools/lsp/preview/element_selection.rs index d5eebf1ac..82420c72d 100644 --- a/tools/lsp/preview/element_selection.rs +++ b/tools/lsp/preview/element_selection.rs @@ -27,6 +27,18 @@ impl ElementRcNode { Some(Self { element, debug_index }) } + pub fn on_element_debug( + &self, + func: impl Fn( + &i_slint_compiler::parser::syntax_nodes::Element, + &Option, + ) -> R, + ) -> R { + let elem = self.element.borrow(); + let (n, l) = &elem.debug.get(self.debug_index).unwrap(); + func(n, l) + } + pub fn on_element_node( &self, func: impl Fn(&i_slint_compiler::parser::syntax_nodes::Element) -> R, @@ -40,6 +52,10 @@ impl ElementRcNode { (n.source_file.path().to_owned(), u32::from(n.text_range().start())) }) } + + pub fn is_layout(&self) -> bool { + self.on_element_debug(|_, l| l.is_some()) + } } #[derive(Clone, Debug)] diff --git a/tools/lsp/util.rs b/tools/lsp/util.rs index 752f82986..c9b96252a 100644 --- a/tools/lsp/util.rs +++ b/tools/lsp/util.rs @@ -70,6 +70,19 @@ pub fn find_element_indent(element: &ElementRc) -> Option { None } +/// Find the indentation of the element itself as well as the indentation of properties inside the element. +/// Returns the element indent followed by the block indent +pub fn find_element_node_indent(element: &ElementRc, debug_index: usize) -> Option { + let mut token = element.borrow().debug.get(debug_index)?.0.first_token()?.prev_token(); + while let Some(t) = token { + if t.kind() == SyntaxKind::Whitespace && t.text().contains('\n') { + return t.text().split('\n').last().map(|s| s.to_owned()); + } + token = t.prev_token(); + } + None +} + /// Given a node within an element, return the Type for the Element under that node. /// (If node is an element, return the Type for that element, otherwise the type of the element under it) /// Will return `Foo` in the following example where `|` is the cursor.