mirror of
https://github.com/zed-industries/zed.git
synced 2024-12-28 20:01:33 +00:00
Tooltips in mouse event handler & fix executor timer
Co-Authored-By: Conrad Irwin <conrad@zed.dev>
This commit is contained in:
parent
f97046b86f
commit
3834e26f71
6 changed files with 67 additions and 59 deletions
|
@ -20,6 +20,7 @@ fn generate_dispatch_bindings() {
|
|||
.header("src/platform/mac/dispatch.h")
|
||||
.allowlist_var("_dispatch_main_q")
|
||||
.allowlist_var("DISPATCH_QUEUE_PRIORITY_DEFAULT")
|
||||
.allowlist_var("DISPATCH_TIME_NOW")
|
||||
.allowlist_function("dispatch_get_global_queue")
|
||||
.allowlist_function("dispatch_async_f")
|
||||
.allowlist_function("dispatch_after_f")
|
||||
|
|
|
@ -17,7 +17,6 @@ use std::{
|
|||
ops::Deref,
|
||||
path::PathBuf,
|
||||
sync::Arc,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
const DRAG_THRESHOLD: f64 = 2.;
|
||||
|
@ -602,13 +601,14 @@ pub trait ElementInteraction<V: 'static>: 'static {
|
|||
|
||||
if let Some(hover_listener) = stateful.hover_listener.take() {
|
||||
let was_hovered = element_state.hover_state.clone();
|
||||
let has_mouse_down = element_state.pending_mouse_down.lock().is_some();
|
||||
let has_mouse_down = element_state.pending_mouse_down.clone();
|
||||
|
||||
cx.on_mouse_event(move |view_state, event: &MouseMoveEvent, phase, cx| {
|
||||
if phase != DispatchPhase::Bubble {
|
||||
return;
|
||||
}
|
||||
let is_hovered = bounds.contains_point(&event.position) && !has_mouse_down;
|
||||
let is_hovered =
|
||||
bounds.contains_point(&event.position) && has_mouse_down.lock().is_none();
|
||||
let mut was_hovered = was_hovered.lock();
|
||||
|
||||
if is_hovered != was_hovered.clone() {
|
||||
|
@ -620,33 +620,30 @@ pub trait ElementInteraction<V: 'static>: 'static {
|
|||
});
|
||||
}
|
||||
|
||||
// if we're hovered:
|
||||
// if no timer, start timer
|
||||
// if timer hits 1s, call tooltip_builder()
|
||||
//
|
||||
if let Some(tooltip_builder) = stateful.tooltip_builder.take() {
|
||||
let tooltip_view = element_state.tooltip_view.clone();
|
||||
let pending_mouse_down = element_state.pending_mouse_down.clone();
|
||||
|
||||
if let Some(tooltip_builder) = &stateful.tooltip_builder {
|
||||
let mut active_tooltip = element_state.active_tooltip.lock();
|
||||
let is_hovered = bounds.contains_point(&cx.mouse_position())
|
||||
&& !element_state.pending_mouse_down.lock().is_some();
|
||||
cx.on_mouse_event(move |view_state, event: &MouseMoveEvent, phase, cx| {
|
||||
if phase != DispatchPhase::Bubble {
|
||||
return;
|
||||
}
|
||||
|
||||
if is_hovered {
|
||||
if let Some(active_tooltip) = active_tooltip {
|
||||
active_tooltip.view = Some(tooltip_builder(cx))
|
||||
let is_hovered = bounds.contains_point(&event.position)
|
||||
&& pending_mouse_down.lock().is_none();
|
||||
let mut tooltip_view = tooltip_view.lock();
|
||||
|
||||
if is_hovered {
|
||||
if tooltip_view.is_none() {
|
||||
*tooltip_view = Some(tooltip_builder(view_state, cx));
|
||||
}
|
||||
} else {
|
||||
*active_tooltip = Some(ActiveTooltip {
|
||||
hover_start: Instant::now(),
|
||||
view: None,
|
||||
});
|
||||
tooltip_view.take();
|
||||
}
|
||||
} else {
|
||||
active_tooltip.take();
|
||||
}
|
||||
});
|
||||
|
||||
if let Some(active_tooltip) = element_state.active_tooltip.lock().as_ref() {
|
||||
if *element_state.hover_state.lock() {
|
||||
cx.active_tooltip = Some(active_tooltip.clone());
|
||||
}
|
||||
if let Some(active_tooltip) = element_state.tooltip_view.lock().as_ref() {
|
||||
cx.active_tooltip = Some(active_tooltip.clone());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -837,12 +834,7 @@ pub struct InteractiveElementState {
|
|||
hover_state: Arc<Mutex<bool>>,
|
||||
pending_mouse_down: Arc<Mutex<Option<MouseDownEvent>>>,
|
||||
scroll_offset: Option<Arc<Mutex<Point<Pixels>>>>,
|
||||
active_tooltip: Arc<Mutex<Option<ActiveTooltip>>>,
|
||||
}
|
||||
|
||||
pub struct ActiveTooltip {
|
||||
hover_start: Instant,
|
||||
view: Option<AnyView>,
|
||||
tooltip_view: Arc<Mutex<Option<AnyView>>>,
|
||||
}
|
||||
|
||||
impl InteractiveElementState {
|
||||
|
@ -1194,7 +1186,7 @@ pub(crate) type DragListener<V> =
|
|||
|
||||
pub(crate) type HoverListener<V> = Box<dyn Fn(&mut V, bool, &mut ViewContext<V>) + 'static>;
|
||||
|
||||
pub(crate) type TooltipBuilder<V> = Arc<dyn Fn(&mut ViewContext<V>) -> AnyView + 'static>;
|
||||
pub(crate) type TooltipBuilder<V> = Arc<dyn Fn(&mut V, &mut ViewContext<V>) -> AnyView + 'static>;
|
||||
|
||||
pub type KeyListener<V> = Box<
|
||||
dyn Fn(
|
||||
|
|
|
@ -11,11 +11,7 @@ use objc::{
|
|||
};
|
||||
use parking::{Parker, Unparker};
|
||||
use parking_lot::Mutex;
|
||||
use std::{
|
||||
ffi::c_void,
|
||||
sync::Arc,
|
||||
time::{Duration, SystemTime},
|
||||
};
|
||||
use std::{ffi::c_void, sync::Arc, time::Duration};
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/dispatch_sys.rs"));
|
||||
|
||||
|
@ -62,16 +58,10 @@ impl PlatformDispatcher for MacDispatcher {
|
|||
}
|
||||
|
||||
fn dispatch_after(&self, duration: Duration, runnable: Runnable) {
|
||||
let now = SystemTime::now();
|
||||
let after_duration = now
|
||||
.duration_since(SystemTime::UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_nanos() as u64
|
||||
+ duration.as_nanos() as u64;
|
||||
unsafe {
|
||||
let queue =
|
||||
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT.try_into().unwrap(), 0);
|
||||
let when = dispatch_time(0, after_duration as i64);
|
||||
let when = dispatch_time(DISPATCH_TIME_NOW as u64, duration.as_nanos() as i64);
|
||||
dispatch_after_f(
|
||||
when,
|
||||
queue,
|
||||
|
|
|
@ -989,11 +989,14 @@ impl<'a> WindowContext<'a> {
|
|||
});
|
||||
} else if let Some(active_tooltip) = self.app.active_tooltip.take() {
|
||||
self.stack(1, |cx| {
|
||||
cx.with_element_offset(Some(cx.mouse_position()), |cx| {
|
||||
let available_space =
|
||||
size(AvailableSpace::MinContent, AvailableSpace::MinContent);
|
||||
active_tooltip.draw(available_space, cx);
|
||||
});
|
||||
cx.with_element_offset(
|
||||
Some(cx.mouse_position() + Point::new(px(8.0), px(8.0))),
|
||||
|cx| {
|
||||
let available_space =
|
||||
size(AvailableSpace::MinContent, AvailableSpace::MinContent);
|
||||
active_tooltip.draw(available_space, cx);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -1,20 +1,44 @@
|
|||
use std::time::Duration;
|
||||
|
||||
use gpui2::{
|
||||
div, px, Div, ParentElement, Render, SharedString, Styled, View, ViewContext, VisualContext,
|
||||
div, px, Component, Div, ParentElement, Render, SharedString, Styled, View, ViewContext,
|
||||
VisualContext, WindowContext,
|
||||
};
|
||||
use theme2::ActiveTheme;
|
||||
|
||||
const DELAY: Duration = Duration::from_millis(500);
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct TextTooltip {
|
||||
title: SharedString,
|
||||
visible: bool,
|
||||
}
|
||||
|
||||
impl TextTooltip {
|
||||
pub fn new(str: SharedString) -> Self {
|
||||
Self { title: str }
|
||||
Self {
|
||||
title: str,
|
||||
visible: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_view<C: VisualContext>(str: SharedString, cx: &mut C) -> C::Result<View<Self>> {
|
||||
cx.build_view(|cx| TextTooltip::new(str))
|
||||
pub fn build_view(str: SharedString, cx: &mut WindowContext) -> View<Self> {
|
||||
let view = cx.build_view(|cx| TextTooltip::new(str));
|
||||
|
||||
let handle = view.downgrade();
|
||||
cx.spawn(|mut cx| async move {
|
||||
cx.background_executor().timer(DELAY).await;
|
||||
|
||||
handle
|
||||
.update(&mut cx, |this, cx| {
|
||||
this.visible = true;
|
||||
cx.notify();
|
||||
})
|
||||
.ok();
|
||||
})
|
||||
.detach();
|
||||
|
||||
view
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -24,9 +48,11 @@ impl Render for TextTooltip {
|
|||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
let theme = cx.theme();
|
||||
div()
|
||||
.when(!self.visible, |this| this.invisible())
|
||||
.bg(theme.colors().background)
|
||||
.rounded(px(8.))
|
||||
.border()
|
||||
.font("Zed Sans")
|
||||
.border_color(theme.colors().border)
|
||||
.text_color(theme.colors().text)
|
||||
.pl_2()
|
||||
|
|
|
@ -9,8 +9,8 @@ use crate::{
|
|||
use anyhow::Result;
|
||||
use collections::{HashMap, HashSet, VecDeque};
|
||||
use gpui::{
|
||||
AnyView, AppContext, AsyncWindowContext, Component, Div, EntityId, EventEmitter, FocusHandle,
|
||||
Model, PromptLevel, Render, Task, View, ViewContext, VisualContext, WeakView, WindowContext,
|
||||
AppContext, AsyncWindowContext, Component, Div, EntityId, EventEmitter, FocusHandle, Model,
|
||||
PromptLevel, Render, Task, View, ViewContext, VisualContext, WeakView, WindowContext,
|
||||
};
|
||||
use parking_lot::Mutex;
|
||||
use project2::{Project, ProjectEntryId, ProjectPath};
|
||||
|
@ -1398,13 +1398,9 @@ impl Pane {
|
|||
.group("")
|
||||
.id(item.id())
|
||||
.cursor_pointer()
|
||||
.on_hover(|_, hovered, _| {
|
||||
dbg!(hovered);
|
||||
})
|
||||
.when_some(item.tab_tooltip_text(cx), |div, text| {
|
||||
div.tooltip(move |_, cx| TextTooltip::build_view(text.clone(), cx))
|
||||
})
|
||||
// .tooltip(|pane, cx| cx.build_view(|cx| div().child(title)))
|
||||
// .on_drag(move |pane, cx| pane.render_tab(ix, item.boxed_clone(), detail, cx))
|
||||
// .drag_over::<DraggedTab>(|d| d.bg(cx.theme().colors().element_drop_target))
|
||||
// .on_drop(|_view, state: View<DraggedTab>, cx| {
|
||||
|
|
Loading…
Reference in a new issue