diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b8501ee..256b157c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -88,24 +88,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 command once, the same as after a restart. - **The sidebar groups tabs by repo automatically; pin what you want to keep** - (#955). Groups now come in two halves, split by a divider. Below it, every - tab you have not pinned is filed under its git repository, and an SSH tab - under the host it is on rather than its remote path — `/home/ubuntu` on two - machines used to share a header. Above it sit the groups you keep, in the - order you drag them into, until you delete them. A pinned group can keep a - folder: a tab whose working directory enters it joins it (the deepest folder - wins when they nest, and a worktree of a pinned repo counts), while a tab you - drag out stays out until it leaves the folder and comes back. Pin an auto - group with the pin on its header or by dragging the header above the - divider; pin a folder by dropping it from Finder, with **Pin as Group** in the - Files panel, or with **Open Folder as Group…** in the palette. **New Group** - in the palette or on a tab's right-click makes a label group. Deleting a - group closes nothing — its tabs go back to auto grouping — and dragging a tab - below the divider does the same for one tab. Groups, their order and which - are folded are stored with the workspace, so every window onto it agrees. - **Settings → Window & Tabs → Auto grouping** replaces the three-way *Sidebar - grouping* choice; off, unpinned tabs sit in one flat list under the pinned - groups. Scratch is now **Ungrouped**. + (#955). Groups now come in two halves. Below, every tab you have not pinned is + filed under its git repository, and an SSH tab under the host it is on rather + than its remote path — `/home/ubuntu` on two machines used to share a header. + Above sit the groups you keep, each marked ◆ beside its name, in the order you + drag them into, until you delete them. A pinned group can keep a folder: a tab + whose working directory enters it joins it (the deepest folder wins when they + nest, and a worktree of a pinned repo counts), while a tab you drag out stays + out until it leaves the folder and comes back. Pin an auto group with the ◆ on + its header or by dragging the header up among the pinned ones; pin a folder by + dropping it from Finder, with **Pin as Group** in the Files panel, or with + **Open Folder as Group…** in the palette. **New Group** in the palette or on a + tab's right-click makes a label group. Deleting a group closes nothing — its + tabs go back to auto grouping — and dragging a tab below the pinned groups + does the same for one tab. Groups, their order and which are folded are stored + with the workspace, so every window onto it agrees. **Settings → Window & Tabs + → Auto grouping** replaces the three-way *Sidebar grouping* choice; off, + unpinned tabs sit in one flat list under the pinned groups. Scratch is now + **Ungrouped**. - **The New Tab menu names three shells, not every one the machine has.** A stock macOS box reports nine, so the `+` menu opened on a column of `csh`, diff --git a/assets/icons/pin.svg b/assets/icons/pin.svg deleted file mode 100644 index 05568e0d..00000000 --- a/assets/icons/pin.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/docs/window/sidebar.mdx b/docs/window/sidebar.mdx index a5c32cd5..e53b214d 100644 --- a/docs/window/sidebar.mdx +++ b/docs/window/sidebar.mdx @@ -18,17 +18,19 @@ is doing. The sidebar groups tabs by repository automatically. Pin what you want to keep. -**Auto groups** sit below a divider. Every tab you have not pinned is filed -under the git repository its working directory is in — a linked worktree under -the repository it belongs to, a submodule under itself — and an SSH tab under -the host it is connected to, so `/home/ubuntu` on two machines is two groups. +**Auto groups** sit below the pinned ones. Every tab you have not pinned is +filed under the git repository its working directory is in — a linked worktree +under the repository it belongs to, a submodule under itself — and an SSH tab +under the host it is connected to, so `/home/ubuntu` on two machines is two +groups. Everything else collects in a trailing **Ungrouped** section. An auto group follows the tab's working directory, not its history: `cd` into another repository and the row moves; switching branches never does. When its last tab leaves, the group is gone. -**Pinned groups** sit above the divider, in the order you put them, and stay -until you delete them — an empty one keeps its place with a **+ New Tab** row. +**Pinned groups** sit at the top, each marked ◆ beside its name, in the order +you put them, and stay until you delete them — an empty one keeps its place +with a **+ New Tab** row. A tab in a pinned group never leaves it on its own. There are two kinds: - A **folder group** keeps a directory. A tab whose working directory *enters* @@ -36,7 +38,7 @@ A tab in a pinned group never leaves it on its own. There are two kinds: repository the folder is. When folders nest, the deepest one wins, so pinning a monorepo and one package in it files the package's tabs under the package. A tab you drag out while it is still inside stays out until it leaves the - folder and comes back. Hover the header for the folder's path; click its pin + folder and comes back. Hover the header for the folder's path; click its ◆ to unpin it, and its tabs go back to auto groups. - A **label group** is just a name, for tabs that belong together for a reason no directory shows. Tabs go in and out of it by hand. @@ -45,7 +47,7 @@ Ways to pin: | To get | Do this | |---|---| -| A folder group from an auto group | Click the pin on its header, or drag the header above the divider | +| A folder group from an auto group | Click the ◆ on its header, or drag the header up among the pinned groups | | A folder group from a folder | Drop it from Finder onto the sidebar, choose **Pin as Group** on it in the Files panel, or run **Open Folder as Group…** from the command palette | | A label group | Right-click a tab → **Move to Group → New Group…**, or run **New Group** from the command palette | @@ -92,8 +94,8 @@ they simply stop opening the overlay. Drag a row to reorder it within its group, or drag a group header to move the group — pinned groups among pinned groups, auto groups among auto groups. Drag -a row onto a pinned group to put it there, or anywhere below the divider to -hand it back to auto grouping. A row cannot be dragged into an auto group: its +a row onto a pinned group to put it there, or anywhere below the pinned groups +to hand it back to auto grouping. A row cannot be dragged into an auto group: its membership comes from the working directory, so `cd` is what moves it. Drag a row out over the panes instead and it stops being a session of its own: diff --git a/src/ui/assets.rs b/src/ui/assets.rs index 73ffca40..1731232b 100644 --- a/src/ui/assets.rs +++ b/src/ui/assets.rs @@ -34,7 +34,6 @@ fn agent_icon(path: &str) -> Option<&'static [u8]> { "icons/panel-left.svg" => include_bytes!("../../assets/icons/panel-left.svg"), "icons/panel-right.svg" => include_bytes!("../../assets/icons/panel-right.svg"), "icons/plus.svg" => include_bytes!("../../assets/icons/plus.svg"), - "icons/pin.svg" => include_bytes!("../../assets/icons/pin.svg"), "icons/ellipsis.svg" => include_bytes!("../../assets/icons/ellipsis.svg"), "icons/folder-closed.svg" => include_bytes!("../../assets/icons/folder-closed.svg"), "icons/folder-open.svg" => include_bytes!("../../assets/icons/folder-open.svg"), diff --git a/src/ui/tab_sidebar.rs b/src/ui/tab_sidebar.rs index e5274f79..4bf498fc 100644 --- a/src/ui/tab_sidebar.rs +++ b/src/ui/tab_sidebar.rs @@ -34,6 +34,17 @@ const GRAB_HANDLE_W: f32 = 48.; const ROW_GAP: f32 = 2.; +/// What marks a kept group, and what the header button that keeps one reads. +/// +/// A character rather than an icon: at a header's size a drawn pin is a +/// smudge, and the mark only has to say "kept" beside the name, the way a +/// bullet does. With no line between kept and derived groups it is also the +/// only thing that tells the two apart, so every kept group carries it. +const PIN_MARK: &str = "\u{25C6}"; + +/// The mark's size: small enough to sit under the header text's x-height. +const PIN_MARK_SIZE: f32 = 8.; + /// A single-line tab row: one line of `text_sm` and a little air, the same /// 28px the search field and the workspace chip above it stand at. const ROW_HEIGHT: f32 = 28.; @@ -1283,37 +1294,39 @@ impl Tty7App { .child(Icon::new(IconName::ChevronRight).xsmall()), ) }) - // The pin marks a folder group: kept, and keeping a folder. - // It is also the way to stop keeping it — a click unpins, - // and the folder's tabs fall back to the groups their cwds - // resolve to. A label group has no such mark: it sits above - // the divider, and there is no folder to fall back on. - .when_some( - pinned_id.filter(|_| pinned_folder.is_some()), - |header, id| { - header.child( - div() - .id(("sidebar-group-unpin", group_ix)) - .flex_shrink_0() - .cursor_pointer() - .hover(|s| s.text_color(cx.theme().foreground)) - .child(Icon::empty().path("icons/pin.svg").xsmall()) - .tooltip(|window, cx| { - gpui_component::tooltip::Tooltip::new(t( - L10nKey::SidebarUnpinGroup, - )) - .build(window, cx) - }) - .on_mouse_down(MouseButton::Left, |_, _, cx| { - cx.stop_propagation() - }) - .on_click(cx.listener(move |this, _, _window, cx| { - cx.stop_propagation(); - this.delete_group(id, cx); - })), - ) - }, - ) + // Every kept group carries the mark; it is what sets them + // apart from the derived groups below. On a folder group it + // is also the way to stop keeping it — a click unpins, and + // the folder's tabs fall back to the groups their cwds + // resolve to. A label group's mark is only a mark: without a + // folder there is nothing to fall back on, so letting go of + // it is Delete Group, in its menu, not a stray click. + .when_some(pinned_id, |header, id| { + let mark = div() + .flex_shrink_0() + .text_size(px(PIN_MARK_SIZE)) + .line_height(px(PIN_MARK_SIZE)) + .child(PIN_MARK); + header.child(match pinned_folder.is_some() { + false => mark.into_any_element(), + true => mark + .id(("sidebar-group-unpin", group_ix)) + .cursor_pointer() + .hover(|s| s.text_color(cx.theme().foreground)) + .tooltip(|window, cx| { + gpui_component::tooltip::Tooltip::new(t( + L10nKey::SidebarUnpinGroup, + )) + .build(window, cx) + }) + .on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation()) + .on_click(cx.listener(move |this, _, _window, cx| { + cx.stop_propagation(); + this.delete_group(id, cx); + })) + .into_any_element(), + }) + }) .child(match renaming_group { Some(input) => div() .id(("sidebar-group-rename", group_ix)) @@ -1823,8 +1836,14 @@ impl Tty7App { .child(handle) } - /// The line between the kept groups and the derived ones, recording where - /// it was drawn so a drag next frame can tell which side it is on. + /// The boundary between the kept groups and the derived ones, recording + /// where it lies so a drag next frame can tell which side it is on. + /// + /// Not drawn at rest — the mark on each kept header already says which + /// side a group is on, and a hairline across the list was one more rule + /// for the eye to cross. It keeps its place in the layout all the same, so + /// nothing jumps when a drag starts, and shows as a line only while it is + /// the target: when letting go would pin a header or hand a tab back. /// /// With nothing pinned yet it only appears while an auto header is being /// carried, and then as a drop zone rather than a hairline: a one-pixel @@ -1833,9 +1852,10 @@ impl Tty7App { /// back to auto grouping. fn sidebar_divider(&self, lit: bool, zone: bool, cx: &Context) -> AnyElement { let bounds = self.sidebar_divider.clone(); - let ink = match lit { - true => cx.theme().drag_border, - false => cx.theme().sidebar_border, + let ink = match (lit, zone) { + (true, _) => cx.theme().drag_border, + (false, true) => cx.theme().sidebar_border, + (false, false) => gpui::transparent_black(), }; let body = match zone { true => h_flex() @@ -1862,7 +1882,9 @@ impl Tty7App { .relative() .w_full() .px_2() - .pt(px(10.)) + // Less than when it was a drawn rule: the next header's own top + // margin already opens the gap between the two kinds of group. + .pt(px(4.)) .pb(px(2.)) .child( canvas(move |b, _window, _cx| bounds.set(Some(b)), |_, _, _, _| {}) @@ -1895,7 +1917,7 @@ impl Tty7App { fade.a = 0.; let muted = cx.theme().muted_foreground; let ink = cx.theme().foreground; - let button = |id: &'static str, path: &'static str, tip: L10nKey| { + let button = |id: &'static str, glyph: AnyElement, tip: L10nKey| { div() .id((id, group_ix)) .flex_shrink_0() @@ -1904,7 +1926,7 @@ impl Tty7App { .cursor_pointer() .text_color(muted) .hover(move |s| s.text_color(ink)) - .child(Icon::empty().path(path).xsmall()) + .child(glyph) .tooltip(move |window, cx| { gpui_component::tooltip::Tooltip::new(t(tip)).build(window, cx) }) @@ -1914,7 +1936,19 @@ impl Tty7App { buttons = buttons.child( button( "sidebar-group-pin", - "icons/pin.svg", + // The mark the group will carry once kept, the size of + // the icon beside it so the two buttons line up. + div() + .size(px(12.)) + .flex() + .items_center() + .justify_center() + .text_size(px(PIN_MARK_SIZE)) + // Its own line box: the header's is several times the + // mark's size and would drop it below the "+" beside it. + .line_height(px(PIN_MARK_SIZE)) + .child(PIN_MARK) + .into_any_element(), L10nKey::SidebarPinGroup, ) .on_mouse_down( @@ -1930,7 +1964,10 @@ impl Tty7App { buttons = buttons.child( button( "sidebar-group-add", - "icons/plus.svg", + Icon::empty() + .path("icons/plus.svg") + .xsmall() + .into_any_element(), L10nKey::SidebarGroupNewTab, ) .on_mouse_down(