pub struct PaintContext<'a> {
pub layers: Vec<Layer>,
pub overlay_layers: Vec<Layer>,
pub clip_stack: ClipStack,
pub origin: Offset,
pub clip: ClipStackSnapshot,
pub text_system: Option<&'a mut TextSystem>,
/* private fields */
}Expand description
Accumulates draw commands, clips, overlays, effects, and paint-time services.
Coordinates are window-space logical pixels unless a widget contract says
otherwise. A fresh context contains one empty base layer, an empty clip
stack, zero origin and frame time, default interaction state, and no text
system. It is single-threaded because it exclusively borrows TextSystem.
§Examples
use ailloli_ui_runtime::scene::PaintCtx;
let ctx = PaintCtx::new();
assert_eq!(ctx.layers.len(), 1);
assert!(ctx.overlay_layers.is_empty() && ctx.text_system.is_none());Fields§
§layers: Vec<Layer>Draw commands grouped by clip layer (scissor / shader / stencil downstream).
overlay_layers: Vec<Layer>Top-level overlay layers appended after all base content.
clip_stack: ClipStackMutable active clip entries, ordered outermost to innermost.
origin: OffsetCaller-managed paint translation in logical pixels.
clip: ClipStackSnapshotSnapshot corresponding to the active clip stack.
text_system: Option<&'a mut TextSystem>Optional shared shaping engine and prepared-layout cache.
Implementations§
Source§impl<'a> PaintContext<'a>
Provides the operations defined for PaintContext<'a>.
impl<'a> PaintContext<'a>
Provides the operations defined for PaintContext<'a>.
Sourcepub fn new() -> Self
pub fn new() -> Self
Creates an empty paint accumulator with one base layer.
§Examples
use ailloli_ui_runtime::scene::{LayerKind, PaintCtx};
let ctx = PaintCtx::new();
assert_eq!(ctx.layers[0].kind, LayerKind::Base);
assert!(ctx.clip.is_empty());Sourcepub fn with_text_system(text_system: &'a mut TextSystem) -> Self
pub fn with_text_system(text_system: &'a mut TextSystem) -> Self
Creates a fresh context borrowing a text system.
Input interaction stays at its default and frame time stays zero.
§Examples
use ailloli_ui_runtime::scene::PaintCtx;
use ailloli_ui_text::TextSystem;
let mut text = TextSystem::new();
let ctx = PaintCtx::with_text_system(&mut text);
assert!(ctx.text_system.is_some());Sourcepub fn with_text_system_and_input(
text_system: &'a mut TextSystem,
input: InputSnapshot,
frame_time_ms: u128,
) -> Self
pub fn with_text_system_and_input( text_system: &'a mut TextSystem, input: InputSnapshot, frame_time_ms: u128, ) -> Self
Creates a text-enabled context with an input snapshot and frame timestamp.
frame_time_ms is an opaque monotonic host timestamp in milliseconds;
it is stored verbatim and is not compared with wall-clock time.
§Examples
use ailloli_ui_runtime::input::InputSnapshot;
use ailloli_ui_runtime::scene::PaintCtx;
use ailloli_ui_text::TextSystem;
let mut text = TextSystem::new();
let ctx = PaintCtx::with_text_system_and_input(
&mut text, InputSnapshot::default(), 250,
);
assert_eq!(ctx.frame_time_ms(), 250);Sourcepub fn text_engine_mut(&mut self) -> Option<&mut ParleyEngine>
pub fn text_engine_mut(&mut self) -> Option<&mut ParleyEngine>
Borrows the underlying Parley engine, or returns None without text services.
Mutations through the engine bypass TextSystem cache accounting; prefer
the higher-level text-system APIs unless direct shaping is required.
§Examples
use ailloli_ui_runtime::scene::PaintCtx;
let mut ctx = PaintCtx::new();
assert!(ctx.text_engine_mut().is_none());Sourcepub fn push(&mut self, cmd: DrawCmd)
pub fn push(&mut self, cmd: DrawCmd)
Appends a command to the current base or innermost isolated layer.
If public mutation removed all base layers, this method first recreates one with the current clip snapshot. Commands preserve call order.
§Panics
May panic if caller mutation leaves an active isolated-layer index that
no longer exists in layers.
§Examples
use ailloli_ui_core::{Color, Rect};
use ailloli_ui_runtime::scene::{DrawCmd, DrawRect, PaintCtx};
let mut ctx = PaintCtx::new();
ctx.push(DrawCmd::Rect(DrawRect { rect: Rect::new(0.0, 0.0, 4.0, 4.0), color: Color::WHITE }));
assert_eq!(ctx.layers[0].cmds.len(), 1);Sourcepub fn push_overlay(&mut self, cmd: DrawCmd)
pub fn push_overlay(&mut self, cmd: DrawCmd)
Pushes a command into the current top-level overlay target.
Outside an explicit overlay clip, consecutive calls share one unclipped
overlay layer. Self::into_scene places all overlay layers after base
layers regardless of when this method was called.
§Examples
use ailloli_ui_core::{Color, Rect};
use ailloli_ui_runtime::scene::{DrawCmd, DrawRect, PaintCtx};
let mut ctx = PaintCtx::new();
ctx.push_overlay(DrawCmd::Rect(DrawRect { rect: Rect::new(0.0, 0.0, 4.0, 4.0), color: Color::WHITE }));
assert_eq!(ctx.overlay_layers[0].cmds.len(), 1);Sourcepub fn with_overlay_clip(&mut self, clip: Rect, f: impl FnOnce(&mut Self))
pub fn with_overlay_clip(&mut self, clip: Rect, f: impl FnOnce(&mut Self))
Pushes overlay commands into a layer clipped by a window-space rect.
Overlay clips are top-level and intentionally do not inherit parent widget clips. Nested overlay scopes target the innermost layer. On normal return, an unscoped overlay command starts a fresh layer after the clipped scope, preserving procedural order.
§Panics
A panic in f propagates without popping the overlay target, so callers
that catch unwinding must discard the context rather than continue it.
§Examples
use ailloli_ui_core::{Color, Rect};
use ailloli_ui_runtime::scene::{DrawCmd, DrawRect, PaintCtx};
let mut ctx = PaintCtx::new();
let clip = Rect::new(10.0, 10.0, 20.0, 20.0);
ctx.with_overlay_clip(clip, |ctx| ctx.push_overlay(DrawCmd::Rect(DrawRect {
rect: clip, color: Color::WHITE,
})));
assert_eq!(ctx.overlay_layers[0].clip.scissor_rect(), Some(clip));Sourcepub fn with_clip(&mut self, clip: Rect, f: impl FnOnce(&mut Self))
pub fn with_clip(&mut self, clip: Rect, f: impl FnOnce(&mut Self))
Runs f with an additional nested rectangular clip and base layer.
The full ancestor stack is retained. A new base pass is started both on
entry and normal exit; empty passes are removed by Self::into_scene.
§Panics
A panic in f propagates before the clip is popped, so a caught unwind
leaves this context unbalanced.
§Examples
use ailloli_ui_core::{Color, Rect};
use ailloli_ui_runtime::scene::{DrawCmd, DrawRect, PaintCtx};
let mut ctx = PaintCtx::new();
let clip = Rect::new(0.0, 0.0, 10.0, 10.0);
ctx.with_clip(clip, |ctx| ctx.push(DrawCmd::Rect(DrawRect { rect: clip, color: Color::WHITE })));
let scene = ctx.into_scene();
assert_eq!(scene.layers[0].clip.scissor_rect(), Some(clip));Sourcepub fn with_clip_isolated(&mut self, clip: Rect, f: impl FnOnce(&mut Self))
pub fn with_clip_isolated(&mut self, clip: Rect, f: impl FnOnce(&mut Self))
Pushes a single rectangular clip for a new layer without inheriting the stack.
Intersects clip with the current parent scissor when present. Useful for
widget-local viewports that need a “fresh” clip stack (e.g. embedded
surfaces, off-screen viewports) so they don’t re-apply expensive ancestor
masks (round mask, stencil) on every contained draw. Most widgets should
prefer Self::with_clip which keeps the full ancestor stack.
This is implemented as an isolated offscreen layer with no effects. If the requested rectangle does not intersect a parent scissor, the current compatibility behavior falls back to the requested rectangle rather than representing an empty clip.
§Panics
A panic in f propagates before the saved stack and snapshot are
restored; a caught unwind leaves the context unsuitable for reuse.
§Examples
use ailloli_ui_core::{Color, Rect};
use ailloli_ui_runtime::scene::{DrawCmd, DrawRect, PaintCtx};
let mut ctx = PaintCtx::new();
let clip = Rect::new(2.0, 3.0, 8.0, 9.0);
ctx.with_clip_isolated(clip, |ctx| ctx.push(DrawCmd::Rect(DrawRect { rect: clip, color: Color::WHITE })));
assert!(ctx.into_scene().layers[0].isolated);Sourcepub fn with_isolated_effects(
&mut self,
clip: Rect,
effects: IsolatedEffects,
f: impl FnOnce(&mut Self),
)
pub fn with_isolated_effects( &mut self, clip: Rect, effects: IsolatedEffects, f: impl FnOnce(&mut Self), )
Runs f in a fresh clipped offscreen layer with post-effects.
The requested logical-pixel clip is intersected with the parent’s coarse
scissor when they overlap; disjoint rectangles currently fall back to
the requested clip. Nested depth starts at zero and is cast to u8, so
more than 256 nested scopes wrap the stored diagnostic depth.
§Panics
A panic in f prevents stack restoration. Public mutation of layers
inside f can also invalidate the recorded isolated index.
§Examples
use ailloli_ui_core::{Color, Rect};
use ailloli_ui_runtime::scene::{DrawCmd, DrawRect, IsolatedEffects, PaintCtx};
let mut ctx = PaintCtx::new();
let clip = Rect::new(0.0, 0.0, 20.0, 20.0);
ctx.with_isolated_effects(clip, IsolatedEffects { opacity: 0.5, ..Default::default() },
|ctx| ctx.push(DrawCmd::Rect(DrawRect { rect: clip, color: Color::WHITE })));
assert_eq!(ctx.into_scene().layers[0].effects.opacity, 0.5);Sourcepub fn with_clip_shape(
&mut self,
clip: ClipShape,
is_window_root: bool,
f: impl FnOnce(&mut Self),
)
pub fn with_clip_shape( &mut self, clip: ClipShape, is_window_root: bool, f: impl FnOnce(&mut Self), )
Runs f with one additional clip shape and base-layer pass.
is_window_root is preserved on the clip entry for downstream surface
handling. The active clip snapshot and stack are restored on normal
return, and a new pass is opened after the scope.
§Panics
A panic in f propagates without restoring the clip stack.
§Examples
use ailloli_ui_core::{ClipShape, Color, Rect};
use ailloli_ui_runtime::scene::{DrawCmd, DrawRect, PaintCtx};
let mut ctx = PaintCtx::new();
let rect = Rect::new(0.0, 0.0, 20.0, 10.0);
ctx.with_clip_shape(ClipShape::Rect(rect), true, |ctx| {
ctx.push(DrawCmd::Rect(DrawRect { rect, color: Color::WHITE }));
});
assert!(ctx.into_scene().layers[0].clip.entries()[0].is_window_root);Sourcepub fn current_clip(&self) -> ClipStackSnapshot
pub fn current_clip(&self) -> ClipStackSnapshot
Clones the active clip snapshot.
The returned snapshot remains unchanged after later scope transitions.
§Examples
use ailloli_ui_runtime::scene::PaintCtx;
assert!(PaintCtx::new().current_clip().is_empty());Sourcepub fn current_clip_bbox(&self) -> Option<Rect>
pub fn current_clip_bbox(&self) -> Option<Rect>
Returns the coarse axis-aligned intersection of active clip bounds.
None means no active clip or a disjoint stack.
§Examples
use ailloli_ui_runtime::scene::PaintCtx;
assert_eq!(PaintCtx::new().current_clip_bbox(), None);Sourcepub fn set_current_interaction(
&mut self,
interaction: InputInteraction,
) -> InputInteraction
pub fn set_current_interaction( &mut self, interaction: InputInteraction, ) -> InputInteraction
Replaces paint-time interaction flags and returns the previous value.
This does not invalidate any element; the paint traversal uses it to scope a widget’s focused, hovered, and pressed state.
§Examples
use ailloli_ui_runtime::input::InputInteraction;
use ailloli_ui_runtime::scene::PaintCtx;
let mut ctx = PaintCtx::new();
let previous = ctx.set_current_interaction(InputInteraction { hovered: true, ..Default::default() });
assert!(!previous.hovered && ctx.is_hovered());Sourcepub fn interaction(&self) -> InputInteraction
pub fn interaction(&self) -> InputInteraction
Returns all current paint-time interaction flags by value.
§Examples
use ailloli_ui_runtime::scene::PaintCtx;
assert_eq!(PaintCtx::new().interaction(), Default::default());Sourcepub fn is_focused(&self) -> bool
pub fn is_focused(&self) -> bool
Returns whether the currently painted widget owns keyboard focus.
§Examples
use ailloli_ui_runtime::scene::PaintCtx;
assert!(!PaintCtx::new().is_focused());Sourcepub fn has_focus_within(&self) -> bool
pub fn has_focus_within(&self) -> bool
Whether this element or a descendant owns keyboard focus.
§Examples
use ailloli_ui_runtime::scene::PaintCtx;
assert!(!PaintCtx::new().has_focus_within());Sourcepub fn is_hovered(&self) -> bool
pub fn is_hovered(&self) -> bool
Returns whether the pointer currently hovers the painted widget.
§Examples
use ailloli_ui_runtime::scene::PaintCtx;
assert!(!PaintCtx::new().is_hovered());Sourcepub fn is_pressed(&self) -> bool
pub fn is_pressed(&self) -> bool
Returns whether the painted widget owns an active pointer press.
§Examples
use ailloli_ui_runtime::scene::PaintCtx;
assert!(!PaintCtx::new().is_pressed());Sourcepub fn frame_time_ms(&self) -> u128
pub fn frame_time_ms(&self) -> u128
Returns the opaque host frame timestamp in milliseconds.
A context created without explicit input uses zero. This is not elapsed time and arithmetic is the caller’s responsibility.
§Examples
use ailloli_ui_runtime::scene::PaintCtx;
assert_eq!(PaintCtx::new().frame_time_ms(), 0);Sourcepub fn into_scene(self) -> Scene
pub fn into_scene(self) -> Scene
Consumes the context and returns non-empty layers in final paint order.
Empty base and overlay layers are removed. Remaining base layers come
first with kind normalized to LayerKind::Base, followed by every
overlay layer normalized to LayerKind::Overlay. Command order within
each layer is unchanged.
§Examples
use ailloli_ui_core::{Color, Rect};
use ailloli_ui_runtime::scene::{DrawCmd, DrawRect, LayerKind, PaintCtx};
let mut ctx = PaintCtx::new();
ctx.push_overlay(DrawCmd::Rect(DrawRect { rect: Rect::new(0.0, 0.0, 1.0, 1.0), color: Color::WHITE }));
let scene = ctx.into_scene();
assert_eq!(scene.layers.len(), 1);
assert_eq!(scene.layers[0].kind, LayerKind::Overlay);Trait Implementations§
Source§impl<'a> Default for PaintContext<'a>
Implements the Default contract for PaintContext<'a>.
impl<'a> Default for PaintContext<'a>
Implements the Default contract for PaintContext<'a>.
Source§impl<'a> Painter for PaintContext<'a>
Implements the Painter contract for PaintContext<'a>.
impl<'a> Painter for PaintContext<'a>
Implements the Painter contract for PaintContext<'a>.