Detailed changes
@@ -69,7 +69,7 @@ pub trait UpdateView {
pub struct Menu<'a> {
pub name: &'a str,
- pub items: &'a [MenuItem<'a>],
+ pub items: Vec<MenuItem<'a>>,
}
pub enum MenuItem<'a> {
@@ -77,6 +77,7 @@ pub enum MenuItem<'a> {
name: &'a str,
keystroke: Option<&'a str>,
action: &'a str,
+ arg: Option<Box<dyn Any>>,
},
Separator,
}
@@ -171,14 +172,14 @@ impl App {
pub fn on_menu_command<F>(self, mut callback: F) -> Self
where
- F: 'static + FnMut(&str, &mut MutableAppContext),
+ F: 'static + FnMut(&str, Option<&dyn Any>, &mut MutableAppContext),
{
let ctx = self.0.clone();
self.0
.borrow()
.platform
- .on_menu_command(Box::new(move |command| {
- callback(command, &mut *ctx.borrow_mut())
+ .on_menu_command(Box::new(move |command, arg| {
+ callback(command, arg, &mut *ctx.borrow_mut())
}));
self
}
@@ -197,7 +198,7 @@ impl App {
self
}
- pub fn set_menus(&self, menus: &[Menu]) {
+ pub fn set_menus(&self, menus: Vec<Menu>) {
self.0.borrow().platform.set_menus(menus);
}
@@ -742,6 +743,7 @@ impl MutableAppContext {
fn open_platform_window(&mut self, window_id: usize) {
match self.platform.open_window(
+ window_id,
WindowOptions {
bounds: RectF::new(vec2f(0., 0.), vec2f(1024., 768.)),
title: "Zed".into(),
@@ -20,6 +20,7 @@ use objc::{
};
use ptr::null_mut;
use std::{
+ any::Any,
cell::RefCell,
ffi::{c_void, CStr},
os::raw::c_char,
@@ -76,7 +77,7 @@ pub struct MacPlatform {
dispatcher: Arc<Dispatcher>,
fonts: Arc<FontSystem>,
callbacks: RefCell<Callbacks>,
- menu_item_actions: RefCell<Vec<String>>,
+ menu_item_actions: RefCell<Vec<(String, Option<Box<dyn Any>>)>>,
}
#[derive(Default)]
@@ -84,7 +85,7 @@ struct Callbacks {
become_active: Option<Box<dyn FnMut()>>,
resign_active: Option<Box<dyn FnMut()>>,
event: Option<Box<dyn FnMut(crate::Event) -> bool>>,
- menu_command: Option<Box<dyn FnMut(&str)>>,
+ menu_command: Option<Box<dyn FnMut(&str, Option<&dyn Any>)>>,
open_files: Option<Box<dyn FnMut(Vec<PathBuf>)>>,
finish_launching: Option<Box<dyn FnOnce() -> ()>>,
}
@@ -99,7 +100,7 @@ impl MacPlatform {
}
}
- unsafe fn create_menu_bar(&self, menus: &[Menu]) -> id {
+ unsafe fn create_menu_bar(&self, menus: Vec<Menu>) -> id {
let menu_bar = NSMenu::new(nil).autorelease();
let mut menu_item_actions = self.menu_item_actions.borrow_mut();
menu_item_actions.clear();
@@ -107,8 +108,9 @@ impl MacPlatform {
for menu_config in menus {
let menu_bar_item = NSMenuItem::new(nil).autorelease();
let menu = NSMenu::new(nil).autorelease();
+ let menu_name = menu_config.name;
- menu.setTitle_(ns_string(menu_config.name));
+ menu.setTitle_(ns_string(menu_name));
for item_config in menu_config.items {
let item;
@@ -121,12 +123,13 @@ impl MacPlatform {
name,
keystroke,
action,
+ arg,
} => {
if let Some(keystroke) = keystroke {
let keystroke = Keystroke::parse(keystroke).unwrap_or_else(|err| {
panic!(
"Invalid keystroke for menu item {}:{} - {:?}",
- menu_config.name, name, err
+ menu_name, name, err
)
});
@@ -161,7 +164,7 @@ impl MacPlatform {
let tag = menu_item_actions.len() as NSInteger;
let _: () = msg_send![item, setTag: tag];
- menu_item_actions.push(action.to_string());
+ menu_item_actions.push((action.to_string(), arg));
}
}
@@ -189,7 +192,7 @@ impl platform::Platform for MacPlatform {
self.callbacks.borrow_mut().event = Some(callback);
}
- fn on_menu_command(&self, callback: Box<dyn FnMut(&str)>) {
+ fn on_menu_command(&self, callback: Box<dyn FnMut(&str, Option<&dyn Any>)>) {
self.callbacks.borrow_mut().menu_command = Some(callback);
}
@@ -231,10 +234,15 @@ impl platform::Platform for MacPlatform {
fn open_window(
&self,
+ id: usize,
options: platform::WindowOptions,
executor: Rc<executor::Foreground>,
) -> Result<Box<dyn platform::Window>> {
- Ok(Box::new(Window::open(options, executor, self.fonts())?))
+ Ok(Box::new(Window::open(id, options, executor, self.fonts())?))
+ }
+
+ fn key_window_id(&self) -> Option<usize> {
+ Window::key_window_id()
}
fn prompt_for_paths(
@@ -292,7 +300,7 @@ impl platform::Platform for MacPlatform {
}
}
- fn set_menus(&self, menus: &[Menu]) {
+ fn set_menus(&self, menus: Vec<Menu>) {
unsafe {
let app: id = msg_send![APP_CLASS, sharedApplication];
app.setMainMenu_(self.create_menu_bar(menus));
@@ -375,8 +383,8 @@ extern "C" fn handle_menu_item(this: &mut Object, _: Sel, item: id) {
if let Some(callback) = platform.callbacks.borrow_mut().menu_command.as_mut() {
let tag: NSInteger = msg_send![item, tag];
let index = tag as usize;
- if let Some(action) = platform.menu_item_actions.borrow().get(index) {
- callback(&action);
+ if let Some((action, arg)) = platform.menu_item_actions.borrow().get(index) {
+ callback(action, arg.as_ref().map(Box::as_ref));
}
}
}
@@ -7,8 +7,8 @@ use crate::{
use anyhow::{anyhow, Result};
use cocoa::{
appkit::{
- NSBackingStoreBuffered, NSScreen, NSView, NSViewHeightSizable, NSViewWidthSizable,
- NSWindow, NSWindowStyleMask,
+ NSApplication, NSBackingStoreBuffered, NSScreen, NSView, NSViewHeightSizable,
+ NSViewWidthSizable, NSWindow, NSWindowStyleMask,
},
base::{id, nil},
foundation::{NSAutoreleasePool, NSInteger, NSSize, NSString},
@@ -118,6 +118,7 @@ unsafe fn build_classes() {
pub struct Window(Rc<RefCell<WindowState>>);
struct WindowState {
+ id: usize,
native_window: id,
event_callback: Option<Box<dyn FnMut(Event)>>,
resize_callback: Option<Box<dyn FnMut(&mut dyn platform::WindowContext)>>,
@@ -131,6 +132,7 @@ struct WindowState {
impl Window {
pub fn open(
+ id: usize,
options: platform::WindowOptions,
executor: Rc<executor::Foreground>,
fonts: Arc<dyn platform::FontSystem>,
@@ -180,6 +182,7 @@ impl Window {
}
let window = Self(Rc::new(RefCell::new(WindowState {
+ id,
native_window,
event_callback: None,
resize_callback: None,
@@ -230,6 +233,19 @@ impl Window {
Ok(window)
}
}
+
+ pub fn key_window_id() -> Option<usize> {
+ unsafe {
+ let app = NSApplication::sharedApplication(nil);
+ let key_window: id = msg_send![app, keyWindow];
+ if key_window.is_null() {
+ None
+ } else {
+ let id = get_window_state(&*key_window).borrow().id;
+ Some(id)
+ }
+ }
+ }
}
impl Drop for Window {
@@ -20,10 +20,10 @@ use crate::{
use anyhow::Result;
use async_task::Runnable;
pub use event::Event;
-use std::{ops::Range, path::PathBuf, rc::Rc, sync::Arc};
+use std::{any::Any, ops::Range, path::PathBuf, rc::Rc, sync::Arc};
pub trait Platform {
- fn on_menu_command(&self, callback: Box<dyn FnMut(&str)>);
+ fn on_menu_command(&self, callback: Box<dyn FnMut(&str, Option<&dyn Any>)>);
fn on_become_active(&self, callback: Box<dyn FnMut()>);
fn on_resign_active(&self, callback: Box<dyn FnMut()>);
fn on_event(&self, callback: Box<dyn FnMut(Event) -> bool>);
@@ -36,13 +36,15 @@ pub trait Platform {
fn activate(&self, ignoring_other_apps: bool);
fn open_window(
&self,
+ id: usize,
options: WindowOptions,
executor: Rc<executor::Foreground>,
) -> Result<Box<dyn Window>>;
+ fn key_window_id(&self) -> Option<usize>;
fn prompt_for_paths(&self, options: PathPromptOptions) -> Option<Vec<PathBuf>>;
fn quit(&self);
fn copy(&self, text: &str);
- fn set_menus(&self, menus: &[Menu]);
+ fn set_menus(&self, menus: Vec<Menu>);
}
pub trait Dispatcher: Send + Sync {
@@ -1,6 +1,6 @@
use pathfinder_geometry::vector::Vector2F;
-use std::rc::Rc;
use std::sync::Arc;
+use std::{any::Any, rc::Rc};
struct Platform {
dispatcher: Arc<dyn super::Dispatcher>,
@@ -27,7 +27,7 @@ impl Platform {
}
impl super::Platform for Platform {
- fn on_menu_command(&self, _: Box<dyn FnMut(&str)>) {}
+ fn on_menu_command(&self, _: Box<dyn FnMut(&str, Option<&dyn Any>)>) {}
fn on_become_active(&self, _: Box<dyn FnMut()>) {}
@@ -53,13 +53,18 @@ impl super::Platform for Platform {
fn open_window(
&self,
+ _: usize,
options: super::WindowOptions,
_executor: Rc<super::executor::Foreground>,
) -> anyhow::Result<Box<dyn super::Window>> {
Ok(Box::new(Window::new(options.bounds.size())))
}
- fn set_menus(&self, _menus: &[crate::Menu]) {}
+ fn key_window_id(&self) -> Option<usize> {
+ None
+ }
+
+ fn set_menus(&self, _menus: Vec<crate::Menu>) {}
fn quit(&self) {}
@@ -10,6 +10,6 @@ mod test;
mod time;
mod timer;
mod util;
-mod watch;
+pub mod watch;
pub mod workspace;
mod worktree;
@@ -4,7 +4,9 @@ use log::LevelFilter;
use simplelog::SimpleLogger;
use std::{fs, path::PathBuf};
use zed::{
- assets, editor, file_finder, menus, settings,
+ assets, editor, file_finder, menus,
+ settings::{self, Settings},
+ watch::Receiver,
workspace::{self, OpenParams},
};
@@ -13,27 +15,28 @@ fn main() {
let app = gpui::App::new(assets::Assets).unwrap();
let (_, settings_rx) = settings::channel(&app.font_cache()).unwrap();
- app.set_menus(menus::MENUS);
- app.on_menu_command({
- let settings_rx = settings_rx.clone();
- move |command, ctx| match command {
- "app:open" => {
- if let Some(paths) = ctx.platform().prompt_for_paths(PathPromptOptions {
- files: true,
- directories: true,
- multiple: true,
- }) {
- ctx.dispatch_global_action(
- "workspace:open_paths",
- OpenParams {
- paths,
- settings: settings_rx.clone(),
- },
- );
- }
+ app.set_menus(menus::menus(settings_rx.clone()));
+ app.on_menu_command(move |command, arg, ctx| match command {
+ "app:open" => {
+ if let Some(paths) = ctx.platform().prompt_for_paths(PathPromptOptions {
+ files: true,
+ directories: true,
+ multiple: true,
+ }) {
+ ctx.dispatch_global_action(
+ "workspace:open_paths",
+ OpenParams {
+ paths,
+ settings: arg
+ .unwrap()
+ .downcast_ref::<Receiver<Settings>>()
+ .unwrap()
+ .clone(),
+ },
+ );
}
- _ => ctx.dispatch_global_action(command, ()),
}
+ _ => ctx.dispatch_global_action(command, ()),
})
.run(move |ctx| {
workspace::init(ctx);
@@ -1,60 +1,71 @@
+use crate::{settings::Settings, watch::Receiver};
use gpui::{Menu, MenuItem};
#[cfg(target_os = "macos")]
-pub const MENUS: &'static [Menu] = &[
- Menu {
- name: "Zed",
- items: &[
- MenuItem::Action {
- name: "About Zedβ¦",
- keystroke: None,
- action: "app:about-zed",
- },
- MenuItem::Separator,
- MenuItem::Action {
- name: "Quit",
- keystroke: Some("cmd-q"),
- action: "app:quit",
- },
- ],
- },
- Menu {
- name: "File",
- items: &[MenuItem::Action {
- name: "Openβ¦",
- keystroke: Some("cmd-o"),
- action: "app:open",
- }],
- },
- Menu {
- name: "Edit",
- items: &[
- MenuItem::Action {
- name: "Undo",
- keystroke: Some("cmd-z"),
- action: "editor:undo",
- },
- MenuItem::Action {
- name: "Redo",
- keystroke: Some("cmd-Z"),
- action: "editor:redo",
- },
- MenuItem::Separator,
- MenuItem::Action {
- name: "Cut",
- keystroke: Some("cmd-x"),
- action: "editor:cut",
- },
- MenuItem::Action {
- name: "Copy",
- keystroke: Some("cmd-c"),
- action: "editor:copy",
- },
- MenuItem::Action {
- name: "Paste",
- keystroke: Some("cmd-v"),
- action: "editor:paste",
- },
- ],
- },
-];
+pub fn menus(settings: Receiver<Settings>) -> Vec<Menu<'static>> {
+ vec![
+ Menu {
+ name: "Zed",
+ items: vec![
+ MenuItem::Action {
+ name: "About Zedβ¦",
+ keystroke: None,
+ action: "app:about-zed",
+ arg: None,
+ },
+ MenuItem::Separator,
+ MenuItem::Action {
+ name: "Quit",
+ keystroke: Some("cmd-q"),
+ action: "app:quit",
+ arg: None,
+ },
+ ],
+ },
+ Menu {
+ name: "File",
+ items: vec![MenuItem::Action {
+ name: "Openβ¦",
+ keystroke: Some("cmd-o"),
+ action: "app:open",
+ arg: Some(Box::new(settings)),
+ }],
+ },
+ Menu {
+ name: "Edit",
+ items: vec![
+ MenuItem::Action {
+ name: "Undo",
+ keystroke: Some("cmd-z"),
+ action: "editor:undo",
+ arg: None,
+ },
+ MenuItem::Action {
+ name: "Redo",
+ keystroke: Some("cmd-Z"),
+ action: "editor:redo",
+ arg: None,
+ },
+ MenuItem::Separator,
+ MenuItem::Action {
+ name: "Cut",
+ keystroke: Some("cmd-x"),
+ action: "editor:cut",
+ arg: None,
+ },
+ MenuItem::Action {
+ name: "Copy",
+ keystroke: Some("cmd-c"),
+ action: "editor:copy",
+ arg: None,
+ },
+ MenuItem::Action {
+ name: "Paste",
+ keystroke: Some("cmd-v"),
+ action: "editor:paste",
+ arg: None,
+ },
+ ],
+ },
+ ]
+}