Repository navigation
feat(app): a menu bar built from the commands hideGit already has - #89
Merged
Merged
Conversation
Three numbers decided how the main window was divided: a 230px sidebar, a 6:4 graph against the detail pane, and a 280px file list inside the working directory. Each was a guess about a window nobody had seen, and no window could argue with it — a long commit message on a laptop screen had four tenths of the height and no way to ask for more. Every divider between those panes is draggable now, the file list included. ## Two units, and the reason for each The sidebar and the file list are pixels: what they need is set by the length of a branch name or a path, and that does not grow because the window did. The graph and the detail pane are a fraction of the height they share, because they are two halves of one view — a taller window should keep the graph the subject and the commit under it the footnote. Nothing can be dragged out of existence. The pixel splits stop at a minimum and at half the space they divide; the fraction keeps 15% for each side. A pane with no height has no divider left to drag it back with. ## How a drag arrives `mouse_area` reports that a press happened, not where, so the first pointer movement after a grab is the anchor and every size is a delta from it. That is what makes `layout.rs` testable without a window: nothing in it knows where anything is on screen. The pointer subscription exists only while a divider is held — following every mouse movement for a session to serve a drag nobody is doing is the most expensive subscription this could have had. `extent` — the size of the space being divided — is the one thing the arithmetic cannot derive, so the view measures it with `responsive` and the grab carries it. ## Where it is written `state.toml`, beside the window's own geometry, on release rather than per frame. It is state, not a setting: it changes by dragging, and nobody is going to open a file to choose a number of pixels. It is clamped again on the way in, so a hand-edited file cannot open hideGit with a sidebar wider than the window. A state file written before this exists loads unchanged. Double-clicking a divider restores it, which is why none of this needs a settings entry: the control that moved it is the control that undoes it. ## What it cost `staging::view` reached fourteen arguments — already flagged in #86 as wanting a struct, and still not this change. Its file list became its own function, because the row it sits in is built inside a `responsive` closure that may run more than once and therefore cannot consume anything. ## Tests Thirteen on the arithmetic, three on the divider widget — including that a press beside it grabs nothing — and five driving `update`, one of which holds that a divider dragged inside the working directory resizes the window's layout rather than the repository's. Two on the config file: a round trip, and a `state.toml` from before the dividers existed.
… off Three places drew text of unbounded length at whatever height it asked for and let the window clip the rest: no scrollbar, no wheel, and no way to read the end of it short of making the window bigger. **A commit message.** The detail pane's header — title, author, body, stats — grew with the body. A release note of four paragraphs pushed the changed files and the diff off the bottom of the window, and the end of the message was not reachable either way. It scrolls now, and is capped at 240px so that reading a long message does not cost the diff underneath it. **Git's stderr in a failed pane.** A rejected push prints a paragraph and a merge prints the conflicted paths. The pane showed the lines that fitted and dropped the rest, which for an error message is the half that says what to do about it. **Git's stderr in a toast.** A toast is bottom-aligned, so its details grew *upwards*: thirty lines carried the summary and both controls — dismiss and Copy details — off the top of the window, on exactly the failures somebody needs to read and report. Both tests assert what is *visible* rather than what is in the tree. A widget pushed out of the window is still in the widget tree, so the usual `shows` helper passes against the bug, and the toast one lays out at 1000x500 rather than the harness's roomy default — the bug is about a toast outgrowing its window, so the window has to be one a toast can outgrow. Both were confirmed to fail with the cap removed.
macOS puts a menu bar on screen whether an application asks for one or not. hideGit never asked, so what was up there was winit's fallback: one menu holding About, Services, Hide and Quit, every item naming the executable rather than the application — "About hidegit", lowercase — and nothing else at all. No File, no Edit, therefore no Copy or Paste in a menu, and no way to discover that Cmd+O opens a repository except by already knowing it. ## It is the command palette's table, in a different shape `widget::palette::COMMANDS` already holds every named action: an id, a title, the chord that runs it and the message it sends. The palette reads it, the shortcut reference is tested against it, and `[shortcuts]` in `config.toml` remaps by its ids. The menu is now assembled from it too, which is why this is a short file and why nothing in it can drift: a menu item whose title, action or shortcut disagreed with the palette's would have to disagree with itself. Three things fall out of that: - **An entry is greyed out exactly when its command has nothing to do.** The rule is the same one the palette already used — Push with no repository open produces no message — so an item cannot be enabled and inert. - **The shortcut shown is the user's own**, from the keymap first and the built-in chord second. A menu accelerator is taken by the operating system before the window sees the key, so advertising the default chord of a moved command would resurrect the binding they replaced. - **Cut, Copy, Paste, Undo, Minimise and Quit are the platform's own items**, not hideGit messages wearing their names. ## Where it is installed, and why there From `update`, in answer to `window::open_events()`. Both halves matter: iced runs `update` on the main thread, and the window having opened is what proves `NSApp` exists to hang a menu from. A failure is logged and otherwise ignored — losing a menu bar costs discoverability, refusing to start costs everything. Clicks arrive on muda's own channel, which is not a future, so one thread parks on it and forwards what it gets. The alternative is a timer waking thirty times a second to find nothing. ## What it does not do **Windows and Linux keep no menu bar.** Attaching one needs a native window handle iced 0.14 does not expose, and on Linux a GTK window hideGit does not have. Said in the module, in UI_SPEC and in the roadmap rather than left to be found. Nothing lives only in the menu — every command in it is still reachable by chord and from the palette — which is what makes its absence survivable. `muda` is a macOS-only dependency with `default-features = false`: the defaults are gtk3 and libxdo, forty packages in the lockfile for a platform this does not run on. ADR-0009 records the decision, including why the `objc2` already under winit is not an option: the workspace forbids `unsafe`.
Linux and Windows failed to compile: `-D warnings` makes dead code an error, and with no `Bar` to build, every one of `Entry`, `Standard`, `Menu`, `BAR`, `enabled`, `label` and `ShellMessage::MenuChosen` was unreachable. macOS passed, which is exactly the shape of failure a platform-gated feature has. The message that reports a menu click, the arms that handle it, and the two subscriptions that raise them are now gated with the rest of the menu. There are no menu clicks without a menu, and nothing to wait for a window to be ready for either. The table and the three functions that read it stay compiled everywhere, behind an `allow` that says why: they are portable data, and their tests — that every id still names a command, that a remap wins over the built-in chord — are worth running on all three runners. On two of them the code is dead by construction rather than by mistake, which is the distinction the lint cannot make and the comment can. Checked by compiling the crate with the gate flipped to a target this machine is not, so the non-macOS path is built rather than assumed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #88 → #87 → #86.
macOS puts a menu bar on screen whether an application asks for one or not. hideGit never asked, so what was up there was winit's fallback: one menu — About, Services, Hide, Show All, Quit — every item naming the executable rather than the application ("About hidegit", lowercase), and nothing else. No File, no Edit, therefore no Copy or Paste in a menu, and no way to discover that
Cmd+Oopens a repository except by already knowing it.It is the command palette's table in a different shape
widget::palette::COMMANDSalready holds every named action: id, title, chord, message. The palette reads it, the shortcut reference is tested against it,[shortcuts]remaps by its ids. The menu is assembled from the same table, so a menu item whose title, action or shortcut disagreed with the palette's would have to disagree with itself.Three things fall out of that, and they are the reason for doing it this way:
Where it is installed
From
update, in answer towindow::open_events(). Both halves matter: iced runsupdateon the main thread, and the window having opened is what provesNSAppexists to hang a menu from. Failure is logged and ignored — losing a menu bar costs discoverability, refusing to start costs everything.Clicks arrive on muda's own channel, which is not a future, so one thread parks on it and forwards what it gets. The alternative is a timer waking thirty times a second to find nothing.
What it does not do, said plainly
Windows and Linux keep no menu bar. Attaching one needs a native window handle iced 0.14 does not expose, and on Linux a GTK window hideGit does not have. This is in the module, in
UI_SPEC.mdand in the roadmap rather than left to be discovered.Nothing lives only in the menu. Every command in it is still reachable by chord and from the palette, which is what makes its absence on two platforms survivable.
mudais macOS-only withdefault-features = false— the defaults are gtk3 and libxdo, forty packages in the lockfile for a platform this does not run on. The lockfile grows by two crates.ADR-0009
Including why the
objc2already sitting under winit is not an option: the workspace setsunsafe_code = "forbid", andforbidcannot be lifted for a module. Weakening a lint that protects four crates is worth more than a menu.Tests
Six, all on the table rather than on an
NSMenu: every id in the bar names a command that exists, no command is in two menus, a repository command is dead until a repository is open, the Help entry opens the page it names, an entry shows the chord the user remapped it to and falls back to the one it ships with — and, on macOS, that every chord in the bar is one the platform can actually take.Cmd+,,Cmd+/,Cmd+EnterandCmd+Backspaceare all in that table, and all four are where an accelerator parser gives up.Not verified in a window by me
It builds, installs without error on a real run against this repository, and the suite is green — but I could not take a screenshot of the menu itself. Worth opening once before merging.
Gate
cargo fmt --all -- --check,cargo clippy --workspace --all-targets --all-features -- -D warnings,cargo test --workspace— 963 tests, all green.