live preview: Drop elements into layouts (MVP!)

This is the minimum layout support: It drops elements
into a layout right before the layout's closing brace.
This commit is contained in:
Tobias Hunger 2024-02-22 22:27:40 +01:00 committed by Tobias Hunger
parent e61c97fbdf
commit 171c9e215a
3 changed files with 60 additions and 22 deletions

View file

@ -1,7 +1,6 @@
// Copyright © SixtyFPS GmbH <info@slint.dev>
// 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<DropInformation> {
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() {

View file

@ -27,6 +27,18 @@ impl ElementRcNode {
Some(Self { element, debug_index })
}
pub fn on_element_debug<R>(
&self,
func: impl Fn(
&i_slint_compiler::parser::syntax_nodes::Element,
&Option<i_slint_compiler::layout::Layout>,
) -> 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<R>(
&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)]

View file

@ -70,6 +70,19 @@ pub fn find_element_indent(element: &ElementRc) -> Option<String> {
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<String> {
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.