mirror of
https://github.com/slint-ui/slint.git
synced 2025-10-01 14:21:16 +00:00
live preview: Insert source code before other elements
... and use that to drop into layouts.
This commit is contained in:
parent
9615fbe4b4
commit
c1707d52e1
6 changed files with 203 additions and 243 deletions
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 => {
|
||||||
|
|
|
@ -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()),
|
||||||
|
|
|
@ -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,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue