diff --git a/crates/gpui2/src/elements/div.rs b/crates/gpui2/src/elements/div.rs index bd19842feeca597577efb28ea80bea7e69833b34..4f18d5884c2ae315dcd8e918abcbddf25eb82b84 100644 --- a/crates/gpui2/src/elements/div.rs +++ b/crates/gpui2/src/elements/div.rs @@ -6,6 +6,7 @@ use crate::{ SharedString, Size, StackingOrder, Style, StyleRefinement, Styled, Task, View, Visibility, WindowContext, }; + use collections::HashMap; use refineable::Refineable; use smallvec::SmallVec; @@ -526,11 +527,19 @@ pub type DragEventListener = Box; +#[track_caller] pub fn div() -> Div { - Div { + let mut div = Div { interactivity: Interactivity::default(), children: SmallVec::default(), + }; + + #[cfg(debug_assertions)] + { + div.interactivity.location = Some(*core::panic::Location::caller()); } + + div } pub struct Div { @@ -708,6 +717,9 @@ pub struct Interactivity { pub drag_listener: Option, pub hover_listener: Option>, pub tooltip_builder: Option, + + #[cfg(debug_assertions)] + pub location: Option>, } #[derive(Clone, Debug)] @@ -775,6 +787,96 @@ impl Interactivity { const FONT_SIZE: crate::Pixels = crate::Pixels(10.); let element_id = format!("{:?}", self.element_id.unwrap()); let str_len = element_id.len(); + + let render_debug_text = |cx: &mut WindowContext| { + if let Some(text) = cx + .text_system() + .shape_text( + &element_id, + FONT_SIZE, + &[cx.text_style().to_run(str_len)], + None, + ) + .ok() + .map(|mut text| text.pop()) + .flatten() + { + text.paint(bounds.origin, FONT_SIZE, cx).ok(); + + let text_bounds = crate::Bounds { + origin: bounds.origin, + size: text.size(FONT_SIZE), + }; + if self.location.is_some() + && text_bounds.contains(&cx.mouse_position()) + && cx.modifiers().command + { + let command_held = cx.modifiers().command; + cx.on_key_event({ + let text_bounds = text_bounds.clone(); + move |e: &crate::ModifiersChangedEvent, _phase, cx| { + if e.modifiers.command != command_held + && text_bounds.contains(&cx.mouse_position()) + { + cx.notify(); + } + } + }); + + let hovered = bounds.contains(&cx.mouse_position()); + cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| { + if phase == DispatchPhase::Capture { + if bounds.contains(&event.position) != hovered { + cx.notify(); + } + } + }); + + cx.on_mouse_event({ + let location = self.location.clone().unwrap(); + let text_bounds = text_bounds.clone(); + move |e: &crate::MouseDownEvent, phase, cx| { + if text_bounds.contains(&e.position) && phase.capture() { + cx.stop_propagation(); + let Ok(dir) = std::env::current_dir() else { + return; + }; + + eprintln!( + "This element is created at:\n{}:{}:{}", + location.file(), + location.line(), + location.column() + ); + + std::process::Command::new("zed") + .arg(format!( + "{}/{}:{}:{}", + dir.to_string_lossy(), + location.file(), + location.line(), + location.column() + )) + .spawn() + .ok(); + } + } + }); + cx.paint_quad(crate::outline( + crate::Bounds { + origin: bounds.origin + + crate::point(crate::px(0.), FONT_SIZE - px(2.)), + size: crate::Size { + width: text_bounds.size.width, + height: crate::px(1.), + }, + }, + crate::red(), + )) + } + } + }; + cx.with_z_index(1, |cx| { cx.with_text_style( Some(crate::TextStyleRefinement { @@ -783,18 +885,7 @@ impl Interactivity { background_color: Some(crate::white()), ..Default::default() }), - |cx| { - if let Ok(text) = cx.text_system().shape_text( - &element_id, - FONT_SIZE, - &[cx.text_style().to_run(str_len)], - None, - ) { - if let Some(text) = text.first() { - text.paint(bounds.origin, FONT_SIZE, cx).ok(); - } - } - }, + render_debug_text, ) }); } @@ -1290,6 +1381,9 @@ impl Default for Interactivity { drag_listener: None, hover_listener: None, tooltip_builder: None, + + #[cfg(debug_assertions)] + location: None, } } } diff --git a/crates/gpui2/src/elements/uniform_list.rs b/crates/gpui2/src/elements/uniform_list.rs index bddfc8c9a19e70d7649b6fc3f2338fd666ecb2d3..debd365c87da2da4fbf402dc6bf4188267c191ee 100644 --- a/crates/gpui2/src/elements/uniform_list.rs +++ b/crates/gpui2/src/elements/uniform_list.rs @@ -10,6 +10,7 @@ use taffy::style::Overflow; /// uniform_list provides lazy rendering for a set of items that are of uniform height. /// When rendered into a container with overflow-y: hidden and a fixed (or max) height, /// uniform_list will only render the visible subset of items. +#[track_caller] pub fn uniform_list( view: View, id: I, @@ -42,6 +43,10 @@ where interactivity: Interactivity { element_id: Some(id.into()), base_style: Box::new(base_style), + + #[cfg(debug_assertions)] + location: Some(*core::panic::Location::caller()), + ..Default::default() }, scroll_handle: None, diff --git a/crates/gpui2/src/platform.rs b/crates/gpui2/src/platform.rs index b9d5a9f2226a757160aea93aacf06c1ca8f0207e..e7ee5f9e29876193451610d54e89dce227c1f5ca 100644 --- a/crates/gpui2/src/platform.rs +++ b/crates/gpui2/src/platform.rs @@ -147,6 +147,7 @@ pub trait PlatformWindow { fn appearance(&self) -> WindowAppearance; fn display(&self) -> Rc; fn mouse_position(&self) -> Point; + fn modifiers(&self) -> Modifiers; fn as_any_mut(&mut self) -> &mut dyn Any; fn set_input_handler(&mut self, input_handler: Box); fn clear_input_handler(&mut self); diff --git a/crates/gpui2/src/platform/mac/window.rs b/crates/gpui2/src/platform/mac/window.rs index dcdf616ffedcb2d052987779be6b91dd2a3e8bc8..12189e198a76343ad9478ffad2ec550b9cd0e9f0 100644 --- a/crates/gpui2/src/platform/mac/window.rs +++ b/crates/gpui2/src/platform/mac/window.rs @@ -9,9 +9,10 @@ use crate::{ use block::ConcreteBlock; use cocoa::{ appkit::{ - CGPoint, NSApplication, NSBackingStoreBuffered, NSFilenamesPboardType, NSPasteboard, - NSScreen, NSView, NSViewHeightSizable, NSViewWidthSizable, NSWindow, NSWindowButton, - NSWindowCollectionBehavior, NSWindowStyleMask, NSWindowTitleVisibility, + CGPoint, NSApplication, NSBackingStoreBuffered, NSEventModifierFlags, + NSFilenamesPboardType, NSPasteboard, NSScreen, NSView, NSViewHeightSizable, + NSViewWidthSizable, NSWindow, NSWindowButton, NSWindowCollectionBehavior, + NSWindowStyleMask, NSWindowTitleVisibility, }, base::{id, nil}, foundation::{ @@ -744,6 +745,26 @@ impl PlatformWindow for MacWindow { convert_mouse_position(position, self.content_size().height) } + fn modifiers(&self) -> Modifiers { + unsafe { + let modifiers: NSEventModifierFlags = msg_send![class!(NSEvent), modifierFlags]; + + let control = modifiers.contains(NSEventModifierFlags::NSControlKeyMask); + let alt = modifiers.contains(NSEventModifierFlags::NSAlternateKeyMask); + let shift = modifiers.contains(NSEventModifierFlags::NSShiftKeyMask); + let command = modifiers.contains(NSEventModifierFlags::NSCommandKeyMask); + let function = modifiers.contains(NSEventModifierFlags::NSFunctionKeyMask); + + Modifiers { + control, + alt, + shift, + command, + function, + } + } + } + fn as_any_mut(&mut self) -> &mut dyn Any { self } diff --git a/crates/gpui2/src/platform/test/window.rs b/crates/gpui2/src/platform/test/window.rs index 5af990514f52aa3d2c17f57acca4e1e38d83d1c3..0f981d44783ed2434361d639234ebed2c1ceb85f 100644 --- a/crates/gpui2/src/platform/test/window.rs +++ b/crates/gpui2/src/platform/test/window.rs @@ -79,6 +79,10 @@ impl PlatformWindow for TestWindow { Point::default() } + fn modifiers(&self) -> crate::Modifiers { + crate::Modifiers::default() + } + fn as_any_mut(&mut self) -> &mut dyn std::any::Any { self } diff --git a/crates/gpui2/src/window.rs b/crates/gpui2/src/window.rs index 554a6d6e6bf0ad17b137323a3ab8ddb920927433..ff15389bc13a9aaec4d672933dc8b22ad6c2f092 100644 --- a/crates/gpui2/src/window.rs +++ b/crates/gpui2/src/window.rs @@ -231,6 +231,7 @@ pub struct Window { pub(crate) blur_listeners: SubscriberSet<(), AnyObserver>, default_prevented: bool, mouse_position: Point, + modifiers: Modifiers, requested_cursor_style: Option, scale_factor: f32, bounds: WindowBounds, @@ -302,6 +303,7 @@ impl Window { let display_id = platform_window.display().id(); let sprite_atlas = platform_window.sprite_atlas(); let mouse_position = platform_window.mouse_position(); + let modifiers = platform_window.modifiers(); let content_size = platform_window.content_size(); let scale_factor = platform_window.scale_factor(); let bounds = platform_window.bounds(); @@ -365,6 +367,7 @@ impl Window { blur_listeners: SubscriberSet::new(), default_prevented: true, mouse_position, + modifiers, requested_cursor_style: None, scale_factor, bounds, @@ -877,6 +880,11 @@ impl<'a> WindowContext<'a> { self.window.mouse_position } + /// The current state of the keyboard's modifiers + pub fn modifiers(&self) -> Modifiers { + self.window.modifiers + } + pub fn set_cursor_style(&mut self, style: CursorStyle) { self.window.requested_cursor_style = Some(style) } @@ -1336,16 +1344,35 @@ impl<'a> WindowContext<'a> { // API for the mouse position can only occur on the main thread. InputEvent::MouseMove(mouse_move) => { self.window.mouse_position = mouse_move.position; + self.window.modifiers = mouse_move.modifiers; InputEvent::MouseMove(mouse_move) } InputEvent::MouseDown(mouse_down) => { self.window.mouse_position = mouse_down.position; + self.window.modifiers = mouse_down.modifiers; InputEvent::MouseDown(mouse_down) } InputEvent::MouseUp(mouse_up) => { self.window.mouse_position = mouse_up.position; + self.window.modifiers = mouse_up.modifiers; InputEvent::MouseUp(mouse_up) } + InputEvent::MouseExited(mouse_exited) => { + // todo!("Should we record that the mouse is outside of the window somehow? Or are these global pixels?") + self.window.mouse_position = mouse_exited.position; + self.window.modifiers = mouse_exited.modifiers; + + InputEvent::MouseExited(mouse_exited) + } + InputEvent::ModifiersChanged(modifiers_changed) => { + self.window.modifiers = modifiers_changed.modifiers; + InputEvent::ModifiersChanged(modifiers_changed) + } + InputEvent::ScrollWheel(scroll_wheel) => { + self.window.mouse_position = scroll_wheel.position; + self.window.modifiers = scroll_wheel.modifiers; + InputEvent::ScrollWheel(scroll_wheel) + } // Translate dragging and dropping of external files from the operating system // to internal drag and drop events. InputEvent::FileDrop(file_drop) => match file_drop { @@ -1388,7 +1415,7 @@ impl<'a> WindowContext<'a> { click_count: 1, }), }, - _ => event, + InputEvent::KeyDown(_) | InputEvent::KeyUp(_) => event, }; if let Some(any_mouse_event) = event.mouse_event() {