From 9977785de65ae96cbe3af34755ba138484b41f01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B7=E7=94=B5=E8=8A=BD=E8=A1=A3?= Date: Sun, 4 Oct 2026 15:53:30 -0400 Subject: [PATCH] =?UTF-8?q?Navigation=20=C2=B7=20Tabs=20on=5Fcontext:=20a?= =?UTF-8?q?=20right=20click=20on=20a=20tab=20reports=20that=20tab=20and=20?= =?UTF-8?q?the=20pointer,=20not=20the=20chosen=20one?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/navigation/tabs.rs | 26 +++++++++++++++++++++++++- src/navigation/tests.rs | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/src/navigation/tabs.rs b/src/navigation/tabs.rs index 9d8489d..d3c7e4b 100644 --- a/src/navigation/tabs.rs +++ b/src/navigation/tabs.rs @@ -2,7 +2,7 @@ use std::rc::Rc; use gpui::{ Animation, AnimationExt, AnyElement, App, ElementId, FontWeight, InteractiveElement, - IntoElement, MouseButton, ParentElement, RenderOnce, Role, SharedString, + IntoElement, MouseButton, ParentElement, Pixels, Point, RenderOnce, Role, SharedString, StatefulInteractiveElement, Styled, Window, div, prelude::*, }; @@ -23,6 +23,7 @@ pub enum TabPlacement { } type OnValue = Rc; +type OnContext = Rc, &mut Window, &mut App)>; /// A strip of tabs and the chosen tab's panel. A line slides to the chosen tab; arrows move it. #[derive(IntoElement)] @@ -33,6 +34,7 @@ pub struct Tabs { placement: TabPlacement, panel: Option, on_change: Option, + on_context: Option, } impl Tabs { @@ -48,6 +50,7 @@ impl Tabs { placement: TabPlacement::default(), panel: None, on_change: None, + on_context: None, } } @@ -71,6 +74,17 @@ impl Tabs { } } +impl Tabs { + /// A right click on a tab: its value and the pointer. + pub fn on_context( + mut self, + handler: impl Fn(&SharedString, Point, &mut Window, &mut App) + 'static, + ) -> Self { + self.on_context = Some(Rc::new(handler)); + self + } +} + impl RenderOnce for Tabs { fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement { let values: Vec = self.tabs.iter().map(|tab| tab.value.clone()).collect(); @@ -148,6 +162,8 @@ impl RenderOnce for Tabs { (false, false) => colors.fg_muted, }; let pick = pick.clone(); + let context = self.on_context.clone().filter(|_| !tab.disabled); + let value = tab.value.clone(); div() .id(("tab", ix)) .role(Role::Tab) @@ -185,6 +201,14 @@ impl RenderOnce for Tabs { } }) }) + .when_some(context, |item, context| { + item.on_mouse_down(MouseButton::Right, move |event, window, cx| { + window.prevent_default(); + cx.stop_propagation(); + log::info!("tabs: context on {value}"); + context(&value, event.position, window, cx); + }) + }) .when_some(tab.icon, |item, icon| { item.child(Icon::new(icon).size(IconSize::Sm).color(fg)) }) diff --git a/src/navigation/tests.rs b/src/navigation/tests.rs index ec9615d..bb3a4d5 100644 --- a/src/navigation/tests.rs +++ b/src/navigation/tests.rs @@ -470,3 +470,41 @@ fn steps_fill_a_column_their_block_measures_by_content(cx: &mut TestAppContext) "the steps span the card inside its padding" ); } + +struct Context_ { + chosen: SharedString, + asked: Option, +} + +impl Render for Context_ { + fn render(&mut self, _: &mut Window, cx: &mut Context) -> impl IntoElement { + let view = cx.entity(); + let tabs = [Choice::new("a", "A"), Choice::new("c", "C")]; + Tabs::new("tabs", tabs, self.chosen.clone()).on_context(move |value, _, _, cx| { + view.update(cx, |view, cx| { + view.asked = Some(value.clone()); + cx.notify(); + }) + }) + } +} + +#[gpui::test] +fn a_right_click_names_its_tab_and_keeps_the_choice(cx: &mut TestAppContext) { + setup(cx); + let (view, cx) = cx.add_window_view(|_, _| Context_ { + chosen: "c".into(), + asked: None, + }); + settle(cx); + let at = point(px(12.0), px(16.0)); + cx.simulate_mouse_move(at, None, Modifiers::none()); + cx.simulate_mouse_down(at, gpui::MouseButton::Right, Modifiers::none()); + let (chosen, asked) = view.read_with(cx, |view, _| (view.chosen.clone(), view.asked.clone())); + assert_eq!( + asked.as_deref(), + Some("a"), + "the clicked tab, not the chosen one" + ); + assert_eq!(chosen, "c"); +}