add cmd-click-to-jump-to-element

This commit is contained in:
Mikayla 2023-12-13 18:15:40 -08:00
parent a4024b297e
commit 0dd84924e4
No known key found for this signature in database
6 changed files with 169 additions and 17 deletions

View file

@ -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,
}
}
}

View file

@ -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,

View file

@ -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);

View file

@ -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
}

View file

@ -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
}

View file

@ -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() {