mirror of
https://github.com/slint-ui/slint.git
synced 2025-10-27 10:26:12 +00:00
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:
parent
e61c97fbdf
commit
171c9e215a
3 changed files with 60 additions and 22 deletions
|
|
@ -1,7 +1,6 @@
|
||||||
// Copyright © SixtyFPS GmbH <info@slint.dev>
|
// Copyright © SixtyFPS GmbH <info@slint.dev>
|
||||||
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.1 OR LicenseRef-Slint-commercial
|
// 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 i_slint_core::lengths::{LogicalLength, LogicalPoint};
|
||||||
use slint_interpreter::ComponentInstance;
|
use slint_interpreter::ComponentInstance;
|
||||||
|
|
||||||
|
|
@ -10,9 +9,10 @@ use crate::preview::element_selection::collect_all_element_nodes_covering;
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
use crate::wasm_prelude::*;
|
use crate::wasm_prelude::*;
|
||||||
|
|
||||||
|
use super::element_selection::ElementRcNode;
|
||||||
|
|
||||||
pub struct DropInformation {
|
pub struct DropInformation {
|
||||||
pub target_element: ElementRc,
|
pub target_element_node: ElementRcNode,
|
||||||
pub node_index: usize,
|
|
||||||
pub insertion_position: crate::common::VersionedPosition,
|
pub insertion_position: crate::common::VersionedPosition,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -21,24 +21,27 @@ fn find_drop_location(
|
||||||
x: f32,
|
x: f32,
|
||||||
y: f32,
|
y: f32,
|
||||||
) -> Option<DropInformation> {
|
) -> Option<DropInformation> {
|
||||||
let elements = collect_all_element_nodes_covering(x, y, &component_instance);
|
let target_element_node = {
|
||||||
let (node_index, target_element) = elements.iter().find_map(|sc| {
|
let mut result = None;
|
||||||
sc.element
|
for sc in &collect_all_element_nodes_covering(x, y, &component_instance) {
|
||||||
.borrow()
|
let Some(en) = sc.as_element_node() else {
|
||||||
.debug
|
continue;
|
||||||
.iter()
|
};
|
||||||
.position(|d| !super::is_element_node_ignored(&d.0))
|
|
||||||
.map(|i| (i, sc.element.clone()))
|
if en.on_element_node(|n| super::is_element_node_ignored(n)) {
|
||||||
})?;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = Some(en);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}?;
|
||||||
|
|
||||||
let insertion_position = {
|
let insertion_position = {
|
||||||
let elem = target_element.borrow();
|
let elem = target_element_node.element.borrow();
|
||||||
|
|
||||||
let (node, layout) = elem.debug.get(node_index)?;
|
let (node, _) = elem.debug.get(target_element_node.debug_index)?;
|
||||||
|
|
||||||
if layout.is_some() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let last_token = crate::util::last_non_ws_token(node)?;
|
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.
|
/// 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 =
|
let click_position =
|
||||||
LogicalPoint::from_lengths(LogicalLength::new(x), LogicalLength::new(y));
|
LogicalPoint::from_lengths(LogicalLength::new(x), LogicalLength::new(y));
|
||||||
|
|
||||||
if let Some(area) = component_instance
|
if drop_info.target_element_node.is_layout() {
|
||||||
.element_position(&drop_info.target_element)
|
vec![]
|
||||||
|
} else if let Some(area) = component_instance
|
||||||
|
.element_position(&drop_info.target_element_node.element)
|
||||||
.iter()
|
.iter()
|
||||||
.find(|p| p.contains(click_position))
|
.find(|p| p.contains(click_position))
|
||||||
{
|
{
|
||||||
|
|
@ -95,7 +100,11 @@ pub fn drop_at(
|
||||||
|
|
||||||
let indentation = format!(
|
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() {
|
let component_text = if properties.is_empty() {
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,18 @@ impl ElementRcNode {
|
||||||
Some(Self { element, debug_index })
|
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>(
|
pub fn on_element_node<R>(
|
||||||
&self,
|
&self,
|
||||||
func: impl Fn(&i_slint_compiler::parser::syntax_nodes::Element) -> R,
|
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()))
|
(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)]
|
#[derive(Clone, Debug)]
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,19 @@ pub fn find_element_indent(element: &ElementRc) -> Option<String> {
|
||||||
None
|
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.
|
/// 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)
|
/// (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.
|
/// Will return `Foo` in the following example where `|` is the cursor.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue