Skip to main content

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}