Detailed changes
@@ -71,6 +71,40 @@ pub trait StatelessInteractive<V: 'static>: Element<V> {
self
}
+ fn on_any_mouse_down(
+ mut self,
+ handler: impl Fn(&mut V, &MouseDownEvent, &mut ViewContext<V>) + 'static,
+ ) -> Self
+ where
+ Self: Sized,
+ {
+ self.stateless_interactivity()
+ .mouse_down_listeners
+ .push(Box::new(move |view, event, bounds, phase, cx| {
+ if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
+ handler(view, event, cx)
+ }
+ }));
+ self
+ }
+
+ fn on_any_mouse_up(
+ mut self,
+ handler: impl Fn(&mut V, &MouseUpEvent, &mut ViewContext<V>) + 'static,
+ ) -> Self
+ where
+ Self: Sized,
+ {
+ self.stateless_interactivity()
+ .mouse_up_listeners
+ .push(Box::new(move |view, event, bounds, phase, cx| {
+ if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
+ handler(view, event, cx)
+ }
+ }));
+ self
+ }
+
fn on_mouse_up(
mut self,
button: MouseButton,
@@ -111,7 +145,6 @@ pub trait StatelessInteractive<V: 'static>: Element<V> {
fn on_mouse_up_out(
mut self,
- button: MouseButton,
handler: impl Fn(&mut V, &MouseUpEvent, &mut ViewContext<V>) + 'static,
) -> Self
where
@@ -120,10 +153,7 @@ pub trait StatelessInteractive<V: 'static>: Element<V> {
self.stateless_interactivity()
.mouse_up_listeners
.push(Box::new(move |view, event, bounds, phase, cx| {
- if phase == DispatchPhase::Capture
- && event.button == button
- && !bounds.contains_point(&event.position)
- {
+ if phase == DispatchPhase::Capture && !bounds.contains_point(&event.position) {
handler(view, event, cx);
}
}));
@@ -101,6 +101,12 @@ pub struct FocusHandle {
handles: Arc<RwLock<SlotMap<FocusId, AtomicUsize>>>,
}
+impl std::fmt::Debug for FocusHandle {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.write_fmt(format_args!("FocusHandle({:?})", self.id))
+ }
+}
+
impl FocusHandle {
pub(crate) fn new(handles: &Arc<RwLock<SlotMap<FocusId, AtomicUsize>>>) -> Self {
let id = handles.write().insert(AtomicUsize::new(1));
@@ -1,7 +1,7 @@
use editor::Editor;
use gpui::{
- div, uniform_list, Component, Div, ParentElement, Render, StatelessInteractive, Styled, Task,
- UniformListScrollHandle, View, ViewContext, VisualContext, WindowContext,
+ div, uniform_list, Component, Div, MouseButton, ParentElement, Render, StatelessInteractive,
+ Styled, Task, UniformListScrollHandle, View, ViewContext, VisualContext, WindowContext,
};
use std::{cmp, sync::Arc};
use ui::{prelude::*, v_stack, Divider, Label, LabelColor};
@@ -11,6 +11,7 @@ pub struct Picker<D: PickerDelegate> {
scroll_handle: UniformListScrollHandle,
editor: View<Editor>,
pending_update_matches: Option<Task<()>>,
+ confirm_on_update: Option<bool>,
}
pub trait PickerDelegate: Sized + 'static {
@@ -44,9 +45,10 @@ impl<D: PickerDelegate> Picker<D> {
cx.subscribe(&editor, Self::on_input_editor_event).detach();
let mut this = Self {
delegate,
+ editor,
scroll_handle: UniformListScrollHandle::new(),
pending_update_matches: None,
- editor,
+ confirm_on_update: None,
};
this.update_matches("".to_string(), cx);
this
@@ -101,11 +103,26 @@ impl<D: PickerDelegate> Picker<D> {
}
fn confirm(&mut self, _: &menu::Confirm, cx: &mut ViewContext<Self>) {
- self.delegate.confirm(false, cx);
+ if self.pending_update_matches.is_some() {
+ self.confirm_on_update = Some(false)
+ } else {
+ self.delegate.confirm(false, cx);
+ }
}
fn secondary_confirm(&mut self, _: &menu::SecondaryConfirm, cx: &mut ViewContext<Self>) {
- self.delegate.confirm(true, cx);
+ if self.pending_update_matches.is_some() {
+ self.confirm_on_update = Some(true)
+ } else {
+ self.delegate.confirm(true, cx);
+ }
+ }
+
+ fn handle_click(&mut self, ix: usize, secondary: bool, cx: &mut ViewContext<Self>) {
+ cx.stop_propagation();
+ cx.prevent_default();
+ self.delegate.set_selected_index(ix, cx);
+ self.delegate.confirm(secondary, cx);
}
fn on_input_editor_event(
@@ -136,6 +153,9 @@ impl<D: PickerDelegate> Picker<D> {
let index = self.delegate.selected_index();
self.scroll_handle.scroll_to_item(index);
self.pending_update_matches = None;
+ if let Some(secondary) = self.confirm_on_update.take() {
+ self.delegate.confirm(secondary, cx);
+ }
cx.notify();
}
}
@@ -173,7 +193,22 @@ impl<D: PickerDelegate> Render for Picker<D> {
let selected_ix = this.delegate.selected_index();
visible_range
.map(|ix| {
- this.delegate.render_match(ix, ix == selected_ix, cx)
+ div()
+ .on_mouse_down(
+ MouseButton::Left,
+ move |this: &mut Self, event, cx| {
+ this.handle_click(
+ ix,
+ event.modifiers.command,
+ cx,
+ )
+ },
+ )
+ .child(this.delegate.render_match(
+ ix,
+ ix == selected_ix,
+ cx,
+ ))
})
.collect()
}
@@ -186,10 +221,11 @@ impl<D: PickerDelegate> Render for Picker<D> {
})
.when(self.delegate.match_count() == 0, |el| {
el.child(
- v_stack()
- .p_1()
- .grow()
- .child(Label::new("No matches").color(LabelColor::Muted)),
+ v_stack().p_1().grow().child(
+ div()
+ .px_1()
+ .child(Label::new("No matches").color(LabelColor::Muted)),
+ ),
)
})
}
@@ -32,6 +32,7 @@ impl KeyBinding {
div()
.flex()
.gap_1()
+ .when(keystroke.modifiers.function, |el| el.child(Key::new("fn")))
.when(keystroke.modifiers.control, |el| el.child(Key::new("^")))
.when(keystroke.modifiers.alt, |el| el.child(Key::new("⌥")))
.when(keystroke.modifiers.command, |el| el.child(Key::new("⌘")))
@@ -136,6 +137,7 @@ mod stories {
.child(KeyBinding::new(binding("a z")))
.child(Story::label(cx, "Chord with Modifier"))
.child(KeyBinding::new(binding("ctrl-a shift-z")))
+ .child(KeyBinding::new(binding("fn-s")))
}
}
}
@@ -2,7 +2,7 @@ use gpui::{
div, px, AnyView, Div, EventEmitter, FocusHandle, ParentElement, Render, StatelessInteractive,
Styled, Subscription, View, ViewContext, VisualContext, WindowContext,
};
-use ui::v_stack;
+use ui::{h_stack, v_stack};
pub struct ActiveModal {
modal: AnyView,
@@ -33,8 +33,6 @@ impl ModalLayer {
V: Modal,
B: FnOnce(&mut ViewContext<V>) -> V,
{
- let previous_focus = cx.focused();
-
if let Some(active_modal) = &self.active_modal {
let is_close = active_modal.modal.clone().downcast::<V>().is_ok();
self.hide_modal(cx);
@@ -85,9 +83,6 @@ impl Render for ModalLayer {
div()
.absolute()
- .flex()
- .flex_col()
- .items_center()
.size_full()
.top_0()
.left_0()
@@ -96,11 +91,21 @@ impl Render for ModalLayer {
v_stack()
.h(px(0.0))
.top_20()
+ .flex()
+ .flex_col()
+ .items_center()
.track_focus(&active_modal.focus_handle)
- .on_mouse_down_out(|this: &mut Self, event, cx| {
- this.hide_modal(cx);
- })
- .child(active_modal.modal.clone()),
+ .child(
+ h_stack()
+ // needed to prevent mouse events leaking to the
+ // UI below. // todo! for gpui3.
+ .on_any_mouse_down(|_, _, cx| cx.stop_propagation())
+ .on_any_mouse_up(|_, _, cx| cx.stop_propagation())
+ .on_mouse_down_out(|this: &mut Self, event, cx| {
+ this.hide_modal(cx);
+ })
+ .child(active_modal.modal.clone()),
+ ),
)
}
}