Skip to main content

shield

Function shield 

Source
pub fn shield<State, V>(child: V) -> ShieldView<State>
where State: 'static, V: View<State>,
Expand description

Platform-view embedding (platform-views feature): reserve layout space for a native view (a map, a video player, …) composited alongside the frust surface. platform_view takes the "dev.frust.<Factory>"-style native factory name registered on each platform and returns a builder (PlatformViewView) over the params/size contract — flat-re-exported from frust-widgets so app code never names that crate directly.

§Paint contract (Mode B)

Under Mode B compositing the frust surface itself is translucent, so any region this slot’s parent doesn’t paint over is a window straight through to the native view (or the OS background) behind it — this is the mechanism Mode B relies on, not a bug. Size/position a slot deliberately, and don’t rely on an unpainted sibling region staying opaque.

Mode B is selected by the generated host’s FRUST_TRANSLUCENT_SURFACE (Android)/translucentSurface (iOS) build-time constant — not from Rust: that constant drives, in one host-glue branch, the native window’s pixel format (PixelFormat.TRANSLUCENT/CAMetalLayer.isOpaque = false), the native-sibling z-order/subview arrangement, and the nativeSetSurfaceMode/frust_set_surface_mode call together (flipping only some of these is a host-template defect). There is no app-Rust opt-in call; the generated project’s template is the sole route into Mode B.

use frust::{AnyView, Column, Component, any, platform_view, text};

#[derive(Default)]
struct MapDemo;

impl Component for MapDemo {
    type State = ();

    fn init(&self) -> Self::State {}

    fn build(&self, _state: &mut Self::State) -> AnyView<Self::State> {
        any(Column(vec![
            any(text("map below")),
            any(platform_view("dev.frust.MapFactory")
                .params_json(r#"{"style":"dark"}"#)
                .size(320.0, 240.0)),
        ]))
    }
}

frust::app!(MapDemo);

§Z-shields (shield)

A slot marked .interactive() forwards a touch-DOWN inside its rect to the native view, including one that landed on frust chrome painted over it (the OS-side hit test knows nothing about the frust scene). Wrap that chrome in shield and it keeps winning input: the wrapper reports the rect it painted every frame, and the shell hands the overlapping ones to the host with the slot’s placement. Mark child as frust content that must keep receiving pointer input even where it paints over an interactive platform-view slot (the z-shield — see the module docs).

Layout/paint/event/semantics transparent: the child is laid out under the wrapper’s own constraints at its own size, painted unchanged, and routed events unchanged. The only added behavior is one PaintCtx::report_input_shield call per paint, carrying the rect the wrapper just painted into.

Wrap the narrowest chrome that actually needs input — a shield is a hole in the native view’s input region, so an over-wide one silently takes touches away from it.

ⓘ
any(Stack(vec![
    any(platform_view("dev.frust.MapFactory").interactive()),
    any(shield(button("recenter", |s: &mut State| s.recenter()))),
]))