WindowFrame
A borderless-window frame: an invisible overlay of resize strips and corner cells along the four edges of a single content widget.
Public types
| Kind | Name |
|---|---|
struct | WindowFrame — Invisible overlay of resize strips and corner cells that gives a borderless window draggable edges |
const | WINDOW_FRAME_RESIZE_THICKNESS — Logical-pixel thickness of each resize strip, and the frame's default |
Public functions
WindowFrame
| Returns | Function |
|---|---|
| Constructors | |
Self | new(host: Rc<dyn PlatformTitleBarHost>) |
| Builder methods | |
Self | thickness(t: f32) |
Self | content(w: impl teksilo_core::IntoTeksiChild) |
Self | content_boxed(w: Box<dyn Widget>) |
| Methods | |
ResizeBorders | coarse_resize_borders(tokens: &InputTokens) |
Detailed description
WindowFrame is the canonical way to wrap a TitleBar + body for an
undecorated Wayland window. The content child fills the entire window
bounds — there is no visible padding — and the resize strips +
corners sit on top of the content along the edges. teksilo-core's
hit_test_recursive walks children in reverse insertion order, so
the strips and corners (added after content) get first crack at any
click that lands within thickness pixels of an edge; clicks
anywhere else fall through to the content.
Layout (with thickness = t):
┌─top─edge───────────────────────┐ ← top strip overlays content (0, 0, w, t)
│TL│ │TR│ ← corners overlay the strip ends
│──│ │──│
│L │ content (full) │R │ ← content fills (0, 0, w, h)
│──│ │──│
│BL│ │BR│
└─bottom─edge────────────────────┘
t defaults to 6 logical pixels but is configurable via
WindowFrame::thickness. With a small thickness the frame is
visually undetectable; the cursor only changes (and the resize
gesture only triggers) when the pointer is within t pixels of the
window boundary — with a mouse. A finger reaches the same edge
through each strip's Widget::hit_outset, which widens the band to
the density's target size without moving a pixel of layout; see
resize_strip.
Telling the OS the same number
On a platform where the window manager answers the resize hit test itself
— Windows, through WM_NCHITTEST — the frame's own strips never see the
press, so the two layers have to agree about how wide the band is or a
finger lands in the gap between them. The frame therefore publishes its
coarse band (the widened one, in logical pixels) as
HitRegions::resize_borders every frame, and the backend applies it to
coarse messages only, never shrinking the band a mouse gets.
That channel has one publisher per window — TitleBar
aggregates the drag region and the control buttons into one snapshot from
its own after_paint. The frame does not compete with it: the snapshot it
publishes carries a non-zero resize_borders and nothing else, which is the
documented shape a backend reads as a band update rather than a
replacement. after_paint is post-order, so wrapping the title bar (the
canonical shape — WindowFrame::content(VStack { TitleBar, body })) puts
the band update after the aggregate snapshot every frame.
Wrapping the frame in a WidgetBuilder method is safe:
WidgetWithHandlers forwards wants_after_paint / after_paint along with
the rest of the trait, so WindowFrame::new(host).content(..).on_tap(..)
still publishes. It did not always — the wrapper's forwarding list was
incomplete, and an unforwarded hook silences a publish with no diagnostic —
so the list is now exhaustive and lint-guarded at its own impl.
API reference
📖 Full rustdoc API for this module
pub struct WindowFrame
Import: use teksilo::widgets::WindowFrame;
Invisible overlay of resize strips and corner cells that gives a borderless window
draggable edges. The content child fills the full client area with no visible inset;
the strips are hit-test-only overlays along the outer thickness pixels.
#![allow(unused)] fn main() { pub struct WindowFrame { /* fields */ } }
Methods
pub fn new(host: Rc<dyn PlatformTitleBarHost>) -> Self
Create a frame bound to the given platform host. Use thickness
and content to configure it before adding to the tree.
pub fn thickness(mut self, t: f32) -> Self
Logical-pixel thickness of each resize strip. Default:
WINDOW_FRAME_RESIZE_THICKNESS.
pub fn content(mut self, w: impl teksilo_core::IntoTeksiChild) -> Self
Set the inner content widget — typically a VStack containing a
TitleBar and the application body.
pub fn content_boxed(mut self, w: Box<dyn Widget>) -> Self
Set the inner content widget from an already-boxed value. Prefer content
for unboxed widgets; use this variant when the concrete type is not known at the call site.
pub fn coarse_resize_borders(&self, tokens: &InputTokens) -> ResizeBorders
The band a coarse pointer actually catches on each edge, in logical
pixels: the strip's painted thickness widened to the density's target
size by Widget::hit_outset (24 dp Compact, 44 dp Touch).
This is what the frame publishes as HitRegions::resize_borders so a
backend that answers the resize hit test itself can use the same number.
It is deliberately not the mouse band: a mouse keeps the painted
thickness, and a backend must never shrink its own metric to this.
Uniform across the four edges — every strip is built at the same thickness — so a caller reading one field reads them all.
pub const WINDOW_FRAME_RESIZE_THICKNESS
Import: use teksilo::widgets::title_bar::WINDOW_FRAME_RESIZE_THICKNESS;
Logical-pixel thickness of each resize strip, and the frame's default.
The same 6 dp gutter the Splitter and the dock resize handle use. It is
fixed at every density: the strip is a hit-test-only overlay drawn over
the window's own edge, so widening it would eat into the content rather
than into empty space. A coarse pointer reaches it through
Widget::hit_outset (24 dp, 44 at Touch) over an unchanged 6 dp visual —
see engineering/docs/density-inventory.md and the touch design's Constants table.
#![allow(unused)] fn main() { pub const WINDOW_FRAME_RESIZE_THICKNESS: f32 = 6.0; }