Skip to main content

Ambient

Trait Ambient 

Source
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§

Source

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§

Source

fn scoped<P>(content: impl FnOnce(Self) -> P + 'static) -> AnyPiece
where P: Piece, Self: Sized,

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.

Source

fn app() -> Self
where 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.

Source

fn ambient() -> Self
where 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.

Source

fn try_ambient() -> Option<Self>
where Self: Sized,

Ambient::ambient without the panic.

Source

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".

Implementors§

Day API ↩ Guides· daybrite.dev