Skip to content

Commit 600fb5c

Browse files
Refactor the old menu bar plumbing to use standard TextButtons (#3444)
* Refactor the old menu bar plumbing to use standard TextButtons * WIP: Fix Mac native menu bar * WIP: fix desktop menu bar mac * Refactor menu bar definitions to use the builder pattern * WIP: fixup desktop * cleanup * fix linux * Remove dead code that was failing to lint --------- Co-authored-by: Timon Schelling <me@timon.zip>
1 parent 3fd0460 commit 600fb5c

34 files changed

+1219
-1352
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

desktop/src/event.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ pub(crate) enum AppEvent {
99
DesktopWrapperMessage(DesktopWrapperMessage),
1010
NodeGraphExecutionResult(NodeGraphExecutionResult),
1111
CloseWindow,
12-
MenuEvent { id: u64 },
12+
MenuEvent { id: String },
1313
}
1414

1515
#[derive(Clone)]

desktop/src/window/mac/menu.rs

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use muda::Menu as MudaMenu;
22
use muda::accelerator::Accelerator;
3-
use muda::{CheckMenuItem, IsMenuItem, MenuEvent, MenuId, MenuItem, MenuItemKind, PredefinedMenuItem, Result, Submenu};
3+
use muda::{CheckMenuItem, IsMenuItem, MenuEvent, MenuItem, MenuItemKind, PredefinedMenuItem, Result, Submenu};
44

55
use crate::event::{AppEvent, AppEventScheduler};
66
use crate::wrapper::messages::MenuItem as WrapperMenuItem;
@@ -30,9 +30,8 @@ impl Menu {
3030
return;
3131
}
3232

33-
if let Some(id) = menu_id_to_u64(event.id()) {
34-
event_scheduler.schedule(AppEvent::MenuEvent { id });
35-
}
33+
let id = event.id().0.clone();
34+
event_scheduler.schedule(AppEvent::MenuEvent { id });
3635
}));
3736

