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,
|
SharedString, Size, StackingOrder, Style, StyleRefinement, Styled, Task, View, Visibility,
|
||||||
WindowContext,
|
WindowContext,
|
||||||
};
|
};
|
||||||
|
|
||||||
use collections::HashMap;
|
use collections::HashMap;
|
||||||
use refineable::Refineable;
|
use refineable::Refineable;
|
||||||
use smallvec::SmallVec;
|
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>;
|
pub type ActionListener = Box<dyn Fn(&dyn Any, DispatchPhase, &mut WindowContext) + 'static>;
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
pub fn div() -> Div {
|
pub fn div() -> Div {
|
||||||
Div {
|
let mut div = Div {
|
||||||
interactivity: Interactivity::default(),
|
interactivity: Interactivity::default(),
|
||||||
children: SmallVec::default(),
|
children: SmallVec::default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
{
|
||||||
|
div.interactivity.location = Some(*core::panic::Location::caller());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Div {
|
pub struct Div {
|
||||||
|
@ -708,6 +717,9 @@ pub struct Interactivity {
|
||||||
pub drag_listener: Option<DragListener>,
|
pub drag_listener: Option<DragListener>,
|
||||||
pub hover_listener: Option<Box<dyn Fn(&bool, &mut WindowContext)>>,
|
pub hover_listener: Option<Box<dyn Fn(&bool, &mut WindowContext)>>,
|
||||||
pub tooltip_builder: Option<TooltipBuilder>,
|
pub tooltip_builder: Option<TooltipBuilder>,
|
||||||
|
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
pub location: Option<core::panic::Location<'static>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
@ -775,6 +787,96 @@ impl Interactivity {
|
||||||
const FONT_SIZE: crate::Pixels = crate::Pixels(10.);
|
const FONT_SIZE: crate::Pixels = crate::Pixels(10.);
|
||||||
let element_id = format!("{:?}", self.element_id.unwrap());
|
let element_id = format!("{:?}", self.element_id.unwrap());
|
||||||
let str_len = element_id.len();
|
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_z_index(1, |cx| {
|
||||||
cx.with_text_style(
|
cx.with_text_style(
|
||||||
Some(crate::TextStyleRefinement {
|
Some(crate::TextStyleRefinement {
|
||||||
|
@ -783,18 +885,7 @@ impl Interactivity {
|
||||||
background_color: Some(crate::white()),
|
background_color: Some(crate::white()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
|cx| {
|
render_debug_text,
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1290,6 +1381,9 @@ impl Default for Interactivity {
|
||||||
drag_listener: None,
|
drag_listener: None,
|
||||||
hover_listener: None,
|
hover_listener: None,
|
||||||
tooltip_builder: 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.
|
/// 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,
|
/// 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.
|
/// uniform_list will only render the visible subset of items.
|
||||||
|
#[track_caller]
|
||||||
pub fn uniform_list<I, R, V>(
|
pub fn uniform_list<I, R, V>(
|
||||||
view: View<V>,
|
view: View<V>,
|
||||||
id: I,
|
id: I,
|
||||||
|
@ -42,6 +43,10 @@ where
|
||||||
interactivity: Interactivity {
|
interactivity: Interactivity {
|
||||||
element_id: Some(id.into()),
|
element_id: Some(id.into()),
|
||||||
base_style: Box::new(base_style),
|
base_style: Box::new(base_style),
|
||||||
|
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
location: Some(*core::panic::Location::caller()),
|
||||||
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
scroll_handle: None,
|
scroll_handle: None,
|
||||||
|
|
|
@ -147,6 +147,7 @@ pub trait PlatformWindow {
|
||||||
fn appearance(&self) -> WindowAppearance;
|
fn appearance(&self) -> WindowAppearance;
|
||||||
fn display(&self) -> Rc<dyn PlatformDisplay>;
|
fn display(&self) -> Rc<dyn PlatformDisplay>;
|
||||||
fn mouse_position(&self) -> Point<Pixels>;
|
fn mouse_position(&self) -> Point<Pixels>;
|
||||||
|
fn modifiers(&self) -> Modifiers;
|
||||||
fn as_any_mut(&mut self) -> &mut dyn Any;
|
fn as_any_mut(&mut self) -> &mut dyn Any;
|
||||||
fn set_input_handler(&mut self, input_handler: Box<dyn PlatformInputHandler>);
|
fn set_input_handler(&mut self, input_handler: Box<dyn PlatformInputHandler>);
|
||||||
fn clear_input_handler(&mut self);
|
fn clear_input_handler(&mut self);
|
||||||
|
|
|
@ -9,9 +9,10 @@ use crate::{
|
||||||
use block::ConcreteBlock;
|
use block::ConcreteBlock;
|
||||||
use cocoa::{
|
use cocoa::{
|
||||||
appkit::{
|
appkit::{
|
||||||
CGPoint, NSApplication, NSBackingStoreBuffered, NSFilenamesPboardType, NSPasteboard,
|
CGPoint, NSApplication, NSBackingStoreBuffered, NSEventModifierFlags,
|
||||||
NSScreen, NSView, NSViewHeightSizable, NSViewWidthSizable, NSWindow, NSWindowButton,
|
NSFilenamesPboardType, NSPasteboard, NSScreen, NSView, NSViewHeightSizable,
|
||||||
NSWindowCollectionBehavior, NSWindowStyleMask, NSWindowTitleVisibility,
|
NSViewWidthSizable, NSWindow, NSWindowButton, NSWindowCollectionBehavior,
|
||||||
|
NSWindowStyleMask, NSWindowTitleVisibility,
|
||||||
},
|
},
|
||||||
base::{id, nil},
|
base::{id, nil},
|
||||||
foundation::{
|
foundation::{
|
||||||
|
@ -744,6 +745,26 @@ impl PlatformWindow for MacWindow {
|
||||||
convert_mouse_position(position, self.content_size().height)
|
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 {
|
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,6 +79,10 @@ impl PlatformWindow for TestWindow {
|
||||||
Point::default()
|
Point::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn modifiers(&self) -> crate::Modifiers {
|
||||||
|
crate::Modifiers::default()
|
||||||
|
}
|
||||||
|
|
||||||
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
|
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
|
@ -231,6 +231,7 @@ pub struct Window {
|
||||||
pub(crate) blur_listeners: SubscriberSet<(), AnyObserver>,
|
pub(crate) blur_listeners: SubscriberSet<(), AnyObserver>,
|
||||||
default_prevented: bool,
|
default_prevented: bool,
|
||||||
mouse_position: Point<Pixels>,
|
mouse_position: Point<Pixels>,
|
||||||
|
modifiers: Modifiers,
|
||||||
requested_cursor_style: Option<CursorStyle>,
|
requested_cursor_style: Option<CursorStyle>,
|
||||||
scale_factor: f32,
|
scale_factor: f32,
|
||||||
bounds: WindowBounds,
|
bounds: WindowBounds,
|
||||||
|
@ -302,6 +303,7 @@ impl Window {
|
||||||
let display_id = platform_window.display().id();
|
let display_id = platform_window.display().id();
|
||||||
let sprite_atlas = platform_window.sprite_atlas();
|
let sprite_atlas = platform_window.sprite_atlas();
|
||||||
let mouse_position = platform_window.mouse_position();
|
let mouse_position = platform_window.mouse_position();
|
||||||
|
let modifiers = platform_window.modifiers();
|
||||||
let content_size = platform_window.content_size();
|
let content_size = platform_window.content_size();
|
||||||
let scale_factor = platform_window.scale_factor();
|
let scale_factor = platform_window.scale_factor();
|
||||||
let bounds = platform_window.bounds();
|
let bounds = platform_window.bounds();
|
||||||
|
@ -365,6 +367,7 @@ impl Window {
|
||||||
blur_listeners: SubscriberSet::new(),
|
blur_listeners: SubscriberSet::new(),
|
||||||
default_prevented: true,
|
default_prevented: true,
|
||||||
mouse_position,
|
mouse_position,
|
||||||
|
modifiers,
|
||||||
requested_cursor_style: None,
|
requested_cursor_style: None,
|
||||||
scale_factor,
|
scale_factor,
|
||||||
bounds,
|
bounds,
|
||||||
|
@ -877,6 +880,11 @@ impl<'a> WindowContext<'a> {
|
||||||
self.window.mouse_position
|
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) {
|
pub fn set_cursor_style(&mut self, style: CursorStyle) {
|
||||||
self.window.requested_cursor_style = Some(style)
|
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.
|
// API for the mouse position can only occur on the main thread.
|
||||||
InputEvent::MouseMove(mouse_move) => {
|
InputEvent::MouseMove(mouse_move) => {
|
||||||
self.window.mouse_position = mouse_move.position;
|
self.window.mouse_position = mouse_move.position;
|
||||||
|
self.window.modifiers = mouse_move.modifiers;
|
||||||
InputEvent::MouseMove(mouse_move)
|
InputEvent::MouseMove(mouse_move)
|
||||||
}
|
}
|
||||||
InputEvent::MouseDown(mouse_down) => {
|
InputEvent::MouseDown(mouse_down) => {
|
||||||
self.window.mouse_position = mouse_down.position;
|
self.window.mouse_position = mouse_down.position;
|
||||||
|
self.window.modifiers = mouse_down.modifiers;
|
||||||
InputEvent::MouseDown(mouse_down)
|
InputEvent::MouseDown(mouse_down)
|
||||||
}
|
}
|
||||||
InputEvent::MouseUp(mouse_up) => {
|
InputEvent::MouseUp(mouse_up) => {
|
||||||
self.window.mouse_position = mouse_up.position;
|
self.window.mouse_position = mouse_up.position;
|
||||||
|
self.window.modifiers = mouse_up.modifiers;
|
||||||
InputEvent::MouseUp(mouse_up)
|
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
|
// Translate dragging and dropping of external files from the operating system
|
||||||
// to internal drag and drop events.
|
// to internal drag and drop events.
|
||||||
InputEvent::FileDrop(file_drop) => match file_drop {
|
InputEvent::FileDrop(file_drop) => match file_drop {
|
||||||
|
@ -1388,7 +1415,7 @@ impl<'a> WindowContext<'a> {
|
||||||
click_count: 1,
|
click_count: 1,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
_ => event,
|
InputEvent::KeyDown(_) | InputEvent::KeyUp(_) => event,
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(any_mouse_event) = event.mouse_event() {
|
if let Some(any_mouse_event) = event.mouse_event() {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue