* window: title_bar module for app-drawn title bars
The empty part of an app's top strip moves the window on drag and follows
the macOS title-bar double-click setting. A control on the strip or an
in-window Order::Middle panel over it (floating docks, egui::Window) keeps
its own drag: window_movable is false there and, on macOS, sync_movable
sets NSWindow.movable from it. No gesture acts on the window in full
screen.
* window: empty_area returns the empty area's response
* feat(window): shared crate deciding who draws the window frame
Add craft-window, which decides whether the desktop or the app draws the
window frame (titlebar, borders, window buttons) and applies it to an
egui ViewportBuilder. This fixes the drift where some Craft apps drew
their own titlebar on KDE while others kept KWin's.
The rule: macOS, Windows and X11 keep the desktop frame; a Wayland
compositor with zxdg_decoration_manager_v1 (KDE, Sway, Hyprland, COSMIC)
keeps its frame; one without it (GNOME) leaves the frame to the app.
<PREFIX>_TITLEBAR=app|system|desktop overrides. When the app draws the
frame, the decision carries the window-button Layout from GNOME's
button-layout so the app's header bar matches the desktop's order.
Decision logic only; drawing the buttons stays in each app. Extracted and
generalized from pdfcraft's decorations.rs. forbid(unsafe_code); default
build has no egui dependency (egui feature adds decorate()).
* style(window): rustfmt the Wayland probe and Dispatch impl
cargo fmt --check failed CI: collapse the thread-spawn builder chain and
the Dispatch::event signature onto the lines rustfmt wants (verified with
rustfmt 1.98 against the workspace rustfmt.toml).
* fix(window): address review — WAYLAND_SOCKET, bounded gsettings, checkout creds
- Skip the Wayland probe when WAYLAND_SOCKET is set: connect_to_env would
take ownership of the inherited fd and unset the variable, closing the
descriptor winit needs. Keep the desktop frame instead.
- Run the gsettings button-layout lookup behind the same 500 ms bounded
worker as the Wayland probe, so a stalled gsettings can't block startup.
Factored the worker into a shared bounded() helper.
- window.yml: set persist-credentials: false on checkout so the token
isn't left in .git/config while cargo runs dependency build scripts.
- README: document the socket guard and the bounded gsettings lookup.
Verified with rustfmt 1.98 and clippy -D warnings (both feature sets).
* fix(window): reap the gsettings child on timeout; document probe tradeoffs
Follow-up to review: the button-layout lookup now spawns gsettings with
piped output, reads it on a worker, and kills+reaps the child whether it
finishes or times out — so a stalled gsettings leaves neither a blocked
process nor a parked worker (it is a one-shot startup call).
Also document, on the Wayland probe, that an inherited WAYLAND_SOCKET
(rare; normal launches get WAYLAND_DISPLAY) deliberately falls back to the
desktop frame to protect winit's connection — the same frame an app that
doesn't probe would get, a safe default rather than a regression.
Verified: rustfmt 1.98 clean, clippy -D warnings clean (default + egui),
tests pass.