pub struct WetLayer(/* private fields */);Expand description
A surface for content being authored right now, which repaints without taking the scene’s item bands with it.
The case is ink. A stroke in flight changes on every pointer sample, and
there is no way to invalidate a SceneView’s foreground alone: the render
walker computes one needs_paint per node and gates that node’s paint
and its post_paint with it, so asking the view to repaint re-runs the
whole Under band as well. A wet layer is its own node, so
request_repaint marks that node and nothing else.
The painter draws in scene coordinates — the node lives inside the view’s content transform, so a wet stroke pans and zooms with the page for free, and a scene point can be drawn at its own value.
Where it lands in the paint order, and why: above every card and every
Interleaved item, under the Over band
and the view’s own chrome (marquee, magnet feedback, transform frame, debug
overlay). docs/teksilo-scene.md states the whole order.
let points: Rc<RefCell<Vec<Point>>> = Rc::default();
let wet = {
let points = points.clone();
WetLayer::new(move |canvas, _ctx| {
for p in points.borrow().iter() {
canvas.fill_circle(*p, 2.0, Color::BLACK);
}
})
};
let view = SceneView::new(Scene::new()).wet_layer(wet.clone());
// …and from a pointer handler, after pushing a point:
// wet.request_repaint(ctx);A cloneable handle: clone it into the pointer handler that feeds it and into
SceneView::wet_layer, the way a SceneModel is cloned into its views.
§More than one view
The comparison with SceneModel is meant literally. Mount the same layer
in two views — an overview beside a detail pane — and both paint it, and
request_repaint repaints both. A second view does
not displace the first.
One thing does not carry across, and it is a property of the door rather
than of this type: a repaint is requested through an
EventContext, and a context belongs to one
window’s tree. So request_repaint repaints the mounts in the calling
context’s own window and leaves a mount in another window to that window’s
own frame — an id from the other tree could not reach it anyway, because
ids are per-arena slot keys and two trees mint the same ones.
That last sentence is the reason request_repaint does not simply push the
ids it holds: it resolves each one in the calling tree through a typed door
first, so a stale id can only ever land on one of this layer’s own nodes
there — or trip an assertion. The rule and its one remaining edge (the same
layer mounted in two window-less trees) are written out at
request_repaint.
Implementations§
Source§impl WetLayer
impl WetLayer
Sourcepub fn new(
painter: impl Fn(&mut Canvas, &SceneItemPaintContext<'_>) + 'static,
) -> Self
pub fn new( painter: impl Fn(&mut Canvas, &SceneItemPaintContext<'_>) + 'static, ) -> Self
A wet surface painted by painter, in scene coordinates.
The closure is called on every repaint of a node this layer is mounted
in, and on nothing else. It owns whatever the app is authoring — a
stroke’s points, a rubber-band shape, a live measurement — typically
through an Rc<RefCell<…>> shared with the pointer handler that feeds
it.
Sourcepub fn request_repaint(&self, ctx: &mut EventContext<'_>)
pub fn request_repaint(&self, ctx: &mut EventContext<'_>)
Repaint the wet surface, and only it.
No relayout, no rebuild, no accessibility re-walk — and, crucially, no
repaint of the scene’s item bands: this marks nodes, one per view that
mounted the layer in ctx’s own window.
A no-op until the layer has been mounted through
SceneView::wet_layer and the view has built, which is the frame
before any handler can run.
§Why the id goes through a typed door
A [WidgetId] is a slot key in some arena, and every tree mints the
same ones. The window a mount was made in narrows the candidates — a
tree belongs to exactly one window — but a headless tree has no window,
so None == None is not a match, it is two unknowns comparing equal.
Marking on that alone is how a repaint from one test’s tree lands on
whatever unrelated widget happens to hold that slot in another’s.
So the mark is not pushed as a bare
request_repaint. It
goes through
with_widget_mut, which
resolves the id in the calling tree and hands the node over only if
it downcasts to this crate’s own WetLayerNode — and the closure then
checks it is a node of this layer. Both checks fail loudly
(debug_assert) rather
than quietly, so a cross-tree repaint is a test failure instead of a
stray dirty bit.
What that buys, stated exactly:
- An id naming a slot the calling tree does not fill, or fills with a
widget that has not opted into
Widget::as_any_mut— which is every ordinary widget, the default beingNone— reaches nothing, in both profiles. That is the case this used to get wrong: the foreign id was marked, and whatever held the slot repainted. - An id naming a
WetLayerNodeof this layer is marked, which is right: that node’s own mount is in the same list, so it wanted repainting anyway. - An id naming a node that has opted into
as_any_mutand is not one of this layer’s trips the assertion, so in a test or a dev build it is a panic. In release the closure is skipped and the node still takes theRepaintOnlymark, which costs one repaint of something already on screen — never a mutation, never a relayout, rebuild or accessibility change.
So this does not make one layer in two window-less trees work; it
makes the attempt fail where it can be seen. Drive each tree’s repaint
itself, from nodes.
Sourcepub fn nodes(&self) -> Vec<WetNode>
pub fn nodes(&self) -> Vec<WetNode>
The nodes this layer paints into — one per mounted view, in mount order, each paired with the window whose tree holds it.
Exposed for a caller that wants to drive the repaint through some other
door than request_repaint — a
WidgetTree::mark_needs_paint in a test, say. Such a caller owns the
question request_repaint answers from its context: which tree a
given node belongs to, which is why this reports
WetNode::window beside the id rather than the id alone. With one
view (the overwhelmingly common case) that is the one tree there is.
Trait Implementations§
Auto Trait Implementations§
impl !RefUnwindSafe for WetLayer
impl !Send for WetLayer
impl !Sync for WetLayer
impl !UnwindSafe for WetLayer
impl Freeze for WetLayer
impl Unpin for WetLayer
impl UnsafeUnpin for WetLayer
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can
then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be
further downcast into Rc<ConcreteType> where ConcreteType implements Trait.§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.impl<T> ErasedDestructor for Twhere
T: 'static,
§impl<T> Instrument for T
impl<T> Instrument for T
§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more