Skip to main content

swiftui_native/view/
system.rs

1//! Files, services, the environment and preferences.
2//!
3//! Part of [`crate::view::View`]'s chainable API; Rust allows a type's
4//! inherent methods to be split across modules, and 400 of them in one file
5//! was a file nobody could find anything in.
6
7use super::{View};
8use crate::views;
9
10impl View {
11    // ── Files ──
12    //
13    // All three are bound: the flag that shows the panel is Rust's, so
14    // dismissing it writes `false` back rather than leaving the two sides to
15    // disagree about whether a panel is up.
16
17    /// `.fileExporter(isPresented:item:contentTypes:defaultFilename:onCompletion:onCancellation:)`
18    pub fn file_exporter(
19        self,
20        state: &views::BoundState<bool>,
21        item: Option<&str>,
22        kinds: &[views::ContentType],
23        default_filename: Option<&str>,
24        completion: impl Fn(views::FileOutcome) + 'static,
25        cancellation: impl Fn() + 'static,
26    ) -> Self {
27        Self::new(views::file_exporter(
28            &self.handle,
29            state,
30            item,
31            kinds,
32            default_filename,
33            completion,
34            cancellation,
35        ))
36    }
37
38    /// `.fileImporter(isPresented:allowedContentTypes:onCompletion:)`
39    pub fn file_importer(
40        self,
41        state: &views::BoundState<bool>,
42        kinds: &[views::ContentType],
43        completion: impl Fn(views::FileOutcome) + 'static,
44    ) -> Self {
45        Self::new(views::file_importer(&self.handle, state, kinds, completion))
46    }
47
48    /// `.fileMover(isPresented:file:onCompletion:)`
49    pub fn file_mover(
50        self,
51        state: &views::BoundState<bool>,
52        file: Option<&str>,
53        completion: impl Fn(views::FileOutcome) + 'static,
54    ) -> Self {
55        Self::new(views::file_mover(&self.handle, state, file, completion))
56    }
57
58    // ── Services and item providers ──
59
60    /// `.itemProvider(_:)` — what this view offers to a drag or a service.
61    pub fn item_provider(self, item: Option<&str>) -> Self {
62        Self::new(views::item_provider(&self.handle, item))
63    }
64
65    /// `.exportsItemProviders(_:items:onEdit:)`
66    pub fn exports_item_providers(
67        self,
68        kinds: &[views::ContentType],
69        items: &[&str],
70        edit: impl Fn(&[views::ItemProvider]) -> bool + 'static,
71    ) -> Self {
72        Self::new(views::exports_item_providers(&self.handle, kinds, items, edit))
73    }
74
75    /// `.importsItemProviders(_:onImport:)`
76    pub fn imports_item_providers(
77        self,
78        kinds: &[views::ContentType],
79        accept: impl Fn(&[views::ItemProvider]) -> bool + 'static,
80    ) -> Self {
81        Self::new(views::imports_item_providers(&self.handle, kinds, accept))
82    }
83
84    /// `.exportableToServices(_:)` — what the Services menu may take.
85    pub fn exportable_to_services(self, items: &[&str]) -> Self {
86        Self::new(views::exportable_to_services(&self.handle, items))
87    }
88
89    /// `.importableFromServices(for:action:)`
90    pub fn importable_from_services(self, accept: impl Fn(&[String]) -> bool + 'static) -> Self {
91        Self::new(views::importable_from_services(&self.handle, accept))
92    }
93
94    /// `.onReceive(_:perform:)` — a publisher's values, as `Int`s.
95    pub fn on_receive(self, complete_immediately: bool, perform: impl Fn(i64) + 'static) -> Self {
96        Self::new(views::on_receive(&self.handle, complete_immediately, perform))
97    }
98
99    /// `.onScrollGeometryChange(for:of:action:)`
100    /// `measure` is handed the scroll view's [`views::ScrollGeometry`] and
101    /// returns the value to watch; `action` is called with the old and the new
102    /// one. Both used to be blind, which left the modifier able to notice that
103    /// *something* had scrolled and nothing more.
104    pub fn on_scroll_geometry_change(
105        self,
106        measure: impl Fn(views::ScrollGeometry) -> i64 + 'static,
107        action: impl Fn(i64, i64) + 'static,
108    ) -> Self {
109        Self::new(views::on_scroll_geometry_change(&self.handle, measure, action))
110    }
111
112    /// `.onScrollGeometryChange(for: Double.self, …)` — the same, watching a
113    /// `Double`, which a scroll offset is.
114    pub fn on_scroll_geometry_change_f64(
115        self,
116        measure: impl Fn(views::ScrollGeometry) -> f64 + 'static,
117        action: impl Fn(f64, f64) + 'static,
118    ) -> Self {
119        Self::new(views::on_scroll_geometry_change_f64(&self.handle, measure, action))
120    }
121
122    /// `.onScrollTargetVisibilityChange(for:threshold:_:)` — which targets are
123    /// on screen, which is how a carousel knows what page it is on.
124    pub fn on_scroll_target_visibility_change(
125        self,
126        threshold: f64,
127        visible: impl Fn(&[i64]) + 'static,
128    ) -> Self {
129        Self::new(views::on_scroll_target_visibility_change(&self.handle, threshold, visible))
130    }
131
132    /// `.dismissalConfirmationDialog(_:shouldPresent:actions:message:)` — ask
133    /// before a sheet closes on unsaved work.
134    pub fn dismissal_confirmation_dialog(
135        self,
136        title: &str,
137        should_present: bool,
138        actions: View,
139        message: View,
140    ) -> Self {
141        Self::new(views::dismissal_confirmation_dialog(
142            &self.handle,
143            title,
144            should_present,
145            actions.handle(),
146            message.handle(),
147        ))
148    }
149
150    /// `.presentationPreventsAppTermination(_:)` — `None` restores the default.
151    pub fn presentation_prevents_app_termination(self, prevents: Option<bool>) -> Self {
152        Self::new(views::presentation_prevents_app_termination(&self.handle, prevents))
153    }
154
155    /// `.textInputFormattingControlVisibility(_:for:)`
156    pub fn text_input_formatting_control_visibility(
157        self,
158        visibility: views::Visibility,
159        placements: views::TextInputFormattingControlPlacementSet,
160    ) -> Self {
161        Self::new(views::text_input_formatting_control_visibility(
162            &self.handle,
163            visibility,
164            placements,
165        ))
166    }
167
168    /// `.handGestureShortcut(_:isEnabled:)` — the pinch that stands in for a
169    /// tap on a watch.
170    pub fn hand_gesture_shortcut(
171        self,
172        shortcut: views::HandGestureShortcut,
173        enabled: bool,
174    ) -> Self {
175        Self::new(views::hand_gesture_shortcut(&self.handle, shortcut, enabled))
176    }
177
178    /// `.assistiveAccessNavigationIcon(_:)`
179    pub fn assistive_access_navigation_icon(self, name: &str) -> Self {
180        Self::new(views::assistive_access_navigation_icon(&self.handle, name))
181    }
182
183    /// `.accessibilityRotor(_:textRanges:)` — a custom rotor VoiceOver can spin
184    /// through.
185    pub fn a11y_rotor(self, label: &str, text: &str) -> Self {
186        Self::new(views::accessibility_rotor(&self.handle, label, text))
187    }
188
189    /// `.accessibilityRotorEntry(id:in:)`
190    pub fn a11y_rotor_entry(self, id: views::HashableValue<'_>) -> Self {
191        Self::new(views::accessibility_rotor_entry(&self.handle, id))
192    }
193
194    /// `.accessibilityChartDescriptor(_:)`
195    pub fn a11y_chart_descriptor(self) -> Self {
196        Self::new(views::accessibility_chart_descriptor(&self.handle))
197    }
198
199    /// `.environmentObject(_:)`
200    pub fn environment_object(self) -> Self {
201        Self::new(views::environment_object(&self.handle))
202    }
203
204    /// `.focusedObject(_:)`
205    pub fn focused_object(self) -> Self {
206        Self::new(views::focused_object(&self.handle))
207    }
208
209    /// `.defaultAppStorage(_:)`
210    pub fn default_app_storage_standard(self) -> Self {
211        Self::new(views::default_app_storage_standard(&self.handle))
212    }
213
214    /// `.textRenderer(_:)`
215    pub fn text_renderer(self) -> Self {
216        Self::new(views::text_renderer(&self.handle))
217    }
218
219    /// `.attributedTextFormattingDefinition(_:)`
220    pub fn attributed_text_formatting_definition(self) -> Self {
221        Self::new(views::attributed_text_formatting_definition(&self.handle))
222    }
223
224    // ── Environment ──
225
226    /// `.environment(\.imageScale, _)`
227    pub fn environment_image_scale(self, scale: views::ImageScale) -> Self {
228        Self::new(views::environment_image_scale(&self.handle, scale))
229    }
230
231    /// `.environment(\.symbolVariants, _)`
232    pub fn environment_symbol_variants(self, value: views::SymbolVariants) -> Self {
233        Self::new(views::environment_symbol_variants(&self.handle, value))
234    }
235
236    /// `.transformEnvironment(\.imageScale, _)`
237    pub fn transform_environment_image_scale(
238        self,
239        action: impl Fn(views::ImageScale) -> views::ImageScale + 'static,
240    ) -> Self {
241        Self::new(views::transform_environment_image_scale(&self.handle, action))
242    }
243
244    /// `.focusedValue(_:)`
245    pub fn focused_value(self, present: bool) -> Self {
246        Self::new(views::focused_value(&self.handle, present))
247    }
248
249    // ── Preferences and anchors ──
250    //
251    // SwiftUI's way of passing a value *up* a view tree: a child writes one, an
252    // ancestor reads it and can place content with it. The key is fixed to an
253    // `Int` here — a generic key needs a Swift type per key, which is a
254    // different piece of work — but the mechanism is the whole of it.
255
256    /// `.preference(key:value:)` — write a value for an ancestor to collect.
257    pub fn preference(self, value: i64) -> Self {
258        Self::new(views::preference_int(&self.handle, value))
259    }
260
261    /// `.onPreferenceChange(_:perform:)` — observe what descendants wrote.
262    ///
263    /// The value is read back with [`views::observed_preference`] rather than
264    /// through a closure, because SwiftUI calls the handler on its own schedule
265    /// and the last value is what a caller wants.
266    pub fn on_preference_change(self) -> Self {
267        Self::new(views::on_preference_change_int(&self.handle))
268    }
269
270    /// `.transformPreference(_:_:)` — rewrite what passes through.
271    pub fn transform_preference(self) -> Self {
272        Self::new(views::transform_preference_double(&self.handle))
273    }
274
275    /// `.anchorPreference(key:value:transform:)` — write a preference derived
276    /// from this view's geometry.
277    pub fn anchor_preference(self, transform: impl Fn() -> i64 + 'static) -> Self {
278        Self::new(views::anchor_preference(&self.handle, transform))
279    }
280
281    /// `.transformAnchorPreference(key:value:transform:)`
282    pub fn transform_anchor_preference(self, transform: impl Fn(i64) -> i64 + 'static) -> Self {
283        Self::new(views::transform_anchor_preference(&self.handle, transform))
284    }
285
286    /// `.overlayPreferenceValue(_:alignment:_:)` — place content over this view
287    /// using what a descendant reported, which is how a badge follows the thing
288    /// it belongs to.
289    pub fn overlay_preference_value(self, alignment: views::Alignment, content: View) -> Self {
290        Self::new(views::overlay_preference_value(&self.handle, alignment, content.handle()))
291    }
292
293    /// `.backgroundPreferenceValue(_:alignment:_:)`
294    pub fn background_preference_value(self, alignment: views::Alignment, content: View) -> Self {
295        Self::new(views::background_preference_value(&self.handle, alignment, content.handle()))
296    }
297
298    /// `.gridCellAnchor(_:)` — where this cell sits in the space the grid gives
299    /// it, as a unit point.
300    pub fn grid_cell_anchor(self, x: f64, y: f64) -> Self {
301        Self::new(views::grid_cell_anchor(&self.handle, x, y))
302    }
303
304    /// `.popover(isPresented:attachmentAnchor:arrowEdge:content:)` bound to Rust
305    /// state — the anchored form, where the arrow points at a chosen point
306    /// rather than the view's middle.
307    pub fn popover_anchored(
308        self,
309        state: &views::BoundState<bool>,
310        anchor: (f64, f64),
311        arrow_edge: views::Edge,
312        content: View,
313    ) -> Self {
314        Self::new(views::popover_anchored(
315            &self.handle,
316            state,
317            anchor,
318            arrow_edge,
319            content.handle(),
320        ))
321    }
322
323    /// `.windowResizeAnchor(_:)` — `None` restores the default.
324    pub fn window_resize_anchor(self, anchor: Option<(f64, f64)>) -> Self {
325        Self::new(views::window_resize_anchor(&self.handle, anchor))
326    }
327
328}