Desktop: Add app menu for Mac (#3428)
* add mac app menu * review fixup * Remove "About Graphite" ellipsis, add "Show All", make it say "Quit Graphite" --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
parent
406f3d93f3
commit
bb4516e377
|
|
@ -174,24 +174,6 @@ impl App {
|
||||||
graphics_state.set_overlays_scene(scene);
|
graphics_state.set_overlays_scene(scene);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DesktopFrontendMessage::MinimizeWindow => {
|
|
||||||
if let Some(window) = &self.window {
|
|
||||||
window.minimize();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DesktopFrontendMessage::MaximizeWindow => {
|
|
||||||
if let Some(window) = &self.window {
|
|
||||||
window.toggle_maximize();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DesktopFrontendMessage::DragWindow => {
|
|
||||||
if let Some(window) = &self.window {
|
|
||||||
window.start_drag();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DesktopFrontendMessage::CloseWindow => {
|
|
||||||
self.app_event_scheduler.schedule(AppEvent::CloseWindow);
|
|
||||||
}
|
|
||||||
DesktopFrontendMessage::PersistenceWriteDocument { id, document } => {
|
DesktopFrontendMessage::PersistenceWriteDocument { id, document } => {
|
||||||
self.persistent_data.write_document(id, document);
|
self.persistent_data.write_document(id, document);
|
||||||
}
|
}
|
||||||
|
|
@ -270,6 +252,39 @@ impl App {
|
||||||
window.update_menu(entries);
|
window.update_menu(entries);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
DesktopFrontendMessage::WindowClose => {
|
||||||
|
self.app_event_scheduler.schedule(AppEvent::CloseWindow);
|
||||||
|
}
|
||||||
|
DesktopFrontendMessage::WindowMinimize => {
|
||||||
|
if let Some(window) = &self.window {
|
||||||
|
window.minimize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DesktopFrontendMessage::WindowMaximize => {
|
||||||
|
if let Some(window) = &self.window {
|
||||||
|
window.toggle_maximize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DesktopFrontendMessage::WindowDrag => {
|
||||||
|
if let Some(window) = &self.window {
|
||||||
|
window.start_drag();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DesktopFrontendMessage::WindowHide => {
|
||||||
|
if let Some(window) = &self.window {
|
||||||
|
window.hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DesktopFrontendMessage::WindowHideOthers => {
|
||||||
|
if let Some(window) = &self.window {
|
||||||
|
window.hide_others();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DesktopFrontendMessage::WindowShowAll => {
|
||||||
|
if let Some(window) = &self.window {
|
||||||
|
window.show_all();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,9 @@ pub(crate) trait NativeWindow {
|
||||||
fn configure(attributes: WindowAttributes, event_loop: &dyn ActiveEventLoop) -> WindowAttributes;
|
fn configure(attributes: WindowAttributes, event_loop: &dyn ActiveEventLoop) -> WindowAttributes;
|
||||||
fn new(window: &dyn WinitWindow, app_event_scheduler: AppEventScheduler) -> Self;
|
fn new(window: &dyn WinitWindow, app_event_scheduler: AppEventScheduler) -> Self;
|
||||||
fn update_menu(&self, _entries: Vec<MenuItem>) {}
|
fn update_menu(&self, _entries: Vec<MenuItem>) {}
|
||||||
|
fn hide(&self) {}
|
||||||
|
fn hide_others(&self) {}
|
||||||
|
fn show_all(&self) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
|
|
@ -93,6 +96,18 @@ impl Window {
|
||||||
let _ = self.winit_window.drag_window();
|
let _ = self.winit_window.drag_window();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn hide(&self) {
|
||||||
|
self.native_handle.hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn hide_others(&self) {
|
||||||
|
self.native_handle.hide_others();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn show_all(&self) {
|
||||||
|
self.native_handle.show_all();
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn set_cursor(&self, cursor: winit::cursor::Cursor) {
|
pub(crate) fn set_cursor(&self, cursor: winit::cursor::Cursor) {
|
||||||
self.winit_window.set_cursor(cursor);
|
self.winit_window.set_cursor(cursor);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,4 +34,16 @@ impl super::NativeWindow for NativeWindowImpl {
|
||||||
fn update_menu(&self, entries: Vec<MenuItem>) {
|
fn update_menu(&self, entries: Vec<MenuItem>) {
|
||||||
self.menu.update(entries);
|
self.menu.update(entries);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn hide(&self) {
|
||||||
|
app::hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hide_others(&self) {
|
||||||
|
app::hide_others();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn show_all(&self) {
|
||||||
|
app::show_all();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,6 @@ use objc2::{ClassType, define_class, msg_send};
|
||||||
use objc2_app_kit::{NSApplication, NSEvent, NSEventType, NSResponder};
|
use objc2_app_kit::{NSApplication, NSEvent, NSEventType, NSResponder};
|
||||||
use objc2_foundation::NSObject;
|
use objc2_foundation::NSObject;
|
||||||
|
|
||||||
pub(super) fn init() {
|
|
||||||
unsafe {
|
|
||||||
let _: &NSApplication = msg_send![GraphiteApplication::class(), sharedApplication];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
define_class!(
|
define_class!(
|
||||||
#[unsafe(super(NSApplication, NSResponder, NSObject))]
|
#[unsafe(super(NSApplication, NSResponder, NSObject))]
|
||||||
#[name = "GraphiteApplication"]
|
#[name = "GraphiteApplication"]
|
||||||
|
|
@ -25,3 +19,23 @@ define_class!(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
fn instance() -> objc2::rc::Retained<NSApplication> {
|
||||||
|
unsafe { msg_send![GraphiteApplication::class(), sharedApplication] }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn init() {
|
||||||
|
let _ = instance();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn hide() {
|
||||||
|
instance().hide(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn hide_others() {
|
||||||
|
instance().hideOtherApplications(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn show_all() {
|
||||||
|
instance().unhideAllApplications(None);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -43,11 +43,11 @@ impl Menu {
|
||||||
let existing_entries = self.inner.items();
|
let existing_entries = self.inner.items();
|
||||||
|
|
||||||
let mut new_entries_iter = new_entries.iter();
|
let mut new_entries_iter = new_entries.iter();
|
||||||
let mut existing_entries_iter = existing_entries.iter().skip(1); // Skip first menu (app menu)
|
let mut existing_entries_iter = existing_entries.iter();
|
||||||
|
|
||||||
let incremental_update_ok = std::iter::from_fn(move || match (existing_entries_iter.next(), new_entries_iter.next()) {
|
let incremental_update_ok = std::iter::from_fn(move || match (existing_entries_iter.next(), new_entries_iter.next()) {
|
||||||
(Some(MenuItemKind::Submenu(old)), Some(MenuItemKind::Submenu(new))) if old.text() == new.text() => {
|
(Some(MenuItemKind::Submenu(old)), Some(MenuItemKind::Submenu(new))) if old.text() == new.text() => {
|
||||||
replace_children(old, 0, new.items());
|
replace_children(old, new.items());
|
||||||
Some(true)
|
Some(true)
|
||||||
}
|
}
|
||||||
(None, None) => None,
|
(None, None) => None,
|
||||||
|
|
@ -57,7 +57,7 @@ impl Menu {
|
||||||
|
|
||||||
if !incremental_update_ok {
|
if !incremental_update_ok {
|
||||||
// Fallback to full replace
|
// Fallback to full replace
|
||||||
replace_children(&self.inner, 1, new_entries); // Skip first menu (app menu)
|
replace_children(&self.inner, new_entries);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -111,10 +111,10 @@ fn menu_id_to_u64(id: &MenuId) -> Option<u64> {
|
||||||
u64::from_str_radix(&id.0, 16).ok()
|
u64::from_str_radix(&id.0, 16).ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn replace_children<'a, T: Into<MenuContainer<'a>>>(menu: T, skip: usize, new_items: Vec<MenuItemKind>) {
|
fn replace_children<'a, T: Into<MenuContainer<'a>>>(menu: T, new_items: Vec<MenuItemKind>) {
|
||||||
let menu: MenuContainer = menu.into();
|
let menu: MenuContainer = menu.into();
|
||||||
let items = menu.items();
|
let items = menu.items();
|
||||||
for item in items.iter().skip(skip) {
|
for item in items.iter() {
|
||||||
menu.remove(menu_item_kind_to_dyn(item)).unwrap();
|
menu.remove(menu_item_kind_to_dyn(item)).unwrap();
|
||||||
}
|
}
|
||||||
let items = new_items.iter().map(|item| menu_item_kind_to_dyn(item)).collect::<Vec<&dyn IsMenuItem>>();
|
let items = new_items.iter().map(|item| menu_item_kind_to_dyn(item)).collect::<Vec<&dyn IsMenuItem>>();
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,7 @@ pub(super) fn handle_desktop_wrapper_message(dispatcher: &mut DesktopWrapperMess
|
||||||
Platform::Mac => AppWindowPlatform::Mac,
|
Platform::Mac => AppWindowPlatform::Mac,
|
||||||
Platform::Linux => AppWindowPlatform::Linux,
|
Platform::Linux => AppWindowPlatform::Linux,
|
||||||
};
|
};
|
||||||
let message = AppWindowMessage::AppWindowUpdatePlatform { platform };
|
let message = AppWindowMessage::UpdatePlatform { platform };
|
||||||
dispatcher.queue_editor_message(message);
|
dispatcher.queue_editor_message(message);
|
||||||
}
|
}
|
||||||
DesktopWrapperMessage::UpdateMaximized { maximized } => {
|
DesktopWrapperMessage::UpdateMaximized { maximized } => {
|
||||||
|
|
|
||||||
|
|
@ -67,18 +67,6 @@ pub(super) fn intercept_frontend_message(dispatcher: &mut DesktopWrapperMessageD
|
||||||
FrontendMessage::TriggerVisitLink { url } => {
|
FrontendMessage::TriggerVisitLink { url } => {
|
||||||
dispatcher.respond(DesktopFrontendMessage::OpenUrl(url));
|
dispatcher.respond(DesktopFrontendMessage::OpenUrl(url));
|
||||||
}
|
}
|
||||||
FrontendMessage::DragWindow => {
|
|
||||||
dispatcher.respond(DesktopFrontendMessage::DragWindow);
|
|
||||||
}
|
|
||||||
FrontendMessage::CloseWindow => {
|
|
||||||
dispatcher.respond(DesktopFrontendMessage::CloseWindow);
|
|
||||||
}
|
|
||||||
FrontendMessage::TriggerMinimizeWindow => {
|
|
||||||
dispatcher.respond(DesktopFrontendMessage::MinimizeWindow);
|
|
||||||
}
|
|
||||||
FrontendMessage::TriggerMaximizeWindow => {
|
|
||||||
dispatcher.respond(DesktopFrontendMessage::MaximizeWindow);
|
|
||||||
}
|
|
||||||
FrontendMessage::UpdateViewportPhysicalBounds { x, y, width, height } => {
|
FrontendMessage::UpdateViewportPhysicalBounds { x, y, width, height } => {
|
||||||
dispatcher.respond(DesktopFrontendMessage::UpdateViewportPhysicalBounds { x, y, width, height });
|
dispatcher.respond(DesktopFrontendMessage::UpdateViewportPhysicalBounds { x, y, width, height });
|
||||||
}
|
}
|
||||||
|
|
@ -131,6 +119,27 @@ pub(super) fn intercept_frontend_message(dispatcher: &mut DesktopWrapperMessageD
|
||||||
|
|
||||||
return Some(FrontendMessage::UpdateMenuBarLayout { layout, layout_target });
|
return Some(FrontendMessage::UpdateMenuBarLayout { layout, layout_target });
|
||||||
}
|
}
|
||||||
|
FrontendMessage::WindowClose => {
|
||||||
|
dispatcher.respond(DesktopFrontendMessage::WindowClose);
|
||||||
|
}
|
||||||
|
FrontendMessage::WindowMinimize => {
|
||||||
|
dispatcher.respond(DesktopFrontendMessage::WindowMinimize);
|
||||||
|
}
|
||||||
|
FrontendMessage::WindowMaximize => {
|
||||||
|
dispatcher.respond(DesktopFrontendMessage::WindowMaximize);
|
||||||
|
}
|
||||||
|
FrontendMessage::WindowDrag => {
|
||||||
|
dispatcher.respond(DesktopFrontendMessage::WindowDrag);
|
||||||
|
}
|
||||||
|
FrontendMessage::WindowHide => {
|
||||||
|
dispatcher.respond(DesktopFrontendMessage::WindowHide);
|
||||||
|
}
|
||||||
|
FrontendMessage::WindowHideOthers => {
|
||||||
|
dispatcher.respond(DesktopFrontendMessage::WindowHideOthers);
|
||||||
|
}
|
||||||
|
FrontendMessage::WindowShowAll => {
|
||||||
|
dispatcher.respond(DesktopFrontendMessage::WindowShowAll);
|
||||||
|
}
|
||||||
m => return Some(m),
|
m => return Some(m),
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
|
|
@ -151,11 +160,7 @@ fn convert_menu_bar_entry_to_menu_item(
|
||||||
}: &MenuBarEntry,
|
}: &MenuBarEntry,
|
||||||
) -> Option<MenuItem> {
|
) -> Option<MenuItem> {
|
||||||
let id = action.widget_id.0;
|
let id = action.widget_id.0;
|
||||||
let text = if label.is_empty() {
|
let text = label.clone();
|
||||||
return None;
|
|
||||||
} else {
|
|
||||||
label.clone()
|
|
||||||
};
|
|
||||||
let enabled = !*disabled;
|
let enabled = !*disabled;
|
||||||
|
|
||||||
if !children.0.is_empty() {
|
if !children.0.is_empty() {
|
||||||
|
|
|
||||||
|
|
@ -32,10 +32,6 @@ pub enum DesktopFrontendMessage {
|
||||||
height: f64,
|
height: f64,
|
||||||
},
|
},
|
||||||
UpdateOverlays(vello::Scene),
|
UpdateOverlays(vello::Scene),
|
||||||
MinimizeWindow,
|
|
||||||
MaximizeWindow,
|
|
||||||
DragWindow,
|
|
||||||
CloseWindow,
|
|
||||||
PersistenceWriteDocument {
|
PersistenceWriteDocument {
|
||||||
id: DocumentId,
|
id: DocumentId,
|
||||||
document: Document,
|
document: Document,
|
||||||
|
|
@ -58,6 +54,13 @@ pub enum DesktopFrontendMessage {
|
||||||
UpdateMenu {
|
UpdateMenu {
|
||||||
entries: Vec<MenuItem>,
|
entries: Vec<MenuItem>,
|
||||||
},
|
},
|
||||||
|
WindowClose,
|
||||||
|
WindowMinimize,
|
||||||
|
WindowMaximize,
|
||||||
|
WindowDrag,
|
||||||
|
WindowHide,
|
||||||
|
WindowHideOthers,
|
||||||
|
WindowShowAll,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum DesktopWrapperMessage {
|
pub enum DesktopWrapperMessage {
|
||||||
|
|
|
||||||
|
|
@ -285,6 +285,7 @@ impl Dispatcher {
|
||||||
pub fn collect_actions(&self) -> ActionList {
|
pub fn collect_actions(&self) -> ActionList {
|
||||||
// TODO: Reduce the number of heap allocations
|
// TODO: Reduce the number of heap allocations
|
||||||
let mut list = Vec::new();
|
let mut list = Vec::new();
|
||||||
|
list.extend(self.message_handlers.app_window_message_handler.actions());
|
||||||
list.extend(self.message_handlers.dialog_message_handler.actions());
|
list.extend(self.message_handlers.dialog_message_handler.actions());
|
||||||
list.extend(self.message_handlers.animation_message_handler.actions());
|
list.extend(self.message_handlers.animation_message_handler.actions());
|
||||||
list.extend(self.message_handlers.input_preprocessor_message_handler.actions());
|
list.extend(self.message_handlers.input_preprocessor_message_handler.actions());
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,12 @@ use super::app_window_message_handler::AppWindowPlatform;
|
||||||
#[impl_message(Message, AppWindow)]
|
#[impl_message(Message, AppWindow)]
|
||||||
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
|
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||||
pub enum AppWindowMessage {
|
pub enum AppWindowMessage {
|
||||||
AppWindowMinimize,
|
UpdatePlatform { platform: AppWindowPlatform },
|
||||||
AppWindowMaximize,
|
Close,
|
||||||
AppWindowUpdatePlatform { platform: AppWindowPlatform },
|
Minimize,
|
||||||
AppWindowDrag,
|
Maximize,
|
||||||
AppWindowClose,
|
Drag,
|
||||||
|
Hide,
|
||||||
|
HideOthers,
|
||||||
|
ShowAll,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,28 +11,41 @@ pub struct AppWindowMessageHandler {
|
||||||
impl MessageHandler<AppWindowMessage, ()> for AppWindowMessageHandler {
|
impl MessageHandler<AppWindowMessage, ()> for AppWindowMessageHandler {
|
||||||
fn process_message(&mut self, message: AppWindowMessage, responses: &mut std::collections::VecDeque<Message>, _: ()) {
|
fn process_message(&mut self, message: AppWindowMessage, responses: &mut std::collections::VecDeque<Message>, _: ()) {
|
||||||
match message {
|
match message {
|
||||||
AppWindowMessage::AppWindowMaximize => {
|
AppWindowMessage::UpdatePlatform { platform } => {
|
||||||
responses.add(FrontendMessage::TriggerMaximizeWindow);
|
|
||||||
}
|
|
||||||
AppWindowMessage::AppWindowMinimize => {
|
|
||||||
responses.add(FrontendMessage::TriggerMinimizeWindow);
|
|
||||||
}
|
|
||||||
AppWindowMessage::AppWindowUpdatePlatform { platform } => {
|
|
||||||
self.platform = platform;
|
self.platform = platform;
|
||||||
responses.add(FrontendMessage::UpdatePlatform { platform: self.platform });
|
responses.add(FrontendMessage::UpdatePlatform { platform: self.platform });
|
||||||
}
|
}
|
||||||
AppWindowMessage::AppWindowDrag => {
|
AppWindowMessage::Close => {
|
||||||
responses.add(FrontendMessage::DragWindow);
|
responses.add(FrontendMessage::WindowClose);
|
||||||
}
|
}
|
||||||
AppWindowMessage::AppWindowClose => {
|
AppWindowMessage::Minimize => {
|
||||||
responses.add(FrontendMessage::CloseWindow);
|
responses.add(FrontendMessage::WindowMinimize);
|
||||||
|
}
|
||||||
|
AppWindowMessage::Maximize => {
|
||||||
|
responses.add(FrontendMessage::WindowMaximize);
|
||||||
|
}
|
||||||
|
AppWindowMessage::Drag => {
|
||||||
|
responses.add(FrontendMessage::WindowDrag);
|
||||||
|
}
|
||||||
|
AppWindowMessage::Hide => {
|
||||||
|
responses.add(FrontendMessage::WindowHide);
|
||||||
|
}
|
||||||
|
AppWindowMessage::HideOthers => {
|
||||||
|
responses.add(FrontendMessage::WindowHideOthers);
|
||||||
|
}
|
||||||
|
AppWindowMessage::ShowAll => {
|
||||||
|
responses.add(FrontendMessage::WindowShowAll);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
advertise_actions!(AppWindowMessageDiscriminant;
|
||||||
fn actions(&self) -> ActionList {
|
Close,
|
||||||
actions!(AppWindowMessageDiscriminant;)
|
Minimize,
|
||||||
}
|
Maximize,
|
||||||
|
Drag,
|
||||||
|
Hide,
|
||||||
|
HideOthers,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Clone, Copy, Default, Debug, serde::Serialize, serde::Deserialize, specta::Type)]
|
#[derive(PartialEq, Eq, Clone, Copy, Default, Debug, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||||
|
|
|
||||||
|
|
@ -121,8 +121,6 @@ pub enum FrontendMessage {
|
||||||
TriggerVisitLink {
|
TriggerVisitLink {
|
||||||
url: String,
|
url: String,
|
||||||
},
|
},
|
||||||
TriggerMinimizeWindow,
|
|
||||||
TriggerMaximizeWindow,
|
|
||||||
|
|
||||||
// Update prefix: give the frontend a new value or state for it to use
|
// Update prefix: give the frontend a new value or state for it to use
|
||||||
UpdateActiveDocument {
|
UpdateActiveDocument {
|
||||||
|
|
@ -348,8 +346,6 @@ pub enum FrontendMessage {
|
||||||
UpdateMaximized {
|
UpdateMaximized {
|
||||||
maximized: bool,
|
maximized: bool,
|
||||||
},
|
},
|
||||||
DragWindow,
|
|
||||||
CloseWindow,
|
|
||||||
UpdateViewportHolePunch {
|
UpdateViewportHolePunch {
|
||||||
active: bool,
|
active: bool,
|
||||||
},
|
},
|
||||||
|
|
@ -365,4 +361,11 @@ pub enum FrontendMessage {
|
||||||
#[derivative(Debug = "ignore", PartialEq = "ignore")]
|
#[derivative(Debug = "ignore", PartialEq = "ignore")]
|
||||||
context: OverlayContext,
|
context: OverlayContext,
|
||||||
},
|
},
|
||||||
|
WindowClose,
|
||||||
|
WindowMinimize,
|
||||||
|
WindowMaximize,
|
||||||
|
WindowDrag,
|
||||||
|
WindowHide,
|
||||||
|
WindowHideOthers,
|
||||||
|
WindowShowAll,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -468,7 +468,7 @@ pub fn input_mappings() -> Mapping {
|
||||||
// Sort `pointer_shake`
|
// Sort `pointer_shake`
|
||||||
sort(&mut pointer_shake);
|
sort(&mut pointer_shake);
|
||||||
|
|
||||||
Mapping {
|
let mut mapping = Mapping {
|
||||||
key_up,
|
key_up,
|
||||||
key_down,
|
key_down,
|
||||||
key_up_no_repeat,
|
key_up_no_repeat,
|
||||||
|
|
@ -477,7 +477,16 @@ pub fn input_mappings() -> Mapping {
|
||||||
wheel_scroll,
|
wheel_scroll,
|
||||||
pointer_move,
|
pointer_move,
|
||||||
pointer_shake,
|
pointer_shake,
|
||||||
|
};
|
||||||
|
|
||||||
|
if cfg!(target_os = "macos") {
|
||||||
|
let remove: [&[&[MappingEntry; 0]; 0]; 0] = [];
|
||||||
|
let add = [entry!(KeyDown(KeyQ); modifiers=[Accel], action_dispatch=AppWindowMessage::Close)];
|
||||||
|
|
||||||
|
apply_mapping_patch(&mut mapping, remove, add);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mapping
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Default mappings except that scrolling without modifier keys held down is bound to zooming instead of vertical panning
|
/// Default mappings except that scrolling without modifier keys held down is bound to zooming instead of vertical panning
|
||||||
|
|
|
||||||
|
|
@ -50,12 +50,65 @@ impl LayoutHolder for MenuBarMessageHandler {
|
||||||
let reset_node_definitions_on_open = self.reset_node_definitions_on_open;
|
let reset_node_definitions_on_open = self.reset_node_definitions_on_open;
|
||||||
let make_path_editable_is_allowed = self.make_path_editable_is_allowed;
|
let make_path_editable_is_allowed = self.make_path_editable_is_allowed;
|
||||||
|
|
||||||
|
let about = MenuBarEntry {
|
||||||
|
#[cfg(not(target_os = "macos"))]
|
||||||
|
label: "About Graphite…".into(),
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
label: "About Graphite".into(),
|
||||||
|
icon: Some("GraphiteLogo".into()),
|
||||||
|
action: MenuBarEntry::create_action(|_| DialogMessage::RequestAboutGraphiteDialog.into()),
|
||||||
|
..MenuBarEntry::default()
|
||||||
|
};
|
||||||
|
let preferences = MenuBarEntry {
|
||||||
|
label: "Preferences…".into(),
|
||||||
|
icon: Some("Settings".into()),
|
||||||
|
shortcut: action_keys!(DialogMessageDiscriminant::RequestPreferencesDialog),
|
||||||
|
action: MenuBarEntry::create_action(|_| DialogMessage::RequestPreferencesDialog.into()),
|
||||||
|
..MenuBarEntry::default()
|
||||||
|
};
|
||||||
|
|
||||||
let menu_bar_entries = vec![
|
let menu_bar_entries = vec![
|
||||||
|
#[cfg(not(target_os = "macos"))]
|
||||||
MenuBarEntry {
|
MenuBarEntry {
|
||||||
icon: Some("GraphiteLogo".into()),
|
icon: Some("GraphiteLogo".into()),
|
||||||
action: MenuBarEntry::create_action(|_| FrontendMessage::TriggerVisitLink { url: "https://graphite.rs".into() }.into()),
|
action: MenuBarEntry::create_action(|_| FrontendMessage::TriggerVisitLink { url: "https://graphite.rs".into() }.into()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
MenuBarEntry::new_root(
|
||||||
|
"".into(),
|
||||||
|
false,
|
||||||
|
MenuBarEntryChildren(vec![
|
||||||
|
vec![about],
|
||||||
|
vec![preferences],
|
||||||
|
vec![
|
||||||
|
MenuBarEntry {
|
||||||
|
label: "Hide Graphite".into(),
|
||||||
|
shortcut: action_keys!(AppWindowMessageDiscriminant::Hide),
|
||||||
|
action: MenuBarEntry::create_action(|_| AppWindowMessage::Hide.into()),
|
||||||
|
..MenuBarEntry::default()
|
||||||
|
},
|
||||||
|
MenuBarEntry {
|
||||||
|
label: "Hide Others".into(),
|
||||||
|
shortcut: action_keys!(AppWindowMessageDiscriminant::HideOthers),
|
||||||
|
action: MenuBarEntry::create_action(|_| AppWindowMessage::HideOthers.into()),
|
||||||
|
..MenuBarEntry::default()
|
||||||
|
},
|
||||||
|
MenuBarEntry {
|
||||||
|
label: "Show All".into(),
|
||||||
|
shortcut: action_keys!(AppWindowMessageDiscriminant::ShowAll),
|
||||||
|
action: MenuBarEntry::create_action(|_| AppWindowMessage::ShowAll.into()),
|
||||||
|
..MenuBarEntry::default()
|
||||||
|
},
|
||||||
|
],
|
||||||
|
vec![MenuBarEntry {
|
||||||
|
label: "Quit Graphite".into(),
|
||||||
|
shortcut: action_keys!(AppWindowMessageDiscriminant::Close),
|
||||||
|
action: MenuBarEntry::create_action(|_| AppWindowMessage::Close.into()),
|
||||||
|
..MenuBarEntry::default()
|
||||||
|
}],
|
||||||
|
]),
|
||||||
|
),
|
||||||
MenuBarEntry::new_root(
|
MenuBarEntry::new_root(
|
||||||
"File".into(),
|
"File".into(),
|
||||||
false,
|
false,
|
||||||
|
|
@ -137,13 +190,8 @@ impl LayoutHolder for MenuBarMessageHandler {
|
||||||
..MenuBarEntry::default()
|
..MenuBarEntry::default()
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
vec![MenuBarEntry {
|
#[cfg(not(target_os = "macos"))]
|
||||||
label: "Preferences…".into(),
|
vec![preferences],
|
||||||
icon: Some("Settings".into()),
|
|
||||||
shortcut: action_keys!(DialogMessageDiscriminant::RequestPreferencesDialog),
|
|
||||||
action: MenuBarEntry::create_action(|_| DialogMessage::RequestPreferencesDialog.into()),
|
|
||||||
..MenuBarEntry::default()
|
|
||||||
}],
|
|
||||||
]),
|
]),
|
||||||
),
|
),
|
||||||
MenuBarEntry::new_root(
|
MenuBarEntry::new_root(
|
||||||
|
|
@ -633,12 +681,8 @@ impl LayoutHolder for MenuBarMessageHandler {
|
||||||
"Help".into(),
|
"Help".into(),
|
||||||
false,
|
false,
|
||||||
MenuBarEntryChildren(vec![
|
MenuBarEntryChildren(vec![
|
||||||
vec![MenuBarEntry {
|
#[cfg(not(target_os = "macos"))]
|
||||||
label: "About Graphite…".into(),
|
vec![about],
|
||||||
icon: Some("GraphiteLogo".into()),
|
|
||||||
action: MenuBarEntry::create_action(|_| DialogMessage::RequestAboutGraphiteDialog.into()),
|
|
||||||
..MenuBarEntry::default()
|
|
||||||
}],
|
|
||||||
vec![
|
vec![
|
||||||
MenuBarEntry {
|
MenuBarEntry {
|
||||||
label: "Donate to Graphite".into(),
|
label: "Donate to Graphite".into(),
|
||||||
|
|
|
||||||
|
|
@ -277,28 +277,28 @@ impl EditorHandle {
|
||||||
/// Minimizes the application window to the taskbar or dock
|
/// Minimizes the application window to the taskbar or dock
|
||||||
#[wasm_bindgen(js_name = appWindowMinimize)]
|
#[wasm_bindgen(js_name = appWindowMinimize)]
|
||||||
pub fn app_window_minimize(&self) {
|
pub fn app_window_minimize(&self) {
|
||||||
let message = AppWindowMessage::AppWindowMinimize;
|
let message = AppWindowMessage::Minimize;
|
||||||
self.dispatch(message);
|
self.dispatch(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Toggles minimizing or restoring down the application window
|
/// Toggles minimizing or restoring down the application window
|
||||||
#[wasm_bindgen(js_name = appWindowMaximize)]
|
#[wasm_bindgen(js_name = appWindowMaximize)]
|
||||||
pub fn app_window_maximize(&self) {
|
pub fn app_window_maximize(&self) {
|
||||||
let message = AppWindowMessage::AppWindowMaximize;
|
let message = AppWindowMessage::Maximize;
|
||||||
self.dispatch(message);
|
self.dispatch(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Closes the application window
|
/// Closes the application window
|
||||||
#[wasm_bindgen(js_name = appWindowClose)]
|
#[wasm_bindgen(js_name = appWindowClose)]
|
||||||
pub fn app_window_close(&self) {
|
pub fn app_window_close(&self) {
|
||||||
let message = AppWindowMessage::AppWindowClose;
|
let message = AppWindowMessage::Close;
|
||||||
self.dispatch(message);
|
self.dispatch(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Drag the application window
|
/// Drag the application window
|
||||||
#[wasm_bindgen(js_name = appWindowDrag)]
|
#[wasm_bindgen(js_name = appWindowDrag)]
|
||||||
pub fn app_window_start_drag(&self) {
|
pub fn app_window_start_drag(&self) {
|
||||||
let message = AppWindowMessage::AppWindowDrag;
|
let message = AppWindowMessage::Drag;
|
||||||
self.dispatch(message);
|
self.dispatch(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue