0003. The frame's API: a TitleBar builder with left and right slots, styled by FrameStyle
Date: 2026-10-11
Status
Proposed. The user asked for the shared frame. The API below is my design.
Context
The two copies of the frame had different signatures:
- Photograph:
title_bar(ui, title, custom, left, right), painted straight from egui's visuals. - UnAmp:
title_bar(ui, style, title, custom, menus). It has aFrameStyleresolved from its skin, and wraps its controls in aMenuBar. It lays the row out in separate child areas, because a menu bar takes its whole width and pushed the panel wider than the window.
Each app also repeated the same setup around the bar:
- the viewport flags;
- the header panel's padding and corner rounding;
- a transparent clear colour;
- the edges call;
- the input hook that returns the button release the compositor keeps.
Decision
-
TitleBar::new(title, &style), with.custom(bool)(default on) and.menu_bar(bool)(default off), finished by.show(ui, left, right). Both slots are always there, so UnAmp passes an empty right slot. -
Layout: each part is laid out in its own child area, in this order:
- the window buttons, at the right;
right, right to left, 8 px from the buttons;left, in the width that's left, measured from inside so a menu bar doesn't count as full width.
The title is centred on the window and truncated. This is UnAmp's overflow fix, extended to the right slot.
-
FrameStyleholds every colour the frame paints, the window controls apart from the app's buttons.FrameStyle::from_visualsis the unskinned look, with the fallbacks from UnAmp's skin code.- This means unskinned Photograph's window-button icons take the widget foreground colours rather than the text colours. The difference is slight.
-
Helpers for the setup:
viewport,header_frame(12×8 px padding, top corners rounded),clear_color,corner_radius,edgesandrelease_grab. The module docs list the five places an app wires them up. -
WINDOW_RADIUS= 15, libadwaita's, as both apps used.
Consequences
- Apps can't easily drift from the header-bar padding and corners, because the helpers set them.
- A builder can grow options, such as hiding maximise for a fixed-size window, without breaking callers.
FrameStylewill be filled by the skin loader once that moves here. Until then each app builds its own, and Photograph usesfrom_visuals.- Both apps change their call sites when they switch.
- Apps still own their panels' bottom-corner rounding, which depends on their layout. A helper for that waits until a pattern shows up.