Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 14 additions & 5 deletions internal/core/item_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -472,13 +472,13 @@ impl ItemRc {
pub fn map_to_window(&self, p: LogicalPoint) -> LogicalPoint {
let mut current = self.clone();
let mut result = p;
let supports_transformations = self
.window_adapter()
.map(|adapter| adapter.renderer().supports_transformations())
.unwrap_or(true);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Should this maybe default to false? That seems safer.
  2. Does it make sense to extract that into some helper function? You are repeating it below and it seems to be generically a useful bit of information that might be relevant in more places.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. This just moves code around. Did that to move it outside of the loop. and i wanted to keep the two functions the same. Not sure true or false is any safer. If there is no window_adapter the result may be wrong.
  2. Yeah, map_to_window and `map_to_item_tree are duplicated code. I wanted to merge them actually. I wanted to actually merge them by calling one from the other, but i wasn't sure about the implications.

while let Some(parent) = current.parent_item(ParentItemTraversalMode::StopAtPopups) {
let geometry = parent.geometry();
if self
.window_adapter()
.map(|adapter| adapter.renderer().supports_transformations())
.unwrap_or(true)
{
if supports_transformations {
if let Some(transform) = parent.children_transform() {
result = transform.transform_point(result.cast()).cast();
}
Expand All @@ -501,11 +501,20 @@ impl ItemRc {
if current.is_root_item_of(item_tree) {
return result;
}
let supports_transformations = self
.window_adapter()
.map(|adapter| adapter.renderer().supports_transformations())
.unwrap_or(true);
while let Some(parent) = current.parent_item(ParentItemTraversalMode::StopAtPopups) {
if parent.is_root_item_of(item_tree) {
break;
}
let geometry = parent.geometry();
if supports_transformations {
if let Some(transform) = parent.children_transform() {
result = transform.transform_point(result.cast()).cast();
}
}
result += geometry.origin.to_vector();
current = parent.clone();
}
Expand Down
4 changes: 2 additions & 2 deletions internal/interpreter/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1503,7 +1503,7 @@ impl ComponentInstance {
&self,
path: &Path,
offset: u32,
) -> Vec<i_slint_core::lengths::LogicalRect> {
) -> Vec<crate::highlight::HighlightedRect> {
crate::highlight::component_positions(&self.inner, path, offset)
}

Expand All @@ -1514,7 +1514,7 @@ impl ComponentInstance {
pub fn element_positions(
&self,
element: &i_slint_compiler::object_tree::ElementRc,
) -> Vec<i_slint_core::lengths::LogicalRect> {
) -> Vec<crate::highlight::HighlightedRect> {
crate::highlight::element_positions(
&self.inner,
element,
Expand Down
55 changes: 48 additions & 7 deletions internal/interpreter/highlight.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@

use crate::dynamic_item_tree::{DynamicComponentVRc, ItemTreeBox};
use i_slint_compiler::object_tree::{Component, Element, ElementRc};
use i_slint_core::graphics::euclid;
use i_slint_core::items::ItemRc;
use i_slint_core::lengths::LogicalRect;
use i_slint_core::lengths::{LogicalPoint, LogicalRect};
use smol_str::SmolStr;
use std::cell::RefCell;
use std::path::Path;
Expand All @@ -27,10 +28,28 @@ fn normalize_repeated_element(element: ElementRc) -> ElementRc {
element
}

/// The rectangle of an element, which may be rotated around its center
#[derive(Clone, Copy, Debug, Default)]
pub struct HighlightedRect {
/// The element's geometry
pub rect: LogicalRect,
/// In degrees, around the center of the element
pub angle: f32,
}
impl HighlightedRect {
/// return true if the point is inside the (potentially rotated) rectangle
pub fn contains(&self, position: LogicalPoint) -> bool {
let center = self.rect.center();
let rotation = euclid::Rotation2D::radians((-self.angle).to_radians());
let transformed = center + rotation.transform_vector(position - center);
self.rect.contains(transformed)
}
}

fn collect_highlight_data(
component: &DynamicComponentVRc,
elements: &[std::rc::Weak<RefCell<Element>>],
) -> Vec<i_slint_core::lengths::LogicalRect> {
) -> Vec<HighlightedRect> {
let component_instance = VRc::downgrade(component);
let component_instance = component_instance.upgrade().unwrap();
generativity::make_guard!(guard);
Expand All @@ -56,7 +75,7 @@ pub(crate) fn component_positions(
component_instance: &DynamicComponentVRc,
path: &Path,
offset: u32,
) -> Vec<i_slint_core::lengths::LogicalRect> {
) -> Vec<HighlightedRect> {
generativity::make_guard!(guard);
let c = component_instance.unerase(guard);

Expand All @@ -82,7 +101,7 @@ pub fn element_positions(
component_instance: &DynamicComponentVRc,
element: &ElementRc,
filter_clipped: ElementPositionFilter,
) -> Vec<LogicalRect> {
) -> Vec<HighlightedRect> {
generativity::make_guard!(guard);
let c = component_instance.unerase(guard);

Expand Down Expand Up @@ -112,7 +131,7 @@ fn fill_highlight_data(
component_instance: &ItemTreeBox,
root_component_instance: &ItemTreeBox,
filter_clipped: ElementPositionFilter,
values: &mut Vec<i_slint_core::lengths::LogicalRect>,
values: &mut Vec<HighlightedRect>,
) {
if element.borrow().repeated.is_some() {
// avoid a panic
Expand Down Expand Up @@ -150,9 +169,31 @@ fn fill_highlight_data(
let item_rc = ItemRc::new(vrc.clone(), index);
if filter_clipped == ElementPositionFilter::IncludeClipped || item_rc.is_visible() {
let geometry = item_rc.geometry();
if geometry.size.is_empty() {
return;
}
let origin = item_rc.map_to_item_tree(geometry.origin, &root_vrc);
let size = geometry.size;
values.push(LogicalRect { origin, size });
let top_right = item_rc.map_to_item_tree(
geometry.origin + euclid::vec2(geometry.size.width, 0.),
&root_vrc,
);
let delta = top_right - origin;
let width = delta.length();
let height = geometry.size.height * width / geometry.size.width;
// Compute the angle between the origin(top-right) and top-left corner
let angle_rad = delta.y.atan2(delta.x);
let (sin, cos) = angle_rad.sin_cos();
let center = euclid::point2(
origin.x + (width / 2.0) * cos - (height / 2.0) * sin,
origin.y + (width / 2.0) * sin + (height / 2.0) * cos,
);
values.push(HighlightedRect {
rect: LogicalRect {
origin: center - euclid::vec2(width / 2.0, height / 2.0),
size: euclid::size2(width, height),
},
angle: angle_rad.to_degrees(),
});
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions tools/lsp/preview.rs
Original file line number Diff line number Diff line change
Expand Up @@ -819,7 +819,7 @@ fn resize_selected_element_impl(

// They all have the same size anyway:
let (path, offset) = element_node.path_and_offset();
let geometry = element_node.geometries(&component_instance).get(instance_index).cloned()?;
let geometry = element_node.geometries(&component_instance).get(instance_index).cloned()?.rect;

let position = rect.origin;
let root_element = element_selection::root_element(&component_instance);
Expand All @@ -830,7 +830,7 @@ fn resize_selected_element_impl(
.element_positions(&parent_element)
.iter()
.find(|g| g.contains(position))
.map(|g| g.origin)
.map(|g| g.rect.origin)
})
.unwrap_or_default();

Expand Down
14 changes: 10 additions & 4 deletions tools/lsp/preview/drop_location.rs
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,10 @@ fn accept_drop_at(
let Some(geometry) = element_node.geometry_at(component_instance, position) else {
return DropAccept::No;
};
calculate_drop_acceptance(&geometry, position, &layout_kind)
if geometry.angle != 0.0 {
return DropAccept::No;
}
calculate_drop_acceptance(&geometry.rect, position, &layout_kind)
}

#[derive(Clone, Debug)]
Expand Down Expand Up @@ -697,11 +700,13 @@ fn find_filtered_location(
.children()
.iter()
.filter(|c| !c.with_element_node(common::is_element_node_ignored))
.filter_map(|c| c.geometry_in(component_instance, &geometry).map(|g| ((mark)(c), g)))
.filter_map(|c| {
c.geometry_in(component_instance, &geometry.rect).map(|g| ((mark)(c), g.rect))
})
.collect();

let (drop_mark, child_index) = calculate_drop_information_for_layout(
&geometry,
&geometry.rect,
position,
&layout_kind,
&children_geometries,
Expand Down Expand Up @@ -1147,7 +1152,8 @@ pub fn create_move_element_workspace_edit(
let placeholder_text = if Some(&drop_info.target_element_node) == parent_of_element.as_ref() {
// We are moving within ourselves!

let size = element.geometries(component_instance).get(instance_index).map(|g| g.size)?;
let size =
element.geometries(component_instance).get(instance_index).map(|g| g.rect.size)?;

if drop_info.target_element_node.layout_kind() == ui::LayoutKind::None {
let (edit, _) = preview::resize_selected_element_impl(
Expand Down
29 changes: 15 additions & 14 deletions tools/lsp/preview/element_selection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ use i_slint_compiler::{
object_tree::ElementRc,
parser::{SyntaxKind, TextSize},
};
use i_slint_core::lengths::{LogicalPoint, LogicalRect};
use slint_interpreter::{ComponentHandle, ComponentInstance};
use i_slint_core::lengths::LogicalPoint;
use slint_interpreter::{highlight::HighlightedRect, ComponentHandle, ComponentInstance};

use crate::common;
use crate::preview::{self, ext::ElementRcNodeExt, ui, SelectionNotification};
Expand Down Expand Up @@ -79,7 +79,7 @@ fn element_covers_point(
position: LogicalPoint,
component_instance: &ComponentInstance,
selected_element: &ElementRc,
) -> Option<LogicalRect> {
) -> Option<HighlightedRect> {
slint_interpreter::highlight::element_positions(
&component_instance.clone_strong().into(),
selected_element,
Expand Down Expand Up @@ -147,10 +147,11 @@ pub fn highlight_positions(
let offset = TextSize::new(offset as u32);
let positions = component_instance.component_positions(&path, offset.into());
let model = slint::VecModel::from_iter(positions.iter().map(|g| ui::SelectionRectangle {
width: g.size.width,
height: g.size.height,
x: g.origin.x,
y: g.origin.y,
width: g.rect.size.width,
height: g.rect.size.height,
x: g.rect.origin.x,
y: g.rect.origin.y,
angle: g.angle,
}));
slint::ModelRc::new(model)
}
Expand Down Expand Up @@ -197,7 +198,7 @@ pub fn root_element(component_instance: &ComponentInstance) -> ElementRc {
pub struct SelectionCandidate {
pub element: ElementRc,
pub debug_index: usize,
pub geometry: LogicalRect,
pub geometry: HighlightedRect,
pub is_in_root_component: bool,
}

Expand Down Expand Up @@ -408,12 +409,12 @@ pub fn selection_stack_at(x: f32, y: f32) -> slint::ModelRc<ui::SelectionStackFr
}
}

let width = (sc.geometry.size.width / root_geometry.size.width) * 100.0;
let height = (sc.geometry.size.height / root_geometry.size.height) * 100.0;
let x = ((sc.geometry.origin.x + root_geometry.origin.x) / root_geometry.size.width)
* 100.0;
let y = ((sc.geometry.origin.y + root_geometry.origin.y) / root_geometry.size.height)
* 100.0;
let root_geo = root_geometry.rect;
let sc_geom = sc.geometry.rect;
let width = (sc_geom.size.width / root_geo.size.width) * 100.0;
let height = (sc_geom.size.height / root_geo.size.height) * 100.0;
let x = ((sc_geom.origin.x + root_geo.origin.x) / root_geo.size.width) * 100.0;
let y = ((sc_geom.origin.y + root_geo.origin.y) / root_geo.size.height) * 100.0;

let is_interactive = known_components
.iter()
Expand Down
23 changes: 9 additions & 14 deletions tools/lsp/preview/ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,30 +7,28 @@ use i_slint_core::lengths::{LogicalPoint, LogicalRect};
use crate::common;
use crate::preview::ui;

use slint_interpreter::highlight::HighlightedRect;
use slint_interpreter::ComponentInstance;

pub trait ElementRcNodeExt {
fn layout_kind(&self) -> crate::preview::ui::LayoutKind;

/// Find all geometries for the given `ElementRcNode`
fn geometries(
&self,
component_instance: &ComponentInstance,
) -> Vec<i_slint_core::lengths::LogicalRect>;
fn geometries(&self, component_instance: &ComponentInstance) -> Vec<HighlightedRect>;

/// Find the first geometry of `ElementRcNode` that includes the point `x`, `y`
fn geometry_at(
&self,
component_instance: &ComponentInstance,
position: LogicalPoint,
) -> Option<i_slint_core::lengths::LogicalRect>;
) -> Option<HighlightedRect>;

/// Find the first geometry of ElementRcNode in `rect`
fn geometry_in(
&self,
component_instance: &ComponentInstance,
rect: &LogicalRect,
) -> Option<i_slint_core::lengths::LogicalRect>;
) -> Option<HighlightedRect>;
}

impl ElementRcNodeExt for common::ElementRcNode {
Expand All @@ -49,26 +47,23 @@ impl ElementRcNodeExt for common::ElementRcNode {
})
}

fn geometries(
&self,
component_instance: &ComponentInstance,
) -> Vec<i_slint_core::lengths::LogicalRect> {
fn geometries(&self, component_instance: &ComponentInstance) -> Vec<HighlightedRect> {
component_instance.element_positions(self.as_element())
}

fn geometry_at(
&self,
component_instance: &ComponentInstance,
position: LogicalPoint,
) -> Option<i_slint_core::lengths::LogicalRect> {
self.geometries(component_instance).iter().find(|g| g.contains(position)).cloned()
) -> Option<HighlightedRect> {
self.geometries(component_instance).iter().find(|g| g.rect.contains(position)).cloned()
}

fn geometry_in(
&self,
component_instance: &ComponentInstance,
rect: &LogicalRect,
) -> Option<i_slint_core::lengths::LogicalRect> {
self.geometries(component_instance).iter().find(|g| rect.contains_rect(g)).cloned()
) -> Option<HighlightedRect> {
self.geometries(component_instance).iter().find(|g| rect.contains_rect(&g.rect)).cloned()
}
}
1 change: 1 addition & 0 deletions tools/lsp/ui/api.slint
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export struct SelectionRectangle {
y: length,
width: length,
height: length,
angle: angle,
}

/// A `Selection`
Expand Down
3 changes: 2 additions & 1 deletion tools/lsp/ui/views/preview-view.slint
Original file line number Diff line number Diff line change
Expand Up @@ -421,7 +421,8 @@ export component PreviewView inherits Rectangle {
y: s.y;
width: s.width;
height: s.height;
interactive: root.mode == DrawAreaMode.selecting;
transform-rotation: s.angle;
interactive: root.mode == DrawAreaMode.selecting && s.angle == 0.0;
selection: Api.selection;
is-primary: self.selection.highlight-index == idx;
resize(x, y, w, h) => {
Expand Down
Loading