Let half actions preserve the window's size on the other axis (opt-in) - #1824
Let half actions preserve the window's size on the other axis (opt-in)#1824X-Wei wants to merge 2 commits into
Conversation
|
Thanks for contributing! Things look good at a quick glance. The list of checkboxes in the General tab is steadily growing and I haven't ironed out my preferred way to prevent it from turning into something unwieldy. Would you mind putting that checkbox at the bottom of the popover displayed from the "Extras" button in the General tab for now? I think I'll end up with another tab for more settings instead of the Extras button, but I haven't decided where everything will go yet. |
Left Half, Right Half, Top Half and Bottom Half always give the window the whole height or width of the screen, so reaching a quarter takes a dedicated corner shortcut. Behind the new halvesPreserveOtherAxisSize default, each of them only changes the axis it belongs to (Left/Right: width, Top/Bottom: height) and keeps the other one, like Win + arrow keys on Windows or keyboard tiling on KDE: Left Half then Top Half lands in the top left quarter, Bottom Half from there expands back to Left Half, Right Half then Left Half fills the screen. The window's state on each axis is recognized purely from its current frame (docked to an edge at the active split ratio or any cycle size, with or without gaps, within a 10 pt tolerance), so nothing new is tracked. The result reports the geometrically equivalent action as resultingAction / subAction so gaps and window history behave as if that action had run; WindowCalculation.calculate now forwards RectResult.resultingAction, which every subclass setting it already handled by overriding calculate. Untiled windows and repeated executions of a plain half fall through to the existing code, so the repeated-command setting keeps working as before. The default is off; with it off nothing changes. - Add HalvesPreserveOtherAxisSize.swift with the per-axis state logic - Consult it first in the left/right, top and bottom half calculations - Add HalvesPreserveOtherAxisSizeTests (32 cases) - Document the default in TerminalCommands.md - Expose it as a "Half actions preserve the window's size on the other axis" checkbox, unchecked by default, at the bottom of the Extras popover in the General settings tab (title and tooltip registered in Main.xcstrings)
7905548 to
0ea5f70
Compare
|
Done — the checkbox now sits at the bottom of the Extras popover, below the side split ratio rows, and the General tab is untouched. Same title, tooltip and default (off); the commit is amended and force-pushed. Happy to move it again once you settle on a tab for these. |
With halvesPreserveOtherAxisSize enabled, the action for the edge a quarter is docked to did nothing, so the repeated-command cycle sizes were only reachable from a plain half. Now it cycles the window through the selected cycle sizes along its own axis and keeps the other one: Left Half takes a top left quarter to two thirds wide, then one third wide, then back to one half, while Top Half does the same to its height. The current size is recognized from the window's frame (with the same tolerance and gap handling as the docking state), so the cycle continues from a column or row that was cycled as a plain half before, and history is not involved. This only applies when the repeated-command setting resizes; otherwise the quarter stays as it is. Plain halves still go through the existing repeated-execution code. The order of the selected cycle sizes moves from RepeatedExecutionsCalculation.sortedCycleSizes() into CycleSize.sortedSelectedSizes() so both can use it; the protocol extension now delegates to it. - Add HalvesPreserveOtherAxisSize.nextCycleSize and use it for same-edge actions inside a quarter - Replace the "does nothing" tests with cycling tests (38 cases total) - Update the TerminalCommands.md section
|
Pushed a second commit (c5c209f) after using this for a couple of days: inside a quarter, the action for the edge the window is docked to now cycles sizes along its own axis instead of doing nothing — Left Half takes a top left quarter to ⅔ wide, then ⅓, then back to ½ (height unchanged), and Top Half does the same to the height. It follows the repeated-command setting (only the resizing modes cycle; the others still leave the quarter alone), and the current size is read from the frame the same way the docked state is, so no new state is tracked. Plain halves are unchanged and still go through the existing repeated-execution code. The only touch outside the feature is that the ordering of the selected cycle sizes moved from |
|
One more thing, for after this is merged rather than for this PR: after using the opt-in mode for a while I found I also wanted to tile three windows in columns, so I have a small follow-up on top of these two commits — when a window is docked at exactly two thirds, the action for the opposite edge shrinks it to the middle third instead of filling the screen (Left Half, Left Half, Right Half → center column). I'll open it as a separate PR once this one lands, to keep this one as is. It's on my fork as |
Summary
Adds an opt-in mode in which Left Half, Right Half, Top Half and Bottom Half only change the axis they belong to (Left/Right → width, Top/Bottom → height) and keep the window's size and position on the other axis — the way Win + arrow keys tile windows on Windows, or keyboard tiling works on KDE.
With it, the four half shortcuts are enough to reach every half and quarter: Left Half then Top Half lands in the top left quarter, Bottom Half from there expands back to Left Half, and Right Half then Left Half fills the screen.
Off by default (
halvesPreserveOtherAxisSize); nothing changes unless it is enabled.Related: discussions #665 and #811 (where this was asked for, and you said you would be fine merging a good PR for it configurable via Terminal command), and #144. This is the narrow version discussed there: no separate "Windows mode" and no preset or extra shortcuts — the existing half actions simply take the window's current position into account, the logic lives in one self-contained class, and everything sits behind a single default.
Behavior when enabled
Each half action looks at where the window currently sits along its own axis and leaves the other axis exactly as it is:
Some sequences:
Untiled windows and repeated executions of a plain half deliberately fall through to the existing code, so the feature never changes what the repeated-command setting does. Inside a quarter the cycle is driven by the window's current size (the size after the one it has now, or the first selected size if it has none of them), not by the execution count, because the history there holds the corner action.
How it works
HalvesPreserveOtherAxisSize.rect(for:)recognizes the window's state on each axis purely from its current frame: an axis counts as "docked" when the window's origin and extent match (within 10 pt, for apps that resize in character cells) an edge-docked rect that a half action produces on that screen — at the active split ratio or any cycle size, with or without gaps applied. Anything else counts as "whole axis". It returnsnilwhenever the existing logic should run.LeftRightHalfCalculation,TopHalfCalculationandBottomHalfCalculationconsult it first when the default is enabled; otherwise they are untouched..topLeft+.topLeftQuarter,.leftHalf,.maximize, …) asresultingAction/subAction, so gaps and the window history behave exactly as if that action had been executed. For that,WindowCalculation.calculatenow forwardsRectResult.resultingActioninstead of ignoring it — every subclass that sets it already overridescalculate, so this is not a behavior change by itself.Testing
HalvesPreserveOtherAxisSizeTests(38 cases): every state transition, cycling inside quarters (both axes, custom cycle-size selection, non-50 % split ratio, gaps, the non-resizing repeated-command modes, no selected sizes), fallback to the existing logic for untiled windows and repeated plain halves, cycled 2/3 and 1/3 columns/rows, non-50 % split ratios, gaps (including the skip-top-edge option), matching tolerance, and the disabled path.Try it
A notarized Apple-silicon build of this branch (unofficial, signed with my own Developer ID) is attached to https://github.com/maxing-labs/Rectangle/releases/tag/v0.99-halvesPreserveOtherAxis-b109 — install, then tick the checkbox at the bottom of the Extras popover in Settings.
Docs / UI
TerminalCommands.md: new section "Make the half actions tile like Windows or KDE" with thedefaults writecommand.RepeatedExecutionsCalculation.sortedCycleSizes()intoCycleSize.sortedSelectedSizes()so the new code can share it; the protocol extension just delegates to it.Main.xcstrings. Happy to drop the checkbox and keep this terminal-only if you prefer.