Skip to content

Redesign top bar + sidebar, add settings (#5)#62

Open
mvbmir wants to merge 2 commits into
am-will:mainfrom
mvbmir:mvbmir/redesign-top-bar-clean
Open

Redesign top bar + sidebar, add settings (#5)#62
mvbmir wants to merge 2 commits into
am-will:mainfrom
mvbmir:mvbmir/redesign-top-bar-clean

Conversation

@mvbmir
Copy link
Copy Markdown
Contributor

@mvbmir mvbmir commented Apr 17, 2026

Summary

Implements the cmux-style top bar and sidebar redesign plus three related settings and the split-ratio fix. Single commit against main.

Top bar

Custom top bar at the top of the window: sidebar toggle, settings cog, "+" new-workspace button, a row of workspace indicator pills, and the window controls (minimize/maximize/close). Empty space drags the window via gtk::WindowHandle. The window controls are plain gtk::Buttons rather than gtk::WindowControls so their hover shape and sizing match the rest of the icons.

Pane header

  • Empty space between the last tab and the action icons is a WindowHandle filler — drags the window without conflicting with the tabs' DragSource.
  • Middle-click on a tab closes it.
  • Settings cog moved out of the per-pane action row and into the top bar, freeing horizontal tab space.

Sidebar

  • Each row has a close X on the top-right (replaces the old star slot). Star moved below next to the folder path; both are hover-to-reveal 20×20 buttons.
  • Double-click a workspace row to inline-rename.
  • Accent-tinted selected-row background; first-row top margin matches the between-workspace gap.
  • No more "WORKSPACES" title or big bottom "+" button.

New-workspace UX

  • Clicking "+" clones the active workspace's folder. The folder picker only appears on first run / when no workspace exists.

Settings → General (three new rows)

  • Top bar (switch) — when off, the top bar is removed entirely. Its buttons relocate into a new sidebar header (sidebar open) or onto the active workspace's leading pane (sidebar collapsed). Sidebar-header layout puts the app buttons on one end and the window controls on the other with an hexpand spacer between.
  • Window controls side (Left / Right) — moves minimize/maximize/close between the two ends of whichever header is active.
  • Workspace indicators on the top bar (switch) — hides the pills without hiding the top bar. Implementation hides each pill individually so indicator_box keeps its hexpand spacer role.
  • All three persist under interface.* in settings.json. Defaults: show_top_bar = true, show_workspace_indicators = true, window_controls_side = Right — upgrading users see the familiar layout.

Split ratio fix

  • shrink_start_child / shrink_end_child on every gtk::Paned so the saved ratio wins over larger child minimums (e.g. a wide tab strip).
  • position-notify now tracks last_size: width changed → auto-adjust (don't touch ratio); width unchanged → real user drag (update ratio). Fixes the silent 0.5 → 0.6 drift that happened when the sidebar toggled or the window resized — GtkPaned::position is absolute pixels, so the old handler computed a different ratio each time the paned's width changed.
  • Per-frame tick callback observes the paned's actual width and re-applies position = ratio × new_width on size changes. GtkWidget::width / height don't reliably emit notify across GTK 4.x versions, so polling is intentional; the check is O(1) with early return.
  • Startup uses a one-shot tick callback to apply the ratio once the paned first has a non-zero allocation.

Structural

  • handle_config_change() dedupes the appearance + top-bar side-effect + save/revert logic that ran from two config-change sites.
  • apply_top_bar_mode() is the single source of truth for how the dock toggle, settings cog, +, indicator pills, and window controls are laid out — respects both the persistent show_top_bar setting and the transient keyboard toggle.
  • Widget tree walkers (is_pane_widget, find_leaf_focused_pane) unwrap gtk::WindowHandle so the dock-parked-on-pane layout still resolves panes correctly.

Closes #5 (notifications explicitly out of scope per the issue).

Notes for reviewers

  • The add_tick_callback polling in split_tree.rs is intentional. GTK 4.x doesn't reliably emit notify on width/height, so the signal-driven approach silently misses resize events; the tick loop catches them at the cost of one integer compare per frame.
  • gtk::WindowControls is replaced by three plain gtk::Buttons wired to window.minimize() / maximize() / close(). Adwaita's default 24 px circular styling couldn't be cleanly overridden to match the other top-bar icons.

Test plan

  • cargo fmt clean
  • cargo clippy --release -- -D warnings clean
  • cargo test — all 181 unit tests pass
  • Manual: top bar on/off, sidebar open/closed, left/right controls — all combinations
  • Manual: workspace indicators on/off toggles the pills; window controls stay at the right edge regardless
  • Manual: middle-click tab close, last tab closes pane, last pane closes workspace
  • Manual: split ratio stable across sidebar toggles and window resizes
  • Manual: double-click inline rename, folder clone on "+"

mvbmir added 2 commits April 17, 2026 18:04
Top bar:
- Custom top bar at the top of the window with the sidebar toggle,
  settings cog, "+" new-workspace button, a row of workspace indicator
  pills, and the window controls (minimize/maximize/close).
- macOS-style indicator pills: subtle hover/active backgrounds, pill
  shows a small dot + bold when the workspace has unread activity.
- Empty header space drags the window via gtk::WindowHandle.
- Custom buttons replace gtk::WindowControls so hover shape, padding,
  and border-radius match the pane action icons exactly.
- Pane action icons (new terminal, split, close pane) reworked to the
  same compact visual language; tab close X the same.
- Settings cog moved out of the per-pane action row into the top bar,
  freeing horizontal tab space.

Pane header:
- Empty space between the last tab and the action icons is a
  WindowHandle filler, so it drags the window without conflicting
  with the tabs' DragSource.
- Middle-click on a tab closes it.

Sidebar:
- Cleaner workspace rows with a close X on the top-right (replaces the
  star's old position). The favorite star moved below next to the
  folder path; both are hover-to-reveal 20×20 buttons.
- Double-click a row to inline-rename.
- Row selection paints the inner row box with an accent-tinted
  background; Adwaita's default ListBox row styling is suppressed.
- First-row top margin bumped so the gap above the first workspace
  matches the between-workspace gap.
- Drops the "WORKSPACES" title and the big "+" button at the bottom.

New-workspace behavior:
- "+" clones the active workspace's folder instead of opening the
  folder picker. The picker only appears on first run / when no
  workspace exists.

Settings (General tab):
- Top bar (switch) — when off, removes the top bar entirely and
  relocates its buttons: dock toggle, settings cog, "+", and window
  controls move into a new sidebar header (with an hexpand spacer
  between the left group and the right group). When the sidebar is
  collapsed, the dock toggle parks on the active workspace's leading
  pane via a new `leading_box` slot on every pane; the rest of the
  controls stay hidden.
- Window controls side (Left/Right) — moves minimize/maximize/close
  between the two ends of whichever header is active. Applies live.
- Workspace indicators on the top bar (switch) — hides the per-
  workspace pills without hiding the top bar. Implementation hides
  each pill individually so `indicator_box` keeps its hexpand role
  between the left group and the window controls.
- All three persist under `interface.*` in settings.json.

Split ratio fix:
- shrink_start_child / shrink_end_child on every Paned so the saved
  ratio wins over larger child minimums (e.g. wide tab strips).
- position-notify tracks `last_size`; width-changed notifies are
  auto-adjusts (skip ratio update), same-width notifies are real user
  drags (update ratio). Without this, sidebar toggles and window
  resizes silently drifted the ratio because GtkPaned's position is
  absolute pixels.
- Per-frame tick callback observes the paned's actual width and re-
  applies `position = ratio × new_width` on size changes. GtkWidget's
  width/height properties don't reliably emit notify across GTK 4.x
  versions, so polling is intentional; the check is O(1).
- Startup uses a one-shot tick callback to apply the ratio once the
  paned first has a non-zero allocation.

Structural:
- Extracted handle_config_change() to dedupe appearance + top-bar
  side-effect + save/revert logic that ran from two config-change
  sites.
- apply_top_bar_mode() is the single source of truth for how the
  dock toggle, settings cog, +, indicator pills, and window controls
  are laid out. Respects both the persistent `show_top_bar` setting
  and the transient keyboard toggle.
- Dropped the unused on_config_changed field from PaneCallbacks; the
  pane no longer opens the Settings dialog.
- Widget tree walkers (is_pane_widget, find_leaf_focused_pane) unwrap
  gtk::WindowHandle so the dock-parked-on-pane case still resolves
  panes correctly.

Defaults: `show_top_bar = true`, `show_workspace_indicators = true`,
`window_controls_side = Right`. Existing users without these fields
in settings.json get the familiar layout.

Closes #5 (notifications explicitly out of scope per the issue).
Rust 1.95 stable (released between main's last green CI run and this
PR's) promotes collapsible_match to a warning, which the workspace's
check.sh treats as an error via -D warnings.

The flagged patterns in limux-core are `match combo_norm { pattern
=> { if palette_visible { … true } else { false } } … }` — they
collapse cleanly into match guards: `pattern if palette_visible =>
{ … true }`. Behavior is unchanged; any non-match combo hits the
catch-all `_ => false` arm.

Unrelated to the redesign; included here to keep CI green.
@mvbmir
Copy link
Copy Markdown
Contributor Author

mvbmir commented Apr 17, 2026

@am-will Apologies for the big PR, adding a top bar is opinionated design wise, considering you don't have one, but since I really felt the need for it, I added it as an optional UI part.

So, for people who like the no top bar, they can keep the dock only, with slight improvements.

There's also some other quality of life things like closing tabs with middle mouse button, making the pane bars drag the window also, some button designs, etc, so if you see anything you'd like me to separate into a different PR let me know.

@mvbmir mvbmir marked this pull request as ready for review April 17, 2026 16:03
@am-will
Copy link
Copy Markdown
Owner

am-will commented Apr 17, 2026

I will take a look when I can ty

@mvbmir
Copy link
Copy Markdown
Contributor Author

mvbmir commented May 5, 2026

Hi @am-will, did you end up taking a look?

@am-will
Copy link
Copy Markdown
Owner

am-will commented May 5, 2026 via email

@am-will
Copy link
Copy Markdown
Owner

am-will commented May 10, 2026

let me merge some other issues and i'll get to this. looks interesting for sure

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

AUR package available: limux-bin

2 participants