swiftui-native 0.0.1

SwiftUI from pure Rust — no Swift bridge, no shim, only swift-runtime calls
Documentation
//! Files, services, the environment and preferences.
//!
//! Part of [`crate::view::View`]'s chainable API; Rust allows a type's
//! inherent methods to be split across modules, and 400 of them in one file
//! was a file nobody could find anything in.

use super::{View};
use crate::views;

impl View {
    // ── Files ──
    //
    // All three are bound: the flag that shows the panel is Rust's, so
    // dismissing it writes `false` back rather than leaving the two sides to
    // disagree about whether a panel is up.

    /// `.fileExporter(isPresented:item:contentTypes:defaultFilename:onCompletion:onCancellation:)`
    pub fn file_exporter(
        self,
        state: &views::BoundState<bool>,
        item: Option<&str>,
        kinds: &[views::ContentType],
        default_filename: Option<&str>,
        completion: impl Fn(views::FileOutcome) + 'static,
        cancellation: impl Fn() + 'static,
    ) -> Self {
        Self::new(views::file_exporter(
            &self.handle,
            state,
            item,
            kinds,
            default_filename,
            completion,
            cancellation,
        ))
    }

    /// `.fileImporter(isPresented:allowedContentTypes:onCompletion:)`
    pub fn file_importer(
        self,
        state: &views::BoundState<bool>,
        kinds: &[views::ContentType],
        completion: impl Fn(views::FileOutcome) + 'static,
    ) -> Self {
        Self::new(views::file_importer(&self.handle, state, kinds, completion))
    }

    /// `.fileMover(isPresented:file:onCompletion:)`
    pub fn file_mover(
        self,
        state: &views::BoundState<bool>,
        file: Option<&str>,
        completion: impl Fn(views::FileOutcome) + 'static,
    ) -> Self {
        Self::new(views::file_mover(&self.handle, state, file, completion))
    }

    // ── Services and item providers ──

    /// `.itemProvider(_:)` — what this view offers to a drag or a service.
    pub fn item_provider(self, item: Option<&str>) -> Self {
        Self::new(views::item_provider(&self.handle, item))
    }

    /// `.exportsItemProviders(_:items:onEdit:)`
    pub fn exports_item_providers(
        self,
        kinds: &[views::ContentType],
        items: &[&str],
        edit: impl Fn(&[views::ItemProvider]) -> bool + 'static,
    ) -> Self {
        Self::new(views::exports_item_providers(&self.handle, kinds, items, edit))
    }

    /// `.importsItemProviders(_:onImport:)`
    pub fn imports_item_providers(
        self,
        kinds: &[views::ContentType],
        accept: impl Fn(&[views::ItemProvider]) -> bool + 'static,
    ) -> Self {
        Self::new(views::imports_item_providers(&self.handle, kinds, accept))
    }

    /// `.exportableToServices(_:)` — what the Services menu may take.
    pub fn exportable_to_services(self, items: &[&str]) -> Self {
        Self::new(views::exportable_to_services(&self.handle, items))
    }

    /// `.importableFromServices(for:action:)`
    pub fn importable_from_services(self, accept: impl Fn(&[String]) -> bool + 'static) -> Self {
        Self::new(views::importable_from_services(&self.handle, accept))
    }

    /// `.onReceive(_:perform:)` — a publisher's values, as `Int`s.
    pub fn on_receive(self, complete_immediately: bool, perform: impl Fn(i64) + 'static) -> Self {
        Self::new(views::on_receive(&self.handle, complete_immediately, perform))
    }

    /// `.onScrollGeometryChange(for:of:action:)`
    /// `measure` is handed the scroll view's [`views::ScrollGeometry`] and
    /// returns the value to watch; `action` is called with the old and the new
    /// one. Both used to be blind, which left the modifier able to notice that
    /// *something* had scrolled and nothing more.
    pub fn on_scroll_geometry_change(
        self,
        measure: impl Fn(views::ScrollGeometry) -> i64 + 'static,
        action: impl Fn(i64, i64) + 'static,
    ) -> Self {
        Self::new(views::on_scroll_geometry_change(&self.handle, measure, action))
    }

