diff --git a/crates/collab/src/tests/integration_tests.rs b/crates/collab/src/tests/integration_tests.rs index be9890dcb84275..13745ab6368652 100644 --- a/crates/collab/src/tests/integration_tests.rs +++ b/crates/collab/src/tests/integration_tests.rs @@ -6248,6 +6248,7 @@ async fn test_right_click_menu_behind_collab_panel(cx: &mut TestAppContext) { cx.simulate_event(MouseDownEvent { button: MouseButton::Right, position: new_tab_button_bounds.center(), + absolute_position: new_tab_button_bounds.center(), modifiers: Modifiers::default(), click_count: 1, first_mouse: false, @@ -6260,6 +6261,7 @@ async fn test_right_click_menu_behind_collab_panel(cx: &mut TestAppContext) { cx.simulate_event(MouseDownEvent { button: MouseButton::Right, position: tab_bounds.center(), + absolute_position: new_tab_button_bounds.center(), modifiers: Modifiers::default(), click_count: 1, first_mouse: false, diff --git a/crates/collab_ui/src/collab_panel.rs b/crates/collab_ui/src/collab_panel.rs index e9afd324875414..61ffc175201cb5 100644 --- a/crates/collab_ui/src/collab_panel.rs +++ b/crates/collab_ui/src/collab_panel.rs @@ -859,7 +859,7 @@ impl CollabPanel { el.on_secondary_mouse_down(cx.listener( move |this, event: &MouseDownEvent, window, cx| { this.deploy_participant_context_menu( - event.position, + event.absolute_position, user_id, role, window, @@ -2438,7 +2438,7 @@ impl CollabPanel { let contact = contact.clone(); move |this, event: &ClickEvent, window, cx| { this.deploy_contact_context_menu( - event.down.position, + event.down.absolute_position, contact.clone(), window, cx, @@ -2451,7 +2451,12 @@ impl CollabPanel { .on_secondary_mouse_down(cx.listener({ let contact = contact.clone(); move |this, event: &MouseDownEvent, window, cx| { - this.deploy_contact_context_menu(event.position, contact.clone(), window, cx); + this.deploy_contact_context_menu( + event.absolute_position, + contact.clone(), + window, + cx, + ); } })) .start_slot( @@ -2711,7 +2716,7 @@ impl CollabPanel { .on_secondary_mouse_down(cx.listener( move |this, event: &MouseDownEvent, window, cx| { this.deploy_channel_context_menu( - event.position, + event.absolute_position, channel_id, ix, window, diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 78f07c60fabfef..3fd616293b816e 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -671,10 +671,10 @@ impl EditorElement { if !position_map.text_hitbox.is_hovered(window) { return; } - let point_for_position = position_map.point_for_position(event.position); + let point_for_position = position_map.point_for_position(event.absolute_position); mouse_context_menu::deploy_context_menu( editor, - Some(event.position), + Some(event.absolute_position), point_for_position.previous_valid, window, cx, @@ -5102,7 +5102,7 @@ impl EditorElement { let hitbox = hitbox.clone(); - let mut mouse_position = window.mouse_position(); + let mut mouse_position = window.mouse_position() - window.element_origin(); move |event: &MouseMoveEvent, phase, window, cx| { if phase == DispatchPhase::Capture { return; diff --git a/crates/gpui/Cargo.toml b/crates/gpui/Cargo.toml index 5795f68c30bded..842e3138018e5c 100644 --- a/crates/gpui/Cargo.toml +++ b/crates/gpui/Cargo.toml @@ -242,6 +242,10 @@ path = "examples/opacity.rs" name = "pattern" path = "examples/pattern.rs" +[[example]] +name = "scale" +path = "examples/scale.rs" + [[example]] name = "set_menus" path = "examples/set_menus.rs" diff --git a/crates/gpui/examples/scale.rs b/crates/gpui/examples/scale.rs new file mode 100644 index 00000000000000..f6ff7933f28e47 --- /dev/null +++ b/crates/gpui/examples/scale.rs @@ -0,0 +1,68 @@ +use std::{f32::consts::PI, time::Duration}; + +use gpui::{ + div, prelude::*, px, rgb, size, white, Animation, AnimationExt, App, Application, Bounds, + Window, +}; + +struct MainView {} + +impl Render for MainView { + fn render(&mut self, _window: &mut Window, _cx: &mut Context<'_, Self>) -> impl IntoElement { + div() + .flex() + .size_full() + .bg(rgb(0x202020)) + .font_family("Sans") + .items_center() + .justify_between() + .child(ChildElement { scale: 1.0 }) + .child(ChildElement { scale: 0.75 }) + .child(ChildElement { scale: 0.5 }) + .with_animation( + "animation", + Animation::new(Duration::from_millis(5000)).repeat(), + |el, delta| { + el.child(ChildElement { + scale: (2.0 * delta * PI).sin() * 0.5 + 1.0, + }) + }, + ) + } +} + +#[derive(IntoElement)] +struct ChildElement { + pub scale: f32, +} + +impl RenderOnce for ChildElement { + fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement { + div() + .flex() + .min_h(px(120.0)) + .min_w(px(120.0)) + .max_h(px(120.0)) + .max_w(px(120.0)) + .scale(self.scale) + .bg(white()) + .items_center() + .justify_center() + .overflow_hidden() + .child(format!("Scale: {:.2}x", self.scale)) + } +} + +fn main() { + Application::new().run(|cx: &mut App| { + let bounds = Bounds::centered(None, size(px(500.0), px(500.0)), cx); + cx.open_window( + gpui::WindowOptions { + window_bounds: Some(gpui::WindowBounds::Windowed(bounds)), + ..Default::default() + }, + |_, cx| cx.new(|_| MainView {}), + ) + .unwrap(); + }); +} diff --git a/crates/gpui/examples/window_shadow.rs b/crates/gpui/examples/window_shadow.rs index bbdab1a8ca3e3c..125dbbf579f1aa 100644 --- a/crates/gpui/examples/window_shadow.rs +++ b/crates/gpui/examples/window_shadow.rs @@ -166,7 +166,9 @@ impl Render for WindowShadow { ) .on_click(|e, window, _| { if e.down.button == MouseButton::Right { - window.show_window_menu(e.up.position); + window.show_window_menu( + e.up.absolute_position, + ); } }) .text_color(black()) diff --git a/crates/gpui/src/app/test_context.rs b/crates/gpui/src/app/test_context.rs index 7ad7623d33fa7f..02385c2a10f649 100644 --- a/crates/gpui/src/app/test_context.rs +++ b/crates/gpui/src/app/test_context.rs @@ -707,6 +707,7 @@ impl VisualTestContext { ) { self.simulate_event(MouseMoveEvent { position, + absolute_position: position, modifiers, pressed_button: button.into(), }) @@ -721,6 +722,7 @@ impl VisualTestContext { ) { self.simulate_event(MouseDownEvent { position, + absolute_position: position, modifiers, button, click_count: 1, @@ -737,6 +739,7 @@ impl VisualTestContext { ) { self.simulate_event(MouseUpEvent { position, + absolute_position: position, modifiers, button, click_count: 1, @@ -747,6 +750,7 @@ impl VisualTestContext { pub fn simulate_click(&mut self, position: Point, modifiers: Modifiers) { self.simulate_event(MouseDownEvent { position, + absolute_position: position, modifiers, button: MouseButton::Left, click_count: 1, @@ -754,6 +758,7 @@ impl VisualTestContext { }); self.simulate_event(MouseUpEvent { position, + absolute_position: position, modifiers, button: MouseButton::Left, click_count: 1, @@ -789,7 +794,7 @@ impl VisualTestContext { window.invalidator.set_phase(DrawPhase::Prepaint); let mut element = Drawable::new(f(window, cx)); element.layout_as_root(space.into(), window, cx); - window.with_absolute_element_offset(origin, |window| element.prepaint(window, cx)); + window.with_element_origin(origin, |window| element.prepaint(window, cx)); window.invalidator.set_phase(DrawPhase::Paint); let (request_layout_state, prepaint_state) = element.paint(window, cx); diff --git a/crates/gpui/src/element.rs b/crates/gpui/src/element.rs index c4e70d78852a46..cc02f56f508d99 100644 --- a/crates/gpui/src/element.rs +++ b/crates/gpui/src/element.rs @@ -274,6 +274,7 @@ enum ElementDrawPhase { node_id: DispatchNodeId, global_id: Option, bounds: Bounds, + scale: f32, request_layout: RequestLayoutState, prepaint: PrepaintState, }, @@ -333,15 +334,26 @@ impl Drawable { debug_assert_eq!(global_id.as_ref().unwrap().0, window.element_id_stack); } - let bounds = window.layout_bounds(layout_id); + let mut bounds = window.layout_bounds(layout_id); + let scale = window.layout_scale(layout_id); + bounds.size *= 1.0 / scale; + window.element_layout_id = Some(layout_id); + let node_id = window.next_frame.dispatch_tree.push_node(); - let prepaint = self.element.prepaint( - global_id.as_ref(), - bounds, - &mut request_layout, - window, - cx, - ); + let prepaint = window.with_element_origin(bounds.origin, |window| { + window.with_element_scale(scale, |window| { + self.element.prepaint( + global_id.as_ref(), + Bounds { + origin: Point::default(), + size: bounds.size, + }, + &mut request_layout, + window, + cx, + ) + }) + }); window.next_frame.dispatch_tree.pop_node(); if global_id.is_some() { @@ -352,6 +364,7 @@ impl Drawable { node_id, global_id, bounds, + scale, request_layout, prepaint, }; @@ -370,6 +383,7 @@ impl Drawable { node_id, global_id, bounds, + scale, mut request_layout, mut prepaint, .. @@ -380,14 +394,21 @@ impl Drawable { } window.next_frame.dispatch_tree.set_active_node(node_id); - self.element.paint( - global_id.as_ref(), - bounds, - &mut request_layout, - &mut prepaint, - window, - cx, - ); + window.with_element_origin(bounds.origin, |window| { + window.with_element_scale(scale, |window| { + self.element.paint( + global_id.as_ref(), + Bounds { + origin: Point::default(), + size: bounds.size, + }, + &mut request_layout, + &mut prepaint, + window, + cx, + ); + }) + }); if global_id.is_some() { window.element_id_stack.pop(); @@ -402,12 +423,21 @@ impl Drawable { pub(crate) fn layout_as_root( &mut self, - available_space: Size, + mut available_space: Size, window: &mut Window, cx: &mut App, ) -> Size { + available_space = available_space.map(|space| match space { + AvailableSpace::Definite(pixels) => { + AvailableSpace::Definite(pixels * window.element_scale()) + } + x => x, + }); + if matches!(&self.phase, ElementDrawPhase::Start) { - self.request_layout(window, cx); + window.with_element_origin(Point::default(), |window| { + self.request_layout(window, cx); + }); } let layout_id = match mem::take(&mut self.phase) { @@ -445,7 +475,7 @@ impl Drawable { _ => panic!("cannot measure after painting"), }; - window.layout_bounds(layout_id).size + window.layout_bounds(layout_id).size / window.element_scale() } } @@ -545,7 +575,10 @@ impl AnyElement { window: &mut Window, cx: &mut App, ) -> Option { - window.with_absolute_element_offset(origin, |window| self.prepaint(window, cx)) + window.with_element_origin( + origin * window.element_scale() + window.element_origin(), + |window| self.prepaint(window, cx), + ) } /// Performs layout on this element in the available space, then prepaints it at the given absolute origin. @@ -558,7 +591,7 @@ impl AnyElement { cx: &mut App, ) -> Option { self.layout_as_root(available_space, window, cx); - window.with_absolute_element_offset(origin, |window| self.prepaint(window, cx)) + self.prepaint_at(origin, window, cx) } } diff --git a/crates/gpui/src/elements/anchored.rs b/crates/gpui/src/elements/anchored.rs index b6ab196f83d79f..1f1950404c4a17 100644 --- a/crates/gpui/src/elements/anchored.rs +++ b/crates/gpui/src/elements/anchored.rs @@ -201,7 +201,7 @@ impl Element for Anchored { let offset = desired.origin - bounds.origin; let offset = point(offset.x.round(), offset.y.round()); - window.with_element_offset(offset, |window| { + window.with_element_origin(offset, |window| { for child in &mut self.children { child.prepaint(window, cx); } diff --git a/crates/gpui/src/elements/deferred.rs b/crates/gpui/src/elements/deferred.rs index 4a60c812d4cc2b..2ed2376a76f802 100644 --- a/crates/gpui/src/elements/deferred.rs +++ b/crates/gpui/src/elements/deferred.rs @@ -1,5 +1,5 @@ use crate::{ - AnyElement, App, Bounds, Element, GlobalElementId, IntoElement, LayoutId, Pixels, Window, + AnyElement, App, Bounds, Element, GlobalElementId, IntoElement, LayoutId, Pixels, Point, Window, }; /// Builds a `Deferred` element, which delays the layout and paint of its child. @@ -54,8 +54,7 @@ impl Element for Deferred { _cx: &mut App, ) { let child = self.child.take().unwrap(); - let element_offset = window.element_offset(); - window.defer_draw(child, element_offset, self.priority) + window.defer_draw(child, Point::default(), self.priority) } fn paint( diff --git a/crates/gpui/src/elements/div.rs b/crates/gpui/src/elements/div.rs index 26c43df702bd8e..f1967130048b40 100644 --- a/crates/gpui/src/elements/div.rs +++ b/crates/gpui/src/elements/div.rs @@ -260,7 +260,7 @@ impl Interactivity { (listener)( &DragMoveEvent { event: event.clone(), - bounds: hitbox.bounds, + bounds: hitbox.bounds + window.element_origin(), drag: PhantomData, dragged_item: Arc::clone(&drag.value), }, @@ -1203,11 +1203,13 @@ impl Element for Div { self.interactivity .request_layout(global_id, window, cx, |style, window, cx| { window.with_text_style(style.text_style().cloned(), |window| { - child_layout_ids = self - .children - .iter_mut() - .map(|child| child.request_layout(window, cx)) - .collect::>(); + window.with_element_scale(style.scale * window.element_scale(), |window| { + child_layout_ids = self + .children + .iter_mut() + .map(|child| child.request_layout(window, cx)) + .collect::>(); + }); window.request_layout(style, child_layout_ids.iter().copied(), cx) }) }); @@ -1232,7 +1234,7 @@ impl Element for Div { let mut child_min = point(Pixels::MAX, Pixels::MAX); let mut child_max = Point::default(); if let Some(handle) = self.interactivity.scroll_anchor.as_ref() { - *handle.last_origin.borrow_mut() = bounds.origin - window.element_offset(); + *handle.last_origin.borrow_mut() = bounds.origin; // TODO } let content_size = if request_layout.child_layout_ids.is_empty() { bounds.size @@ -1276,7 +1278,7 @@ impl Element for Div { window, cx, |_style, scroll_offset, hitbox, window, cx| { - window.with_element_offset(scroll_offset, |window| { + window.with_element_origin(scroll_offset + window.element_origin(), |window| { for child in &mut self.children { child.prepaint(window, cx); } @@ -2011,14 +2013,16 @@ impl Interactivity { // Use bounds instead of testing hitbox since this is called during prepaint. let check_is_hovered_during_prepaint = Rc::new({ let pending_mouse_down = pending_mouse_down.clone(); - let source_bounds = hitbox.bounds; + let mut source_bounds = hitbox.bounds; + source_bounds.origin += window.element_origin(); move |window: &Window| { pending_mouse_down.borrow().is_none() && source_bounds.contains(&window.mouse_position()) } }); let check_is_hovered = Rc::new({ - let hitbox = hitbox.clone(); + let mut hitbox = hitbox.clone(); + hitbox.bounds.origin += window.element_origin(); move |window: &Window| { pending_mouse_down.borrow().is_none() && hitbox.is_hovered(window) } diff --git a/crates/gpui/src/elements/list.rs b/crates/gpui/src/elements/list.rs index 10d64d7f7511f3..41886dd03fe03c 100644 --- a/crates/gpui/src/elements/list.rs +++ b/crates/gpui/src/elements/list.rs @@ -992,6 +992,7 @@ mod test { // And then receive a scroll event _before_ the next paint cx.simulate_event(ScrollWheelEvent { position: point(px(1.), px(1.)), + absolute_position: point(px(1.), px(1.)), delta: ScrollDelta::Pixels(point(px(0.), px(-500.))), ..Default::default() }); diff --git a/crates/gpui/src/elements/svg.rs b/crates/gpui/src/elements/svg.rs index c5997bb3405e0c..beb4b96c66cd06 100644 --- a/crates/gpui/src/elements/svg.rs +++ b/crates/gpui/src/elements/svg.rs @@ -99,7 +99,10 @@ impl Element for Svg { .transformation .as_ref() .map(|transformation| { - transformation.into_matrix(bounds.center(), window.scale_factor()) + transformation.into_matrix( + (bounds + window.element_origin()).center(), + window.scale_factor(), + ) }) .unwrap_or_default(); diff --git a/crates/gpui/src/elements/text.rs b/crates/gpui/src/elements/text.rs index 132135d4d604bb..7438dca86ddd9b 100644 --- a/crates/gpui/src/elements/text.rs +++ b/crates/gpui/src/elements/text.rs @@ -666,7 +666,7 @@ impl Element for InteractiveText { |interactive_state, window| { let mut interactive_state = interactive_state.unwrap_or_default(); if let Some(click_listener) = self.click_listener.take() { - let mouse_position = window.mouse_position(); + let mouse_position = window.mouse_position() - window.element_origin(); if let Ok(ix) = text_layout.index_for_position(mouse_position) { if self .clickable_ranges @@ -744,9 +744,10 @@ impl Element for InteractiveText { let build_tooltip = Rc::new({ let tooltip_is_hoverable = false; let text_layout = text_layout.clone(); + let origin = window.element_origin(); move |window: &mut Window, cx: &mut App| { text_layout - .index_for_position(window.mouse_position()) + .index_for_position(window.mouse_position() - origin) .ok() .and_then(|position| tooltip_builder(position, window, cx)) .map(|view| (view, tooltip_is_hoverable)) @@ -758,11 +759,12 @@ impl Element for InteractiveText { let source_bounds = hitbox.bounds; let text_layout = text_layout.clone(); let pending_mouse_down = interactive_state.mouse_down_index.clone(); + let origin = window.element_origin(); move |window: &Window| { text_layout - .index_for_position(window.mouse_position()) + .index_for_position(window.mouse_position() - origin) .is_ok() - && source_bounds.contains(&window.mouse_position()) + && source_bounds.contains(&(window.mouse_position() - origin)) && pending_mouse_down.get().is_none() } }); @@ -771,9 +773,10 @@ impl Element for InteractiveText { let hitbox = hitbox.clone(); let text_layout = text_layout.clone(); let pending_mouse_down = interactive_state.mouse_down_index.clone(); + let origin = window.element_origin(); move |window: &Window| { text_layout - .index_for_position(window.mouse_position()) + .index_for_position(window.mouse_position() - origin) .is_ok() && hitbox.is_hovered(window) && pending_mouse_down.get().is_none() diff --git a/crates/gpui/src/elements/uniform_list.rs b/crates/gpui/src/elements/uniform_list.rs index baf3d77c0e6a51..e4202cfcd003bf 100644 --- a/crates/gpui/src/elements/uniform_list.rs +++ b/crates/gpui/src/elements/uniform_list.rs @@ -410,7 +410,6 @@ impl Element for UniformList { for decoration in &self.decorations { let mut decoration = decoration.as_ref().compute( visible_range.clone(), - bounds, item_height, self.item_count, window, @@ -475,7 +474,6 @@ pub trait UniformListDecoration { fn compute( &self, visible_range: Range, - bounds: Bounds, item_height: Pixels, item_count: usize, window: &mut Window, diff --git a/crates/gpui/src/interactive.rs b/crates/gpui/src/interactive.rs index f1f6e60e7f4c83..67757751a7d15a 100644 --- a/crates/gpui/src/interactive.rs +++ b/crates/gpui/src/interactive.rs @@ -15,7 +15,10 @@ pub trait InputEvent: Sealed + 'static { pub trait KeyEvent: InputEvent {} /// A mouse event from the platform. -pub trait MouseEvent: InputEvent {} +pub trait MouseEvent: InputEvent + Clone { + /// Converts `absolute_position` to local coordinates and assigns it to `position` + fn with_local_coordinates(self, origin: Point, scale: f32) -> Self; +} /// The key down event equivalent for the platform. #[derive(Clone, Debug, Eq, PartialEq)] @@ -92,9 +95,12 @@ pub struct MouseDownEvent { /// Which mouse button was pressed. pub button: MouseButton, - /// The position of the mouse on the window. + /// The position within the current element pub position: Point, + /// The position of the mouse on the window. + pub absolute_position: Point, + /// The modifiers that were held down when the mouse was pressed. pub modifiers: Modifiers, @@ -111,7 +117,12 @@ impl InputEvent for MouseDownEvent { PlatformInput::MouseDown(self) } } -impl MouseEvent for MouseDownEvent {} +impl MouseEvent for MouseDownEvent { + fn with_local_coordinates(mut self, origin: Point, scale: f32) -> Self { + self.position = (self.absolute_position - origin) / scale; + self + } +} /// A mouse up event from the platform #[derive(Clone, Debug, Default)] @@ -119,9 +130,12 @@ pub struct MouseUpEvent { /// Which mouse button was released. pub button: MouseButton, - /// The position of the mouse on the window. + /// The position within the current element pub position: Point, + /// The position of the mouse on the window. + pub absolute_position: Point, + /// The modifiers that were held down when the mouse was released. pub modifiers: Modifiers, @@ -135,7 +149,12 @@ impl InputEvent for MouseUpEvent { PlatformInput::MouseUp(self) } } -impl MouseEvent for MouseUpEvent {} +impl MouseEvent for MouseUpEvent { + fn with_local_coordinates(mut self, origin: Point, scale: f32) -> Self { + self.position = (self.absolute_position - origin) / scale; + self + } +} /// A click event, generated when a mouse button is pressed and released. #[derive(Clone, Debug, Default)] @@ -215,9 +234,12 @@ impl Default for NavigationDirection { /// A mouse move event from the platform #[derive(Clone, Debug, Default)] pub struct MouseMoveEvent { - /// The position of the mouse on the window. + /// The position within the current element pub position: Point, + /// The position of the mouse on the window. + pub absolute_position: Point, + /// The mouse button that was pressed, if any. pub pressed_button: Option, @@ -231,7 +253,12 @@ impl InputEvent for MouseMoveEvent { PlatformInput::MouseMove(self) } } -impl MouseEvent for MouseMoveEvent {} +impl MouseEvent for MouseMoveEvent { + fn with_local_coordinates(mut self, origin: Point, scale: f32) -> Self { + self.position = (self.absolute_position - origin) / scale; + self + } +} impl MouseMoveEvent { /// Returns true if the left mouse button is currently held down. @@ -243,9 +270,12 @@ impl MouseMoveEvent { /// A mouse wheel event from the platform #[derive(Clone, Debug, Default)] pub struct ScrollWheelEvent { - /// The position of the mouse on the window. + /// The position within the current element pub position: Point, + /// The position of the mouse on the window. + pub absolute_position: Point, + /// The change in scroll wheel position for this event. pub delta: ScrollDelta, @@ -262,7 +292,12 @@ impl InputEvent for ScrollWheelEvent { PlatformInput::ScrollWheel(self) } } -impl MouseEvent for ScrollWheelEvent {} +impl MouseEvent for ScrollWheelEvent { + fn with_local_coordinates(mut self, origin: Point, scale: f32) -> Self { + self.position = (self.absolute_position - origin) / scale; + self + } +} impl Deref for ScrollWheelEvent { type Target = Modifiers; @@ -364,7 +399,11 @@ impl InputEvent for MouseExitEvent { PlatformInput::MouseExited(self) } } -impl MouseEvent for MouseExitEvent {} +impl MouseEvent for MouseExitEvent { + fn with_local_coordinates(self, _origin: Point, _scale: f32) -> Self { + self + } +} impl Deref for MouseExitEvent { type Target = Modifiers; @@ -397,20 +436,26 @@ impl Render for ExternalPaths { pub enum FileDropEvent { /// The files have entered the window. Entered { - /// The position of the mouse relative to the window. + /// The position relative to the current element position: Point, + /// The position of the mouse relative to the window + absolute_position: Point, /// The paths of the files that are being dragged. paths: ExternalPaths, }, /// The files are being dragged over the window Pending { - /// The position of the mouse relative to the window. + /// The position relative to the current element position: Point, + /// The position of the mouse relative to the window + absolute_position: Point, }, /// The files have been dropped onto the window. Submit { - /// The position of the mouse relative to the window. + /// The position relative to the current element position: Point, + /// The position of the mouse relative to the window + absolute_position: Point, }, /// The user has stopped dragging the files over the window. Exited, @@ -422,7 +467,36 @@ impl InputEvent for FileDropEvent { PlatformInput::FileDrop(self) } } -impl MouseEvent for FileDropEvent {} +impl MouseEvent for FileDropEvent { + fn with_local_coordinates(self, origin: Point, scale: f32) -> Self { + match self { + Self::Entered { + position: _, + absolute_position, + paths, + } => Self::Entered { + position: (absolute_position - origin) / scale, + absolute_position, + paths, + }, + Self::Pending { + position: _, + absolute_position, + } => Self::Pending { + position: (absolute_position - origin) / scale, + absolute_position, + }, + Self::Submit { + position: _, + absolute_position, + } => Self::Submit { + position: (absolute_position - origin) / scale, + absolute_position, + }, + x => x, + } + } +} /// An enum corresponding to all kinds of platform input events. #[derive(Clone, Debug)] diff --git a/crates/gpui/src/platform/linux/wayland/client.rs b/crates/gpui/src/platform/linux/wayland/client.rs index 5c5ab5a3929ab5..7c9af69262eec3 100644 --- a/crates/gpui/src/platform/linux/wayland/client.rs +++ b/crates/gpui/src/platform/linux/wayland/client.rs @@ -1527,6 +1527,7 @@ impl Dispatch for WaylandClientStatePtr { } let input = PlatformInput::MouseMove(MouseMoveEvent { position: state.mouse_location.unwrap(), + absolute_position: state.mouse_location.unwrap(), pressed_button: state.button_pressed, modifiers: state.modifiers, }); @@ -1592,6 +1593,7 @@ impl Dispatch for WaylandClientStatePtr { let input = PlatformInput::MouseDown(MouseDownEvent { button, position: state.mouse_location.unwrap(), + absolute_position: state.mouse_location.unwrap(), modifiers: state.modifiers, click_count: state.click.current_count, first_mouse: state.enter_token.take().is_some(), @@ -1607,6 +1609,7 @@ impl Dispatch for WaylandClientStatePtr { let input = PlatformInput::MouseUp(MouseUpEvent { button, position: state.mouse_location.unwrap(), + absolute_position: state.mouse_location.unwrap(), modifiers: state.modifiers, click_count: state.click.current_count, }); @@ -1721,6 +1724,7 @@ impl Dispatch for WaylandClientStatePtr { if let Some(window) = state.mouse_focused_window.clone() { let input = PlatformInput::ScrollWheel(ScrollWheelEvent { position: state.mouse_location.unwrap(), + absolute_position: state.mouse_location.unwrap(), delta: ScrollDelta::Pixels(continuous), modifiers: state.modifiers, touch_phase: TouchPhase::Moved, @@ -1732,6 +1736,7 @@ impl Dispatch for WaylandClientStatePtr { if let Some(window) = state.mouse_focused_window.clone() { let input = PlatformInput::ScrollWheel(ScrollWheelEvent { position: state.mouse_location.unwrap(), + absolute_position: state.mouse_location.unwrap(), delta: ScrollDelta::Lines(discrete), modifiers: state.modifiers, touch_phase: TouchPhase::Moved, @@ -1882,6 +1887,7 @@ impl Dispatch for WaylandClientStatePtr { let input = PlatformInput::FileDrop(FileDropEvent::Entered { position, + absolute_position: position, paths: crate::ExternalPaths(paths), }); @@ -1904,7 +1910,10 @@ impl Dispatch for WaylandClientStatePtr { let position = Point::new(x.into(), y.into()); state.drag.position = position; - let input = PlatformInput::FileDrop(FileDropEvent::Pending { position }); + let input = PlatformInput::FileDrop(FileDropEvent::Pending { + position, + absolute_position: position, + }); drop(state); drag_window.handle_input(input); } @@ -1935,6 +1944,7 @@ impl Dispatch for WaylandClientStatePtr { let input = PlatformInput::FileDrop(FileDropEvent::Submit { position: state.drag.position, + absolute_position: state.drag.position, }); drop(state); drag_window.handle_input(input); diff --git a/crates/gpui/src/platform/linux/x11/client.rs b/crates/gpui/src/platform/linux/x11/client.rs index 4a4521b35d05ca..22f465af84bb13 100644 --- a/crates/gpui/src/platform/linux/x11/client.rs +++ b/crates/gpui/src/platform/linux/x11/client.rs @@ -697,8 +697,10 @@ impl X11Client { } else if event.type_ == state.atoms.XdndLeave { let position = state.xdnd_state.position; drop(state); - window - .handle_input(PlatformInput::FileDrop(FileDropEvent::Pending { position })); + window.handle_input(PlatformInput::FileDrop(FileDropEvent::Pending { + position, + absolute_position: position, + })); window.handle_input(PlatformInput::FileDrop(FileDropEvent::Exited {})); self.0.borrow_mut().xdnd_state = Xdnd::default(); } else if event.type_ == state.atoms.XdndPosition { @@ -732,8 +734,10 @@ impl X11Client { ); let position = state.xdnd_state.position; drop(state); - window - .handle_input(PlatformInput::FileDrop(FileDropEvent::Pending { position })); + window.handle_input(PlatformInput::FileDrop(FileDropEvent::Pending { + position, + absolute_position: position, + })); } else if event.type_ == state.atoms.XdndDrop { xdnd_send_finished( &state.xcb_connection, @@ -743,8 +747,10 @@ impl X11Client { ); let position = state.xdnd_state.position; drop(state); - window - .handle_input(PlatformInput::FileDrop(FileDropEvent::Submit { position })); + window.handle_input(PlatformInput::FileDrop(FileDropEvent::Submit { + position, + absolute_position: position, + })); self.0.borrow_mut().xdnd_state = Xdnd::default(); } } @@ -772,6 +778,7 @@ impl X11Client { .collect(); let input = PlatformInput::FileDrop(FileDropEvent::Entered { position: state.xdnd_state.position, + absolute_position: state.xdnd_state.position, paths: crate::ExternalPaths(paths), }); drop(state); @@ -1028,6 +1035,7 @@ impl X11Client { window.handle_input(PlatformInput::MouseDown(crate::MouseDownEvent { button, position, + absolute_position: position, modifiers, click_count: current_count, first_mouse: false, @@ -1074,6 +1082,7 @@ impl X11Client { window.handle_input(PlatformInput::MouseUp(crate::MouseUpEvent { button, position, + absolute_position: position, modifiers, click_count, })); @@ -1097,6 +1106,7 @@ impl X11Client { if event.valuator_mask[0] & 3 != 0 { window.handle_input(PlatformInput::MouseMove(crate::MouseMoveEvent { position, + absolute_position: position, pressed_button, modifiers, })); @@ -1933,6 +1943,7 @@ fn make_scroll_wheel_event( }; crate::ScrollWheelEvent { position, + absolute_position: position, delta: ScrollDelta::Lines(delta), modifiers, touch_phase: TouchPhase::default(), diff --git a/crates/gpui/src/platform/mac/events.rs b/crates/gpui/src/platform/mac/events.rs index 4d2fc12dc15128..a265739e280360 100644 --- a/crates/gpui/src/platform/mac/events.rs +++ b/crates/gpui/src/platform/mac/events.rs @@ -120,13 +120,16 @@ impl PlatformInput { _ => return None, }; window_height.map(|window_height| { + let position = point( + px(native_event.locationInWindow().x as f32), + // MacOS screen coordinates are relative to bottom left + window_height - px(native_event.locationInWindow().y as f32), + ); + Self::MouseDown(MouseDownEvent { button, - position: point( - px(native_event.locationInWindow().x as f32), - // MacOS screen coordinates are relative to bottom left - window_height - px(native_event.locationInWindow().y as f32), - ), + position, + absolute_position: position, modifiers: read_modifiers(native_event), click_count: native_event.clickCount() as usize, first_mouse: false, @@ -147,12 +150,15 @@ impl PlatformInput { }; window_height.map(|window_height| { + let position = point( + px(native_event.locationInWindow().x as f32), + window_height - px(native_event.locationInWindow().y as f32), + ); + Self::MouseUp(MouseUpEvent { button, - position: point( - px(native_event.locationInWindow().x as f32), - window_height - px(native_event.locationInWindow().y as f32), - ), + position, + absolute_position: position, modifiers: read_modifiers(native_event), click_count: native_event.clickCount() as usize, }) @@ -171,12 +177,15 @@ impl PlatformInput { match navigation_direction { Some(direction) => window_height.map(|window_height| { + let position = point( + px(native_event.locationInWindow().x as f32), + window_height - px(native_event.locationInWindow().y as f32), + ); + Self::MouseDown(MouseDownEvent { button: MouseButton::Navigate(direction), - position: point( - px(native_event.locationInWindow().x as f32), - window_height - px(native_event.locationInWindow().y as f32), - ), + position, + absolute_position: position, modifiers: read_modifiers(native_event), click_count: 1, first_mouse: false, @@ -205,11 +214,14 @@ impl PlatformInput { ScrollDelta::Lines(raw_data) }; + let position = point( + px(native_event.locationInWindow().x as f32), + window_height - px(native_event.locationInWindow().y as f32), + ); + Self::ScrollWheel(ScrollWheelEvent { - position: point( - px(native_event.locationInWindow().x as f32), - window_height - px(native_event.locationInWindow().y as f32), - ), + position, + absolute_position: position, delta, touch_phase: phase, modifiers: read_modifiers(native_event), @@ -229,22 +241,28 @@ impl PlatformInput { }; window_height.map(|window_height| { + let position = point( + px(native_event.locationInWindow().x as f32), + window_height - px(native_event.locationInWindow().y as f32), + ); + Self::MouseMove(MouseMoveEvent { pressed_button: Some(pressed_button), - position: point( - px(native_event.locationInWindow().x as f32), - window_height - px(native_event.locationInWindow().y as f32), - ), + position, + absolute_position: position, modifiers: read_modifiers(native_event), }) }) } NSEventType::NSMouseMoved => window_height.map(|window_height| { + let position = point( + px(native_event.locationInWindow().x as f32), + window_height - px(native_event.locationInWindow().y as f32), + ); + Self::MouseMove(MouseMoveEvent { - position: point( - px(native_event.locationInWindow().x as f32), - window_height - px(native_event.locationInWindow().y as f32), - ), + position, + absolute_position: position, pressed_button: None, modifiers: read_modifiers(native_event), }) diff --git a/crates/gpui/src/platform/mac/window.rs b/crates/gpui/src/platform/mac/window.rs index ba5011ef160af2..c6380a34486602 100644 --- a/crates/gpui/src/platform/mac/window.rs +++ b/crates/gpui/src/platform/mac/window.rs @@ -1862,9 +1862,13 @@ extern "C" fn dragging_entered(this: &Object, _: Sel, dragging_info: id) -> NSDr let window_state = unsafe { get_window_state(this) }; let position = drag_event_position(&window_state, dragging_info); let paths = external_paths_from_event(dragging_info); - if let Some(event) = - paths.map(|paths| PlatformInput::FileDrop(FileDropEvent::Entered { position, paths })) - { + if let Some(event) = paths.map(|paths| { + PlatformInput::FileDrop(FileDropEvent::Entered { + position, + absolute_position: position, + paths, + }) + }) { if send_new_event(&window_state, event) { window_state.lock().external_files_dragged = true; return NSDragOperationCopy; @@ -1878,7 +1882,10 @@ extern "C" fn dragging_updated(this: &Object, _: Sel, dragging_info: id) -> NSDr let position = drag_event_position(&window_state, dragging_info); if send_new_event( &window_state, - PlatformInput::FileDrop(FileDropEvent::Pending { position }), + PlatformInput::FileDrop(FileDropEvent::Pending { + position, + absolute_position: position, + }), ) { NSDragOperationCopy } else { @@ -1900,7 +1907,10 @@ extern "C" fn perform_drag_operation(this: &Object, _: Sel, dragging_info: id) - let position = drag_event_position(&window_state, dragging_info); if send_new_event( &window_state, - PlatformInput::FileDrop(FileDropEvent::Submit { position }), + PlatformInput::FileDrop(FileDropEvent::Submit { + position, + absolute_position: position, + }), ) { YES } else { diff --git a/crates/gpui/src/platform/windows/events.rs b/crates/gpui/src/platform/windows/events.rs index 14b34764ae4590..f6e5b1e80df2d1 100644 --- a/crates/gpui/src/platform/windows/events.rs +++ b/crates/gpui/src/platform/windows/events.rs @@ -284,6 +284,7 @@ fn handle_mouse_move_msg( let y = lparam.signed_hiword() as f32; let event = MouseMoveEvent { position: logical_point(x, y, scale_factor), + absolute_position: logical_point(x, y, scale_factor), pressed_button, modifiers: current_modifiers(), }; @@ -464,6 +465,7 @@ fn handle_mouse_down_msg( let event = MouseDownEvent { button, position: logical_point(x, y, scale_factor), + absolute_position: logical_point(x, y, scale_factor), modifiers: current_modifiers(), click_count, first_mouse: false, @@ -499,6 +501,7 @@ fn handle_mouse_up_msg( let event = MouseUpEvent { button, position: logical_point(x, y, scale_factor), + absolute_position: logical_point(x, y, scale_factor), modifiers: current_modifiers(), click_count, }; @@ -554,6 +557,11 @@ fn handle_mouse_wheel_msg( unsafe { ScreenToClient(handle, &mut cursor_point).ok().log_err() }; let event = ScrollWheelEvent { position: logical_point(cursor_point.x as f32, cursor_point.y as f32, scale_factor), + absolute_position: logical_point( + cursor_point.x as f32, + cursor_point.y as f32, + scale_factor, + ), delta: ScrollDelta::Lines(match modifiers.shift { true => Point { x: wheel_distance, @@ -600,6 +608,11 @@ fn handle_mouse_horizontal_wheel_msg( unsafe { ScreenToClient(handle, &mut cursor_point).ok().log_err() }; let event = ScrollWheelEvent { position: logical_point(cursor_point.x as f32, cursor_point.y as f32, scale_factor), + absolute_position: logical_point( + cursor_point.x as f32, + cursor_point.y as f32, + scale_factor, + ), delta: ScrollDelta::Lines(Point { x: wheel_distance, y: 0.0, @@ -968,6 +981,11 @@ fn handle_nc_mouse_move_msg( unsafe { ScreenToClient(handle, &mut cursor_point).ok().log_err() }; let event = MouseMoveEvent { position: logical_point(cursor_point.x as f32, cursor_point.y as f32, scale_factor), + absolute_position: logical_point( + cursor_point.x as f32, + cursor_point.y as f32, + scale_factor, + ), pressed_button: None, modifiers: current_modifiers(), }; @@ -1009,6 +1027,11 @@ fn handle_nc_mouse_down_msg( let event = MouseDownEvent { button, position: logical_point(cursor_point.x as f32, cursor_point.y as f32, scale_factor), + absolute_position: logical_point( + cursor_point.x as f32, + cursor_point.y as f32, + scale_factor, + ), modifiers: current_modifiers(), click_count, first_mouse: false, @@ -1064,6 +1087,11 @@ fn handle_nc_mouse_up_msg( let event = MouseUpEvent { button, position: logical_point(cursor_point.x as f32, cursor_point.y as f32, scale_factor), + absolute_position: logical_point( + cursor_point.x as f32, + cursor_point.y as f32, + scale_factor, + ), modifiers: current_modifiers(), click_count: 1, }; diff --git a/crates/gpui/src/platform/windows/window.rs b/crates/gpui/src/platform/windows/window.rs index d85762680adc0d..14d77743ac8e90 100644 --- a/crates/gpui/src/platform/windows/window.rs +++ b/crates/gpui/src/platform/windows/window.rs @@ -851,6 +851,11 @@ impl IDropTarget_Impl for WindowsDragDropHandler_Impl { cursor_position.y as f32, scale_factor, ), + absolute_position: logical_point( + cursor_position.x as f32, + cursor_position.y as f32, + scale_factor, + ), paths: ExternalPaths(paths), }); self.handle_drag_drop(input); @@ -880,6 +885,11 @@ impl IDropTarget_Impl for WindowsDragDropHandler_Impl { cursor_position.y as f32, scale_factor, ), + absolute_position: logical_point( + cursor_position.x as f32, + cursor_position.y as f32, + scale_factor, + ), }); self.handle_drag_drop(input); @@ -913,6 +923,11 @@ impl IDropTarget_Impl for WindowsDragDropHandler_Impl { cursor_position.y as f32, scale_factor, ), + absolute_position: logical_point( + cursor_position.x as f32, + cursor_position.y as f32, + scale_factor, + ), }); self.handle_drag_drop(input); diff --git a/crates/gpui/src/scene.rs b/crates/gpui/src/scene.rs index b837f2ad9131b6..feda10991f4057 100644 --- a/crates/gpui/src/scene.rs +++ b/crates/gpui/src/scene.rs @@ -696,6 +696,25 @@ impl Path { } } + /// Translate this path by a given offset. + pub fn offset(&self, offset: Point) -> Path { + Path { + id: self.id, + order: self.order, + bounds: self.bounds + offset, + content_mask: self.content_mask.clone(), + vertices: self + .vertices + .iter() + .map(|vertex| vertex.offset(offset)) + .collect(), + start: self.start + offset, + current: self.current + offset, + contour_count: self.contour_count, + color: self.color, + } + } + /// Scale this path by the given factor. pub fn scale(&self, factor: f32) -> Path { Path { @@ -805,6 +824,16 @@ pub(crate) struct PathVertex { } impl PathVertex { + pub fn offset(&self, offset: Point) -> Self { + Self { + xy_position: self.xy_position + offset, + st_position: self.st_position, + content_mask: ContentMask { + bounds: self.content_mask.bounds + offset, + }, + } + } + pub fn scale(&self, factor: f32) -> PathVertex { PathVertex { xy_position: self.xy_position.scale(factor), diff --git a/crates/gpui/src/style.rs b/crates/gpui/src/style.rs index 8c1c20c2ac2e65..e223d146e0bff0 100644 --- a/crates/gpui/src/style.rs +++ b/crates/gpui/src/style.rs @@ -177,6 +177,8 @@ pub struct Style { /// Controls the maximum size of the item #[refineable] pub max_size: Size, + /// Controls the scale of the item and its children, relative to its parent's scale. + pub scale: f32, /// Sets the preferred aspect ratio for the item. The ratio is calculated as width divided by height. pub aspect_ratio: Option, @@ -711,6 +713,7 @@ impl Default for Style { size: Size::auto(), min_size: Size::auto(), max_size: Size::auto(), + scale: 1.0, aspect_ratio: None, gap: Size::default(), // Alignment diff --git a/crates/gpui/src/styled.rs b/crates/gpui/src/styled.rs index c1f6a9f61754c9..4e44ebb408a762 100644 --- a/crates/gpui/src/styled.rs +++ b/crates/gpui/src/styled.rs @@ -351,6 +351,12 @@ pub trait Styled: Sized { self } + /// Sets the scale of the element and its children, relative to the scale of its parent. + fn scale(mut self, scale: f32) -> Self { + self.style().scale = Some(scale); + self + } + /// Sets the background color of the element. fn bg(mut self, fill: F) -> Self where diff --git a/crates/gpui/src/taffy.rs b/crates/gpui/src/taffy.rs index 7b96a7f5754abe..8654bc413f9fb8 100644 --- a/crates/gpui/src/taffy.rs +++ b/crates/gpui/src/taffy.rs @@ -20,7 +20,8 @@ struct NodeContext { } pub struct TaffyLayoutEngine { taffy: TaffyTree, - absolute_layout_bounds: FxHashMap>, + layout_bounds: FxHashMap>, + layout_scales: FxHashMap, computed_layouts: FxHashSet, } @@ -30,24 +31,27 @@ impl TaffyLayoutEngine { pub fn new() -> Self { TaffyLayoutEngine { taffy: TaffyTree::new(), - absolute_layout_bounds: FxHashMap::default(), + layout_bounds: FxHashMap::default(), + layout_scales: FxHashMap::default(), computed_layouts: FxHashSet::default(), } } pub fn clear(&mut self) { self.taffy.clear(); - self.absolute_layout_bounds.clear(); + self.layout_bounds.clear(); + self.layout_scales.clear(); self.computed_layouts.clear(); } pub fn request_layout( &mut self, style: Style, + scale: f32, rem_size: Pixels, children: &[LayoutId], ) -> LayoutId { - let taffy_style = style.to_taffy(rem_size); + let taffy_style = style.to_taffy(scale, rem_size); let layout_id = if children.is_empty() { self.taffy .new_leaf(taffy_style) @@ -64,17 +68,19 @@ impl TaffyLayoutEngine { .into(); parent_id }; + self.layout_scales.insert(layout_id, style.scale); layout_id } pub fn request_measured_layout( &mut self, style: Style, + scale: f32, rem_size: Pixels, measure: impl FnMut(Size>, Size, &mut Window, &mut App) -> Size + 'static, ) -> LayoutId { - let taffy_style = style.to_taffy(rem_size); + let taffy_style = style.to_taffy(scale, rem_size); let layout_id = self .taffy @@ -86,6 +92,7 @@ impl TaffyLayoutEngine { ) .expect(EXPECT_MESSAGE) .into(); + self.layout_scales.insert(layout_id, style.scale); layout_id } @@ -159,7 +166,7 @@ impl TaffyLayoutEngine { let mut stack = SmallVec::<[LayoutId; 64]>::new(); stack.push(id); while let Some(id) = stack.pop() { - self.absolute_layout_bounds.remove(&id); + self.layout_bounds.remove(&id); stack.extend( self.taffy .children(id.into()) @@ -195,7 +202,7 @@ impl TaffyLayoutEngine { } pub fn layout_bounds(&mut self, id: LayoutId) -> Bounds { - if let Some(layout) = self.absolute_layout_bounds.get(&id).cloned() { + if let Some(layout) = self.layout_bounds.get(&id).cloned() { return layout; } @@ -205,14 +212,13 @@ impl TaffyLayoutEngine { size: layout.size.into(), }; - if let Some(parent_id) = self.taffy.parent(id.0) { - let parent_bounds = self.layout_bounds(parent_id.into()); - bounds.origin += parent_bounds.origin; - } - self.absolute_layout_bounds.insert(id, bounds); - + self.layout_bounds.insert(id, bounds); bounds } + + pub fn layout_scale(&mut self, id: LayoutId) -> f32 { + *self.layout_scales.get(&id).unwrap() + } } /// A unique identifier for a layout node, generated when requesting a layout from Taffy @@ -239,32 +245,32 @@ impl From for NodeId { } trait ToTaffy { - fn to_taffy(&self, rem_size: Pixels) -> Output; + fn to_taffy(&self, scale: f32, rem_size: Pixels) -> Output; } impl ToTaffy for Style { - fn to_taffy(&self, rem_size: Pixels) -> taffy::style::Style { + fn to_taffy(&self, scale: f32, rem_size: Pixels) -> taffy::style::Style { taffy::style::Style { display: self.display, overflow: self.overflow.into(), scrollbar_width: self.scrollbar_width, position: self.position, - inset: self.inset.to_taffy(rem_size), - size: self.size.to_taffy(rem_size), - min_size: self.min_size.to_taffy(rem_size), - max_size: self.max_size.to_taffy(rem_size), + inset: self.inset.to_taffy(scale, rem_size), + size: self.size.to_taffy(scale, rem_size), + min_size: self.min_size.to_taffy(scale, rem_size), + max_size: self.max_size.to_taffy(scale, rem_size), aspect_ratio: self.aspect_ratio, - margin: self.margin.to_taffy(rem_size), - padding: self.padding.to_taffy(rem_size), - border: self.border_widths.to_taffy(rem_size), + margin: self.margin.to_taffy(scale, rem_size), + padding: self.padding.to_taffy(scale, rem_size), + border: self.border_widths.to_taffy(scale, rem_size), align_items: self.align_items, align_self: self.align_self, align_content: self.align_content, justify_content: self.justify_content, - gap: self.gap.to_taffy(rem_size), + gap: self.gap.to_taffy(scale, rem_size), flex_direction: self.flex_direction, flex_wrap: self.flex_wrap, - flex_basis: self.flex_basis.to_taffy(rem_size), + flex_basis: self.flex_basis.to_taffy(scale, rem_size), flex_grow: self.flex_grow, flex_shrink: self.flex_shrink, ..Default::default() // Ignore grid properties for now @@ -273,32 +279,32 @@ impl ToTaffy for Style { } impl ToTaffy for Length { - fn to_taffy(&self, rem_size: Pixels) -> taffy::prelude::LengthPercentageAuto { + fn to_taffy(&self, scale: f32, rem_size: Pixels) -> taffy::prelude::LengthPercentageAuto { match self { - Length::Definite(length) => length.to_taffy(rem_size), + Length::Definite(length) => length.to_taffy(scale, rem_size), Length::Auto => taffy::prelude::LengthPercentageAuto::Auto, } } } impl ToTaffy for Length { - fn to_taffy(&self, rem_size: Pixels) -> taffy::prelude::Dimension { + fn to_taffy(&self, scale: f32, rem_size: Pixels) -> taffy::prelude::Dimension { match self { - Length::Definite(length) => length.to_taffy(rem_size), + Length::Definite(length) => length.to_taffy(scale, rem_size), Length::Auto => taffy::prelude::Dimension::Auto, } } } impl ToTaffy for DefiniteLength { - fn to_taffy(&self, rem_size: Pixels) -> taffy::style::LengthPercentage { + fn to_taffy(&self, scale: f32, rem_size: Pixels) -> taffy::style::LengthPercentage { match self { DefiniteLength::Absolute(length) => match length { AbsoluteLength::Pixels(pixels) => { - taffy::style::LengthPercentage::Length(pixels.into()) + taffy::style::LengthPercentage::Length((*pixels * scale).into()) } AbsoluteLength::Rems(rems) => { - taffy::style::LengthPercentage::Length((*rems * rem_size).into()) + taffy::style::LengthPercentage::Length((*rems * rem_size * scale).into()) } }, DefiniteLength::Fraction(fraction) => { @@ -309,14 +315,14 @@ impl ToTaffy for DefiniteLength { } impl ToTaffy for DefiniteLength { - fn to_taffy(&self, rem_size: Pixels) -> taffy::style::LengthPercentageAuto { + fn to_taffy(&self, scale: f32, rem_size: Pixels) -> taffy::style::LengthPercentageAuto { match self { DefiniteLength::Absolute(length) => match length { AbsoluteLength::Pixels(pixels) => { - taffy::style::LengthPercentageAuto::Length(pixels.into()) + taffy::style::LengthPercentageAuto::Length((*pixels * scale).into()) } AbsoluteLength::Rems(rems) => { - taffy::style::LengthPercentageAuto::Length((*rems * rem_size).into()) + taffy::style::LengthPercentageAuto::Length((*rems * rem_size * scale).into()) } }, DefiniteLength::Fraction(fraction) => { @@ -327,12 +333,14 @@ impl ToTaffy for DefiniteLength { } impl ToTaffy for DefiniteLength { - fn to_taffy(&self, rem_size: Pixels) -> taffy::style::Dimension { + fn to_taffy(&self, scale: f32, rem_size: Pixels) -> taffy::style::Dimension { match self { DefiniteLength::Absolute(length) => match length { - AbsoluteLength::Pixels(pixels) => taffy::style::Dimension::Length(pixels.into()), + AbsoluteLength::Pixels(pixels) => { + taffy::style::Dimension::Length((*pixels * scale).into()) + } AbsoluteLength::Rems(rems) => { - taffy::style::Dimension::Length((*rems * rem_size).into()) + taffy::style::Dimension::Length((*rems * rem_size * scale).into()) } }, DefiniteLength::Fraction(fraction) => taffy::style::Dimension::Percent(*fraction), @@ -341,11 +349,13 @@ impl ToTaffy for DefiniteLength { } impl ToTaffy for AbsoluteLength { - fn to_taffy(&self, rem_size: Pixels) -> taffy::style::LengthPercentage { + fn to_taffy(&self, scale: f32, rem_size: Pixels) -> taffy::style::LengthPercentage { match self { - AbsoluteLength::Pixels(pixels) => taffy::style::LengthPercentage::Length(pixels.into()), + AbsoluteLength::Pixels(pixels) => { + taffy::style::LengthPercentage::Length((*pixels * scale).into()) + } AbsoluteLength::Rems(rems) => { - taffy::style::LengthPercentage::Length((*rems * rem_size).into()) + taffy::style::LengthPercentage::Length((*rems * rem_size * scale).into()) } } } @@ -380,10 +390,10 @@ impl ToTaffy> for Size where T: ToTaffy + Clone + Default + Debug, { - fn to_taffy(&self, rem_size: Pixels) -> TaffySize { + fn to_taffy(&self, scale: f32, rem_size: Pixels) -> TaffySize { TaffySize { - width: self.width.to_taffy(rem_size), - height: self.height.to_taffy(rem_size), + width: self.width.to_taffy(scale, rem_size), + height: self.height.to_taffy(scale, rem_size), } } } @@ -392,12 +402,12 @@ impl ToTaffy> for Edges where T: ToTaffy + Clone + Default + Debug, { - fn to_taffy(&self, rem_size: Pixels) -> TaffyRect { + fn to_taffy(&self, scale: f32, rem_size: Pixels) -> TaffyRect { TaffyRect { - top: self.top.to_taffy(rem_size), - right: self.right.to_taffy(rem_size), - bottom: self.bottom.to_taffy(rem_size), - left: self.left.to_taffy(rem_size), + top: self.top.to_taffy(scale, rem_size), + right: self.right.to_taffy(scale, rem_size), + bottom: self.bottom.to_taffy(scale, rem_size), + left: self.left.to_taffy(scale, rem_size), } } } diff --git a/crates/gpui/src/text_system/line.rs b/crates/gpui/src/text_system/line.rs index 27da7d66c59a87..318a1b39f53830 100644 --- a/crates/gpui/src/text_system/line.rs +++ b/crates/gpui/src/text_system/line.rs @@ -351,8 +351,8 @@ fn paint_line( } let max_glyph_bounds = Bounds { - origin: glyph_origin, - size: max_glyph_size, + origin: glyph_origin * window.element_scale() + window.element_origin(), + size: max_glyph_size * px(window.element_scale()), }; let content_mask = window.content_mask(); diff --git a/crates/gpui/src/window.rs b/crates/gpui/src/window.rs index b3afcdb63d3082..869330c51becea 100644 --- a/crates/gpui/src/window.rs +++ b/crates/gpui/src/window.rs @@ -615,7 +615,6 @@ pub struct Window { pub(crate) element_id_stack: SmallVec<[ElementId; 32]>, pub(crate) text_style_stack: Vec, pub(crate) rendered_entity_stack: Vec, - pub(crate) element_offset_stack: Vec>, pub(crate) element_opacity: Option, pub(crate) content_mask_stack: Vec>, pub(crate) requested_autoscroll: Option>, @@ -633,6 +632,9 @@ pub struct Window { mouse_hit_test: HitTest, modifiers: Modifiers, scale_factor: f32, + pub(crate) element_layout_id: Option, + element_origin: Point, + element_scale: f32, pub(crate) bounds_observers: SubscriberSet<(), AnyObserver>, appearance: WindowAppearance, pub(crate) appearance_observers: SubscriberSet<(), AnyObserver>, @@ -899,8 +901,10 @@ impl Window { element_id_stack: SmallVec::default(), text_style_stack: Vec::new(), rendered_entity_stack: Vec::new(), - element_offset_stack: Vec::new(), content_mask_stack: Vec::new(), + element_layout_id: None, + element_origin: Point::default(), + element_scale: 1.0, element_opacity: None, requested_autoscroll: None, rendered_frame: Frame::new(DispatchTree::new(cx.keymap.clone(), cx.actions.clone())), @@ -1497,6 +1501,16 @@ impl Window { .is_action_available(action, target) } + /// The absolute origin of the current element within the window. + pub fn element_origin(&self) -> Point { + self.element_origin + } + + /// The absolute scale of the current element + pub fn element_scale(&self) -> f32 { + self.element_scale + } + /// The position of the mouse relative to the window. pub fn mouse_position(&self) -> Point { self.mouse_position @@ -1722,9 +1736,7 @@ impl Window { continue; } - self.with_absolute_element_offset(tooltip_bounds.origin, |window| { - element.prepaint(window, cx) - }); + self.with_element_origin(tooltip_bounds.origin, |window| element.prepaint(window, cx)); self.tooltip_bounds = Some(TooltipBounds { id: tooltip_request.id, @@ -1752,7 +1764,7 @@ impl Window { let prepaint_start = self.prepaint_index(); if let Some(element) = deferred_draw.element.as_mut() { self.with_rendered_view(deferred_draw.current_view, |window| { - window.with_absolute_element_offset(deferred_draw.absolute_offset, |window| { + window.with_element_origin(deferred_draw.absolute_offset, |window| { element.prepaint(window, cx) }); }) @@ -1951,8 +1963,9 @@ impl Window { f: impl FnOnce(&mut Self) -> R, ) -> R { self.invalidator.debug_assert_paint_or_prepaint(); - if let Some(mask) = mask { - let mask = mask.intersect(&self.content_mask()); + if let Some(mut mask) = mask { + mask.bounds = self.to_absolute_bounds(mask.bounds); + mask = mask.intersect(&self.content_mask()); self.content_mask_stack.push(mask); let result = f(self); self.content_mask_stack.pop(); @@ -1962,38 +1975,37 @@ impl Window { } } - /// Updates the global element offset relative to the current offset. This is used to implement - /// scrolling. This method should only be called during the prepaint phase of element drawing. - pub fn with_element_offset( + pub(crate) fn with_element_origin( &mut self, - offset: Point, + origin: Point, f: impl FnOnce(&mut Self) -> R, ) -> R { - self.invalidator.debug_assert_prepaint(); - - if offset.is_zero() { - return f(self); - }; - - let abs_offset = self.element_offset() + offset; - self.with_absolute_element_offset(abs_offset, f) + let old_origin = self.element_origin(); + self.element_origin = origin; + let result = f(self); + self.element_origin = old_origin; + result } - /// Updates the global element offset based on the given offset. This is used to implement - /// drag handles and other manual painting of elements. This method should only be called during - /// the prepaint phase of element drawing. - pub fn with_absolute_element_offset( + pub(crate) fn with_element_scale( &mut self, - offset: Point, + scale: f32, f: impl FnOnce(&mut Self) -> R, ) -> R { - self.invalidator.debug_assert_prepaint(); - self.element_offset_stack.push(offset); + let old_scale = self.element_scale; + self.element_scale = scale; let result = f(self); - self.element_offset_stack.pop(); + self.element_scale = old_scale; result } + fn to_absolute_bounds(&self, mut bounds: Bounds) -> Bounds { + bounds.origin *= self.element_scale(); + bounds.size *= self.element_scale(); + bounds.origin += self.element_origin(); + bounds + } + pub(crate) fn with_element_opacity( &mut self, opacity: Option, @@ -2081,15 +2093,6 @@ impl Window { None }) } - /// Obtain the current element offset. This method should only be called during the - /// prepaint phase of element drawing. - pub fn element_offset(&self) -> Point { - self.invalidator.debug_assert_prepaint(); - self.element_offset_stack - .last() - .copied() - .unwrap_or_default() - } /// Obtain the current element opacity. This method should only be called during the /// prepaint phase of element drawing. @@ -2275,7 +2278,9 @@ impl Window { let scale_factor = self.scale_factor(); let content_mask = self.content_mask(); - let clipped_bounds = bounds.intersect(&content_mask.bounds); + let clipped_bounds = self + .to_absolute_bounds(bounds) + .intersect(&content_mask.bounds); if !clipped_bounds.is_empty() { self.next_frame .scene @@ -2303,16 +2308,19 @@ impl Window { self.invalidator.debug_assert_paint(); let scale_factor = self.scale_factor(); + let scale = self.element_scale() * scale_factor; let content_mask = self.content_mask(); let opacity = self.element_opacity(); for shadow in shadows { - let shadow_bounds = (bounds + shadow.offset).dilate(shadow.spread_radius); + let shadow_bounds = self + .to_absolute_bounds(bounds + shadow.offset) + .dilate(shadow.spread_radius * self.element_scale()); self.next_frame.scene.insert_primitive(Shadow { order: 0, - blur_radius: shadow.blur_radius.scale(scale_factor), + blur_radius: shadow.blur_radius.scale(scale), bounds: shadow_bounds.scale(scale_factor), content_mask: content_mask.scale(scale_factor), - corner_radii: corner_radii.scale(scale_factor), + corner_radii: corner_radii.scale(scale), color: shadow.color.opacity(opacity), }); } @@ -2327,17 +2335,18 @@ impl Window { self.invalidator.debug_assert_paint(); let scale_factor = self.scale_factor(); + let scale = self.element_scale() * scale_factor; let content_mask = self.content_mask(); let opacity = self.element_opacity(); self.next_frame.scene.insert_primitive(Quad { order: 0, pad: 0, - bounds: quad.bounds.scale(scale_factor), + bounds: self.to_absolute_bounds(quad.bounds).scale(scale_factor), content_mask: content_mask.scale(scale_factor), background: quad.background.opacity(opacity), border_color: quad.border_color.opacity(opacity), - corner_radii: quad.corner_radii.scale(scale_factor), - border_widths: quad.border_widths.scale(scale_factor), + corner_radii: quad.corner_radii.scale(scale), + border_widths: quad.border_widths.scale(scale), }); } @@ -2353,9 +2362,10 @@ impl Window { path.content_mask = content_mask; let color: Background = color.into(); path.color = color.opacity(opacity); - self.next_frame - .scene - .insert_primitive(path.scale(scale_factor)); + self.next_frame.scene.insert_primitive( + path.offset(self.element_origin() / self.element_scale()) + .scale(scale_factor * self.element_scale()), + ); } /// Paint an underline into the scene for the next frame at the current z-index. @@ -2375,10 +2385,10 @@ impl Window { } else { style.thickness }; - let bounds = Bounds { + let bounds = self.to_absolute_bounds(Bounds { origin, size: size(width, height), - }; + }); let content_mask = self.content_mask(); let element_opacity = self.element_opacity(); @@ -2388,7 +2398,7 @@ impl Window { bounds: bounds.scale(scale_factor), content_mask: content_mask.scale(scale_factor), color: style.color.unwrap_or_default().opacity(element_opacity), - thickness: style.thickness.scale(scale_factor), + thickness: style.thickness.scale(scale_factor * self.element_scale()), wavy: style.wavy, }); } @@ -2406,10 +2416,10 @@ impl Window { let scale_factor = self.scale_factor(); let height = style.thickness; - let bounds = Bounds { + let bounds = self.to_absolute_bounds(Bounds { origin, size: size(width, height), - }; + }); let content_mask = self.content_mask(); let opacity = self.element_opacity(); @@ -2418,7 +2428,7 @@ impl Window { pad: 0, bounds: bounds.scale(scale_factor), content_mask: content_mask.scale(scale_factor), - thickness: style.thickness.scale(scale_factor), + thickness: style.thickness.scale(scale_factor * self.element_scale()), color: style.color.unwrap_or_default().opacity(opacity), wavy: false, }); @@ -2444,7 +2454,8 @@ impl Window { let element_opacity = self.element_opacity(); let scale_factor = self.scale_factor(); - let glyph_origin = origin.scale(scale_factor); + let glyph_origin = + (origin * self.element_scale() + self.element_origin()).scale(scale_factor); let subpixel_variant = Point { x: (glyph_origin.x.0.fract() * SUBPIXEL_VARIANTS as f32).floor() as u8, y: (glyph_origin.y.0.fract() * SUBPIXEL_VARIANTS as f32).floor() as u8, @@ -2452,7 +2463,7 @@ impl Window { let params = RenderGlyphParams { font_id, glyph_id, - font_size, + font_size: font_size * self.element_scale(), subpixel_variant, scale_factor, is_emoji: false, @@ -2503,11 +2514,12 @@ impl Window { self.invalidator.debug_assert_paint(); let scale_factor = self.scale_factor(); - let glyph_origin = origin.scale(scale_factor); + let glyph_origin = + (origin * self.element_scale() + self.element_origin()).scale(scale_factor); let params = RenderGlyphParams { font_id, glyph_id, - font_size, + font_size: font_size * self.element_scale(), // We don't render emojis with subpixel variants. subpixel_variant: Default::default(), scale_factor, @@ -2560,7 +2572,7 @@ impl Window { let element_opacity = self.element_opacity(); let scale_factor = self.scale_factor(); - let bounds = bounds.scale(scale_factor); + let bounds = self.to_absolute_bounds(bounds).scale(scale_factor); let params = RenderSvgParams { path, size: bounds.size.map(|pixels| { @@ -2611,7 +2623,7 @@ impl Window { self.invalidator.debug_assert_paint(); let scale_factor = self.scale_factor(); - let bounds = bounds.scale(scale_factor); + let bounds = self.to_absolute_bounds(bounds).scale(scale_factor); let params = RenderImageParams { image_id: data.id, frame_index, @@ -2656,7 +2668,7 @@ impl Window { self.invalidator.debug_assert_paint(); let scale_factor = self.scale_factor(); - let bounds = bounds.scale(scale_factor); + let bounds = self.to_absolute_bounds(bounds).scale(scale_factor); let content_mask = self.content_mask().scale(scale_factor); self.next_frame.scene.insert_primitive(PaintSurface { order: 0, @@ -2696,12 +2708,16 @@ impl Window { cx.layout_id_buffer.clear(); cx.layout_id_buffer.extend(children); + + let scale = self.element_scale() * style.scale; let rem_size = self.rem_size(); - self.layout_engine - .as_mut() - .unwrap() - .request_layout(style, rem_size, &cx.layout_id_buffer) + self.layout_engine.as_mut().unwrap().request_layout( + style, + scale, + rem_size, + &cx.layout_id_buffer, + ) } /// Add a node to the layout tree for the current frame. Instead of taking a `Style` and children, @@ -2718,15 +2734,34 @@ impl Window { >( &mut self, style: Style, - measure: F, + mut measure: F, ) -> LayoutId { self.invalidator.debug_assert_prepaint(); + let scale = self.element_scale() * style.scale; let rem_size = self.rem_size(); + self.layout_engine .as_mut() .unwrap() - .request_measured_layout(style, rem_size, measure) + .request_measured_layout( + style, + scale, + rem_size, + move |mut known_size, mut available_space, window, cx| { + // Unscale size inputs to measure function to ensure normal layout behavior + known_size = known_size.map(|size| size.map(|size| size / scale)); + available_space = available_space.map(|space| match space { + AvailableSpace::Definite(pixels) => { + AvailableSpace::Definite(pixels / scale) + } + x => x, + }); + + // Measure and rescale output size for layout + measure(known_size, available_space, window, cx).map(|size| size * scale) + }, + ) } /// Compute the layout for the given id within the given available space. @@ -2760,10 +2795,27 @@ impl Window { .unwrap() .layout_bounds(layout_id) .map(Into::into); - bounds.origin += self.element_offset(); + + if Some(layout_id) != self.element_layout_id { + bounds.origin += self.element_origin(); + } else { + bounds.origin = self.element_origin(); + } + bounds } + pub fn layout_scale(&mut self, layout_id: LayoutId) -> f32 { + self.invalidator.debug_assert_prepaint(); + + let mut scale = self.layout_engine.as_mut().unwrap().layout_scale(layout_id); + if Some(layout_id) != self.element_layout_id { + scale * self.element_scale + } else { + self.element_scale + } + } + /// This method should be called during `prepaint`. You can use /// the returned [Hitbox] during `paint` or in an event handler /// to determine whether the inserted hitbox was the topmost. @@ -2781,7 +2833,13 @@ impl Window { content_mask, opaque, }; - self.next_frame.hitboxes.push(hitbox.clone()); + self.next_frame.hitboxes.push(Hitbox { + bounds: Bounds { + origin: bounds.origin * self.element_scale() + self.element_origin(), + size: bounds.size * px(self.element_scale()), + }, + ..hitbox.clone() + }); hitbox } @@ -2868,10 +2926,15 @@ impl Window { ) { self.invalidator.debug_assert_paint(); + let origin = self.element_origin(); + let scale = self.element_scale(); self.next_frame.mouse_listeners.push(Some(Box::new( move |event: &dyn Any, phase: DispatchPhase, window: &mut Window, cx: &mut App| { - if let Some(event) = event.downcast_ref() { - handler(event, phase, window, cx) + if let Some(event) = event.downcast_ref::() { + window.with_element_origin(origin, |window| { + let event = event.clone().with_local_coordinates(origin, scale); + handler(&event, phase, window, cx) + }) } }, ))); @@ -2891,10 +2954,11 @@ impl Window { ) { self.invalidator.debug_assert_paint(); + let origin = self.element_origin(); self.next_frame.dispatch_tree.on_key_event(Rc::new( move |event: &dyn Any, phase, window: &mut Window, cx: &mut App| { if let Some(event) = event.downcast_ref::() { - listener(event, phase, window, cx) + window.with_element_origin(origin, |window| listener(event, phase, window, cx)) } }, )); @@ -2912,9 +2976,10 @@ impl Window { ) { self.invalidator.debug_assert_paint(); + let origin = self.element_origin(); self.next_frame.dispatch_tree.on_modifiers_changed(Rc::new( move |event: &ModifiersChangedEvent, window: &mut Window, cx: &mut App| { - listener(event, window, cx) + window.with_element_origin(origin, |window| listener(event, window, cx)) }, )); } @@ -2929,10 +2994,13 @@ impl Window { mut listener: impl FnMut(&mut Window, &mut App) + 'static, ) -> Subscription { let focus_id = handle.id; + let origin = self.element_origin(); let (subscription, activate) = self.new_focus_listener(Box::new(move |event, window, cx| { if event.is_focus_in(focus_id) { - listener(window, cx); + window.with_element_origin(origin, |window| { + listener(window, cx); + }); } true })); @@ -2949,6 +3017,7 @@ impl Window { mut listener: impl FnMut(FocusOutEvent, &mut Window, &mut App) + 'static, ) -> Subscription { let focus_id = handle.id; + let origin = self.element_origin(); let (subscription, activate) = self.new_focus_listener(Box::new(move |event, window, cx| { if let Some(blurred_id) = event.previous_focus_path.last().copied() { @@ -2959,7 +3028,7 @@ impl Window { handles: Arc::downgrade(&cx.focus_handles), }, }; - listener(event, window, cx) + window.with_element_origin(origin, |window| listener(event, window, cx)) } } true @@ -3038,17 +3107,17 @@ impl Window { // Track the mouse position with our own state, since accessing the platform // API for the mouse position can only occur on the main thread. PlatformInput::MouseMove(mouse_move) => { - self.mouse_position = mouse_move.position; + self.mouse_position = mouse_move.absolute_position; self.modifiers = mouse_move.modifiers; PlatformInput::MouseMove(mouse_move) } PlatformInput::MouseDown(mouse_down) => { - self.mouse_position = mouse_down.position; + self.mouse_position = mouse_down.absolute_position; self.modifiers = mouse_down.modifiers; PlatformInput::MouseDown(mouse_down) } PlatformInput::MouseUp(mouse_up) => { - self.mouse_position = mouse_up.position; + self.mouse_position = mouse_up.absolute_position; self.modifiers = mouse_up.modifiers; PlatformInput::MouseUp(mouse_up) } @@ -3061,42 +3130,55 @@ impl Window { PlatformInput::ModifiersChanged(modifiers_changed) } PlatformInput::ScrollWheel(scroll_wheel) => { - self.mouse_position = scroll_wheel.position; + self.mouse_position = scroll_wheel.absolute_position; self.modifiers = scroll_wheel.modifiers; PlatformInput::ScrollWheel(scroll_wheel) } // Translate dragging and dropping of external files from the operating system // to internal drag and drop events. PlatformInput::FileDrop(file_drop) => match file_drop { - FileDropEvent::Entered { position, paths } => { - self.mouse_position = position; + FileDropEvent::Entered { + position, + absolute_position, + paths, + } => { + self.mouse_position = absolute_position; if cx.active_drag.is_none() { cx.active_drag = Some(AnyDrag { value: Arc::new(paths.clone()), view: cx.new(|_| paths).into(), - cursor_offset: position, + cursor_offset: absolute_position, }); } PlatformInput::MouseMove(MouseMoveEvent { position, + absolute_position, pressed_button: Some(MouseButton::Left), modifiers: Modifiers::default(), }) } - FileDropEvent::Pending { position } => { - self.mouse_position = position; + FileDropEvent::Pending { + position, + absolute_position, + } => { + self.mouse_position = absolute_position; PlatformInput::MouseMove(MouseMoveEvent { position, + absolute_position, pressed_button: Some(MouseButton::Left), modifiers: Modifiers::default(), }) } - FileDropEvent::Submit { position } => { + FileDropEvent::Submit { + position, + absolute_position, + } => { cx.activate(true); self.mouse_position = position; PlatformInput::MouseUp(MouseUpEvent { button: MouseButton::Left, position, + absolute_position, modifiers: Modifiers::default(), click_count: 1, }) @@ -3738,9 +3820,13 @@ impl Window { action_type: TypeId, listener: impl Fn(&dyn Any, DispatchPhase, &mut Window, &mut App) + 'static, ) { - self.next_frame - .dispatch_tree - .on_action(action_type, Rc::new(listener)); + let origin = self.element_origin(); + self.next_frame.dispatch_tree.on_action( + action_type, + Rc::new(move |action, phase, window, cx| { + window.with_element_origin(origin, |window| listener(action, phase, window, cx)) + }), + ); } /// Read information about the GPU backing this window. diff --git a/crates/markdown/src/markdown.rs b/crates/markdown/src/markdown.rs index d6190c43dbed3a..ecd48d259247e2 100644 --- a/crates/markdown/src/markdown.rs +++ b/crates/markdown/src/markdown.rs @@ -416,7 +416,7 @@ impl MarkdownElement { let is_hovering_link = hitbox.is_hovered(window) && !self.markdown.read(cx).selection.pending && rendered_text - .link_for_position(window.mouse_position()) + .link_for_position(window.mouse_position() - window.element_origin()) .is_some(); if is_hovering_link { diff --git a/crates/outline_panel/src/outline_panel.rs b/crates/outline_panel/src/outline_panel.rs index b5b637ce04f88f..dc25740235ae92 100644 --- a/crates/outline_panel/src/outline_panel.rs +++ b/crates/outline_panel/src/outline_panel.rs @@ -2492,7 +2492,7 @@ impl OutlinePanel { // panel from being deployed. cx.stop_propagation(); outline_panel.deploy_context_menu( - event.position, + event.absolute_position, rendered_entry.clone(), window, cx, @@ -4929,10 +4929,15 @@ impl Render for OutlinePanel { MouseButton::Right, cx.listener(move |outline_panel, event: &MouseDownEvent, window, cx| { if let Some(entry) = outline_panel.selected_entry().cloned() { - outline_panel.deploy_context_menu(event.position, entry, window, cx) + outline_panel.deploy_context_menu( + event.absolute_position, + entry, + window, + cx, + ) } else if let Some(entry) = outline_panel.fs_entries.first().cloned() { outline_panel.deploy_context_menu( - event.position, + event.absolute_position, PanelEntry::Fs(entry), window, cx, diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index cf83d7b72692ad..54be22b0fa44ca 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -3633,7 +3633,7 @@ impl ProjectPanel { }) .on_drag_move::(cx.listener( move |this, event: &DragMoveEvent, window, cx| { - if event.bounds.contains(&event.event.position) { + if event.bounds.contains(&event.event.absolute_position) { if this.last_selection_drag_over_entry == Some(entry_id) { return; } @@ -4025,7 +4025,7 @@ impl ProjectPanel { if !this.marked_entries.contains(&selection) { this.marked_entries.clear(); } - this.deploy_context_menu(event.position, entry_id, window, cx); + this.deploy_context_menu(event.absolute_position, entry_id, window, cx); }, )) .overflow_x(), @@ -4482,7 +4482,7 @@ impl Render for ProjectPanel { // When deploying the context menu anywhere below the last project entry, // act as if the user clicked the root of the last worktree. if let Some(entry_id) = this.last_worktree_root_id { - this.deploy_context_menu(event.position, entry_id, window, cx); + this.deploy_context_menu(event.absolute_position, entry_id, window, cx); } }), ) diff --git a/crates/terminal_view/src/terminal_view.rs b/crates/terminal_view/src/terminal_view.rs index 6d2540940ad595..8abb5e0bb7b1b4 100644 --- a/crates/terminal_view/src/terminal_view.rs +++ b/crates/terminal_view/src/terminal_view.rs @@ -1160,7 +1160,7 @@ impl Render for TerminalView { MouseButton::Right, cx.listener(|this, event: &MouseDownEvent, window, cx| { if !this.terminal.read(cx).mouse_mode(event.modifiers.shift) { - this.deploy_context_menu(event.position, window, cx); + this.deploy_context_menu(event.absolute_position, window, cx); cx.notify(); } }), diff --git a/crates/title_bar/src/title_bar.rs b/crates/title_bar/src/title_bar.rs index 9f430585c41656..751e28a9283ea5 100644 --- a/crates/title_bar/src/title_bar.rs +++ b/crates/title_bar/src/title_bar.rs @@ -239,7 +239,9 @@ impl Render for TitleBar { .when(supported_controls.window_menu, |titlebar| { titlebar.on_mouse_down( gpui::MouseButton::Right, - move |ev, window, _| window.show_window_menu(ev.position), + move |ev, window, _| { + window.show_window_menu(ev.absolute_position) + }, ) }) .on_mouse_move(cx.listener(move |this, _ev, window, _| { diff --git a/crates/ui/src/components/indent_guides.rs b/crates/ui/src/components/indent_guides.rs index 87c390ef77d2e7..d6a10138b5f41f 100644 --- a/crates/ui/src/components/indent_guides.rs +++ b/crates/ui/src/components/indent_guides.rs @@ -140,7 +140,6 @@ mod uniform_list { fn compute( &self, visible_range: Range, - bounds: Bounds, item_height: Pixels, item_count: usize, window: &mut Window, @@ -160,7 +159,7 @@ mod uniform_list { visible_range.start, includes_trailing_indent, ); - let mut indent_guides = if let Some(ref custom_render) = self.render_fn { + let indent_guides = if let Some(ref custom_render) = self.render_fn { let params = RenderIndentGuideParams { indent_guides, indent_size: self.indent_size, @@ -184,12 +183,6 @@ mod uniform_list { }) .collect() }; - for guide in &mut indent_guides { - guide.bounds.origin += bounds.origin; - if let Some(hitbox) = guide.hitbox.as_mut() { - hitbox.origin += bounds.origin; - } - } let indent_guides = IndentGuidesElement { indent_guides: Rc::new(indent_guides),