Repository navigation
Pop-out and settings fixes: a restored float stays in front, picker drags float off the desktop, menus keep off the edge, settings scroll steady, the carried view is always a window (Liquid Glass on macOS 26) - #224
Merged
Conversation
…ks too A float restored at launch opened behind the main window and stayed there. Float windows are put back over the main window whenever it comes forward: focused (the app activating, at launch too), pressed, shown or restored (`order_pending`). That check sat only in the polled event loop (`addAllEvents`). Fizzy runs on SDL's callbacks on macOS, where events arrive through `appEvent`, so it never ran there, and a float window was ordered over the main window only on its first show. At launch that often comes before the main window is visible, and the main window then showed over it. `noteMainForward` is called from both event paths. In a sandbox launched with a saved float, the main window's `SDL_EVENT_WINDOW_SHOWN` now reaches it (it was dropped before), and the float stays in front. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… go, as a corner-button drag does A view carried out of the place picker couldn't be dropped outside the window: `floatAway` (and `floatOut` under it) took only a drag with a source place, and the picker's is a loose drag. A loose drag of a view now floats the view where it is let go over no window. It comes out of whichever place holds it now (`holderOf`). That place is its home when the float closes, and it shuts if the view leaves it empty. A view held nowhere goes home by its keywords. Documents stay out until workspaces (`docs/WORKSPACES_PLAN.md`). Each picker card publishes a `picker:<surface id>` demo anchor. In a sandbox, a tape opens the Panel's picker and drags Output past the main window's edge: the carry window follows it out there, and a float window holding Output opens where it is let go. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The place picker opened from a corner button at the window's right edge sat flush against that edge. Every floating menu was placed (`dvui.placeOnScreen`) against the bare rect of the screen it opens on: the main window's, or a float's own window's (`core.screens`). `FloatingMenu` now insets that screen by `screen_margin` (8 points), for menus pushed in from an edge and for centred ones. In a frame dump the Panel's picker sits 8 points in from the window's right edge. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… the pane no longer rubber-bands Scrolling up through the settings (Appearance among them), the pane snapped back every few ticks. Rows out of view are skipped and keep the height they were last drawn at. A row coming back into view is drawn in full, and on that first frame it measured short: what its widgets knew of their own sizes was forgotten while they weren't drawn. The next frame it was its own height again. The content's height dipped by 40 to 140 px for one frame and came back, and everything under the row moved with it. A row back in view after being skipped now keeps its last height for that frame, at the same width. Scrolled up 3000 px in 30 px ticks in a sandbox, with every section open, the pane's content height is now the same on every frame (4772.4 px, 96 of 96). Before it was 4634.9 to 4772.4, changing every few ticks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With floats as windows, a carried view was drawn in whichever window it was over. Dragged off a float's window, it was cut off at that window's edge until it crossed, then drawn in the main window, behind the float. Where the carry window took it over (past the main window, under a ghost), its glass turned from glass refracting the app under it into the window's material, and back, at every crossing. Showing the carry window only where the bubble wasn't wholly in one unobstructed window still popped near edges, and the change of look stayed jarring. On macOS, with float windows, the carry window now shows the carried view for the whole drag, wherever it is (`Popout.carryFrame`): - over the main window, at its place in the main window's frame; - over a float's window, at the screen position of that window's band, its picture still read from the band (`Cover`, the window it overlaps most, since near an edge its middle is past it while the pointer is still on the window). The copy left in the app's own windows under it casts no shadow of its own beside the OS's (`core.dialogs.carried_in_window`). Polled in a sandbox, the carry window follows both drags from lift to drop with no gap: Explorer across the main window and into the float it opens, and a view out of a float's window along its edge and on over the main window. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ms out of the glass as it opens As asked: the carried view's window draws no base fill, so while dragged it is the OS's frosted material and what it carries: lighter, more glass-like. Dialogs and float windows keep their opacity. As a float's window grows out of the drop (`Popout.growFrame`), its base now fades in with the growth, from the carried glass's none to the window's own opacity, which it reaches as the real window takes over under it. The window forms out of the glass, as the in-window glass used to, instead of arriving with its fill already on. `carryBegin` takes the share of the base to lay down (`fill`). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…coming in as it grows You saw the bubble's middle as opaque, with only its rim showing the material. The fill fading in as a float's window opened couldn't be seen, and the window seemed to appear first and fill in after. **The photograph.** It was taken from the frame as drawn, the place's background and the window's under it. It was then laid over an opaque fill (`backed`), so text never floated in the frost. In a window of its own that picture covered the window's material. With carry windows (`core.dialogs.carry_windows`, set by `Popout`), the view is now captured alone (`photographFromFrame` declines, and `keepShot` doesn't back it). Its content sits over the material, which shows between, and the base fading in under it as the window opens can be seen. A frosted pane inside a view draws dark in that capture. **The overlap.** Over the second half of the growth (`landingFraction` past ½), the window comes in under the growing glass while the glass fades off it, one into the other. Before, the window came in only after the growth ended. Polled in a sandbox, the window's opacity rises from 0 to 1 as the glass's falls from 1 to 0, both finishing as the glass settles on the window's frame. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…hes toward its tail The app's own windows drew a copy of the carried view under the carry window — its glass, a drop's tail, the drop it ran into — presented at another moment than the window server moved the carry window, so a fast drag left it trailing behind. Where carried things are windows of their own, what is carried (card, tab or drop) is now drawn in a layer of its own (`core.screens.markCarried`) that the carry window takes whole; the main window and float windows draw none of it, and no in-app glass is drawn for it: the OS's material is its glass. The drop zones no longer run the carried drop into their glass there; the bubble it is aimed at lights. A drop's carry window is the union of its head and its tail as that lags on its spring, so it stretches behind a fast drag and swings back when it stops — the wobble, from the window itself. `core.dialogs.carried_in_window` is gone with the copy it kept from casting a second shadow. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The carried bubble's window, and the window a float grows into as it opens, use Apple's Liquid Glass (`NSGlassEffectView`, Clear style) from macOS 26: its rim, its continuous corners, set from the window's shape. Before macOS 26 they keep the vibrancy material. The class is looked up by name and its properties set by key, so fizzy still builds against older SDKs (CI's macos-14). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…size The window a float grows into faded in over the second half of the growth while the carried glass still grew — past the window's frame and back, at the playful motion level — so two sizes of one window showed at once, and the photograph scaled to the overshoot doubled what the window showed (the user's capture). The window now shows nothing until the glass has landed at its frame; what the glass carries comes in as it grows instead: the carried view's photograph goes, and the float's own picture (last frame's) arrives in its place, scaled to the glass, its base with it. Landed, the glass is the window's size and shows what the window does: the window shows under it at once, and it fades off it. Polled: the float window opens at alpha 0, stays so while the glass overshoots to 386×479 and settles at 356×439, and shows at 356×439 as the glass fades. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The user saw the bubble's glass as a frost, without the refracted edges of Control Center's. Both of AppKit's public glass styles are frosted, in a window and behind one. One of the glass's private variants (`_variant` 11, measured on macOS 26.5) is a clear lens: unblurred, bending and drawing in what is behind it toward its rim — what is behind another window included, the window server doing the lensing — as a drop of water, as the app's own glass is. The carry window's glass takes it, through its typed setter, only on macOS 26 and only where the setter is there: a later macOS that renumbered the variants could leave the bubble with no glass (13 draws none), and key-value coding an unexpected value into the glass's Swift enums kills the process. Elsewhere it keeps the public Clear style. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…cture uncropped, and hands over in one frame Two things the user still saw as a float's window opened out of a drop. The text doubled near the growth's peak: the carried photograph (cropped to keep its proportions) crossed with the float's picture (stretched to the glass), each laid out at a different size. The picture now takes over in the first quarter of the growth, while the glass is small, and fills the glass with its proportions kept, so at the window's size it is the window's picture exactly. And the window flared opaque at the very end: it showed under the glass as the glass faded off it over 120 ms, both with the window's base. Now the window shows and the glass goes in the same frame (polled: the float window goes to alpha 1 at 356×439 in the poll the glass, at 356×439, is gone). The glass grows as frost (Liquid Glass's own variant), as the window's material is, not as the carried lens (`viewports.carryLens`). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…he carried glass's was The carried glass rides below and right of the pointer, but a float let go over no window opened centred on the pointer, so the glass grew out round it rather than down from where it was. The float now opens with its top left at the carried glass's (the drop's head, or its card), and the glass grows into it right and down; the float's picture fills the growing glass from its top left too. With nothing carried (the picker, a test) it still opens round where it was let go. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e frame it is photographed Where carried things are windows, the lift's photograph is captured from the place's own draw without its background (6105a2f), and that capture is put back on the screen for the frame. It was put back with `blit`, a dissolve that writes the capture's pixels exactly, clear ones included, so for the frame a view was lifted on the window was see-through under the place — the user saw the desktop through the sidebar for one frame. It is put back source-over (`blitOpaque`) now, over what the frame already has there, as the place's draw would have gone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A float's header is the area it is moved by, and dvui shows the move cursor over all of it — over its OS window's traffic lights too, which lie on the header where the OS frames the window. The header's drag area now starts past them: the window's own buttons measured each frame (`viewports.buttonsWidth`, `fizzy_macos_window_buttons_width`: the zoom button's right edge and as much again as the close button stands in), handed to the float (`Floats.Viewport.buttons_w`). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With the carried bubble's glass a clear lens, the view's picture in it, over whatever it passed, did not read (the user). The bubble now has the window's base under its picture at 20% opacity — lighter than a window still. `carryBegin`'s `fill` is that opacity outright now, no longer a share of the window's (nothing else passed one). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Bugs you found after #223 merged, one commit each.
A float restored at launch opened behind the main window. Float windows are put back over the main window whenever it comes forward: focused (the app activating), pressed, shown or restored. That check sat only in the polled event loop. Fizzy runs on SDL's callbacks on macOS, so it never ran there, and a float window was ordered over the main window only on its first show. At launch that often comes before the main window is visible. The check now runs from both event paths (
noteMainForward).A view dragged out of the place picker couldn't leave the window. The picker's drag has no source place, and floating off the desktop only took drags that had one. A view dragged out of the picker now floats where it's let go over no window, as a corner-button drag does:
Documents stay out until workspaces. Picker cards publish
picker:<surface id>demo anchors.The place picker sat flush against the window's edge. Every floating menu was placed against the bare rect of its screen, either the main window or a float's own window. Menus now stay 8 points in from its edges.
Two more, after more testing:
Settings rubber-banding. Scrolling up through the settings, the pane snapped back every few ticks. Rows out of view are skipped and keep the height they were last drawn at. A row coming back into view measured short on its first frame, because its widgets had forgotten their sizes while not drawn, then corrected itself the next frame. Everything under the row moved with it. Such a row now keeps its last height for that first frame. Scrolled back up 3000 px, the content height is identical on every frame, where before it changed every few ticks.
A carried view is a window of its own for the whole drag (macOS, float windows). Dragged off a float window, the bubble was cut off at the window's edge, then drawn behind the float once over the main window. Wherever the carry window took over, the glass also switched from refracting the app to the window's material. The carry window now shows the carried view from lift to drop, wherever it is. Over a float window it's placed by where that window sits on screen. Polled in a sandbox, it follows a drag out of a float window along its edge and on over the main window with no gap.
The carried bubble is bare material, and a float window forms out of the glass. As you asked, the carried view's window draws no base fill, so it's the OS's frosted material plus what it carries. Dialogs and float windows keep their opacity. When a float window grows out of a drop, its base fades in as it grows, reaching the window's own opacity as the real window takes over.
The carried view's content sits over the material, and a float window's content comes in as it grows. You saw the bubble's content as opaque and its fill change as invisible. Where carried things are windows, the photo is now taken without its place's background, so its content sits directly on the material. When a float grows out of a drop, the window's content arrives inside the glass as it grows (see below), not after the window appears.
A carried view is drawn by its carry window alone. A fast drag over the main window left a cutout-shaped trail. The app's own windows were still drawing a copy of the carried view under the carry window: its frosted glass, a drop's springy tail, and the drop it ran into. That copy was presented at a different moment than the window server moved the carry window, so it lagged behind. Following your rule (nothing in the app tracks the bubble unless it is all in the app), the carried view (card, tab or drop) is now drawn in a layer of its own, which the carry window takes whole (
core.screens.markCarried). The main window and float windows draw nothing of it, and it has no in-app glass: the OS's material is its glass.Liquid Glass on macOS 26, as a drop of water. The carry window (the bubble, and the window a float grows into) uses Apple's
NSGlassEffectView. AppKit's two public styles are both frosted, so the bubble showed no refracted edge. One of the glass's private variants (_variant11, measured on 26.5) is a clear lens. It refracts what's behind it, even behind another window, with no blur, like a drop of water. The bubble uses it only on macOS 26 and only where its setter exists. Otherwise it keeps the public Clear style, and older macOS keeps the vibrancy material. The class is looked up at runtime, so fizzy still builds against older SDKs (CI's macos-14). The measurements and the plan are in #226. Merging with the drop zones as Liquid Glass is built in #227, stacked on this PR.A float window opens cleanly out of the drop. From your captures:
No move cursor over a float window's traffic lights. The float's header is the area it's moved by, and it showed the move cursor everywhere, including over the OS's buttons. The drag area now starts past the window's own buttons, measured from the window each frame.
The carried bubble stands on a fifth of the window's base. With the glass a clear lens, the view's picture didn't read over whatever the bubble passed. It now has the window's base under it at 20% opacity.
The lifted place no longer goes see-through for a frame. Where floats are windows, the lift's photo is captured from the place's own draw without its background, and put back on screen for that frame. It was put back with a blend that writes the capture's clear pixels too, so the window showed the desktop through the place for one frame (your Plugins sidebar capture). It's now drawn on top of what's already there, as the place itself would be.
Verified
zig build,zig build test,zig build test-integration(28/28 steps, 278/278 tests),zig build check-web,zig build test-sdk-version(fingerprint unchanged). Cross-builds for Windows and Linux.NSGlassEffectViewin a test window over a striped pattern: Clear is light with Apple's rim, Regular dark and heavily blurred. Resized every 8 ms, it stayed matched to the window. Variant 11 refracts the stripes, from a separate window too.Not verified:
🤖 Generated with Claude Code