live preview: Insert source code before other elements

... and use that to drop into layouts.
This commit is contained in:
Tobias Hunger 2024-04-22 12:03:30 +02:00 committed by Tobias Hunger
parent 9615fbe4b4
commit c1707d52e1
6 changed files with 203 additions and 243 deletions

View file

@ -49,16 +49,20 @@ impl ElementRcNode {
Some(Self { element, debug_index }) Some(Self { element, debug_index })
} }
pub fn find_in_or_below(element: ElementRc, path: &std::path::Path, offset: u32) -> Option<Self> { pub fn find_in_or_below(
let debug_index = element.borrow().debug.iter().position(|(n, _)| { element: ElementRc,
path: &std::path::Path,
offset: u32,
) -> Option<Self> {
let debug_index = element.borrow().debug.iter().position(|(n, _)| {
u32::from(n.text_range().start()) == offset && n.source_file.path() == path u32::from(n.text_range().start()) == offset && n.source_file.path() == path
}); });
if let Some(debug_index) = debug_index { if let Some(debug_index) = debug_index {
Some(Self { element, debug_index }) Some(Self { element, debug_index })
} else { } else {
for c in &element.borrow().children { for c in &element.borrow().children {
let result = Self::find_in_or_below(c.clone(), path, offset); let result = Self::find_in_or_below(c.clone(), path, offset);
if result.is_some() { if result.is_some() {
return result; return result;
} }

View file

@ -539,8 +539,7 @@ fn format_argument_declaration(
writer: &mut impl TokenWriter, writer: &mut impl TokenWriter,
state: &mut FormatState, state: &mut FormatState,
) -> Result<(), std::io::Error> { ) -> Result<(), std::io::Error> {
let mut sub = node.children_with_tokens(); for n in node.children_with_tokens() {
while let Some(n) = sub.next() {
state.skip_all_whitespace = true; state.skip_all_whitespace = true;
match n.kind() { match n.kind() {
SyntaxKind::Colon => { SyntaxKind::Colon => {

View file

@ -26,6 +26,10 @@ fn builtin_component_info(name: &str, fills_parent: bool) -> ComponentInformatio
let default_properties = match name { let default_properties = match name {
"Text" | "TextInput" => vec![PropertyChange::new("text", format!("\"{name}\""))], "Text" | "TextInput" => vec![PropertyChange::new("text", format!("\"{name}\""))],
"Image" => vec![PropertyChange::new("source", "@image-url(\"EDIT_ME.png\")".to_string())], "Image" => vec![PropertyChange::new("source", "@image-url(\"EDIT_ME.png\")".to_string())],
"GridLayout" | "HorizontalLayout" | "VerticalLayout" => vec![
PropertyChange::new("min-width", "16px".to_string()),
PropertyChange::new("min-height", "16px".to_string()),
],
_ => vec![], _ => vec![],
}; };
@ -60,6 +64,10 @@ fn std_widgets_info(name: &str, is_global: bool) -> ComponentInformation {
"ComboBox" => { "ComboBox" => {
vec![PropertyChange::new("model", "[\"first\", \"second\", \"third\"]".to_string())] vec![PropertyChange::new("model", "[\"first\", \"second\", \"third\"]".to_string())]
} }
"GridBox" | "HorizontalBox" | "VerticalBox" => vec![
PropertyChange::new("min-width", "16px".to_string()),
PropertyChange::new("min-height", "16px".to_string()),
],
"Slider" | "SpinBox" => vec![ "Slider" | "SpinBox" => vec![
PropertyChange::new("minimum", "0".to_string()), PropertyChange::new("minimum", "0".to_string()),
PropertyChange::new("value", "42".to_string()), PropertyChange::new("value", "42".to_string()),

View file

@ -1,10 +1,8 @@
// 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.2 OR LicenseRef-Slint-commercial // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.2 OR LicenseRef-Slint-commercial
use std::path::PathBuf;
use i_slint_compiler::parser::{syntax_nodes, SyntaxKind, SyntaxNode}; use i_slint_compiler::parser::{syntax_nodes, SyntaxKind, SyntaxNode};
use i_slint_core::lengths::{LogicalPoint, LogicalRect, LogicalSize, PointLengths}; use i_slint_core::lengths::{LogicalPoint, LogicalRect, LogicalSize};
use slint_interpreter::ComponentInstance; use slint_interpreter::ComponentInstance;
use crate::common; use crate::common;
@ -98,7 +96,7 @@ fn calculate_drop_acceptance(
position: LogicalPoint, position: LogicalPoint,
layout_kind: &crate::preview::ui::LayoutKind, layout_kind: &crate::preview::ui::LayoutKind,
) -> DropAccept { ) -> DropAccept {
assert!(geometry.contains(position)); // Just checked that before callig this assert!(geometry.contains(position)); // Just checked that before calling this
let horizontal = border_size(geometry.size.width); let horizontal = border_size(geometry.size.width);
let vertical = border_size(geometry.size.height); let vertical = border_size(geometry.size.height);
@ -119,7 +117,7 @@ fn calculate_drop_acceptance(
LogicalPoint::new(geometry.origin.x + horizontal, geometry.origin.y), LogicalPoint::new(geometry.origin.x + horizontal, geometry.origin.y),
LogicalSize::new(geometry.size.width - (2.0 * horizontal), geometry.size.height), LogicalSize::new(geometry.size.width - (2.0 * horizontal), geometry.size.height),
), ),
ui::LayoutKind::Grid => geometry.clone(), ui::LayoutKind::Grid => *geometry,
}; };
if certain_rect.contains(position) { if certain_rect.contains(position) {
@ -129,141 +127,146 @@ fn calculate_drop_acceptance(
} }
} }
#[derive(Debug)]
struct ChildrenInformation {
geometry: LogicalRect,
path: PathBuf,
range: (u32, u32),
}
// calculate where to draw the `DropMark` // calculate where to draw the `DropMark`
fn calculate_drop_information_for_layout( fn calculate_drop_information_for_layout(
geometry: &LogicalRect, geometry: &LogicalRect,
position: LogicalPoint, position: LogicalPoint,
layout_kind: &crate::preview::ui::LayoutKind, layout_kind: &crate::preview::ui::LayoutKind,
children_information: &[ChildrenInformation], children_geometries: &[LogicalRect],
) -> (Option<DropMark>, usize) { ) -> (Option<DropMark>, usize) {
eprintln!("calculate_drop_information_for_layout({geometry:?}, {position:?}, {layout_kind:?}, {children_information:?}): Starting up");
let horizontal = border_size(geometry.size.width);
let vertical = border_size(geometry.size.height);
match layout_kind { match layout_kind {
ui::LayoutKind::None => unreachable!("We are in a layout"), ui::LayoutKind::None => unreachable!("We are in a layout"),
ui::LayoutKind::Horizontal => { ui::LayoutKind::Horizontal => {
if children_information.len() == 0 { if children_geometries.is_empty() {
eprintln!("Horizontal, No child");
// No children: Draw a drop mark in the middle // No children: Draw a drop mark in the middle
// TODO: Take padding into account: We have no way to get that though // TODO: Take padding into account: We have no way to get that though
return ( let start = (geometry.origin.x + (geometry.size.width / 2.0)).floor();
(
Some(DropMark { Some(DropMark {
start: LogicalPoint::new( start: LogicalPoint::new(start, geometry.origin.y),
(geometry.origin.x + (geometry.size.width / 2.0)).floor(),
geometry.origin.y,
),
end: LogicalPoint::new( end: LogicalPoint::new(
(geometry.origin.x + (geometry.size.width / 2.0)).ceil(), start + 1.0,
geometry.origin.y + geometry.size.height, geometry.origin.y + geometry.size.height,
), ),
}), }),
usize::MAX, usize::MAX,
); )
} else { } else {
let mut last_midpoint = geometry.origin.x; let mut last_midpoint = geometry.origin.x;
let mut last_endpoint = geometry.origin.x; let mut last_endpoint = geometry.origin.x;
for (pos, c) in children_information.iter().enumerate() { for (pos, c) in children_geometries.iter().enumerate() {
let new_midpoint = c.geometry.origin.x + c.geometry.size.width / 2.0; let new_midpoint = c.origin.x + c.size.width / 2.0;
let hit_rect = LogicalRect::new(LogicalPoint::new(last_midpoint, geometry.origin.y), LogicalSize::new(new_midpoint - last_midpoint, geometry.size.height)); let hit_rect = LogicalRect::new(
LogicalPoint::new(last_midpoint, geometry.origin.y),
LogicalSize::new(new_midpoint - last_midpoint, geometry.size.height),
);
if hit_rect.contains(position) { if hit_rect.contains(position) {
eprintln!("Horizontal, before_child {pos} with geometry {:?}: {hit_rect:?} cointains {position:?}? => HIT", c.geometry); let start = (c.origin.x - last_endpoint) / 2.0;
let start = (c.geometry.origin.x - last_endpoint) / 2.0; let start_pos = last_endpoint
let start_pos = last_endpoint + if start.floor() < geometry.origin.x { + if start.floor() < geometry.origin.x {
geometry.origin.x geometry.origin.x
} else { } else {
start start
}; };
let end_pos = start_pos + 1.0; let end_pos = start_pos + 1.0;
return (
Some(DropMark {
start: LogicalPoint::new(start_pos, geometry.origin.y),
end: LogicalPoint::new(end_pos, geometry.origin.y + geometry.size.height),
}),
pos,
);
}
eprintln!("Horizontal, before_child {pos} with geometry {:?}: {hit_rect:?} cointains {position:?}? => MISS", c.geometry);
last_midpoint = new_midpoint;
last_endpoint = c.geometry.origin.x + c.geometry.size.width;
}
eprintln!("Horizontal, after last child...");
return ( return (
Some(DropMark { Some(DropMark {
start: LogicalPoint::new(geometry.origin.x + geometry.size.width - 1.0, geometry.origin.y), start: LogicalPoint::new(start_pos, geometry.origin.y),
end: LogicalPoint::new(geometry.origin.x + geometry.size.width, geometry.origin.y + geometry.size.height), end: LogicalPoint::new(
}), end_pos,
usize::MAX, geometry.origin.y + geometry.size.height,
); ),
} }),
} pos,
ui::LayoutKind::Vertical => { );
if children_information.len() == 0 { }
eprintln!("Vertical, No child"); last_midpoint = new_midpoint;
// No children: Draw a drop mark in the middle last_endpoint = c.origin.x + c.size.width;
// TODO: Take padding into account: We have no way to get that though }
return ( (
Some(DropMark { Some(DropMark {
start: LogicalPoint::new( start: LogicalPoint::new(
geometry.origin.x, geometry.origin.x + geometry.size.width - 1.0,
(geometry.origin.y + (geometry.size.height / 2.0)).floor(), geometry.origin.y,
), ),
end: LogicalPoint::new( end: LogicalPoint::new(
geometry.origin.x + geometry.size.width, geometry.origin.x + geometry.size.width,
(geometry.origin.y + (geometry.size.height / 2.0)).ceil(), geometry.origin.y + geometry.size.height,
), ),
}), }),
usize::MAX, usize::MAX,
); )
}
}
ui::LayoutKind::Vertical => {
if children_geometries.is_empty() {
// No children: Draw a drop mark in the middle
// TODO: Take padding into account: We have no way to get that though
let start = (geometry.origin.y + (geometry.size.height / 2.0)).floor();
(
Some(DropMark {
start: LogicalPoint::new(geometry.origin.x, start),
end: LogicalPoint::new(
geometry.origin.x + geometry.size.width,
start + 1.0,
),
}),
usize::MAX,
)
} else { } else {
let mut last_midpoint = geometry.origin.y; let mut last_midpoint = geometry.origin.y;
let mut last_endpoint = geometry.origin.y; let mut last_endpoint = geometry.origin.y;
for (pos, c) in children_information.iter().enumerate() { for (pos, c) in children_geometries.iter().enumerate() {
let new_midpoint = c.geometry.origin.y + c.geometry.size.height / 2.0; let new_midpoint = c.origin.y + c.size.height / 2.0;
let hit_rect = LogicalRect::new(LogicalPoint::new(geometry.origin.y, last_midpoint), LogicalSize::new(geometry.size.width, new_midpoint - last_midpoint)); let hit_rect = LogicalRect::new(
LogicalPoint::new(geometry.origin.y, last_midpoint),
LogicalSize::new(geometry.size.width, new_midpoint - last_midpoint),
);
if hit_rect.contains(position) { if hit_rect.contains(position) {
eprintln!("Vertical, before_child {pos} with geometry {:?}: {hit_rect:?} cointains {position:?}? => HIT", c.geometry); let start = (c.origin.y - last_endpoint) / 2.0;
let start = (c.geometry.origin.y - last_endpoint) / 2.0; let start_pos = last_endpoint
let start_pos = last_endpoint + if start.floor() < geometry.origin.y { + if start.floor() < geometry.origin.y {
geometry.origin.y geometry.origin.y
} else { } else {
start start
}; };
let end_pos = start_pos + 1.0; let end_pos = start_pos + 1.0;
return (
Some(DropMark {
start: LogicalPoint::new(geometry.origin.x, start_pos),
end: LogicalPoint::new(geometry.origin.x + geometry.size.width, end_pos),
}),
pos,
);
}
eprintln!("Vertical, before_child {pos} with geometry {:?}: {hit_rect:?} cointains {position:?}? => MISS", c.geometry);
last_midpoint = new_midpoint;
last_endpoint = c.geometry.origin.y + c.geometry.size.height;
}
eprintln!("Vertical, after last child...");
return ( return (
Some(DropMark { Some(DropMark {
start: LogicalPoint::new(geometry.origin.x, geometry.origin.y + geometry.size.height - 1.0), start: LogicalPoint::new(geometry.origin.x, start_pos),
end: LogicalPoint::new(geometry.origin.x + geometry.size.width, geometry.origin.y + geometry.size.height), end: LogicalPoint::new(
geometry.origin.x + geometry.size.width,
end_pos,
),
}),
pos,
);
}
last_midpoint = new_midpoint;
last_endpoint = c.origin.y + c.size.height;
}
(
Some(DropMark {
start: LogicalPoint::new(
geometry.origin.x,
geometry.origin.y + geometry.size.height - 1.0,
),
end: LogicalPoint::new(
geometry.origin.x + geometry.size.width,
geometry.origin.y + geometry.size.height,
),
}), }),
usize::MAX, usize::MAX,
); )
} }
} }
ui::LayoutKind::Grid => todo!(), ui::LayoutKind::Grid => {
// TODO: Do something here
(None, usize::MAX)
}
} }
} }
@ -274,7 +277,6 @@ fn accept_drop_at(
) -> DropAccept { ) -> DropAccept {
let layout_kind = element_node.layout_kind(); let layout_kind = element_node.layout_kind();
let Some(geometry) = element_node.geometry_at(component_instance, position) else { let Some(geometry) = element_node.geometry_at(component_instance, position) else {
eprintln!(" accept_drop_at: NO, element not at position");
return DropAccept::No; return DropAccept::No;
}; };
calculate_drop_acceptance(&geometry, position, &layout_kind) calculate_drop_acceptance(&geometry, position, &layout_kind)
@ -310,7 +312,7 @@ fn insert_position_at_end(
} else if before_closing.kind() == SyntaxKind::Whitespace } else if before_closing.kind() == SyntaxKind::Whitespace
&& !before_closing.text().contains('\n') && !before_closing.text().contains('\n')
{ {
let indent = util::find_element_indent(&target_element_node).unwrap_or_default(); let indent = util::find_element_indent(target_element_node).unwrap_or_default();
let ws_len = before_closing.text().len() as u32; let ws_len = before_closing.text().len() as u32;
( (
format!("\n{indent} "), format!("\n{indent} "),
@ -320,7 +322,7 @@ fn insert_position_at_end(
ws_len, ws_len,
) )
} else { } else {
let indent = util::find_element_indent(&target_element_node).unwrap_or_default(); let indent = util::find_element_indent(target_element_node).unwrap_or_default();
(format!("\n{indent} "), format!("{indent} "), indent, closing_brace_offset, 0) (format!("\n{indent} "), format!("{indent} "), indent, closing_brace_offset, 0)
}; };
@ -340,11 +342,72 @@ fn insert_position_at_end(
}) })
} }
fn insert_position_before_child(
target_element_node: &common::ElementRcNode,
child_index: usize,
) -> Option<InsertInformation> {
target_element_node.with_element_node(|node| {
for (index, child_node) in node
.children()
.filter(|n| {
[
SyntaxKind::SubElement,
SyntaxKind::RepeatedElement,
SyntaxKind::ConditionalElement,
]
.contains(&n.kind())
})
.enumerate()
{
if index < child_index {
continue;
}
assert!(index == child_index);
let first_token = child_node.first_token()?;
let first_token_offset = u32::from(first_token.text_range().start());
let before_first_token = first_token.prev_token()?;
let (pre_indent, indent) = if before_first_token.kind() == SyntaxKind::Whitespace
&& before_first_token.text().contains('\n')
{
let element_indent = before_first_token.text().split('\n').last().unwrap(); // must exist in this branch
("".to_string(), element_indent.to_string())
} else if before_first_token.kind() == SyntaxKind::Whitespace
&& !before_first_token.text().contains('\n')
{
let indent = util::find_element_indent(target_element_node).unwrap_or_default();
("".to_string(), format!("{indent} "))
} else {
let indent = util::find_element_indent(target_element_node).unwrap_or_default();
(format!("\n{indent} "), format!("{indent} "))
};
let url = lsp_types::Url::from_file_path(child_node.source_file.path()).ok()?;
let (version, _) = preview::get_url_from_cache(&url)?;
return Some(InsertInformation {
insertion_position: common::VersionedPosition::new(
crate::common::VersionedUrl::new(url, version),
first_token_offset,
),
replacement_range: 0,
pre_indent,
indent: indent.clone(),
post_indent: indent,
});
}
// We should never get here...
None
})
}
// find all elements covering the given `position`. // find all elements covering the given `position`.
fn drop_target_element_nodes( fn drop_target_element_nodes(
component_instance: &ComponentInstance, component_instance: &ComponentInstance,
position: LogicalPoint, position: LogicalPoint,
component_type: &str,
) -> Vec<common::ElementRcNode> { ) -> Vec<common::ElementRcNode> {
let mut result = Vec::with_capacity(3); let mut result = Vec::with_capacity(3);
@ -378,70 +441,11 @@ fn extract_element(node: SyntaxNode) -> Option<syntax_nodes::Element> {
} }
} }
fn examine_target_element_node(
context: &str,
component_instance: &ComponentInstance,
position: LogicalPoint,
target_element_node: &common::ElementRcNode,
) {
let geometry = target_element_node.geometry_at(component_instance, position);
// let mut result = Vec::new();
if let Some(geometry) = geometry {
for (i, c) in target_element_node.element.borrow().children.iter().enumerate() {
let c = common::ElementRcNode::new(c.clone(), 0).unwrap();
}
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()
};
}
});
}
}
fn drop_into_layout(
component_instance: &ComponentInstance,
element_node: common::ElementRcNode,
position: LogicalPoint,
layout_kind: ui::LayoutKind,
children_geometries: &[LogicalRect],
) -> Option<DropInformation> {
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,
position: LogicalPoint,
) -> Option<DropInformation> {
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: None })
}
fn find_element_to_drop_into( fn find_element_to_drop_into(
component_instance: &ComponentInstance, component_instance: &ComponentInstance,
position: LogicalPoint, position: LogicalPoint,
component_type: &str,
) -> Option<common::ElementRcNode> { ) -> Option<common::ElementRcNode> {
let all_element_nodes = drop_target_element_nodes(component_instance, position, component_type); let all_element_nodes = drop_target_element_nodes(component_instance, position);
let mut tmp = None; let mut tmp = None;
for element_node in &all_element_nodes { for element_node in &all_element_nodes {
@ -465,39 +469,27 @@ fn find_drop_location(
position: LogicalPoint, position: LogicalPoint,
component_type: &str, component_type: &str,
) -> Option<DropInformation> { ) -> Option<DropInformation> {
eprintln!("find_drop_location at {position:?}: Considering ["); let drop_target_node = find_element_to_drop_into(component_instance, position)?;
let drop_target_node = find_element_to_drop_into(component_instance, position, component_type)?;
eprintln!("find_drop_location at {position:?}: ] => {drop_target_node:?}");
let (path, _) = drop_target_node.path_and_offset(); let (path, _) = drop_target_node.path_and_offset();
let tl = component_instance.definition().type_loader(); let tl = component_instance.definition().type_loader();
let Some(doc) = tl.get_document(&path) else { let doc = tl.get_document(&path)?;
eprintln!("find_drop_location at {position:?}: No document found for {drop_target_node:?} => NONE");
return None;
};
if let Some(element_type) = drop_target_node.with_element_node(|node| { if let Some(element_type) = drop_target_node.with_element_node(|node| {
util::lookup_current_element_type((node.clone()).into(), &doc.local_registry) util::lookup_current_element_type((node.clone()).into(), &doc.local_registry)
}) { }) {
if drop_target_node.layout_kind() == ui::LayoutKind::None if drop_target_node.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()
{ {
eprintln!("find_drop_location at {position:?}: {drop_target_node:?} does not accept {component_type} => NONE");
return None; return None;
} }
} }
let layout_kind = drop_target_node.layout_kind(); let layout_kind = drop_target_node.layout_kind();
if layout_kind != ui::LayoutKind::None { if layout_kind != ui::LayoutKind::None {
let parent_kind = drop_target_node.with_element_node(|node| node.kind());
let geometry = drop_target_node.geometry_at(component_instance, position)?; let geometry = drop_target_node.geometry_at(component_instance, position)?;
let children_information: Vec<_> = drop_target_node.with_element_node(|node| { let children_information: Vec<_> = drop_target_node.with_element_node(|node| {
let mut children_info = Vec::new(); let mut children_info = Vec::new();
for c in node.children() { for c in node.children() {
eprintln!("Child syntax_node of {parent_kind:?}: {:?}", c.kind());
let c_path = c.source_file.path().to_path_buf();
let c_range = (u32::from(c.text_range().start()), u32::from(c.text_range().end()));
if let Some(element) = extract_element(c.clone()) { if let Some(element) = extract_element(c.clone()) {
let e_path = element.source_file.path().to_path_buf(); let e_path = element.source_file.path().to_path_buf();
let e_offset = u32::from(element.text_range().start()); let e_offset = u32::from(element.text_range().start());
@ -509,12 +501,11 @@ fn find_drop_location(
) else { ) else {
continue; continue;
}; };
let Some(c_geometry) = child_node.geometry_in(component_instance, &geometry) else { continue; }; let Some(c_geometry) = child_node.geometry_in(component_instance, &geometry)
children_info.push(ChildrenInformation { else {
geometry: c_geometry.clone(), continue;
path: c_path, };
range: c_range, children_info.push(c_geometry);
});
} }
} }
@ -532,13 +523,18 @@ fn find_drop_location(
if child_index == usize::MAX { if child_index == usize::MAX {
insert_position_at_end(&drop_target_node) insert_position_at_end(&drop_target_node)
} else { } else {
insert_position_at_end(&drop_target_node) insert_position_before_child(&drop_target_node, child_index)
} }
}?; }?;
Some(DropInformation { target_element_node: drop_target_node, insert_info, drop_mark }) Some(DropInformation { target_element_node: drop_target_node, insert_info, drop_mark })
} else { } else {
drop_into_element(component_instance, drop_target_node, position) let insert_info = insert_position_at_end(&drop_target_node)?;
Some(DropInformation {
target_element_node: drop_target_node,
insert_info,
drop_mark: None,
})
} }
} }

View file

@ -3,10 +3,8 @@
use std::{path::PathBuf, rc::Rc}; use std::{path::PathBuf, rc::Rc};
use i_slint_compiler::diagnostics::SourceFile;
use i_slint_compiler::object_tree::{Component, ElementRc}; use i_slint_compiler::object_tree::{Component, ElementRc};
use i_slint_core::lengths::LogicalPoint; use i_slint_core::lengths::LogicalPoint;
use rowan::TextRange;
use slint_interpreter::ComponentInstance; use slint_interpreter::ComponentInstance;
use crate::common::ElementRcNode; use crate::common::ElementRcNode;
@ -142,16 +140,6 @@ fn select_element_node(
} }
} }
fn element_node_source_range(
element: &ElementRc,
debug_index: usize,
) -> Option<(SourceFile, TextRange)> {
let node = element.borrow().debug.get(debug_index)?.0.clone();
let source_file = node.source_file.clone();
let range = node.text_range();
Some((source_file, range))
}
// Return the real root element, skipping any WindowElement that got added // Return the real root element, skipping any WindowElement that got added
pub fn root_element(component_instance: &ComponentInstance) -> ElementRc { pub fn root_element(component_instance: &ComponentInstance) -> ElementRc {
let root_element = component_instance.definition().root_component().root_element.clone(); let root_element = component_instance.definition().root_component().root_element.clone();
@ -167,7 +155,6 @@ pub struct SelectionCandidate {
pub component_stack: Vec<Rc<Component>>, pub component_stack: Vec<Rc<Component>>,
pub element: ElementRc, pub element: ElementRc,
pub debug_index: usize, pub debug_index: usize,
pub text_range: Option<(SourceFile, TextRange)>,
} }
impl SelectionCandidate { impl SelectionCandidate {
@ -227,12 +214,10 @@ fn collect_all_element_nodes_covering_impl(
if element_covers_point(position, component_instance, &ce) { if element_covers_point(position, component_instance, &ce) {
for (i, _) in ce.borrow().debug.iter().enumerate().rev() { for (i, _) in ce.borrow().debug.iter().enumerate().rev() {
// All nodes have the same geometry // All nodes have the same geometry
let text_range = element_node_source_range(&ce, i);
result.push(SelectionCandidate { result.push(SelectionCandidate {
element: ce.clone(), element: ce.clone(),
debug_index: i, debug_index: i,
component_stack: component_stack.clone(), component_stack: component_stack.clone(),
text_range,
}); });
} }
} }
@ -490,7 +475,7 @@ export component Entry inherits Main { /* @lsp:ignore-node */ } // 401
&component_instance, &component_instance,
); );
// Remove the "button" implenmentation details. They must be at the start: // Remove the "button" implementation details. They must be at the start:
let button_path = PathBuf::from("builtin:/fluent-base/button.slint"); let button_path = PathBuf::from("builtin:/fluent-base/button.slint");
let first_non_button = covers_center let first_non_button = covers_center
.iter() .iter()
@ -543,7 +528,7 @@ export component Entry inherits Main { /* @lsp:ignore-node */ } // 401
let first_non_button = covers_center.iter().position(|(p, _)| p != &button_path).unwrap(); 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 covers_center.drain(1..(first_non_button - 1)); // strip all but first/last of button
// Select without crossing file boundries // Select without crossing file boundaries
let select = super::select_element_at_impl( let select = super::select_element_at_impl(
&component_instance, &component_instance,
LogicalPoint::new(100.0, 100.0), LogicalPoint::new(100.0, 100.0),
@ -655,7 +640,7 @@ export component Entry inherits Main { /* @lsp:ignore-node */ } // 401
None None
); );
// Select with crossing file boundries // Select with crossing file boundaries
let select = super::select_element_at_impl( let select = super::select_element_at_impl(
&component_instance, &component_instance,
LogicalPoint::new(100.0, 100.0), LogicalPoint::new(100.0, 100.0),

View file

@ -6,7 +6,6 @@ use i_slint_core::lengths::{LogicalPoint, LogicalRect};
use crate::common; use crate::common;
use crate::preview::ui; use crate::preview::ui;
use crate::util;
use slint_interpreter::ComponentInstance; use slint_interpreter::ComponentInstance;
@ -32,13 +31,6 @@ pub trait ElementRcNodeExt {
component_instance: &ComponentInstance, component_instance: &ComponentInstance,
rect: &LogicalRect, rect: &LogicalRect,
) -> Option<i_slint_core::lengths::LogicalRect>; ) -> Option<i_slint_core::lengths::LogicalRect>;
/// Checks wether the given type is acceptable as a child
fn accepts_child_type(
&self,
component_instance: &ComponentInstance,
component_type: &str,
) -> bool;
} }
impl ElementRcNodeExt for common::ElementRcNode { impl ElementRcNodeExt for common::ElementRcNode {
@ -61,7 +53,7 @@ impl ElementRcNodeExt for common::ElementRcNode {
&self, &self,
component_instance: &ComponentInstance, component_instance: &ComponentInstance,
) -> Vec<i_slint_core::lengths::LogicalRect> { ) -> Vec<i_slint_core::lengths::LogicalRect> {
component_instance.element_positions(&self.as_element()) component_instance.element_positions(self.as_element())
} }
fn geometry_at( fn geometry_at(
@ -77,30 +69,6 @@ impl ElementRcNodeExt for common::ElementRcNode {
component_instance: &ComponentInstance, component_instance: &ComponentInstance,
rect: &LogicalRect, rect: &LogicalRect,
) -> Option<i_slint_core::lengths::LogicalRect> { ) -> Option<i_slint_core::lengths::LogicalRect> {
self.geometries(component_instance) self.geometries(component_instance).iter().find(|g| rect.contains_rect(g)).cloned()
.iter()
.find(|g| rect.contains_rect(g))
.cloned()
}
fn accepts_child_type(
&self,
component_instance: &ComponentInstance,
component_type: &str,
) -> bool {
let tl = component_instance.definition().type_loader();
let (path, _) = self.path_and_offset();
let Some(doc) = tl.get_document(&path) else {
return false;
};
let Some(element_type) = self.with_element_node(|node| {
util::lookup_current_element_type((node.clone()).into(), &doc.local_registry)
}) else {
return false;
};
self.layout_kind() != ui::LayoutKind::None
|| element_type.accepts_child_element(component_type, &doc.local_registry).is_ok()
} }
} }