Detailed changes
@@ -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<dyn Fn(&MouseMoveEvent, &mut WindowContext) + '
pub type ActionListener = Box<dyn Fn(&dyn Any, DispatchPhase, &mut WindowContext) + 'static>;
+#[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<DragListener>,
pub hover_listener: Option<Box<dyn Fn(&bool, &mut WindowContext)>>,
pub tooltip_builder: Option<TooltipBuilder>,
+
+ #[cfg(debug_assertions)]
+ pub location: Option<core::panic::Location<'static>>,
}
#[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,
}
}
}
@@ -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<I, R, V>(
view: View<V>,
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,
@@ -147,6 +147,7 @@ pub trait PlatformWindow {
fn appearance(&self) -> WindowAppearance;
fn display(&self) -> Rc<dyn PlatformDisplay>;
fn mouse_position(&self) -> Point<Pixels>;
+ fn modifiers(&self) -> Modifiers;
fn as_any_mut(&mut self) -> &mut dyn Any;
fn set_input_handler(&mut self, input_handler: Box<dyn PlatformInputHandler>);
fn clear_input_handler(&mut self);
@@ -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
}
@@ -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
}
@@ -231,6 +231,7 @@ pub struct Window {
pub(crate) blur_listeners: SubscriberSet<(), AnyObserver>,
default_prevented: bool,
mouse_position: Point<Pixels>,
+ modifiers: Modifiers,
requested_cursor_style: Option<CursorStyle>,
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() {