Remove `Profile`. What it switched is now a `DockStyle` setting with the
dock-and-canvas behaviour as the default: `side_edge_zones`,
`new_dock_zones`, `maximize_item`, `close_others_item`,
`command_press_floats`, `float_hint`, and plain bools for
`wheel_selects_tabs` and `middle_click_closes_tabs`. The undock and
Close Panel Group labels go; an app sets `float_label`,
`float_group_label` and `close_group_label` instead.
Remove the size machinery that made frames stick: `DockContent::bare`,
`FixedSlot` and the split that took a parent's or sibling's half beside
a fixed-size frame, and the gap snapping to the nearest frame. A tab
group takes the largest maximum of its panels, so a panel without one
lifts the cap of the group it joins. A dragged divider stores a ratio
whatever mode it was saved with.
Sizes follow the reference rules: a divider resizes only the groups
that touch it and stops at their minimums; a four-way handle where a
divider ends on another moves both (`Divider::Intersection`); a split or
outer-edge drop that would leave a group without `min_pane` is not
offered, and a group too small to halve takes the drop as a tab.
Tests: one per rule, the stuck cases from the FilmCraft map, and a
property test over random drops checking minimums and divider travel.
While a drag offers a docking target, a hint under the pointer reads
"Hold Ctrl to float" ("Hold ⌘ to float" on macOS), from the
localizable float_hint_label (key dock.floatHint).
A side split replaced the anchor frame in place, so a split beside a
fixed-width frame such as a tool strip took the frame's fixed slot:
the two frames shared its width, and the parent's divider resized both.
The frame now keeps its size: on its outer side the new split wraps the
parent, on its inner side it halves the parent's other child. The drop
preview shows the half the new frame takes.
A side zone is a quarter of the frame deep, at least 32 pt and at most
half the frame, so a 64 pt tool strip offers its left and right edges
past the outer edge band. A pointer in the gap between frames resolves
to the nearest frame's edge instead of floating.
DockArea::float_bounds sets where floating windows, flyouts and drag ghosts may go; the default is the whole viewport. An app with its own title bar under a transparent macOS title bar passes the viewport below it, so a floating window can't be dropped, dragged or restored where pressing it moves the app window.
The drag strip beside the tabs, the column bar, the floating title bar and the strip above an icon group stay draggable, with the grab cursor and the hover outline, but paint no grip lines.
OpenAt checked the opened panel's movable, so a locked workspace refused
OpenAt while Open worked. It checks movable only for a panel already
shown. Workspace::set_visible takes the app's permissions, so hiding
cannot close a protected canvas.
Keeping focus on the handle after a pointer drag broke app shortcuts
that defer to a focused widget (egui_wants_keyboard_input), and arrow
keys moved both the canvas selection and the divider. A handle focused
by the pointer gives the focus up on release; one reached with Tab or
F6 keeps it, and arrow keys resize it.
Restoring beside a sibling subtree needed the exact same set of panels,
so a migrated layout without the canvas put the canvas in hidden. Restore
splits the anchor alone when the subtree changed. reconcile takes the
panels' permissions and never leaves a registered panel that cannot
close in hidden: it goes back to its recorded or default place, else to
the outer edge of the docked area.
On the first panel of the last column, the command picked that panel as
its own anchor and failed with InvalidPlacement. The anchor is a panel of
the last column outside the moving unit; when the unit already is that
column, the command does nothing.
A resizable FloatingFrame keeps a 4 pt band above its contents for the
Top, TopLeft and TopRight handles, so they take no title bar moves or
button clicks. They keep the opposite edges in place, respect the size
limits and the screen, take arrow keys when focused, and have hit ids.
Moves carry the whole frame rect, band included.
Ported from #189.
Co-authored-by: filipessmedeiros-collab <343926472+filipessmedeiros-collab@users.noreply.github.com>
Dock targets check the floating windows drawn this frame instead of every
window's last egui area rect. floating_layer says that an undrawn window
keeps its last area rect.
Arrow keys resize the handle right after a drag. Focus that came from
the pointer draws no highlight until a key is pressed, and hovering
still focuses nothing.
A left, right, top or bottom zone without anchor or side maps to
Placement::Edge, anchored on any docked panel that does not move. A
hidden panel is shown and moved in one step, all or nothing.
DockArea::hit_id gives the egui id of the control a Hit names, and
DockArea::floating_layer the layer of a floating window, so apps and
their tests stop rebuilding internal ids. The divider between stacked
groups has one id in docks and floating windows.
A panel body that reaches the bottom of its rect has no height of its
own, so a floated panel or group falls back to floating_size, else 320
points, instead of the whole frame height. A body in a window fitted to
its recorded height keeps that height. Panels leaving a frame fit their
content like panels leaving a dock.
Both are on in the Frame profile and off by default in the Dock profile;
DockStyle::wheel_selects_tabs and middle_click_closes_tabs set them. A
middle click closes only a closable panel.
Action::MovePanel moves one panel to a Target; NewDock and Float give it a group of its own.
DockCommand::Dock with a side that has no dock used to fail with InvalidPlacement for a panel
that shares its group.
A docked pane draws its body in a child ui clipped to the pane, so a body
wider than the pane no longer shifts a right or bottom pane's reserved
rect into the document. PaneLabels::move_pane takes {title}, so each
title bar's accessible name includes its pane ("Move Styles pane").
Pane title bars mirror with .rtl(true).
- Permissions::accepts_splits; PROTECTED takes no side splits, so a
group dropped on a protected canvas floats.
- A press of any pointer button in a panel body, stripless panels
included, reports the panel in DockOutput::focused.
- Right to left, tab strips, their header and drag bar, insertion slots
(DropTarget::right_to_left), accordion headers and chrome::TitleBar
(ChromeStyle::right_to_left) mirror. TitleBar truncates galley titles
like tab labels.
- DockStyle::close_menu_items and close_button hide the built-in close
controls without changing Permissions::closable.
- Menu labels say what moves: Move Panel to Group (move_panel_to_label)
and Move Group to (move_group_to_label).
- The column bar's button has its own accessibility labels
(collapse_column_label, expand_column_label).
- Placement::for_zone maps zones onto what the anchor's container takes;
DockCommand uses it and takes right_to_left.
- Layout::apply, apply_with_permissions and apply_all return the closed
panels.
- Placement::Group takes an optional preferred height.
docs/ui-docking.md covers the Dock and Frame profiles, the model and
actions, Workspace, the renderer and DockContent, gestures, DockCommand,
DockStyle::localized, hit rects and the DockDriver test driver, and links
the task-pane guide.
Also: ui.dock open, close and activate map in one place, the command tests
build as a cfg(test) module, the hit-kind table gets a type alias, the
testing example uses public API, and rustfmt.
Consent expiry is now per kind of data: renewing text no longer renews an expired image permission. A connection edit keeps models found by a test at the new address, and drops only the ones saved for the old one.
Add shared config.toml storage while keeping settings types, defaults and UI policy in each app.
Includes isolated in-memory operation, configurable portable paths, atomic saves with private backups, damaged-file preservation, schema migrations, and documented best-effort handling of external edits.
Validation: native macOS formatting, strict workspace clippy, 172 workspace tests and the craft-settings WASM check. Independent backup-permission and failed external-migration repros fail before the fixes and pass afterward.
Hosted CI: all seven UI and printing jobs pass across macOS, Linux, Windows and UI WASM.
Refs #12