    /// `.onScrollGeometryChange(for: Double.self, …)` — the same, watching a
    /// `Double`, which a scroll offset is.
    pub fn on_scroll_geometry_change_f64(
        self,
        measure: impl Fn(views::ScrollGeometry) -> f64 + 'static,
        action: impl Fn(f64, f64) + 'static,
    ) -> Self {
        Self::new(views::on_scroll_geometry_change_f64(&self.handle, measure, action))
    }

    /// `.onScrollTargetVisibilityChange(for:threshold:_:)` — which targets are
    /// on screen, which is how a carousel knows what page it is on.
    pub fn on_scroll_target_visibility_change(
        self,
        threshold: f64,
        visible: impl Fn(&[i64]) + 'static,
    ) -> Self {
        Self::new(views::on_scroll_target_visibility_change(&self.handle, threshold, visible))
    }

    /// `.dismissalConfirmationDialog(_:shouldPresent:actions:message:)` — ask
    /// before a sheet closes on unsaved work.
    pub fn dismissal_confirmation_dialog(
        self,
        title: &str,
        should_present: bool,
        actions: View,
        message: View,
    ) -> Self {
        Self::new(views::dismissal_confirmation_dialog(
            &self.handle,
            title,
            should_present,
            actions.handle(),
            message.handle(),
        ))
    }

    /// `.presentationPreventsAppTermination(_:)` — `None` restores the default.
    pub fn presentation_prevents_app_termination(self, prevents: Option<bool>) -> Self {
        Self::new(views::presentation_prevents_app_termination(&self.handle, prevents))
    }

    /// `.textInputFormattingControlVisibility(_:for:)`
    pub fn text_input_formatting_control_visibility(
        self,
        visibility: views::Visibility,
        placements: views::TextInputFormattingControlPlacementSet,
    ) -> Self {
        Self::new(views::text_input_formatting_control_visibility(
            &self.handle,
            visibility,
            placements,
        ))
    }

    /// `.handGestureShortcut(_:isEnabled:)` — the pinch that stands in for a
    /// tap on a watch.
    pub fn hand_gesture_shortcut(
        self,
        shortcut: views::HandGestureShortcut,
        enabled: bool,
    ) -> Self {
        Self::new(views::hand_gesture_shortcut(&self.handle, shortcut, enabled))
    }

    /// `.assistiveAccessNavigationIcon(_:)`
    pub fn assistive_access_navigation_icon(self, name: &str) -> Self {
        Self::new(views::assistive_access_navigation_icon(&self.handle, name))
    }

    /// `.accessibilityRotor(_:textRanges:)` — a custom rotor VoiceOver can spin
    /// through.
    pub fn a11y_rotor(self, label: &str, text: &str) -> Self {
        Self::new(views::accessibility_rotor(&self.handle, label, text))
    }

    /// `.accessibilityRotorEntry(id:in:)`
    pub fn a11y_rotor_entry(self, id: views::HashableValue<'_>) -> Self {
        Self::new(views::accessibility_rotor_entry(&self.handle, id))
    }

    /// `.accessibilityChartDescriptor(_:)`
    pub fn a11y_chart_descriptor(self) -> Self {
        Self::new(views::accessibility_chart_descriptor(&self.handle))
    }

    /// `.environmentObject(_:)`
    pub fn environment_object(self) -> Self {
        Self::new(views::environment_object(&self.handle))
    }

    /// `.focusedObject(_:)`
    pub fn focused_object(self) -> Self {
        Self::new(views::focused_object(&self.handle))
    }

    /// `.defaultAppStorage(_:)`
    pub fn default_app_storage_standard(self) -> Self {
        Self::new(views::default_app_storage_standard(&self.handle))
    }

    /// `.textRenderer(_:)`
    pub fn text_renderer(self) -> Self {
        Self::new(views::text_renderer(&self.handle))
    }

    /// `.attributedTextFormattingDefinition(_:)`
    pub fn attributed_text_formatting_definition(self) -> Self {
        Self::new(views::attributed_text_formatting_definition(&self.handle))
    }

    // ── Environment ──

