Skip to main content

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}