Carry the Windows notch across monitors by its handle - #287
Conversation
A plain press on the pill plus 4 px of movement dragged the window anywhere, and letting go snapped it to the nearest edge of whichever monitor it was over, saving one position that every edge shared. Since vinzdg#244 a click on a ring refreshes it, so a click that slipped moved the notch instead (vinzdg#251); and since vinzdg#246 the move handle exists to change edge, so the drag was doing its job too. The drag now needs Alt held, and without it every press on the pill is a click. It follows the pointer along the edge the notch is on and nowhere else, inside the work area, and never changes edge: the move handle and the Edge picker in Settings do that. It is the Mac's ⌥-drag (NotchWindowController.dragged), which only ever moves alongOffset. Each edge now remembers its own place, as the Mac keeps notchOffset per edge. A move to another edge lands where the notch was last left on that edge, centred if it has never been slid along it, and Recentre centres only the edge it is on. A carry used to land centred on purpose, because every edge shared one position and a carry dragged the old edge's fraction along with it; with a place per edge that reason has gone. The place is kept as a fraction of the edge rather than the Mac's points, so an existing notch_y becomes the place for the edge it was saved on without needing a screen to convert it, and a change of resolution keeps the notch in the same part of the edge. notch_y is read once and never written again. One thing is lost: dragging no longer takes the notch to another monitor. The Screen picker in Settings does, as on the Mac, and its caption no longer claims the drag can. The drag hint is rewritten in the Mac's own words, with its ru, zh-Hans, zh-Hant, ja and uk translations taken from Localizable.xcstrings and ⌥ read as Alt; the Mac has no Korean, so that sentence is new. The Ukrainian hint had been keyed to an older English sentence and never showed; it does now.
The move handle only ever offered the edges of one monitor. begin_move took the notch's screen once, read every target against it, and on release wrote the edge alone, so a pointer carried onto a second monitor still picked an edge of the first, and the overlay stayed behind — drawn there at the wrong size, since its figures belonged to the other screen. The zones now follow the pointer. Whichever screen it is over, the overlay moves there, the edge is read against that screen's work area, and letting go writes the screen as well as the edge; the right edge of another screen is a move too, though the edge has the same name. Over the gap a shorter screen leaves beside a taller one, the carry keeps the screen it was last over rather than flicking between them. With the drag held to its own edge, this is the one gesture left that takes the notch to another monitor; the Screen picker in Settings is the other way. The Mac's handle is single-screen — beginMove takes currentScreen() once — so this goes past it rather than porting it. The overlay is pinned in physical pixels, and pinned again if Windows resizes it for a new scale after the move, as place_notch does for the notch. On a screen at another scale that resize and its correction would both play out as the zones shrinking and growing again, so the overlay is hidden for the crossing and shown a couple of frames after the new zones are sent. It is also made unfocusable, like the notch: shown again on every screen crossed, a window that could take focus would take it from whatever the user was working in. The silhouette is in logical pixels, so it keeps its size on a screen at another scale, the size the notch will land at. Screens are told apart by where they are rather than by name, which the platform is not obliged to report.
Arriving on a monitor at another scale, Windows resizes the notch by the ratio of the two scales before place_notch puts it right, and the page then re-zooms itself for the new pixel ratio a debounce later, which can bring one more zoom correction from report_dpr. All of it played out on screen: a notch carried to another monitor jumped sizes as it landed. Hiding the window for the landing does not help. A hidden WebView2 stops painting and throttles its timers, so the page only re-laid itself out once it was shown again, in plain view — however long the delay before showing it. So the window stays up and the page empties itself instead: the window is transparent, so an empty page is an invisible notch, while the WebView keeps doing its layout. Rust waits for the page to confirm it has painted empty before moving it, and reveals it on the first report_dpr from the end of a burst of resizes that needs no further correction — the layout has stopped changing — rather than after a guessed delay. A fallback shows it after 700 ms whatever happens, numbered so it can never reveal a later landing early. At the same scale nothing is resized on arrival, so nothing is hidden. Every other placement stays in sight as before.
|
Merged as I pushed one follow-up on top, Two smaller things I left alone: the 40 ms |
The move handle only ever offered the edges of one monitor.
begin_movetook the notch's screen once, read every target against it, and on release wrote the edge alone. A pointer carried onto a second monitor still picked an edge of the first, and the overlay stayed behind — drawn there at the wrong size, since its figures belonged to the other screen.The zones now follow the pointer. Whichever screen it is over, the overlay moves there, the edge is read against that screen's work area, and letting go writes the screen as well as the edge. The right edge of another screen counts as a move too, though the edge has the same name. Over the gap a shorter screen leaves beside a taller one, the carry keeps the screen it was last over rather than flicking between them.
Why now: since the Alt+drag change (#276), the drag no longer leaves its own edge, so this becomes the one gesture that takes the notch to another monitor; the Screen picker in Settings is the other way. The Mac's handle is single-screen —
beginMovetakescurrentScreen()once — so this goes past it rather than porting it.Scale. The overlay is pinned in physical pixels, and pinned again if Windows resizes it for a new DPI after the move, the way
place_notchpins the notch. The silhouette is in logical pixels, so it keeps its size on a screen at another scale — the size the notch will land at. Screens are told apart by where they are rather than by name, which the platform is not obliged to report.Moving between scales without it showing. Crossing onto a monitor at another scale, Windows first resizes a window by the ratio of the two scales, and the fix-up above then puts it right — which is correct, but played out on screen: the zones shrank and grew again on every crossing, and a notch landing on the other monitor visibly jumped sizes. So the overlay is hidden for the crossing and shown again once the new zones are drawn. The overlay is also made unfocusable, like the notch, since it is shown again on every crossing.
The notch needs more care, because hiding its window does not work: a hidden WebView2 stops painting and throttles its timers, so the page re-zoomed itself for the new pixel ratio only once shown again, in plain view. Instead the window stays up and the page empties itself — the window is transparent, so that is an invisible notch — while the WebView keeps doing its layout. It is revealed on the first
report_dprfrom the end of a burst of resizes that needs no further correction, i.e. once the layout has actually stopped changing, with a 700 ms fallback so it can never stay hidden. A landing at the same scale is not hidden at all, and neither is any other placement.Testing:
cargo test104 pass, 0 fail, 2 ignored, including a new test for which screen a carry is over, the shared border between two, and the gap beside a shorter one. Clippy unchanged.Depends on #276 (Alt+drag): both touch the landing in
begin_move, so this is built on top of it and should go in after. #269 movesdropzones.rsandmain.rstodesktop/; #239, #257 and #210 touchmain.rsin other functions.