    /// `.environment(\.imageScale, _)`
    pub fn environment_image_scale(self, scale: views::ImageScale) -> Self {
        Self::new(views::environment_image_scale(&self.handle, scale))
    }

    /// `.environment(\.symbolVariants, _)`
    pub fn environment_symbol_variants(self, value: views::SymbolVariants) -> Self {
        Self::new(views::environment_symbol_variants(&self.handle, value))
    }

    /// `.transformEnvironment(\.imageScale, _)`
    pub fn transform_environment_image_scale(
        self,
        action: impl Fn(views::ImageScale) -> views::ImageScale + 'static,
    ) -> Self {
        Self::new(views::transform_environment_image_scale(&self.handle, action))
    }

    /// `.focusedValue(_:)`
    pub fn focused_value(self, present: bool) -> Self {
        Self::new(views::focused_value(&self.handle, present))
    }

    // ── Preferences and anchors ──
    //
    // SwiftUI's way of passing a value *up* a view tree: a child writes one, an
    // ancestor reads it and can place content with it. The key is fixed to an
    // `Int` here — a generic key needs a Swift type per key, which is a
    // different piece of work — but the mechanism is the whole of it.

    /// `.preference(key:value:)` — write a value for an ancestor to collect.
    pub fn preference(self, value: i64) -> Self {
        Self::new(views::preference_int(&self.handle, value))
    }

    /// `.onPreferenceChange(_:perform:)` — observe what descendants wrote.
    ///
    /// The value is read back with [`views::observed_preference`] rather than
    /// through a closure, because SwiftUI calls the handler on its own schedule
    /// and the last value is what a caller wants.
    pub fn on_preference_change(self) -> Self {
        Self::new(views::on_preference_change_int(&self.handle))
    }

    /// `.transformPreference(_:_:)` — rewrite what passes through.
    pub fn transform_preference(self) -> Self {
        Self::new(views::transform_preference_double(&self.handle))
    }

    /// `.anchorPreference(key:value:transform:)` — write a preference derived
    /// from this view's geometry.
    pub fn anchor_preference(self, transform: impl Fn() -> i64 + 'static) -> Self {
        Self::new(views::anchor_preference(&self.handle, transform))
    }

    /// `.transformAnchorPreference(key:value:transform:)`
    pub fn transform_anchor_preference(self, transform: impl Fn(i64) -> i64 + 'static) -> Self {
        Self::new(views::transform_anchor_preference(&self.handle, transform))
    }

    /// `.overlayPreferenceValue(_:alignment:_:)` — place content over this view
    /// using what a descendant reported, which is how a badge follows the thing
    /// it belongs to.
    pub fn overlay_preference_value(self, alignment: views::Alignment, content: View) -> Self {
        Self::new(views::overlay_preference_value(&self.handle, alignment, content.handle()))
    }

    /// `.backgroundPreferenceValue(_:alignment:_:)`
    pub fn background_preference_value(self, alignment: views::Alignment, content: View) -> Self {
        Self::new(views::background_preference_value(&self.handle, alignment, content.handle()))
    }

    /// `.gridCellAnchor(_:)` — where this cell sits in the space the grid gives
    /// it, as a unit point.
    pub fn grid_cell_anchor(self, x: f64, y: f64) -> Self {
        Self::new(views::grid_cell_anchor(&self.handle, x, y))
    }

    /// `.popover(isPresented:attachmentAnchor:arrowEdge:content:)` bound to Rust
    /// state — the anchored form, where the arrow points at a chosen point
    /// rather than the view's middle.
    pub fn popover_anchored(
        self,
        state: &views::BoundState<bool>,
        anchor: (f64, f64),
        arrow_edge: views::Edge,
        content: View,
    ) -> Self {
        Self::new(views::popover_anchored(
            &self.handle,
            state,
            anchor,
            arrow_edge,
            content.handle(),
        ))
    }

    /// `.windowResizeAnchor(_:)` — `None` restores the default.
    pub fn window_resize_anchor(self, anchor: Option<(f64, f64)>) -> Self {
        Self::new(views::window_resize_anchor(&self.handle, anchor))
    }

}