pub struct BackFallbackView<V> { /* private fields */ }Expand description
An app-level back rule for per-tab navigator stacks: BackFallback claims
a back press only when no navigator does (it ranks after every
navigator, whatever the wire order), and back_fallback wraps the view it
belongs to. The Android/Material tab convention — back at a tab root
returns to the home tab, back at the home tab root exits — is one fallback:
use frust::{
BackFallback, Component, Get, GetUntracked, IndexedStack, NavigatorController, RwSignal,
Set, View, back_fallback, navigator, text,
};
const HOME: usize = 0;
#[derive(Default)]
struct App;
struct AppState {
selected: RwSignal<usize>,
tabs: [NavigatorController<AppState>; 3],
home_fallback: BackFallback,
}
impl Component for App {
type State = AppState;
fn init(&self) -> AppState {
let selected = RwSignal::new(HOME);
let tabs = [
NavigatorController::new(),
NavigatorController::new(),
NavigatorController::new(),
];
let probe = tabs.clone();
// Constructed ONCE. It claims a press only off the home tab with the
// selected tab at its root; any poppable tab claims first anyway.
let home_fallback = BackFallback::new(
move || {
let sel = selected.get_untracked();
sel != HOME && probe[sel].depth() <= 1
},
move || selected.set(HOME),
);
AppState { selected, tabs, home_fallback }
}
fn build(&self, state: &mut AppState) -> impl View<AppState> {
let [home, b, c] = &state.tabs;
// A tracked read: a selection change rebuilds.
let sel = state.selected.get();
back_fallback(
&state.home_fallback,
IndexedStack(
Some(sel),
(
navigator(home, || text("home")),
navigator(b, || text("search")),
navigator(c, || text("profile")),
),
),
)
}
}
frust::app!(App);Liveness contract. A fallback claims only while a back_fallback
wrapper over it is mounted: leaving the screen unmounts the wrapper and the
fallback stops claiming, though the handle lives on in app state. Wrap the
view every Component::build that should honor it, like
navigator.
Accepted residual. A wrapper mounted inside a branch input does not
reach — a non-selected IndexedStack child, or the page under a covering
page whose own navigator does not claim — still counts as live (it is
mounted). Gate the interest closure there on that branch being current.
An app-level back rule for per-tab navigator stacks: BackFallback claims
a back press only when no navigator does (it ranks after every
navigator, whatever the wire order), and back_fallback wraps the view it
belongs to. The Android/Material tab convention — back at a tab root
returns to the home tab, back at the home tab root exits — is one fallback:
use frust::{
BackFallback, Component, Get, GetUntracked, IndexedStack, NavigatorController, RwSignal,
Set, View, back_fallback, navigator, text,
};
const HOME: usize = 0;
#[derive(Default)]
struct App;
struct AppState {
selected: RwSignal<usize>,
tabs: [NavigatorController<AppState>; 3],
home_fallback: BackFallback,
}
impl Component for App {
type State = AppState;
fn init(&self) -> AppState {
let selected = RwSignal::new(HOME);
let tabs = [
NavigatorController::new(),
NavigatorController::new(),
NavigatorController::new(),
];
let probe = tabs.clone();
// Constructed ONCE. It claims a press only off the home tab with the
// selected tab at its root; any poppable tab claims first anyway.
let home_fallback = BackFallback::new(
move || {
let sel = selected.get_untracked();
sel != HOME && probe[sel].depth() <= 1
},
move || selected.set(HOME),
);
AppState { selected, tabs, home_fallback }
}
fn build(&self, state: &mut AppState) -> impl View<AppState> {
let [home, b, c] = &state.tabs;
// A tracked read: a selection change rebuilds.
let sel = state.selected.get();
back_fallback(
&state.home_fallback,
IndexedStack(
Some(sel),
(
navigator(home, || text("home")),
navigator(b, || text("search")),
navigator(c, || text("profile")),
),
),
)
}
}
frust::app!(App);Liveness contract. A fallback claims only while a back_fallback
wrapper over it is mounted: leaving the screen unmounts the wrapper and the
fallback stops claiming, though the handle lives on in app state. Wrap the
view every Component::build that should honor it, like
navigator.
Accepted residual. A wrapper mounted inside a branch input does not
reach — a non-selected IndexedStack child, or the page under a covering
page whose own navigator does not claim — still counts as live (it is
mounted). Gate the interest closure there on that branch being current.
An app-level back rule for per-tab navigator stacks: BackFallback claims
a back press only when no navigator does (it ranks after every
navigator, whatever the wire order), and back_fallback wraps the view it
belongs to. The Android/Material tab convention — back at a tab root
returns to the home tab, back at the home tab root exits — is one fallback:
use frust::{
BackFallback, Component, Get, GetUntracked, IndexedStack, NavigatorController, RwSignal,
Set, View, back_fallback, navigator, text,
};
const HOME: usize = 0;
#[derive(Default)]
struct App;
struct AppState {
selected: RwSignal<usize>,
tabs: [NavigatorController<AppState>; 3],
home_fallback: BackFallback,
}
impl Component for App {
type State = AppState;
fn init(&self) -> AppState {
let selected = RwSignal::new(HOME);
let tabs = [
NavigatorController::new(),
NavigatorController::new(),
NavigatorController::new(),
];
let probe = tabs.clone();
// Constructed ONCE. It claims a press only off the home tab with the
// selected tab at its root; any poppable tab claims first anyway.
let home_fallback = BackFallback::new(
move || {
let sel = selected.get_untracked();
sel != HOME && probe[sel].depth() <= 1
},
move || selected.set(HOME),
);
AppState { selected, tabs, home_fallback }
}
fn build(&self, state: &mut AppState) -> impl View<AppState> {
let [home, b, c] = &state.tabs;
// A tracked read: a selection change rebuilds.
let sel = state.selected.get();
back_fallback(
&state.home_fallback,
IndexedStack(
Some(sel),
(
navigator(home, || text("home")),
navigator(b, || text("search")),
navigator(c, || text("profile")),
),
),
)
}
}
frust::app!(App);Liveness contract. A fallback claims only while a back_fallback
wrapper over it is mounted: leaving the screen unmounts the wrapper and the
fallback stops claiming, though the handle lives on in app state. Wrap the
view every Component::build that should honor it, like
navigator.
Accepted residual. A wrapper mounted inside a branch input does not
reach — a non-selected IndexedStack child, or the page under a covering
page whose own navigator does not claim — still counts as live (it is
mounted). Gate the interest closure there on that branch being current.
The pass-through view back_fallback returns: renders its child unchanged
(Element is the child’s own) and counts its own mounts into the
BackFallback’s liveness — build increments it before the child builds,
teardown decrements it after the child is torn down.
Trait Implementations§
Source§impl<State: 'static, V: View<State>> View<State> for BackFallbackView<V>
impl<State: 'static, V: View<State>> View<State> for BackFallbackView<V>
Source§fn build(&self, ctx: &mut BuildCtx<'_>) -> Self::Element
fn build(&self, ctx: &mut BuildCtx<'_>) -> Self::Element
Source§fn rebuild(
&self,
prev: &Self,
element: &mut Self::Element,
ctx: &mut BuildCtx<'_>,
) -> ChangeFlags
fn rebuild( &self, prev: &Self, element: &mut Self::Element, ctx: &mut BuildCtx<'_>, ) -> ChangeFlags
prev (the previous view of the same type) against the live
element, mutating it in place and reporting what changed.Auto Trait Implementations§
impl<V> !RefUnwindSafe for BackFallbackView<V>
impl<V> !Send for BackFallbackView<V>
impl<V> !Sync for BackFallbackView<V>
impl<V> !UnwindSafe for BackFallbackView<V>
impl<V> Freeze for BackFallbackView<V>where
V: Freeze,
impl<V> Unpin for BackFallbackView<V>where
V: Unpin,
impl<V> UnsafeUnpin for BackFallbackView<V>where
V: UnsafeUnpin,
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> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§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.Source§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.Source§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.Source§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,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
impl<T> Read<Exclusive, BecauseExclusive> for Twhere
T: ?Sized,
Source§impl<T> StorageAccess<T> for T
impl<T> StorageAccess<T> for T
Source§fn as_borrowed(&self) -> &T
fn as_borrowed(&self) -> &T
Source§fn into_taken(self) -> T
fn into_taken(self) -> T
Source§impl<State, V> ViewSeq<State, One> for Vwhere
State: 'static,
V: View<State>,
impl<State, V> ViewSeq<State, One> for Vwhere
State: 'static,
V: View<State>,
Source§fn extend_views(self, out: &mut Vec<AnyView<State>>)
fn extend_views(self, out: &mut Vec<AnyView<State>>)
out — erasing each
with AnyView::new (a no-op for an element already an AnyView).