nifty_runtime/backend.rs
1//! The contract each platform (`AppKit`, `Win32`, `GTK`) implements with its own native widgets.
2
3use crate::dialog::{DialogHandler, FileDialog};
4use crate::geometry::{Frame, Size};
5use std::path::Path;
6
7use crate::menu::{Menu, MenuEntry};
8use crate::style::{Backdrop, ButtonStyle, Icon, ImageSize, LabelStyle, Surface, ToggleStyle};
9
10pub type ClickHandler = Box<dyn Fn()>;
11pub type TextHandler = Box<dyn Fn(&str)>;
12pub type ToggleHandler = Box<dyn Fn(bool)>;
13pub type NumberHandler = Box<dyn Fn(i64)>;
14/// Runs with the number of the menu item the user chose.
15pub type MenuHandler = Box<dyn Fn(usize)>;
16/// Runs with what the user dropped: a path for each file, an address for each link.
17pub type DropHandler = Box<dyn Fn(Vec<String>)>;
18pub type ResizeHandler = Box<dyn Fn(Size)>;
19/// Runs when the user asks to close a window.
20pub type CloseHandler = Box<dyn Fn()>;
21pub type WakeHandler = Box<dyn Fn()>;
22
23/// Where a new widget goes: straight into a window's content area, or into the content of
24/// a scroll area, which scrolls with it. Frames are relative to that parent's content.
25pub enum Parent<'a, B: Backend + ?Sized> {
26 Window(&'a B::Window),
27 Scroll(&'a B::Widget),
28}
29
30impl<B: Backend + ?Sized> Clone for Parent<'_, B> {
31 fn clone(&self) -> Self {
32 *self
33 }
34}
35
36impl<B: Backend + ?Sized> Copy for Parent<'_, B> {}
37
38impl<B: Backend + ?Sized> std::fmt::Debug for Parent<'_, B> {
39 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
40 f.write_str(match self {
41 Self::Window(_) => "Window",
42 Self::Scroll(_) => "Scroll",
43 })
44 }
45}
46
47/// Asks the UI thread to run its wake handler; see [`Backend::waker`].
48pub trait Waker: Send + Sync + 'static {
49 fn wake(&self);
50}
51
52pub trait Backend: 'static {
53 type Window;
54 type Widget;
55 type Waker: Waker;
56
57 /// The window starts hidden; `on_resize` receives the new content size after every
58 /// resize, and whenever something else changes how the content must be laid out, such
59 /// as the width of scroll bars. `on_close` runs when the user asks to close the window,
60 /// such as with its close button, in place of closing it: the app closes it with
61 /// [`Backend::close_window`]. The app quits once its last window closes.
62 fn open_window(
63 &self,
64 title: &str,
65 size: Size,
66 on_resize: ResizeHandler,
67 on_close: CloseHandler,
68 ) -> Self::Window;
69 fn content_size(&self, window: &Self::Window) -> Size;
70 /// Shows the window in front of the others, with the keyboard.
71 fn show_window(&self, window: &Self::Window);
72 /// Brings a window already shown to the front, with the keyboard.
73 fn focus_window(&self, window: &Self::Window);
74 fn set_title(&self, window: &Self::Window, title: &str);
75 /// Draws the window on `backdrop`, or as close to it as the system allows.
76 fn set_backdrop(&self, window: &Self::Window, backdrop: Backdrop);
77 /// Resizes the window's content area; its `on_resize` handler then reports the new size.
78 fn set_content_size(&self, window: &Self::Window, size: Size);
79 /// Closes the window, whose widgets have all been removed already.
80 fn close_window(&self, window: Self::Window);
81 /// How far the title bar reaches down over the content area, which is `0` where the
82 /// title bar sits outside it.
83 fn title_bar_inset(&self, window: &Self::Window) -> f32;
84 /// Stops the user from making the content area narrower than `width`.
85 fn set_min_content_width(&self, window: &Self::Window, width: f32);
86
87 /// Draws `surface` behind the widgets created after it. Widgets stack in the order
88 /// they are created, and a stack's surface is created before its children, so the
89 /// surface is behind everything inside it. It is never [`Surface::None`].
90 fn create_surface(&self, parent: Parent<'_, Self>, surface: Surface) -> Self::Widget;
91 /// An area whose content scrolls up and down when it is taller than the area. Widgets
92 /// created with it as their [`Parent::Scroll`] are its content.
93 fn create_scroll(&self, parent: Parent<'_, Self>) -> Self::Widget;
94 /// How much of a scroll area's width its vertical scroll bar takes from the content, in
95 /// points; `0` where scroll bars float over the content. It is asked again at every
96 /// layout, since the user can change it while the app runs.
97 fn scroll_bar_width(&self, window: &Self::Window) -> f32;
98 /// Sets the size of a scroll area's content, which is as wide as the area less its
99 /// scroll bar, and may be taller than the area.
100 fn set_scroll_content(&self, scroll: &Self::Widget, size: Size);
101
102 fn create_label(&self, parent: Parent<'_, Self>, text: &str, style: LabelStyle)
103 -> Self::Widget;
104 /// A button that shows whether it is selected when `selected` is `Some`; clicking
105 /// it leaves that to `set_checked`.
106 fn create_button(
107 &self,
108 parent: Parent<'_, Self>,
109 title: &str,
110 style: ButtonStyle,
111 icon: Icon,
112 selected: Option<bool>,
113 on_click: ClickHandler,
114 ) -> Self::Widget;
115 /// A row of a sidebar list: an icon, a title and a badge such as a count, highlighted
116 /// while `selected`. `on_click` runs when the user picks it; it stays as it is until
117 /// `set_checked` changes it.
118 fn create_nav_item(
119 &self,
120 parent: Parent<'_, Self>,
121 title: &str,
122 icon: Icon,
123 badge: &str,
124 selected: bool,
125 on_click: ClickHandler,
126 ) -> Self::Widget;
127 /// A text field `lines` lines tall: one line, or more that wrap the text, take
128 /// **Return** as a new line, and scroll; only a one-line field shows its placeholder.
129 /// `on_change` runs after each edit the user makes, not when `set_text` changes the
130 /// value, with lines ending in `\n`. A one-line field with `on_submit` runs it when
131 /// the user presses **Return** in it, instead of pressing the window's primary button.
132 fn create_text_field(
133 &self,
134 parent: Parent<'_, Self>,
135 text: &str,
136 placeholder: &str,
137 lines: u32,
138 on_change: TextHandler,
139 on_submit: Option<ClickHandler>,
140 ) -> Self::Widget;
141 /// A checkbox ticks a box before its title, and a switch sits at the end of its title.
142 /// `on_toggle` runs when the user toggles it, not when `set_checked` changes it.
143 fn create_checkbox(
144 &self,
145 parent: Parent<'_, Self>,
146 title: &str,
147 checked: bool,
148 style: ToggleStyle,
149 on_toggle: ToggleHandler,
150 ) -> Self::Widget;
151 /// A slider for whole numbers from `min` to `max`, at `value`. `on_change` runs with
152 /// each new value as the user drags it, not when `set_value` moves it.
153 fn create_slider(
154 &self,
155 parent: Parent<'_, Self>,
156 value: i64,
157 min: i64,
158 max: i64,
159 on_change: NumberHandler,
160 ) -> Self::Widget;
161 /// A pop-up menu of `options`, showing the one equal to `selected`, or none when no
162 /// option is. `on_pick` runs with the option the user picks, not when `set_text` or
163 /// `set_options` change what it shows.
164 fn create_picker(
165 &self,
166 parent: Parent<'_, Self>,
167 options: &[String],
168 selected: &str,
169 on_pick: TextHandler,
170 ) -> Self::Widget;
171 /// The image in the file at `path`, as large as fits its frame, keeping its shape, and
172 /// centred; it measures as `size.frame` of the image's own size, in points. With no
173 /// path, or a file that can't be read as an image, it shows nothing.
174 fn create_image(
175 &self,
176 parent: Parent<'_, Self>,
177 path: Option<&Path>,
178 size: ImageSize,
179 ) -> Self::Widget;
180 /// Shows the image at `path` instead, as `create_image` does.
181 fn set_image(&self, widget: &Self::Widget, path: Option<&Path>);
182 /// A thin line separating content; it is drawn along its longer side.
183 fn create_divider(&self, parent: Parent<'_, Self>) -> Self::Widget;
184 /// A progress bar filled to `percent`, from 0 to 100, or one showing that work is under
185 /// way, animated, when there is no percentage.
186 fn create_progress(&self, parent: Parent<'_, Self>, percent: Option<i64>) -> Self::Widget;
187
188 /// Sets a label's text, a button's or nav item's title, a text field's value, or the
189 /// option a picker shows.
190 fn set_text(&self, widget: &Self::Widget, text: &str);
191 /// Ticks a checkbox or switch, highlights a nav item as selected, or shows a button
192 /// made with a selection as selected.
193 fn set_checked(&self, widget: &Self::Widget, checked: bool);
194 /// Sets a nav item's badge; empty hides it.
195 fn set_badge(&self, widget: &Self::Widget, badge: &str);
196 /// Fills a progress bar to `value` percent, from 0 to 100, or moves a slider to `value`.
197 fn set_value(&self, widget: &Self::Widget, value: i64);
198 /// Replaces a picker's options, keeping the one it shows if it is still there.
199 fn set_options(&self, widget: &Self::Widget, options: &[String]);
200 /// The widget's natural size for its current content.
201 fn measure(&self, widget: &Self::Widget) -> Size;
202 /// Positions the widget relative to its parent's content.
203 fn set_frame(&self, widget: &Self::Widget, frame: Frame);
204 /// Takes the widget off screen. It may be the widget whose handler is running right now,
205 /// so the backend must keep that handler alive until the handler returns.
206 fn remove_widget(&self, widget: Self::Widget);
207
208 /// An area that shows a menu of `entries` when the user right-clicks in it, or opens
209 /// the context menu from the keyboard there. It draws nothing, takes clicks from no
210 /// control, and is placed like any widget; where areas overlap, the smallest one's menu
211 /// shows. Controls that have a menu of their own, such as text fields, keep it.
212 /// `on_select` runs with the number of the item the user chooses.
213 fn create_context_menu(
214 &self,
215 parent: Parent<'_, Self>,
216 entries: &[MenuEntry<usize>],
217 on_select: MenuHandler,
218 ) -> Self::Widget;
219
220 /// An area that takes files and links the user drags onto it from other apps, such as
221 /// the Finder, File Explorer or a browser. It draws nothing, takes clicks from no
222 /// control, and is placed like any widget; where areas overlap, the smallest one takes
223 /// the drop. `on_drop` runs with a path for each file and an address for each link,
224 /// in the order the system gives them.
225 fn create_drop_target(&self, parent: Parent<'_, Self>, on_drop: DropHandler) -> Self::Widget;
226
227 /// Gives the app `menus`, in place of any it had, alongside the system's own: in the
228 /// menu bar on macOS, and in every window's menu bar on Windows, with their items'
229 /// shortcuts working wherever the app has the keyboard. `on_select` runs with an
230 /// item's number when the user chooses it or presses its shortcut.
231 fn set_menu_bar(&self, menus: &[Menu<usize>], on_select: MenuHandler);
232
233 /// `on_wake` runs on the UI thread soon after each `wake`, which any thread may call.
234 fn waker(&self, on_wake: WakeHandler) -> Self::Waker;
235
236 /// Shows `dialog` over the app's active window, or on its own when no window is active,
237 /// and passes how it closed to `on_close` on the UI thread. It is only called between
238 /// events, so it may run a modal loop and call `on_close` before returning.
239 fn show_file_dialog(&self, dialog: &FileDialog, on_close: DialogHandler);
240
241 /// Runs the platform event loop until the app quits.
242 fn run(&self);
243}