From fb30e9130d8c4d7dc1c34ef9dd85688556063c98 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Mon, 20 May 2024 16:27:34 -0600 Subject: [PATCH] Add "new window" option to the dock menu Fixes: #11651 Co-Authored-By: versecafe <147033096+versecafe@users.noreply.github.com> --- crates/gpui/src/app.rs | 9 +++- crates/gpui/src/platform.rs | 1 + crates/gpui/src/platform/linux/platform.rs | 1 + crates/gpui/src/platform/mac/platform.rs | 53 +++++++++++++++++++- crates/gpui/src/platform/test/platform.rs | 1 + crates/gpui/src/platform/windows/platform.rs | 1 + crates/zed/src/zed.rs | 3 +- 7 files changed, 65 insertions(+), 4 deletions(-) diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index 560a24456aa3..e04d526c387e 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -29,8 +29,8 @@ use crate::{ current_platform, init_app_menus, Action, ActionRegistry, Any, AnyView, AnyWindowHandle, AppMetadata, AssetCache, AssetSource, BackgroundExecutor, ClipboardItem, Context, DispatchPhase, DisplayId, Entity, EventEmitter, ForegroundExecutor, Global, KeyBinding, Keymap, - Keystroke, LayoutId, Menu, PathPromptOptions, Pixels, Platform, PlatformDisplay, Point, - PromptBuilder, PromptHandle, PromptLevel, Render, RenderablePromptHandle, Reservation, + Keystroke, LayoutId, Menu, MenuItem, PathPromptOptions, Pixels, Platform, PlatformDisplay, + Point, PromptBuilder, PromptHandle, PromptLevel, Render, RenderablePromptHandle, Reservation, SharedString, SubscriberSet, Subscription, SvgRenderer, Task, TextSystem, View, ViewContext, Window, WindowAppearance, WindowContext, WindowHandle, WindowId, }; @@ -1167,6 +1167,11 @@ impl AppContext { self.platform.set_menus(menus, &self.keymap.borrow()); } + /// Sets the right click menu for the app icon in the dock + pub fn set_dock_menu(&mut self, menus: Vec) { + self.platform.set_dock_menu(menus, &self.keymap.borrow()); + } + /// Adds given path to the bottom of the list of recent paths for the application. /// The list is usually shown on the application icon's context menu in the dock, /// and allows to open the recent files via that context menu. diff --git a/crates/gpui/src/platform.rs b/crates/gpui/src/platform.rs index f7183ad0bf34..11ba6af6c8ea 100644 --- a/crates/gpui/src/platform.rs +++ b/crates/gpui/src/platform.rs @@ -135,6 +135,7 @@ pub(crate) trait Platform: 'static { fn on_reopen(&self, callback: Box); fn set_menus(&self, menus: Vec, keymap: &Keymap); + fn set_dock_menu(&self, menu: Vec, keymap: &Keymap); fn add_recent_document(&self, _path: &Path) {} fn on_app_menu_action(&self, callback: Box); fn on_will_open_app_menu(&self, callback: Box); diff --git a/crates/gpui/src/platform/linux/platform.rs b/crates/gpui/src/platform/linux/platform.rs index 8e261d906f29..01d10d322c77 100644 --- a/crates/gpui/src/platform/linux/platform.rs +++ b/crates/gpui/src/platform/linux/platform.rs @@ -375,6 +375,7 @@ impl Platform for P { // todo(linux) fn set_menus(&self, menus: Vec, keymap: &Keymap) {} + fn set_dock_menu(&self, menu: Vec, keymap: &Keymap) {} fn local_timezone(&self) -> UtcOffset { UtcOffset::UTC diff --git a/crates/gpui/src/platform/mac/platform.rs b/crates/gpui/src/platform/mac/platform.rs index ca93260b5449..97e52f8dc9dc 100644 --- a/crates/gpui/src/platform/mac/platform.rs +++ b/crates/gpui/src/platform/mac/platform.rs @@ -20,7 +20,7 @@ use cocoa::{ }, }; use core_foundation::{ - base::{CFType, CFTypeRef, OSStatus, TCFType as _}, + base::{CFRelease, CFType, CFTypeRef, OSStatus, TCFType as _}, boolean::CFBoolean, data::CFData, dictionary::{CFDictionary, CFDictionaryRef, CFMutableDictionary}, @@ -120,6 +120,10 @@ unsafe fn build_classes() { sel!(menuWillOpen:), menu_will_open as extern "C" fn(&mut Object, Sel, id), ); + decl.add_method( + sel!(applicationDockMenu:), + handle_dock_menu as extern "C" fn(&mut Object, Sel, id) -> id, + ); decl.add_method( sel!(application:openURLs:), open_urls as extern "C" fn(&mut Object, Sel, id, id), @@ -147,6 +151,7 @@ pub(crate) struct MacPlatformState { menu_actions: Vec>, open_urls: Option)>>, finish_launching: Option>, + dock_menu: Option, } impl Default for MacPlatform { @@ -174,6 +179,7 @@ impl MacPlatform { menu_actions: Default::default(), open_urls: None, finish_launching: None, + dock_menu: None, })) } @@ -226,6 +232,27 @@ impl MacPlatform { application_menu } + unsafe fn create_dock_menu( + &self, + menu_items: Vec, + delegate: id, + actions: &mut Vec>, + keymap: &Keymap, + ) -> id { + let dock_menu = NSMenu::new(nil); + dock_menu.setDelegate_(delegate); + for item_config in menu_items { + dock_menu.addItem_(Self::create_menu_item( + item_config, + delegate, + actions, + keymap, + )); + } + + dock_menu + } + unsafe fn create_menu_item( item: MenuItem, delegate: id, @@ -731,6 +758,18 @@ impl Platform for MacPlatform { } } + fn set_dock_menu<'a>(&self, menu: Vec>, keymap: &Keymap) { + unsafe { + let app: id = msg_send![APP_CLASS, sharedApplication]; + let mut state = self.0.lock(); + let actions = &mut state.menu_actions; + let new = self.create_dock_menu(menu, app.delegate(), actions, keymap); + if let Some(old) = state.dock_menu.replace(new) { + CFRelease(old as _) + } + } + } + fn add_recent_document(&self, path: &Path) { if let Some(path_str) = path.to_str() { unsafe { @@ -1128,6 +1167,18 @@ extern "C" fn menu_will_open(this: &mut Object, _: Sel, _: id) { } } +extern "C" fn handle_dock_menu(this: &mut Object, _: Sel, _: id) -> id { + unsafe { + let platform = get_mac_platform(this); + let mut state = platform.0.lock(); + if let Some(id) = state.dock_menu { + id + } else { + nil + } + } +} + unsafe fn ns_string(string: &str) -> id { NSString::alloc(nil).init_str(string).autorelease() } diff --git a/crates/gpui/src/platform/test/platform.rs b/crates/gpui/src/platform/test/platform.rs index b60ecba864fe..16898d463616 100644 --- a/crates/gpui/src/platform/test/platform.rs +++ b/crates/gpui/src/platform/test/platform.rs @@ -233,6 +233,7 @@ impl Platform for TestPlatform { } fn set_menus(&self, _menus: Vec, _keymap: &Keymap) {} + fn set_dock_menu(&self, _menu: Vec, _keymap: &Keymap) {} fn add_recent_document(&self, _paths: &Path) {} diff --git a/crates/gpui/src/platform/windows/platform.rs b/crates/gpui/src/platform/windows/platform.rs index 2430e94283c9..0b0c6ad3c289 100644 --- a/crates/gpui/src/platform/windows/platform.rs +++ b/crates/gpui/src/platform/windows/platform.rs @@ -450,6 +450,7 @@ impl Platform for WindowsPlatform { // todo(windows) fn set_menus(&self, menus: Vec, keymap: &Keymap) {} + fn set_dock_menu(&self, menus: Vec, keymap: &Keymap) {} fn on_app_menu_action(&self, callback: Box) { self.state.borrow_mut().callbacks.app_menu_action = Some(callback); diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 21d375b17187..1d57263671a3 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -10,7 +10,7 @@ use client::ZED_URL_SCHEME; use collections::VecDeque; use editor::{scroll::Autoscroll, Editor, MultiBuffer}; use gpui::{ - actions, point, px, AppContext, AsyncAppContext, Context, FocusableView, PromptLevel, + actions, point, px, AppContext, AsyncAppContext, Context, FocusableView, MenuItem, PromptLevel, TitlebarOptions, View, ViewContext, VisualContext, WindowKind, WindowOptions, }; pub use open_listener::*; @@ -670,6 +670,7 @@ fn reload_keymaps(cx: &mut AppContext, keymap_content: &KeymapFile) { load_default_keymap(cx); keymap_content.clone().add_to_cx(cx).log_err(); cx.set_menus(app_menus()); + cx.set_dock_menu(vec![MenuItem::action("New Window", workspace::NewWindow)]) } pub fn load_default_keymap(cx: &mut AppContext) {