inset_embedder/views.rs
1//! Flutter counterpart: dart:ui `FlutterView`. The host owns the native
2//! window; this stable handle exposes its identity and current configuration.
3
4use crate::{Matrix4, Rect, ScreenId, Size, TextEditingValue, TextInputConfiguration};
5
6/// Opaque id for one [`View`] (`FlutterView.viewId`).
7#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
8pub struct ViewId(pub u64);
9
10/// An event for the engine to communicate view focus changes to the app.
11/// Flutter counterpart: `ViewFocusEvent` in `dart:ui/platform_dispatcher.dart`.
12#[derive(Clone, Copy, Debug)]
13pub struct ViewFocusEvent {
14 /// The ID of the view that experienced a focus change.
15 pub view_id: ViewId,
16 /// The state focus changed to.
17 pub state: ViewFocusState,
18 /// The direction focus changed to.
19 pub direction: ViewFocusDirection,
20}
21
22/// Represents the focus state of a given view.
23#[derive(Clone, Copy, Debug, PartialEq, Eq)]
24pub enum ViewFocusState {
25 /// The view does not have platform focus.
26 Unfocused,
27 /// The view has platform focus.
28 Focused,
29}
30
31/// Represents the direction in which focus transitioned across views.
32#[derive(Clone, Copy, Debug, PartialEq, Eq)]
33pub enum ViewFocusDirection {
34 /// The transition did not have a direction, such as a programmatic request or focus loss.
35 Undefined,
36 /// The transition was forward, typically from pressing Tab.
37 Forward,
38 /// The transition was backward, typically from pressing Shift+Tab.
39 Backward,
40}
41
42/// Physical padding on each side of a view (`dart:ui` `ViewPadding`).
43#[derive(Clone, Copy, Debug, PartialEq)]
44pub struct ViewPadding {
45 pub left: f64,
46 pub top: f64,
47 pub right: f64,
48 pub bottom: f64,
49}
50
51impl ViewPadding {
52 /// A view padding that has zeros for each edge.
53 pub const ZERO: ViewPadding = ViewPadding {
54 left: 0.0,
55 top: 0.0,
56 right: 0.0,
57 bottom: 0.0,
58 };
59}
60
61/// Immutable layout constraints for a view (`dart:ui` `ViewConstraints`).
62#[derive(Clone, Copy, Debug, PartialEq)]
63pub struct ViewConstraints {
64 pub min_width: f64,
65 pub max_width: f64,
66 pub min_height: f64,
67 pub max_height: f64,
68}
69
70impl ViewConstraints {
71 pub const fn new(
72 min_width: f64,
73 max_width: f64,
74 min_height: f64,
75 max_height: f64,
76 ) -> ViewConstraints {
77 ViewConstraints {
78 min_width,
79 max_width,
80 min_height,
81 max_height,
82 }
83 }
84
85 /// Constraints respected only by the given size.
86 pub const fn tight(width: f64, height: f64) -> ViewConstraints {
87 ViewConstraints {
88 min_width: width,
89 max_width: width,
90 min_height: height,
91 max_height: height,
92 }
93 }
94}
95
96impl Default for ViewConstraints {
97 fn default() -> ViewConstraints {
98 ViewConstraints {
99 min_width: 0.0,
100 max_width: f64::INFINITY,
101 min_height: 0.0,
102 max_height: f64::INFINITY,
103 }
104 }
105}
106
107/// Per-view geometry the host reports (`_ViewConfiguration` fields we use).
108#[derive(Clone, Copy, Debug, PartialEq)]
109pub struct ViewMetrics {
110 pub physical_size: [f64; 2],
111 pub physical_constraints: ViewConstraints,
112 pub device_pixel_ratio: f64,
113 pub padding: ViewPadding,
114 pub view_padding: ViewPadding,
115 pub view_insets: ViewPadding,
116 /// The screen the view is on, one of `Platform::screens`: `_ViewConfiguration.displayId`,
117 /// which `FlutterView.display` looks up. `None` while it is on none the host can name, as a
118 /// view without a window is.
119 pub screen: Option<ScreenId>,
120}
121
122impl Default for ViewMetrics {
123 fn default() -> ViewMetrics {
124 ViewMetrics {
125 physical_size: [0.0, 0.0],
126 physical_constraints: ViewConstraints::tight(0.0, 0.0),
127 device_pixel_ratio: 1.0,
128 padding: ViewPadding::ZERO,
129 view_padding: ViewPadding::ZERO,
130 view_insets: ViewPadding::ZERO,
131 screen: None,
132 }
133 }
134}
135
136/// A `null` field indicates that the platform or view does not have a preference
137/// and the fallback constants should be used instead.
138///
139/// Flutter counterpart: `GestureSettings` (`dart:ui` `window.dart`).
140#[derive(Clone, Copy, Debug, Default, PartialEq)]
141pub struct GestureSettings {
142 /// The number of physical pixels a pointer is allowed to drift before it is
143 /// considered an intentional movement.
144 ///
145 /// If `None`, the framework's default touch slop configuration should be used
146 /// instead.
147 pub physical_touch_slop: Option<f64>,
148
149 /// The number of physical pixels that the first and second tap of a double tap
150 /// can drift apart to still be recognized as a double tap.
151 ///
152 /// If `None`, the framework's default double tap slop configuration should be used
153 /// instead.
154 pub physical_double_tap_slop: Option<f64>,
155}
156
157impl GestureSettings {
158 /// Create a new [`GestureSettings`] object from an existing value, overwriting
159 /// all of the provided fields.
160 pub fn copy_with(
161 self,
162 physical_touch_slop: Option<f64>,
163 physical_double_tap_slop: Option<f64>,
164 ) -> GestureSettings {
165 GestureSettings {
166 physical_touch_slop: physical_touch_slop.or(self.physical_touch_slop),
167 physical_double_tap_slop: physical_double_tap_slop.or(self.physical_double_tap_slop),
168 }
169 }
170}
171
172/// A stable handle to one host-provided view.
173///
174/// [`present`](View::present) is Flutter `FlutterView.render`. The host owns
175/// the surface; the framework records a [`crate::Picture`] and hands it over.
176pub trait View: 'static {
177 fn id(&self) -> ViewId;
178 fn metrics(&self) -> ViewMetrics;
179
180 /// Flutter `FlutterView.gestureSettings`.
181 fn gesture_settings(&self) -> GestureSettings {
182 GestureSettings::default()
183 }
184
185 /// Renders and presents one scene, shared so the host may keep it: to present it
186 /// again when the window is seen again, and to tell a scene equal to the last from one
187 /// worth drawing. Physical pixels; the presenter applies no extra scaling. A host with
188 /// a compositor places the scene's surfaces in the same native transaction as the
189 /// picture; a host without renders the picture alone.
190 fn present(&self, scene: std::sync::Arc<crate::Scene>);
191
192 /// The view's text input, where the host has one.
193 fn text_input(&self) -> Option<&dyn TextInputHost> {
194 None
195 }
196
197 /// What whoever gave or asked for this view attached to it, such as which screen to show:
198 /// a request's [`ViewRequest::initial_data`](crate::ViewRequest::initial_data), or what an
199 /// embedder said, as Flutter web's `initialData` does. `None` for a view with none.
200 fn initial_data(&self) -> Option<String> {
201 None
202 }
203
204 /// The request this view arrived for ([`crate::Platform::request_view`]); `None` for a view
205 /// the platform gave the app unasked.
206 fn request_id(&self) -> Option<crate::RequestId> {
207 None
208 }
209
210 /// The window this view is in, where the platform lets the app manage it, as `NSView.window`
211 /// answers: every window on a desktop, whether the app asked for it or the platform gave it,
212 /// such as WaOS's window around a program's view. `None` for a view in nothing the app may
213 /// manage: a phone's screen, a web page's element, a host app's view. Closing a view is its
214 /// window's: a view with none is not the app's to close, and its way out is to quit.
215 fn window(&self) -> Option<crate::WindowRef> {
216 None
217 }
218}
219
220/// The host side of Flutter's text input connection for one view.
221pub trait TextInputHost {
222 /// Flutter `TextInput.attach` / `TextInputConnection.show`: start an IME
223 /// session for this view.
224 fn start(&self, configuration: &TextInputConfiguration);
225
226 /// Flutter `TextInputConnection.close`: end the IME session.
227 fn stop(&self);
228
229 /// Flutter `TextInputConnection.setEditingState`.
230 fn set_editing_state(&self, value: &TextEditingValue);
231
232 /// Flutter `TextInput.setComposingRect`.
233 fn set_composing_rect(&self, rect: Rect);
234
235 /// Flutter `TextInput.setCaretRect`.
236 fn set_caret_rect(&self, rect: Rect);
237
238 /// Flutter `TextInput.setEditableSizeAndTransform`.
239 fn set_client_geometry(&self, size: Size, transform: &Matrix4);
240
241 /// Whether this text input turns editing keys into edits before the framework sees
242 /// them: backspace and delete, caret movement, the line and document ends.
243 ///
244 /// Flutter has no such question because each of its embedders is written for one host:
245 /// its macOS and iOS embedders do interpret those keys, and its text field bindings for
246 /// those platforms step aside so a key is not acted on twice.
247 ///
248 /// Defaults to false, which is what a host built on a windowing library that reports
249 /// plain key presses should answer, whichever platform it runs on. A host that hands the
250 /// framework the operating system's own editing commands answers true, and the field
251 /// then leaves those keys to it.
252 fn handles_editing_keys(&self) -> bool {
253 false
254 }
255}