Conversation
|
Thanks! I pushed a branch, |
Moving a window to the next, previous or a specific display centered it on the destination, which loses the spot the user had put it in. DisplayTransfer works out each axis on its own from whether the window was against a screen edge: against both edges it follows both and spans the destination, against one it keeps its size and stays against that edge, against neither it keeps its size and its center keeps the same relative spot. The distance from an edge is carried over rather than flattened, so a window snapped with gaps arrives with the same gaps. This is now the default for next/previous and specific display moves. The old centering is available as centerOnDisplayChange, and attemptMatchOnNextPrevDisplay still replays the last Rectangle action where there is one. Closes rxhanson#1666
The test double overrides setFrame(_:adjustSizeFirst:), but AccessibilityElement.setFrame gained an adjustPosition parameter in "Add optional snapping animations and a blurred snap preview" (rxhanson#1849), so the override no longer matches and the test target does not compile: RectangleTests.swift:6153:23: error: method does not override any method from its superclass This is not related to the rest of the branch - it reproduces on a clean checkout - but the test target has to build before any of the tests can run.
d2d0663 to
0675d33
Compare
|
Thanks — that branch was exactly what was needed. I cherry-picked onto 1. The test target didn't compile, on a clean
2. With that out of the way, my change broke four existing tests — which I'd flagged as unverified and which turned out to be a real gap.
I rewrote the class to assert the same four #1723 geometries against
The first row is the one to look at if you're weighing defaults: under the old mapping a right-third window stays a right third, under this one it keeps its 1000pt width and becomes two thirds of a smaller display. That's the size-preserving trade-off from the PR description, made concrete. If you'd rather that case scaled, that's Full suite: 431 tests, 0 failures, including the 15 in |
|
Thanks for walking through the test issues, I will test it out soon. I'm getting ready to draft a release this week, and I'll aim to get this in so it makes it into the release. |
|
Finally got a chance to test this out. I like it! Two things:
|
…position-across-displays
A window against three screen edges - spanning one axis and against one edge of the other, like a left half or a top half - was stretched along the whole of the spanning axis when it moved between a landscape and a portrait display, turning a left half into a tall sliver. It now keeps its size and is centered along the middle one of its three edges. Picking a corner instead would mean guessing from how the displays are arranged; centering makes no assumption. Keeping a window's position is now a setting, "Keep window position when moving across displays" in the Behavior tab, backed by keepWindowPositionOnDisplayChange. It is on by default. Turned off, display moves behave as they did before this branch: the window is centered, or with attemptMatchOnNextPrevDisplay on and no action to replay, mapped proportionally as in rxhanson#1809. relativePositionedRect and its tests are restored for that path, and the centerOnDisplayChange terminal command is gone, since the setting replaces it.
|
Thanks for testing it! Both done in cd9625e, with 1. Three edges, changing orientation. That made sense. When a window is against three screen edges (it spans one axis and touches one edge of the other, like a left half or a top half) and the move goes landscape to portrait or back, it now keeps its size and is centered along the middle edge of the three. A left half lands against the left edge, centered vertically. A top half lands against the top edge, centered horizontally. Gaps against that edge are kept. If the window is longer than the destination on that axis, it just spans it. Moves that keep the same orientation, maximized windows and windows touching only two opposite edges behave as before. This lives in 2. Toggle. It's in the Behavior tab, next to "Move cursor along with window across displays": Keep window position when moving across displays, with a short caption. It's backed by Full suite: 442 tests, 0 failures. That run was |
|
Excellent! |
Closes #1666.
Moving a window to the next, previous or a specific display centers it on the destination, which throws away the spot the user put it in. #1666 asks for that spot to be kept, and your comment on the issue sketched the behaviour:
The rule
DisplayTransferworks out each axis on its own, and both axes use the same rule: an edge that was against the source screen edge is put back against the matching destination screen edge. The three cases you called out fall out of that one rule rather than needing to be decided separately:Sizes only change where the window has to follow the screen edges. That is the part I went back and forth on, and I landed there because it is what dragging a window across displays yourself does, and because scaling everything proportionally shrinks a 400x300 utility window to 236x197 on the way from a 27" to a laptop. A window that is larger than the destination display is the one exception - it gets cut down to fit.
The distance from an edge is carried over rather than flattened to zero, which is what makes this work with gaps turned on: with a 10pt gap nothing is ever flush, so "against the edge" has to allow for the gap, and the same gap has to come out the other side.
edgeToleranceis4 + Defaults.gapSize.Three edges, changing orientation
One exception, from your testing: a window against three screen edges - spanning one axis and against one edge of the other, like a left half or a top half - moving between a landscape and a portrait display. Following the rule above stretched a left half into a tall sliver down the whole portrait display. It now keeps its size and is centered along the middle one of its three edges (a left half sits against the left edge, centered vertically; a top half sits against the top edge, centered horizontally). If the window is longer than the destination on that axis, it just spans it. Moves that don't change orientation, maximized windows and windows against only two opposite edges are unaffected.
Defaults
This is on by default for
nextDisplay,previousDisplayand the specific display actions, and can be turned off in Settings > Behavior > Keep window position when moving across displays, backed bykeepWindowPositionOnDisplayChange(anOptionalBoolDefault, on unless set to 2):Turned off, display moves behave exactly as on
main: the window is centered, or, withattemptMatchOnNextPrevDisplayon and no action to replay, mapped proportionally as in #1809.attemptMatchOnNextPrevDisplayis untouched in intent and still off by default: with a replayable action it replays that snap on the destination, which resizes the window to the new display instead of keeping its size. I retitled its section in TerminalCommands.md to say what it now does.A maximized window still goes through
autoMaximizerather than the transfer, so it stays recorded as maximized on the destination and can be restored from there.Relationship to #1809
relativePositionedRectfrom #1809 is kept, and still used when the setting is off andattemptMatchOnNextPrevDisplayis on with no action to replay. With the setting on, the edge rule above covers that case instead.Tests
RectangleTests/DisplayTransferTests.swift, 26 cases: the three branches, both directions between a 1512x945 built-in and a 2560x1415 external placed to the right and lower down (so a transfer that forgets to translate coordinates is caught), a 1440x2560 portrait display for the three-edge orientation cases (left/right/top/bottom halves, gaps, a window too long to center, maximized and two-edge windows unaffected), the #1723 geometries under the edge rule, gaps, windows hanging off the edge, windows too large for the destination, identical displays, empty frames, and a sweep asserting the result always fits on the destination. The originalNextPrevDisplayMappingTestsforrelativePositionedRectare unchanged frommain.Full suite, run on
mainmerged in plus your twomacOS26build commits applied locally (not part of this branch): 442 tests, 0 failures.