Skip to content

backend: floats are OS windows again on macOS - #592

Merged
foxnne merged 1 commit into
mainfrom
backend/viewports-pub
Oct 18, 2026
Merged

foxnne merged 1 commit into
mainfrom
backend/viewports-pub

Conversation

@foxnne

@foxnne foxnne commented Oct 18, 2026

Copy link
Copy Markdown
Collaborator

On macOS, a float (and a menu, a dialog) opens in an OS window of its own again.

What broke

#549 inserted pub const glass_look = @import("glass_look"); between the doc comment and pub const viewports = @import("viewports.zig"); in backend/src/SDLBackend.zig, and dropped the pub from viewports. src/backend/backend_native.zig chose its viewports with @hasDecl(@import("backend"), "viewports"); a non-pub declaration is invisible to @hasDecl from another file, so fizzy silently took viewports_none (supported = false) and Popout.enabled() returned false at comptime.

The fix

  • SDLBackend.zig: viewports is pub again, with its comment. glass_look goes private, above it — the same shape backend: a plain app's in-app floating windows are made of the window material #589 gives those lines (nothing reaches backend.glass_look; everyone uses core.glass_look).
  • The silent fallback is gone: build/exe.zig hands the executable a native_backend options module (fizzy = native_backend == .fizzy), and backend_native.viewports takes @import("backend").viewports directly when it is set. A viewports that loses its pub is now error: 'viewports' is not marked 'pub' instead of popouts turned off.
  • viewports_none.zig's doc no longer recommends the @hasDecl pattern, and says why.

Verified

  • With pub removed again, zig build fails at src/backend/backend_native.zig:155: 'viewports' is not marked 'pub'.
  • Live on macOS (2026-10-18, before this PR, with the pub restored by hand): floating the Output view opens an OS window that fizzy.screen lists as fizzy.window:float:Output; without it the float stays inside the main window.
  • Gates, macOS: scripts/check-fmt.sh, zig build, zig build test, zig build test-integration (80/80 steps), zig build check-web, zig build test-sdk-version, and zig build -Dnative-backend=sdl3 (dvui's own backend still takes viewports_none). scripts/check-examples.sh left to CI.

Overlap

#589 rewrites the same three lines of SDLBackend.zig; whichever lands second has a one-line conflict (keep pub on viewports). #590 touches a different part of the file.

SDK

No boundary change; fingerprint and sdk_version untouched.

🤖 Generated with Claude Code

#549 dropped the `pub` from `SDLBackend.viewports` (its doc comment
ended up on `glass_look`). fizzy picked its viewports with
`@hasDecl(@import("backend"), "viewports")`, which cannot see a
non-pub declaration in another file, so it quietly fell back to
`viewports_none`: `Popout.enabled()` was false at comptime, and floats,
menus and dialogs stayed drawn inside the main window.

Restore the `pub` and the comment, and choose viewports by the backend
the build linked (a `native_backend` options module from build/exe.zig)
rather than by probing for the declaration, so a `viewports` the backend
stops exporting is a compile error, not popouts turned off.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@foxnne
foxnne marked this pull request as ready for review October 18, 2026 21:36
@foxnne
foxnne merged commit c20b317 into main Oct 18, 2026
11 checks passed
@foxnne
foxnne deleted the backend/viewports-pub branch October 18, 2026 22:10
foxnne added a commit that referenced this pull request Oct 19, 2026
…584)

Part of #260 (step 5 of `plans/WORKSPACES_PLAN.md`: floats and
tear-off).

## What changes

Behind `FIZZY_WORKSPACES=1`, a document tab can float. It floats inside
a workspace of its own, so the new window is a tabbed area of documents
holding just that one.

- **Torn off over the desktop** (where floats are OS windows, macOS
today): a tab let go over no window of the app's opens a float there,
holding a new workspace with that document. Before, nothing happened: a
document could not float.
- **Dropped on its own pane's middle:** the same, the float opening over
the pane. The drop's middle shows the float icon there, as it does for a
view over its own place.
- **A workspace's only tab carries the workspace**
(`State.Documents.alone`, from #462): the workspace itself floats,
rather than a new one being made round the document and the old one
closing behind it. Already alone in a float nobody split, it doesn't
float again; the pane takes the drop.
- **Its float closes when the workspace empties** (its last tab closed
or carried out), as any float its last view leaves.
- **Closing the float gives its documents to the main window's
workspace**: the one the person was in, when that is in the main window,
else the first there. They go in after its own tabs, in order, and the
one the float showed is shown. The emptied workspace then goes. With no
workspace in the main window, it goes home as a view, as before.

How: `State.Documents` gets one more member, `fold(id) -> bool`, which
`ViewDrag.sendHome` asks before sending a closing float's view home.
`ViewDrag.floatDocument` floats the view a document carries alone
(`carriedView`), or a new one (`houseView`). A new workspace floats out
of the pane its photograph was taken in, at that pane's size.

With the flag off, `documents` is null, and nothing changes.

## SDK impact

- [x] None. `State.Documents` is the app framework's (`app/layout`), not
the plugin boundary.

## Verified

- [x] `zig build`, `zig build test`, `zig build test-integration`, `zig
build check-web`, `zig build test-sdk-version`, `zig build
check-tests-reached`, `scripts/check-fmt.sh`.
- [x] New integration test, "float: a document tab dropped on its own
pane's middle floats in a view holding it". It covers one tab of two
floating in a new view, a lone tab floating its workspace, no second
float for one already alone, and closing the float folding instead of
going home.
- [x] macOS, a sandbox profile run with `FIZZY_WORKSPACES=1`, driven
through the agent plugin (`fizzy_act`), each checked by `fizzy_screen`
and a capture:
- `tear-c.txt`, one of two tabs, dropped on its own pane's middle:
`Float 1` opens over the pane in an OS window. It holds a new workspace
(`Pane 3`) with only `tear-c.txt`, and the pane keeps `tt-b.pixi`.
- Closing `tear-c.txt` in the float: the float closes, and the main
window is unchanged.
- `tt-a.pixi`, its workspace's only tab, dropped on its own pane's
middle: that workspace (`Pane 4`) floats, and the emptied half of Main
closes into the other.
- [x] macOS, with the tear-out fixes since merged into a local build
(#591, #592, #594): a tab carried past the window's edge opens its own
window, holding a new workspace with just that document (`Float 1`,
`Pane 6`, `tear-d.txt`).
- [x] Review: `fold` copied the workspace's tab list but not the id
strings in it, then emptied the workspace, and `State.setAssignment`
frees those strings. It now copies the ids too. The gates pass on the
branch merged with current `main`.
- [ ] macOS, still to see with a real mouse: a tab torn off over the
desktop (my run was interrupted by a person's input), and a float closed
from its OS close button folding into the main workspace (the
integration test covers the layout side).
- [ ] Windows:
- [ ] Linux:
- [ ] Web: builds; the flag is never on there.

## Follow-ups

- A window from ⌘N (step 8) stays open when it empties. The float needs
to remember which kind it is then.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant