add cmd-click-to-jump-to-element
This commit is contained in:
parent
a4024b297e
commit
0dd84924e4
6 changed files with 169 additions and 17 deletions
|
@ -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() {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue