From 1eb50fbdbae240c0151d4b4dd2be0fb4681375ea Mon Sep 17 00:00:00 2001 From: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com> Date: Sun, 26 Jul 2026 18:10:31 +0800 Subject: [PATCH] fix(files): move the dotfile switch into the tree's right-click menu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The eye tile spent the panel's scarcest row — the header the tab tiles now share — on an option that gets set once and then forgotten. It moves into the menu the tree rows already have, as a row under "Reveal in Finder" whose label says what the click will do ("Show Dotfiles" / "Hide Dotfiles") rather than checking off the current state, since one checked item makes `PopupMenu` reserve an icon gutter on every row in the menu. The SFTP browser's header keeps its own controls: Refresh and the "⋯" menu are actions on a live connection, not a view option. --- src/ui/file_tree.rs | 40 ++++++++++++++++++++++++++++++++++++++-- src/ui/right_panel.rs | 31 ++++--------------------------- 2 files changed, 42 insertions(+), 29 deletions(-) diff --git a/src/ui/file_tree.rs b/src/ui/file_tree.rs index ae4dfa7f..770500a3 100644 --- a/src/ui/file_tree.rs +++ b/src/ui/file_tree.rs @@ -1247,9 +1247,18 @@ impl Tty7App { let app = cx.entity().downgrade(); let path = path.clone(); let is_root = row.is_root; + let show_hidden = self.file_tree.show_hidden; move |menu, _window, cx| { let danger = cx.theme().danger; - Self::tree_row_context_menu(menu, &path, is_dir, is_root, danger, &app) + Self::tree_row_context_menu( + menu, + &path, + is_dir, + is_root, + show_hidden, + danger, + &app, + ) } }); @@ -1279,12 +1288,16 @@ impl Tty7App { out } - /// The per-row right-click menu, mirroring Warp's Project Explorer set. + /// The per-row right-click menu, mirroring Warp's Project Explorer set, plus + /// the tree's one view option (dotfiles) — which lives here rather than as a + /// header button: it is set once and then forgotten, and a tile in the header + /// spends the panel's scarcest row on it forever. fn tree_row_context_menu( menu: PopupMenu, path: &Path, is_dir: bool, is_root: bool, + show_hidden: bool, danger: gpui::Hsla, app: &gpui::WeakEntity, ) -> PopupMenu { @@ -1379,6 +1392,8 @@ impl Tty7App { } })); + menu = menu.separator().item(dotfiles_menu_item(show_hidden, app)); + if !is_root { menu = menu.separator().item( PopupMenuItem::element(move |_window, _cx| { @@ -1397,6 +1412,27 @@ impl Tty7App { } } +/// The tree's dotfile switch as a row of the tree's existing right-click menu. +/// +/// The label states what the click will do rather than checking off the current +/// state: a single checked item makes `PopupMenu` reserve a left icon gutter on +/// *every* row in the menu (see `tab_strip::window_chrome`), and the menu has a +/// dozen rows with nothing to put in one. +fn dotfiles_menu_item(show_hidden: bool, app: &gpui::WeakEntity) -> PopupMenuItem { + let app = app.clone(); + PopupMenuItem::new(if show_hidden { + "Hide Dotfiles" + } else { + "Show Dotfiles" + }) + .on_click(move |_, _window, cx| { + let _ = app.update(cx, |this, cx| { + this.file_tree.show_hidden = !this.file_tree.show_hidden; + cx.notify(); + }); + }) +} + /// The little drag ghost shown while a row is dragged toward a terminal. struct DragGhost { name: String, diff --git a/src/ui/right_panel.rs b/src/ui/right_panel.rs index 2ee6d791..bf38b2af 100644 --- a/src/ui/right_panel.rs +++ b/src/ui/right_panel.rs @@ -432,31 +432,6 @@ impl Tty7App { .into_any_element() } - /// The Files header's one control. No refresh button: the tree runs a - /// recursive filesystem watcher over its roots and invalidates its own caches, - /// so a manual refresh is a button that does what already happened. - fn files_controls(&self, cx: &mut Context) -> AnyElement { - let show_hidden = self.file_tree.show_hidden; - crate::ui::tab_strip::chrome_tile_sized( - Button::new("panel-tree-hidden").icon(Icon::new(IconName::Eye)), - TILE_SIZE_SM, - TILE_GLYPH_SM, - show_hidden, - cx, - ) - .rounded_md() - .tooltip(if show_hidden { - "Hide dotfiles" - } else { - "Show dotfiles" - }) - .on_click(cx.listener(|this, _, _w, cx| { - this.file_tree.show_hidden = !this.file_tree.show_hidden; - cx.notify(); - })) - .into_any_element() - } - /// A tab's filter box — the same borderless magnifier + input the tab rail /// uses, so everything in the window searches the same way. Sits under the /// header rather than in it: it's a full-width control, not a trailing tile. @@ -1334,8 +1309,10 @@ impl Tty7App { return self.render_panel_sftp(host.unwrap_or_default(), window, cx); } - let controls = self.files_controls(cx); - let title = self.panel_title("Files", None, Some(controls), cx); + // No header control: the tree's one view option (dotfiles) is a + // right-click away in the tree itself (`file_tree::dotfiles_menu_item`), + // which is where you are when you want it. + let title = self.panel_title("Files", None, None, cx); let search = self.panel_search(&self.file_search.clone(), cx); let rows = self.render_file_tree_rows(window, cx); v_flex()