@@ -522,6 +522,20 @@ impl Interactivity {
}));
}
+ /// Bind the given callback to non-primary click events of this element.
+ /// The imperative API equivalent to [`StatefulInteractiveElement::on_aux_click`].
+ ///
+ /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
+ pub fn on_aux_click(&mut self, listener: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static)
+ where
+ Self: Sized,
+ {
+ self.aux_click_listeners
+ .push(Rc::new(move |event, window, cx| {
+ listener(event, window, cx)
+ }));
+ }
+
/// On drag initiation, this callback will be used to create a new view to render the dragged value for a
/// drag and drop operation. This API should also be used as the equivalent of 'on drag start' with
/// the [`Self::on_drag_move`] API.
@@ -1190,6 +1204,21 @@ pub trait StatefulInteractiveElement: InteractiveElement {
self
}
+ /// Bind the given callback to non-primary click events of this element.
+ /// The fluent API equivalent to [`Interactivity::on_aux_click`].
+ ///
+ /// See [`Context::listener`](crate::Context::listener) to get access to a view's state from this callback.
+ fn on_aux_click(
+ mut self,
+ listener: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static,
+ ) -> Self
+ where
+ Self: Sized,
+ {
+ self.interactivity().on_aux_click(listener);
+ self
+ }
+
/// On drag initiation, this callback will be used to create a new view to render the dragged value for a
/// drag and drop operation. This API should also be used as the equivalent of 'on drag start' with
/// the [`InteractiveElement::on_drag_move`] API.
@@ -1622,6 +1651,7 @@ pub struct Interactivity {
pub(crate) drop_listeners: Vec<(TypeId, DropListener)>,
pub(crate) can_drop_predicate: Option<CanDropPredicate>,
pub(crate) click_listeners: Vec<ClickListener>,
+ pub(crate) aux_click_listeners: Vec<ClickListener>,
pub(crate) drag_listener: Option<(Arc<dyn Any>, DragListener)>,
pub(crate) hover_listener: Option<Box<dyn Fn(&bool, &mut Window, &mut App)>>,
pub(crate) tooltip_builder: Option<TooltipBuilder>,
@@ -1815,6 +1845,7 @@ impl Interactivity {
|| !self.mouse_down_listeners.is_empty()
|| !self.mouse_move_listeners.is_empty()
|| !self.click_listeners.is_empty()
+ || !self.aux_click_listeners.is_empty()
|| !self.scroll_wheel_listeners.is_empty()
|| self.drag_listener.is_some()
|| !self.drop_listeners.is_empty()
@@ -2237,6 +2268,7 @@ impl Interactivity {
let mut drag_listener = mem::take(&mut self.drag_listener);
let drop_listeners = mem::take(&mut self.drop_listeners);
let click_listeners = mem::take(&mut self.click_listeners);
+ let aux_click_listeners = mem::take(&mut self.aux_click_listeners);
let can_drop_predicate = mem::take(&mut self.can_drop_predicate);
if !drop_listeners.is_empty() {
@@ -2273,7 +2305,10 @@ impl Interactivity {
}
if let Some(element_state) = element_state {
- if !click_listeners.is_empty() || drag_listener.is_some() {
+ if !click_listeners.is_empty()
+ || !aux_click_listeners.is_empty()
+ || drag_listener.is_some()
+ {
let pending_mouse_down = element_state
.pending_mouse_down
.get_or_insert_with(Default::default)
@@ -2287,9 +2322,10 @@ impl Interactivity {
window.on_mouse_event({
let pending_mouse_down = pending_mouse_down.clone();
let hitbox = hitbox.clone();
+ let has_aux_click_listeners = !aux_click_listeners.is_empty();
move |event: &MouseDownEvent, phase, window, _cx| {
if phase == DispatchPhase::Bubble
- && event.button == MouseButton::Left
+ && (event.button == MouseButton::Left || has_aux_click_listeners)
&& hitbox.is_hovered(window)
{
*pending_mouse_down.borrow_mut() = Some(event.clone());
@@ -2311,6 +2347,7 @@ impl Interactivity {
&& !cx.has_active_drag()
&& (event.position - mouse_down.position).magnitude() > DRAG_THRESHOLD
&& let Some((drag_value, drag_listener)) = drag_listener.take()
+ && mouse_down.button == MouseButton::Left
{
*clicked_state.borrow_mut() = ElementClickedState::default();
let cursor_offset = event.position - hitbox.origin;
@@ -2387,12 +2424,24 @@ impl Interactivity {
// Fire click handlers during the bubble phase.
DispatchPhase::Bubble => {
if let Some(mouse_down) = captured_mouse_down.take() {
+ let btn = mouse_down.button;
+
let mouse_click = ClickEvent::Mouse(MouseClickEvent {
down: mouse_down,
up: event.clone(),
});
- for listener in &click_listeners {
- listener(&mouse_click, window, cx);
+
+ match btn {
+ MouseButton::Left => {
+ for listener in &click_listeners {
+ listener(&mouse_click, window, cx);
+ }
+ }
+ _ => {
+ for listener in &aux_click_listeners {
+ listener(&mouse_click, window, cx);
+ }
+ }
}
}
}
@@ -2790,8 +2790,6 @@ impl Pane {
let item_handle = item.boxed_clone();
move |pane: &mut Self, event: &ClickEvent, window, cx| {
if event.click_count() > 1 {
- // On double-click, dispatch the Rename action (when available)
- // instead of just activating the item.
pane.unpreview_item_if_preview(item_id);
let extra_actions = item_handle.tab_extra_context_menu_actions(window, cx);
if let Some((_, action)) = extra_actions
@@ -2809,10 +2807,12 @@ impl Pane {
pane.activate_item(ix, true, true, window, cx)
}
}))
- // TODO: This should be a click listener with the middle mouse button instead of a mouse down listener.
- .on_mouse_down(
- MouseButton::Middle,
- cx.listener(move |pane, _event, window, cx| {
+ .on_aux_click(
+ cx.listener(move |pane: &mut Self, event: &ClickEvent, window, cx| {
+ if !event.is_middle_click() {
+ return;
+ }
+
pane.close_item_by_id(item_id, SaveIntent::Close, window, cx)
.detach_and_log_err(cx);
}),