Skip to main content

nifty_rt/
program.rs

1//! The descriptor a compiled program hands the runtime: its states, windows, views and the
2//! compiled functions behind every dynamic part of their UI.
3//!
4//! `nf build` emits these as data next to the machine code, laid out as the `#[repr(C)]`
5//! types here say, so their layout is the contract between the two.
6
7use nifty_runtime::{
8    Axis, Backdrop, ButtonStyle, Icon, ImageSize, LabelStyle, Shortcut, StackStyle, TextStyle,
9};
10
11use crate::context::Context;
12use crate::ffi::{Slice, Str};
13use crate::show::Sink;
14use crate::value::{Slot, TypeDesc};
15
16/// Bumped whenever these layouts or the meaning of a runtime call change, so a program
17/// built against another runtime is refused instead of misread.
18pub const ABI_VERSION: u32 = 5;
19
20/// Writes a state's starting value to `out`.
21pub type InitFn = unsafe extern "C" fn(context: *const Context, out: *mut Slot) -> bool;
22/// Writes the value a piece of UI shows, such as a string, an `int` or a `bool`, to `out`;
23/// `frame` holds its locals.
24pub type ReadFn =
25    unsafe extern "C" fn(context: *const Context, frame: *const Slot, out: *mut Slot) -> bool;
26/// Writes `value`, which it takes ownership of, to a bound place.
27pub type WriteFn =
28    unsafe extern "C" fn(context: *const Context, frame: *const Slot, value: Slot) -> bool;
29/// Runs a button's action in a copy of its frame, which it may change.
30pub type ActionFn = unsafe extern "C" fn(context: *const Context, frame: *mut Slot) -> bool;
31/// Tells `sink` what a region shows now, through the `nf_show_*` calls.
32pub type ExpandFn =
33    unsafe extern "C" fn(context: *const Context, frame: *const Slot, sink: *mut Sink) -> bool;
34
35/// Every function returns `false` after a runtime call recorded why it failed.
36#[derive(Debug)]
37#[repr(C)]
38pub struct ProgramDesc {
39    pub abi_version: u32,
40    pub states: Slice<StateDesc>,
41    pub windows: Slice<WindowDesc>,
42    /// Indexed by view id.
43    pub views: Slice<ViewDesc>,
44    /// Indexed by function id, for errors that name a function.
45    pub functions: Slice<Str>,
46    /// Indexed by span id, for errors that point at code.
47    pub spans: Slice<SpanDesc>,
48    /// The menu bar's menus, in order.
49    pub menus: Slice<MenuDesc>,
50    /// The locals the menu items' actions share.
51    pub menu_frame: FrameDesc,
52}
53
54/// A menu: its title and its entries.
55#[derive(Debug)]
56#[repr(C)]
57pub struct MenuDesc {
58    pub title: Str,
59    pub entries: Slice<MenuEntryDesc>,
60}
61
62#[derive(Clone, Copy, Debug, PartialEq, Eq)]
63#[repr(u8)]
64pub enum MenuEntryKind {
65    Item,
66    Separator,
67    Submenu,
68}
69
70/// One entry of a menu. Which fields matter depends on `kind`:
71///
72/// - `Item`: `title`, `shortcut`, and `action`.
73/// - `Submenu`: `title` and `entries`.
74#[derive(Debug)]
75#[repr(C)]
76pub struct MenuEntryDesc {
77    pub kind: MenuEntryKind,
78    pub title: Str,
79    pub shortcut: ShortcutDesc,
80    pub action: Option<ActionFn>,
81    pub entries: Slice<MenuEntryDesc>,
82}
83
84/// An item's shortcut, which `shortcut` holds when `present`.
85#[derive(Clone, Copy, Debug)]
86#[repr(C)]
87pub struct ShortcutDesc {
88    pub present: bool,
89    pub shortcut: Shortcut,
90}
91
92#[derive(Debug)]
93#[repr(C)]
94pub struct StateDesc {
95    pub ty: &'static TypeDesc,
96    pub init: InitFn,
97}
98
99#[derive(Debug)]
100#[repr(C)]
101pub struct SpanDesc {
102    pub file: Str,
103    pub line: u32,
104    pub column: u32,
105}
106
107/// The types of a frame's locals; a frame starts with every local empty.
108#[derive(Debug)]
109#[repr(C)]
110pub struct FrameDesc {
111    pub locals: Slice<&'static TypeDesc>,
112}
113
114#[derive(Debug)]
115#[repr(C)]
116pub struct WindowDesc {
117    pub title: Str,
118    pub width: f32,
119    pub height: f32,
120    pub backdrop: Backdrop,
121    /// Whether it opens when the app starts, rather than when code opens it.
122    pub starts_open: bool,
123    pub style: StackStyle,
124    pub children: Slice<TemplateDesc>,
125    pub frame: FrameDesc,
126}
127
128#[derive(Debug)]
129#[repr(C)]
130pub struct ViewDesc {
131    pub body: Slice<TemplateDesc>,
132    /// Its parameters are the first locals, in order.
133    pub frame: FrameDesc,
134}
135
136#[derive(Clone, Copy, Debug, PartialEq, Eq)]
137#[repr(u8)]
138pub enum TemplateKind {
139    Stack,
140    Label,
141    Button,
142    TextField,
143    Checkbox,
144    Spacer,
145    Divider,
146    Region,
147    NavItem,
148    Progress,
149    Scroll,
150    Switch,
151    Slider,
152    Picker,
153    Image,
154    ContextMenu,
155    DropTarget,
156}
157
158#[derive(Clone, Copy, Debug)]
159#[repr(C)]
160pub struct BindingDesc {
161    pub read: Option<ReadFn>,
162    pub write: Option<WriteFn>,
163}
164
165#[derive(Debug)]
166#[repr(C)]
167pub struct RegionDesc {
168    pub expand: Option<ExpandFn>,
169    /// The template lists it can show, which `nf_show_same` and `nf_show_item` pick by
170    /// index.
171    pub lists: Slice<Slice<TemplateDesc>>,
172}
173
174/// One element of a window's UI. Which fields matter depends on `kind`:
175///
176/// - `Stack` and `Scroll`: `stack` and `children`.
177/// - `Label`: `label` and `text`.
178/// - `Button`: `button`, `icon`, `text` as its title, `action`, and `value` as whether it
179///   is selected, if it shows that.
180/// - `TextField`: `binding`, `placeholder`, `lines`, and `action` to run on Return, if
181///   it has one.
182/// - `Checkbox` and `Switch`: `binding`, and `text` as its title if it has one.
183/// - `Slider`: `binding` to its value, from `min` to `max`.
184/// - `Picker`: `binding` to its selection, and `value` as its list of options.
185/// - `NavItem`: `text` as its title, `value`, `binding` as its selection, `icon`, and
186///   `badge` if it has one.
187/// - `Image`: `text` as its file's path, and `image`.
188/// - `Progress`: `value` as its percentage, if it has one.
189/// - `ContextMenu`: `menu`.
190/// - `DropTarget`: `binding` to the list it writes, and `action` to run then, if any.
191/// - `Region`: `region`.
192///
193/// `deps` are the state ids whose changes require showing a widget again, or rebuilding a
194/// region.
195#[derive(Debug)]
196#[repr(C)]
197pub struct TemplateDesc {
198    pub kind: TemplateKind,
199    pub button: ButtonStyle,
200    pub icon: Icon,
201    pub deps: Slice<u32>,
202    pub stack: StackStyle,
203    pub label: LabelStyle,
204    pub image: ImageSize,
205    pub text: Option<ReadFn>,
206    pub value: Option<ReadFn>,
207    pub badge: Option<ReadFn>,
208    pub binding: BindingDesc,
209    pub min: i64,
210    pub max: i64,
211    pub lines: u32,
212    pub action: Option<ActionFn>,
213    pub placeholder: Str,
214    pub children: Slice<TemplateDesc>,
215    pub menu: Slice<MenuEntryDesc>,
216    pub region: RegionDesc,
217}
218
219impl TemplateDesc {
220    /// A template of `kind` with every other field empty, to build on.
221    #[must_use]
222    pub const fn empty(kind: TemplateKind) -> Self {
223        Self {
224            kind,
225            button: ButtonStyle::Normal,
226            icon: Icon::None,
227            deps: Slice::EMPTY,
228            stack: StackStyle::new(Axis::Vertical),
229            label: LabelStyle::new(TextStyle::Body),
230            image: ImageSize {
231                width: ImageSize::FIT,
232                height: ImageSize::FIT,
233            },
234            text: None,
235            value: None,
236            badge: None,
237            binding: BindingDesc {
238                read: None,
239                write: None,
240            },
241            min: 0,
242            max: 0,
243            lines: 1,
244            action: None,
245            placeholder: Str::EMPTY,
246            children: Slice::EMPTY,
247            menu: Slice::EMPTY,
248            region: RegionDesc {
249                expand: None,
250                lists: Slice::EMPTY,
251            },
252        }
253    }
254}