3837
Menu { inner: menu }
@@ -67,13 +66,11 @@ fn menu_items_from_wrapper(entries: Vec<WrapperMenuItem>) -> Vec<MenuItemKind> {
6766
for entry in entries {
6867
match entry {
6968
WrapperMenuItem::Action { id, text, enabled, shortcut } => {
70-
let id = u64_to_menu_id(id);
7169
let accelerator = shortcut.map(|s| Accelerator::new(Some(s.modifiers), s.key));
7270
let item = MenuItem::with_id(id, text, enabled, accelerator);
7371
menu_items.push(MenuItemKind::MenuItem(item));
7472
}
7573
WrapperMenuItem::Checkbox { id, text, enabled, shortcut, checked } => {
76-
let id = u64_to_menu_id(id);
7774
let accelerator = shortcut.map(|s| Accelerator::new(Some(s.modifiers), s.key));
7875
let check = CheckMenuItem::with_id(id, text, enabled, checked, accelerator);
7976
menu_items.push(MenuItemKind::Check(check));
@@ -103,14 +100,6 @@ fn menu_item_kind_to_dyn(item: &MenuItemKind) -> &dyn IsMenuItem {
103100
}
104101
}
105102

106-
fn u64_to_menu_id(id: u64) -> String {
107-
format!("{id:08x}")
108-
}
109-
110-
fn menu_id_to_u64(id: &MenuId) -> Option<u64> {
111-
u64::from_str_radix(&id.0, 16).ok()
112-
}
113-
114103
fn replace_children<'a, T: Into<MenuContainer<'a>>>(menu: T, new_items: Vec<MenuItemKind>) {
115104
let menu: MenuContainer = menu.into();
116105
let items = menu.items();

desktop/wrapper/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,4 @@ image = { workspace = true }
3131
serde = { workspace = true }
3232
serde_json = { workspace = true }
3333
keyboard-types = { workspace = true }
34+
base64 = { workspace = true }

desktop/wrapper/src/handle_desktop_wrapper_message.rs

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,10 @@
11
use graphene_std::Color;
22
use graphene_std::raster::Image;
33
use graphite_editor::messages::app_window::app_window_message_handler::AppWindowPlatform;
4-
use graphite_editor::messages::layout::LayoutMessage;
54
use graphite_editor::messages::prelude::*;
6-
use graphite_editor::messages::tool::tool_messages::tool_prelude::{LayoutTarget, WidgetId};
7-
8-
use crate::messages::Platform;
95

106
use super::DesktopWrapperMessageDispatcher;
11-
use super::messages::{DesktopFrontendMessage, DesktopWrapperMessage, EditorMessage, OpenFileDialogContext, SaveFileDialogContext};
7+
use super::messages::{DesktopFrontendMessage, DesktopWrapperMessage, EditorMessage, OpenFileDialogContext, Platform, SaveFileDialogContext};
128

139
pub(super) fn handle_desktop_wrapper_message(dispatcher: &mut DesktopWrapperMessageDispatcher, message: DesktopWrapperMessage) {
1410
match message {
@@ -150,13 +146,15 @@ pub(super) fn handle_desktop_wrapper_message(dispatcher: &mut DesktopWrapperMess
150146
let message = PreferencesMessage::Load { preferences };
151147
dispatcher.queue_editor_message(message);
152148
}
149+
#[cfg(target_os = "macos")]
153150
DesktopWrapperMessage::MenuEvent { id } => {
154-
let message = LayoutMessage::WidgetValueUpdate {
155-
layout_target: LayoutTarget::MenuBar,
156-
widget_id: WidgetId(id),
157-
value: serde_json::Value::Bool(true),
158-
};
159-
dispatcher.queue_editor_message(message);
151+
if let Some(message) = crate::utils::menu::parse_item_path(id) {
152+
dispatcher.queue_editor_message(message);
153+
} else {
154+
tracing::error!("Received a malformed MenuEvent id");
155+
}
160156
}
157+
#[cfg(not(target_os = "macos"))]
158+
DesktopWrapperMessage::MenuEvent { id: _ } => {}
161159
}
162160
}
Lines changed: 19 additions & 197 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
use std::path::PathBuf;
22

3-
use graphite_editor::messages::input_mapper::utility_types::input_keyboard::{Key, LayoutKey, LayoutKeysGroup};
4-
use graphite_editor::messages::input_mapper::utility_types::misc::ActionKeys;
5-
use graphite_editor::messages::layout::utility_types::widgets::menu_widgets::MenuBarEntry;
63
use graphite_editor::messages::prelude::FrontendMessage;
74

85
use super::DesktopWrapperMessageDispatcher;
9-
use super::messages::{DesktopFrontendMessage, Document, FileFilter, KeyCode, MenuItem, Modifiers, OpenFileDialogContext, SaveFileDialogContext, Shortcut};
6+
use super::messages::{DesktopFrontendMessage, Document, FileFilter, OpenFileDialogContext, SaveFileDialogContext};
107

118
pub(super) fn intercept_frontend_message(dispatcher: &mut DesktopWrapperMessageDispatcher, message: FrontendMessage) -> Option<FrontendMessage> {
129
match message {
@@ -113,11 +110,24 @@ pub(super) fn intercept_frontend_message(dispatcher: &mut DesktopWrapperMessageD
113110
FrontendMessage::TriggerLoadPreferences => {
114111
dispatcher.respond(DesktopFrontendMessage::PersistenceLoadPreferences);
115112
}
116-
FrontendMessage::UpdateMenuBarLayout { layout_target, layout } => {
117-
let entries = convert_menu_bar_entries_to_menu_items(&layout);
118-
dispatcher.respond(DesktopFrontendMessage::UpdateMenu { entries });
119-
120-
return Some(FrontendMessage::UpdateMenuBarLayout { layout, layout_target });
113+
#[cfg(target_os = "macos")]
114+
FrontendMessage::UpdateMenuBarLayout {
115+
layout_target: graphite_editor::messages::tool::tool_messages::tool_prelude::LayoutTarget::MenuBar,
116+
diff,
117+
} => {
118+
use graphite_editor::messages::tool::tool_messages::tool_prelude::{DiffUpdate, WidgetDiff};
119+
match diff.as_slice() {
120+
[
121+
WidgetDiff {
122+
widget_path,
123+
new_value: DiffUpdate::SubLayout(layout),
124+
},
125+
] if widget_path.is_empty() => {
126+
let entries = crate::utils::menu::convert_menu_bar_layout_to_menu_items(layout);
127+
dispatcher.respond(DesktopFrontendMessage::UpdateMenu { entries });
128+
}
129+
_ => {}
130+
}
121131
}
122132
FrontendMessage::WindowClose => {
123133
dispatcher.respond(DesktopFrontendMessage::WindowClose);
@@ -144,191 +154,3 @@ pub(super) fn intercept_frontend_message(dispatcher: &mut DesktopWrapperMessageD
144154
}
145155
None
146156
}
147-
148-
fn convert_menu_bar_entries_to_menu_items(layout: &[MenuBarEntry]) -> Vec<MenuItem> {
149-
layout.iter().filter_map(convert_menu_bar_entry_to_menu_item).collect()
150-
}
151-
152-
fn convert_menu_bar_entry_to_menu_item(
153-
MenuBarEntry {
154-
label,
155-
icon,
156-
shortcut,
157-
action,
158-
children,
159-
disabled,
160-
}: &MenuBarEntry,
161-
) -> Option<MenuItem> {
162-
let id = action.widget_id.0;
163-
let text = label.clone();
164-
let enabled = !*disabled;
165-
166-
if !children.0.is_empty() {
167-
let items = convert_menu_bar_entry_children_to_menu_items(&children.0);
168-
return Some(MenuItem::SubMenu { id, text, enabled, items });
169-
}
170-
171-
let shortcut = match shortcut {
172-
Some(ActionKeys::Keys(LayoutKeysGroup(keys))) => convert_layout_keys_to_shortcut(keys),
173-
_ => None,
174-
};
175-
176-
// TODO: Find a better way to determine if this is a checkbox
177-
match icon.as_deref() {
178-
Some("CheckboxChecked") => {
179-
return Some(MenuItem::Checkbox {
180-
id,
181-
text,
182-
enabled,
183-
shortcut,
184-
checked: true,
185-
});
186-
}
187-
Some("CheckboxUnchecked") => {
188-
return Some(MenuItem::Checkbox {
189-
id,
190-
text,
191-
enabled,
192-
shortcut,
193-
checked: false,
194-
});
195-
}
196-
_ => {}
197-
}
198-
199-
Some(MenuItem::Action { id, text, shortcut, enabled })
200-
}
201-
202-
fn convert_menu_bar_entry_children_to_menu_items(children: &[Vec<MenuBarEntry>]) -> Vec<MenuItem> {
203-
let mut items = Vec::new();
204-
for (i, section) in children.iter().enumerate() {
205-
for entry in section.iter() {
206-
if let Some(item) = convert_menu_bar_entry_to_menu_item(entry) {
207-
items.push(item);
208-
}
209-
}
210-
if i != children.len() - 1 {
211-
items.push(MenuItem::Separator);
212-
}
213-
}
214-
items
215-
}
216-
217-
fn convert_layout_keys_to_shortcut(layout_keys: &Vec<LayoutKey>) -> Option<Shortcut> {
218-
let mut key: Option<KeyCode> = None;
219-
let mut modifiers = Modifiers::default();
220-
for layout_key in layout_keys {
221-
match layout_key.key() {
222-
Key::Shift => modifiers |= Modifiers::SHIFT,
223-
Key::Control => modifiers |= Modifiers::CONTROL,
224-
Key::Alt => modifiers |= Modifiers::ALT,
225-
Key::Meta => modifiers |= Modifiers::META,
226-
Key::Command => modifiers |= Modifiers::ALT,
227-
Key::Accel => modifiers |= Modifiers::META,
228-
Key::Digit0 => key = Some(KeyCode::Digit0),
229-
Key::Digit1 => key = Some(KeyCode::Digit1),
230-
Key::Digit2 => key = Some(KeyCode::Digit2),
231-
Key::Digit3 => key = Some(KeyCode::Digit3),
232-
Key::Digit4 => key = Some(KeyCode::Digit4),
233-
Key::Digit5 => key = Some(KeyCode::Digit5),
234-
Key::Digit6 => key = Some(KeyCode::Digit6),
235-
Key::Digit7 => key = Some(KeyCode::Digit7),
236-
Key::Digit8 => key = Some(KeyCode::Digit8),
237-
Key::Digit9 => key = Some(KeyCode::Digit9),
238-
Key::KeyA => key = Some(KeyCode::KeyA),
239-
Key::KeyB => key = Some(KeyCode::KeyB),
240-
Key::KeyC => key = Some(KeyCode::KeyC),
241-
Key::KeyD => key = Some(KeyCode::KeyD),
242-
Key::KeyE => key = Some(KeyCode::KeyE),
243-
Key::KeyF => key = Some(KeyCode::KeyF),
244-
Key::KeyG => key = Some(KeyCode::KeyG),
245-
Key::KeyH => key = Some(KeyCode::KeyH),
246-
Key::KeyI => key = Some(KeyCode::KeyI),
247-
Key::KeyJ => key = Some(KeyCode::KeyJ),
248-
Key::KeyK => key = Some(KeyCode::KeyK),
249-
Key::KeyL => key = Some(KeyCode::KeyL),
250-
Key::KeyM => key = Some(KeyCode::KeyM),
251-
Key::KeyN => key = Some(KeyCode::KeyN),
252-
Key::KeyO => key = Some(KeyCode::KeyO),
253-
Key::KeyP => key = Some(KeyCode::KeyP),
254-
Key::KeyQ => key = Some(KeyCode::KeyQ),
255-
Key::KeyR => key = Some(KeyCode::KeyR),
256-
Key::KeyS => key = Some(KeyCode::KeyS),
257-
Key::KeyT => key = Some(KeyCode::KeyT),
258-
Key::KeyU => key = Some(KeyCode::KeyU),
259-
Key::KeyV => key = Some(KeyCode::KeyV),
260-
Key::KeyW => key = Some(KeyCode::KeyW),
261-
Key::KeyX => key = Some(KeyCode::KeyX),
262-
Key::KeyY => key = Some(KeyCode::KeyY),
263-
Key::KeyZ => key = Some(KeyCode::KeyZ),
264-
Key::Backquote => key = Some(KeyCode::Backquote),
265-
Key::Backslash => key = Some(KeyCode::Backslash),
266-
Key::BracketLeft => key = Some(KeyCode::BracketLeft),
267-
Key::BracketRight => key = Some(KeyCode::BracketRight),
268-
Key::Comma => key = Some(KeyCode::Comma),
269-
Key::Equal => key = Some(KeyCode::Equal),
270-
Key::Minus => key = Some(KeyCode::Minus),
271-
Key::Period => key = Some(KeyCode::Period),
272-
Key::Quote => key = Some(KeyCode::Quote),
273-
Key::Semicolon => key = Some(KeyCode::Semicolon),
274-
Key::Slash => key = Some(KeyCode::Slash),
275-
Key::Backspace => key = Some(KeyCode::Backspace),
276-
Key::CapsLock => key = Some(KeyCode::CapsLock),
277-
Key::ContextMenu => key = Some(KeyCode::ContextMenu),
278-
Key::Enter => key = Some(KeyCode::Enter),
279-
Key::Space => key = Some(KeyCode::Space),
280-
Key::Tab => key = Some(KeyCode::Tab),
281-
Key::Delete => key = Some(KeyCode::Delete),
282-
Key::End => key = Some(KeyCode::End),
283-
Key::Help => key = Some(KeyCode::Help),
284-
Key::Home => key = Some(KeyCode::Home),
285-
Key::Insert => key = Some(KeyCode::Insert),
286-
Key::PageDown => key = Some(KeyCode::PageDown),
287-
Key::PageUp => key = Some(KeyCode::PageUp),
288-
Key::ArrowDown => key = Some(KeyCode::ArrowDown),
289-
Key::ArrowLeft => key = Some(KeyCode::ArrowLeft),
290-
Key::ArrowRight => key = Some(KeyCode::ArrowRight),
291-
Key::ArrowUp => key = Some(KeyCode::ArrowUp),
292-
Key::NumLock => key = Some(KeyCode::NumLock),
293-
Key::NumpadAdd => key = Some(KeyCode::NumpadAdd),
294-
Key::NumpadHash => key = Some(KeyCode::NumpadHash),
295-
Key::NumpadMultiply => key = Some(KeyCode::NumpadMultiply),
296-
Key::NumpadParenLeft => key = Some(KeyCode::NumpadParenLeft),
297-
Key::NumpadParenRight => key = Some(KeyCode::NumpadParenRight),
298-
Key::Escape => key = Some(KeyCode::Escape),
299-
Key::F1 => key = Some(KeyCode::F1),
300-
Key::F2 => key = Some(KeyCode::F2),
301-
Key::F3 => key = Some(KeyCode::F3),
302-
Key::F4 => key = Some(KeyCode::F4),
303-
Key::F5 => key = Some(KeyCode::F5),
304-
Key::F6 => key = Some(KeyCode::F6),
305-
Key::F7 => key = Some(KeyCode::F7),
306-
Key::F8 => key = Some(KeyCode::F8),
307-
Key::F9 => key = Some(KeyCode::F9),
308-
Key::F10 => key = Some(KeyCode::F10),
309-
Key::F11 => key = Some(KeyCode::F11),
310-
Key::F12 => key = Some(KeyCode::F12),
311-
Key::F13 => key = Some(KeyCode::F13),
312-
Key::F14 => key = Some(KeyCode::F14),
313-
Key::F15 => key = Some(KeyCode::F15),
314-
Key::F16 => key = Some(KeyCode::F16),
315-
Key::F17 => key = Some(KeyCode::F17),
316-
Key::F18 => key = Some(KeyCode::F18),
317-
Key::F19 => key = Some(KeyCode::F19),
318-
Key::F20 => key = Some(KeyCode::F20),
319-
Key::F21 => key = Some(KeyCode::F21),
320-
Key::F22 => key = Some(KeyCode::F22),
321-
Key::F23 => key = Some(KeyCode::F23),
322-
Key::F24 => key = Some(KeyCode::F24),
323-
Key::Fn => key = Some(KeyCode::Fn),
324-
Key::FnLock => key = Some(KeyCode::FnLock),
325-
Key::PrintScreen => key = Some(KeyCode::PrintScreen),
326-
Key::ScrollLock => key = Some(KeyCode::ScrollLock),
327-
Key::Pause => key = Some(KeyCode::Pause),
328-
Key::Unidentified => key = Some(KeyCode::Unidentified),
329-
Key::FakeKeyPlus => key = Some(KeyCode::Equal),
330-
_ => key = None,
331-
}
332-
}
333-
key.map(|key| Shortcut { key, modifiers })
334-
}

desktop/wrapper/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ mod handle_desktop_wrapper_message;
2020
mod intercept_editor_message;
2121
mod intercept_frontend_message;
2222

23+
pub(crate) mod utils;
24+
2325
pub struct DesktopWrapper {
2426
editor: Editor,
2527
}

desktop/wrapper/src/messages.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ pub enum DesktopWrapperMessage {
112112
preferences: Option<Preferences>,
113113
},
114114
MenuEvent {
115-
id: u64,
115+
id: String,
116116
},
117117
}
118118

@@ -147,20 +147,20 @@ pub enum Platform {
147147

148148
pub enum MenuItem {
149149
Action {
150-
id: u64,
150+
id: String,
151151
text: String,
152152
enabled: bool,
153153
shortcut: Option<Shortcut>,
154154
},
155155
Checkbox {
156-
id: u64,
156+
id: String,
157157
text: String,
158158
enabled: bool,
159159
shortcut: Option<Shortcut>,
160160
checked: bool,
161161
},
162162
SubMenu {
163-
id: u64,
163+
id: String,
164164
text: String,
165165
enabled: bool,
166166
items: Vec<MenuItem>,

0 commit comments

Comments
 (0)