pub trait Ambient: Clone + 'static {
// Required method
fn create() -> Self;
// Provided methods
fn scoped<P>(content: impl FnOnce(Self) -> P + 'static) -> AnyPiece
where P: Piece,
Self: Sized { ... }
fn app() -> Self
where Self: Sized { ... }
fn ambient() -> Self
where Self: Sized { ... }
fn try_ambient() -> Option<Self>
where Self: Sized { ... }
fn focused() -> Option<Self>
where Self: Sized { ... }
}Expand description
State an ancestor provides once and any descendant reads back by type: SwiftUI’s
@EnvironmentObject, and Day’s answer to “where does app state live?” (docs/state.md).
Implement it on a Copy struct of handles. Signal, Memo, Trigger and Store are all
Copy and all cheap, so the struct is a bundle of pointers that rides into closures without
Rc or clone() ceremony:
#[derive(Clone, Copy)]
struct Scene { selected: Signal<Option<u32>>, items: Store<Keyed<Item>> }
impl Ambient for Scene {
fn create() -> Self { Scene { selected: Signal::new(None), items: Store::new(..) } }
}
// one per window; File ▸ New Window gets its own:
Scene::scoped(|scene| my_shell(scene))
// anywhere below it:
let scene = Scene::ambient();
// in an app-wide menu action, which belongs to no window:
menu_item("New").action(|| if let Some(s) = Scene::focused() { s.add() })The alternative, a thread_local! holding Signal::global, is one instance for the whole
process, which is indistinguishable from correct until the app opens a second window
(docs/windows.md) and both windows start sharing a selection.
Required Methods§
Sourcefn create() -> Self
fn create() -> Self
A fresh instance. Called once per providing site: once per window for Ambient::scoped,
once per process for Ambient::app.
Provided Methods§
Sourcefn scoped<P>(content: impl FnOnce(Self) -> P + 'static) -> AnyPiece
fn scoped<P>(content: impl FnOnce(Self) -> P + 'static) -> AnyPiece
One instance owned by the scope this piece BUILDS in, provided to content and
everything under it. The per-window idiom: call it from a window’s shell and each window
gets its own.
Creation is deferred to build time, which is what makes that true: a piece’s
construction runs in the caller’s scope, and only its build runs inside the window’s
(day_core::launch_with for the primary, open_window for the rest). Creating the state
eagerly would hand every window the first one’s.
It provides on the current scope rather than a fresh child, and at a window’s root that
scope is the window’s own. That is what lets Ambient::focused find it: a context
lookup walks ancestors, so a value tucked into a child of the window scope would be
invisible to anything resolving from the window down, including every app-wide menu
command.
Sourcefn app() -> Selfwhere
Self: Sized,
fn app() -> Selfwhere
Self: Sized,
The one app-wide instance, created on first use and alive for the whole process. Visible
from every window, menu action, and task, with no with_environment needed.
Sourcefn ambient() -> Selfwhere
Self: Sized,
fn ambient() -> Selfwhere
Self: Sized,
The nearest instance an ancestor provided, panicking when there is none. The read a piece
writes when the value is a precondition of it existing at all, like
@EnvironmentObject, which likewise traps rather than rendering something wrong.
A build-time read, like environment: call it in a piece’s body and capture the value
in whatever closures need it. Calling it inside a reactive closure works on the first
run and panics on the next, because a re-running reaction is no longer inside the scope
that provided the value.
Sourcefn try_ambient() -> Option<Self>where
Self: Sized,
fn try_ambient() -> Option<Self>where
Self: Sized,
Ambient::ambient without the panic.
Sourcefn focused() -> Option<Self>where
Self: Sized,
fn focused() -> Option<Self>where
Self: Sized,
The instance belonging to the window that currently has focus, which is what an app-wide
menu command acts on. See focused_environment.
Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".