Skip to main content

apace_traits/
shell.rs

1//! Abstraction over windowing / operating system ("shell") functionality
2
3use cursor_icon::CursorIcon;
4
5/// Input-method intent, independent of a particular windowing library.
6#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
7pub enum ImePurpose {
8    #[default]
9    Normal,
10    Password,
11}
12
13/// Type representing an error performing a clipboard operation
14// TODO: fill out with meaningful errors
15pub struct ClipboardError;
16
17/// Abstraction over windowing / operating system ("shell") functionality that allows a Apace document
18/// to access that functionality without depending on a specific shell environment.
19pub trait ShellProvider: Send + Sync + 'static {
20    fn set_ime_purpose(&self, purpose: ImePurpose) {
21        let _ = purpose;
22    }
23    fn request_redraw(&self) {}
24    fn set_cursor(&self, icon: Option<CursorIcon>) {
25        let _ = icon;
26    }
27    fn set_window_title(&self, title: String) {
28        let _ = title;
29    }
30    fn set_ime_enabled(&self, is_enabled: bool) {
31        let _ = is_enabled;
32    }
33    fn set_ime_cursor_area(&self, x: f32, y: f32, width: f32, height: f32) {
34        let _ = x;
35        let _ = y;
36        let _ = width;
37        let _ = height;
38    }
39    /// Opt into bounded surrounding text for native IME. The default exports no
40    /// document text; hosts must opt in explicitly and passwords stay excluded.
41    fn supports_ime_surrounding_text(&self) -> bool {
42        false
43    }
44    /// A native input callback can change its local text store even when the
45    /// application's final value compares equal (for example IME cancellation).
46    fn invalidate_ime_context(&self) {}
47    /// UTF-8 excerpt (under 4000 bytes), with validated byte cursor/anchor.
48    fn set_ime_surrounding_text(&self, text: &str, cursor: usize, anchor: usize) {
49        let _ = (text, cursor, anchor);
50    }
51    fn get_clipboard_text(&self) -> Result<String, ClipboardError> {
52        Err(ClipboardError)
53    }
54    fn set_clipboard_text(&self, text: String) -> Result<(), ClipboardError> {
55        let _ = text;
56        Err(ClipboardError)
57    }
58    fn open_file_dialog(
59        &self,
60        multiple: bool,
61        filter: Option<FileDialogFilter>,
62    ) -> Vec<std::path::PathBuf> {
63        let _ = multiple;
64        let _ = filter;
65        vec![]
66    }
67
68    // Window chrome controls, for documents that draw their own titlebar
69    // (e.g. a frameless window with an HTML titlebar).
70    fn request_window_close(&self) {}
71    fn set_window_minimized(&self, minimized: bool) {
72        let _ = minimized;
73    }
74    fn set_window_maximized(&self, maximized: bool) {
75        let _ = maximized;
76    }
77    fn is_window_maximized(&self) -> bool {
78        false
79    }
80    fn set_window_decorations(&self, decorations: bool) {
81        let _ = decorations;
82    }
83    /// Begin an interactive user-driven move of the window (call from a
84    /// mousedown handler on a drag region)
85    fn drag_window(&self) {}
86}
87
88pub struct DummyShellProvider;
89impl ShellProvider for DummyShellProvider {}
90
91/// The system color scheme (light and dark mode)
92#[derive(Default, Debug, Clone, Copy, PartialEq)]
93pub enum ColorScheme {
94    #[default]
95    Light,
96    Dark,
97}
98
99#[derive(Debug, Clone, PartialEq)]
100pub struct Viewport {
101    pub color_scheme: ColorScheme,
102    pub window_size: (u32, u32),
103    pub hidpi_scale: f32,
104    pub zoom: f32,
105}
106
107impl Default for Viewport {
108    fn default() -> Self {
109        Self {
110            window_size: (0, 0),
111            hidpi_scale: 1.0,
112            zoom: 1.0,
113            color_scheme: ColorScheme::Light,
114        }
115    }
116}
117
118impl Viewport {
119    pub fn new(
120        physical_width: u32,
121        physical_height: u32,
122        scale_factor: f32,
123        color_scheme: ColorScheme,
124    ) -> Self {
125        Self {
126            window_size: (physical_width, physical_height),
127            hidpi_scale: scale_factor,
128            zoom: 1.0,
129            color_scheme,
130        }
131    }
132
133    /// Total scaling, computed as `hidpi_scale_factor * zoom`
134    pub fn scale(&self) -> f32 {
135        self.hidpi_scale * self.zoom
136    }
137    /// Same as [`scale`](Self::scale) but `f64` instead of `f32`
138    pub fn scale_f64(&self) -> f64 {
139        self.scale() as f64
140    }
141
142    /// The viewport size in CSS pixels (physical window size divided by the
143    /// total scale factor)
144    pub fn logical_size(&self) -> (f32, f32) {
145        let scale = self.scale();
146        (
147            self.window_size.0 as f32 / scale,
148            self.window_size.1 as f32 / scale,
149        )
150    }
151
152    /// Set hidpi scale factor
153    pub fn set_hidpi_scale(&mut self, scale: f32) {
154        self.hidpi_scale = scale;
155    }
156
157    /// Get document zoom level
158    pub fn zoom(&self) -> f32 {
159        self.zoom
160    }
161
162    /// Set document zoom level (`1.0` is unzoomed)
163    pub fn set_zoom(&mut self, zoom: f32) {
164        self.zoom = zoom;
165    }
166
167    pub fn zoom_by(&mut self, zoom: f32) {
168        self.zoom += zoom;
169    }
170
171    pub fn zoom_mut(&mut self) -> &mut f32 {
172        &mut self.zoom
173    }
174}
175
176/// Filter provided by the dom for an file picker
177pub struct FileDialogFilter {
178    pub name: String,
179    pub extensions: Vec<String>,
180}