Skip to main content

nifty_codegen/compile/
menu.rs

1//! The menu bar: its menus as data the runtime reads, and their items' compiled actions,
2//! which share one frame of locals.
3
4use std::mem::{offset_of, size_of};
5
6use cranelift_module::Module;
7use nifty_check::hir::{Block as Stmts, Menu, MenuEntry};
8use nifty_rt::{MenuDesc, MenuEntryDesc, MenuEntryKind, ShortcutDesc};
9use nifty_runtime::Shortcut;
10
11use super::ui::{Aliases, Frame, Ui};
12use super::{List, Result, Shared, put_str};
13use crate::emit::{Blob, Emitter, Target};
14
15/// The menus, and the types of the locals their actions share.
16pub(super) fn menu_bar<M: Module>(
17    emitter: &mut Emitter<M>,
18    shared: &mut Shared<'_>,
19) -> Result<(List, List)> {
20    let program = shared.program;
21    let mut ui = Ui { emitter, shared };
22    let frame = Frame::new(&program.menu_bar.locals);
23    let menus = ui.menus(&frame, &program.menu_bar.menus)?;
24    let locals = ui.frame_types(&frame)?;
25    Ok((menus, locals))
26}
27
28impl<M: Module> Ui<'_, '_, M> {
29    fn menus(&mut self, frame: &Frame<'_>, menus: &[Menu<Stmts>]) -> Result<List> {
30        let size = size_of::<MenuDesc>();
31        let mut blob = Blob::zeroed(menus.len() * size);
32        for (index, menu) in menus.iter().enumerate() {
33            let at = index * size;
34            let title = at + offset_of!(MenuDesc, title);
35            put_str(self.emitter, self.shared, &mut blob, title, &menu.title)?;
36            let entries = self.menu_entries(frame, &Aliases::new(), &menu.entries)?;
37            entries.put(&mut blob, at + offset_of!(MenuDesc, entries));
38        }
39        self.list(blob, menus.len())
40    }
41
42    /// A menu's entries, whose items' actions see `aliases`, as a context menu's do.
43    pub(super) fn menu_entries(
44        &mut self,
45        frame: &Frame<'_>,
46        aliases: &Aliases,
47        entries: &[MenuEntry<Stmts>],
48    ) -> Result<List> {
49        let size = size_of::<MenuEntryDesc>();
50        let mut blob = Blob::zeroed(entries.len() * size);
51        for (index, entry) in entries.iter().enumerate() {
52            let at = index * size;
53            let title_at = at + offset_of!(MenuEntryDesc, title);
54            let kind = match entry {
55                MenuEntry::Item {
56                    title,
57                    shortcut,
58                    action,
59                } => {
60                    put_str(self.emitter, self.shared, &mut blob, title_at, title)?;
61                    if let Some(shortcut) = shortcut {
62                        put_shortcut(
63                            &mut blob,
64                            at + offset_of!(MenuEntryDesc, shortcut),
65                            *shortcut,
66                        );
67                    }
68                    let action = self.action(frame, aliases, action)?;
69                    blob.put_pointer(
70                        at + offset_of!(MenuEntryDesc, action),
71                        Target::Function(action),
72                    );
73                    MenuEntryKind::Item
74                }
75                MenuEntry::Separator => MenuEntryKind::Separator,
76                MenuEntry::Submenu(submenu) => {
77                    put_str(
78                        self.emitter,
79                        self.shared,
80                        &mut blob,
81                        title_at,
82                        &submenu.title,
83                    )?;
84                    let entries = self.menu_entries(frame, aliases, &submenu.entries)?;
85                    entries.put(&mut blob, at + offset_of!(MenuEntryDesc, entries));
86                    MenuEntryKind::Submenu
87                }
88            };
89            blob.put_u8(at + offset_of!(MenuEntryDesc, kind), kind as u8);
90        }
91        self.list(blob, entries.len())
92    }
93}
94
95fn put_shortcut(blob: &mut Blob, at: usize, shortcut: Shortcut) {
96    blob.put_u8(at + offset_of!(ShortcutDesc, present), 1);
97    let at = at + offset_of!(ShortcutDesc, shortcut);
98    blob.put_u8(at + offset_of!(Shortcut, key), shortcut.key.code());
99    blob.put_u8(at + offset_of!(Shortcut, shift), u8::from(shortcut.shift));
100    blob.put_u8(at + offset_of!(Shortcut, alt), u8::from(shortcut.alt));
101}