Merge pull request #1203 from zed-industries/themes-in-app-menu

Themes in app menu
This commit is contained in:
Max Brunsfeld
2022-06-16 17:59:10 -07:00
committed by GitHub
5 changed files with 160 additions and 98 deletions
+2 -1
View File
@@ -133,11 +133,12 @@ pub struct Menu<'a> {
}
pub enum MenuItem<'a> {
Separator,
Submenu(Menu<'a>),
Action {
name: &'a str,
action: Box<dyn Action>,
},
Separator,
}
#[derive(Clone)]
+4 -7
View File
@@ -40,13 +40,10 @@ impl Element for KeystrokeLabel {
let mut element = if let Some(keystrokes) = cx.keystrokes_for_action(self.action.as_ref()) {
Flex::row()
.with_children(keystrokes.iter().map(|keystroke| {
Label::new(
keystroke.to_string().to_uppercase(),
self.text_style.clone(),
)
.contained()
.with_style(self.container_style)
.boxed()
Label::new(keystroke.to_string(), self.text_style.clone())
.contained()
.with_style(self.container_style)
.boxed()
}))
.boxed()
} else {
+20 -14
View File
@@ -4,7 +4,7 @@ use smallvec::SmallVec;
use std::{
any::{Any, TypeId},
collections::{HashMap, HashSet},
fmt::Debug,
fmt::{Debug, Write},
};
use tree_sitter::{Language, Node, Parser};
@@ -318,28 +318,34 @@ impl Keystroke {
impl std::fmt::Display for Keystroke {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.ctrl {
write!(f, "{}", "^")?;
f.write_char('^')?;
}
if self.alt {
write!(f, "{}", "")?;
f.write_char('⎇')?;
}
if self.cmd {
write!(f, "{}", "")?;
f.write_char('⌘')?;
}
if self.shift {
write!(f, "{}", "")?;
f.write_char('⇧')?;
}
let key = match self.key.as_str() {
"backspace" => "",
"up" => "",
"down" => "",
"left" => "",
"right" => "",
"tab" => "",
"escape" => "",
key => key,
"backspace" => '⌫',
"up" => '↑',
"down" => '↓',
"left" => '←',
"right" => '→',
"tab" => '⇥',
"escape" => '⎋',
key => {
if key.len() == 1 {
key.chars().next().unwrap().to_ascii_uppercase()
} else {
return f.write_str(key);
}
}
};
write!(f, "{}", key)
f.write_char(key)
}
}
+117 -68
View File
@@ -127,89 +127,131 @@ impl MacForegroundPlatform {
&self,
menus: Vec<Menu>,
delegate: id,
actions: &mut Vec<Box<dyn Action>>,
keystroke_matcher: &keymap::Matcher,
) -> id {
let menu_bar = NSMenu::new(nil).autorelease();
menu_bar.setDelegate_(delegate);
let mut state = self.0.borrow_mut();
state.menu_actions.clear();
let application_menu = NSMenu::new(nil).autorelease();
application_menu.setDelegate_(delegate);
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_name));
menu.setTitle_(ns_string(menu_config.name));
menu.setDelegate_(delegate);
for item_config in menu_config.items {
let item;
match item_config {
MenuItem::Separator => {
item = NSMenuItem::separatorItem(nil);
}
MenuItem::Action { name, action } => {
let mut keystroke = None;
if let Some(binding) = keystroke_matcher
.bindings_for_action_type(action.as_any().type_id())
.find(|binding| binding.action().eq(action.as_ref()))
{
if binding.keystrokes().len() == 1 {
keystroke = binding.keystrokes().first()
}
}
if let Some(keystroke) = keystroke {
let mut mask = NSEventModifierFlags::empty();
for (modifier, flag) in &[
(keystroke.cmd, NSEventModifierFlags::NSCommandKeyMask),
(keystroke.ctrl, NSEventModifierFlags::NSControlKeyMask),
(keystroke.alt, NSEventModifierFlags::NSAlternateKeyMask),
] {
if *modifier {
mask |= *flag;
}
}
item = NSMenuItem::alloc(nil)
.initWithTitle_action_keyEquivalent_(
ns_string(name),
selector("handleGPUIMenuItem:"),
ns_string(key_to_native(&keystroke.key).as_ref()),
)
.autorelease();
item.setKeyEquivalentModifierMask_(mask);
} else {
item = NSMenuItem::alloc(nil)
.initWithTitle_action_keyEquivalent_(
ns_string(name),
selector("handleGPUIMenuItem:"),
ns_string(""),
)
.autorelease();
}
let tag = state.menu_actions.len() as NSInteger;
let _: () = msg_send![item, setTag: tag];
state.menu_actions.push(action);
}
}
menu.addItem_(item);
menu.addItem_(self.create_menu_item(
item_config,
delegate,
actions,
keystroke_matcher,
));
}
menu_bar_item.setSubmenu_(menu);
menu_bar.addItem_(menu_bar_item);
let menu_item = NSMenuItem::new(nil).autorelease();
menu_item.setSubmenu_(menu);
application_menu.addItem_(menu_item);
if menu_name == "Window" {
if menu_config.name == "Window" {
let app: id = msg_send![APP_CLASS, sharedApplication];
app.setWindowsMenu_(menu);
}
}
menu_bar
application_menu
}
unsafe fn create_menu_item(
&self,
item: MenuItem,
delegate: id,
actions: &mut Vec<Box<dyn Action>>,
keystroke_matcher: &keymap::Matcher,
) -> id {
match item {
MenuItem::Separator => NSMenuItem::separatorItem(nil),
MenuItem::Action { name, action } => {
let keystrokes = keystroke_matcher
.bindings_for_action_type(action.as_any().type_id())
.find(|binding| binding.action().eq(action.as_ref()))
.map(|binding| binding.keystrokes());
let item;
if let Some(keystrokes) = keystrokes {
if keystrokes.len() == 1 {
let keystroke = &keystrokes[0];
let mut mask = NSEventModifierFlags::empty();
for (modifier, flag) in &[
(keystroke.cmd, NSEventModifierFlags::NSCommandKeyMask),
(keystroke.ctrl, NSEventModifierFlags::NSControlKeyMask),
(keystroke.alt, NSEventModifierFlags::NSAlternateKeyMask),
] {
if *modifier {
mask |= *flag;
}
}
item = NSMenuItem::alloc(nil)
.initWithTitle_action_keyEquivalent_(
ns_string(name),
selector("handleGPUIMenuItem:"),
ns_string(key_to_native(&keystroke.key).as_ref()),
)
.autorelease();
item.setKeyEquivalentModifierMask_(mask);
}
// For multi-keystroke bindings, render the keystroke as part of the title.
else {
use std::fmt::Write;
let mut name = format!("{name} [");
for (i, keystroke) in keystrokes.iter().enumerate() {
if i > 0 {
name.push(' ');
}
write!(&mut name, "{}", keystroke).unwrap();
}
name.push(']');
item = NSMenuItem::alloc(nil)
.initWithTitle_action_keyEquivalent_(
ns_string(&name),
selector("handleGPUIMenuItem:"),
ns_string(""),
)
.autorelease();
}
} else {
item = NSMenuItem::alloc(nil)
.initWithTitle_action_keyEquivalent_(
ns_string(name),
selector("handleGPUIMenuItem:"),
ns_string(""),
)
.autorelease();
}
let tag = actions.len() as NSInteger;
let _: () = msg_send![item, setTag: tag];
actions.push(action);
item
}
MenuItem::Submenu(Menu { name, items }) => {
let item = NSMenuItem::new(nil).autorelease();
let submenu = NSMenu::new(nil).autorelease();
submenu.setDelegate_(delegate);
for item in items {
submenu.addItem_(self.create_menu_item(
item,
delegate,
actions,
keystroke_matcher,
));
}
item.setSubmenu_(submenu);
item.setTitle_(ns_string(name));
item
}
}
}
}
@@ -270,7 +312,14 @@ impl platform::ForegroundPlatform for MacForegroundPlatform {
fn set_menus(&self, menus: Vec<Menu>, keystroke_matcher: &keymap::Matcher) {
unsafe {
let app: id = msg_send![APP_CLASS, sharedApplication];
app.setMainMenu_(self.create_menu_bar(menus, app.delegate(), keystroke_matcher));
let mut state = self.0.borrow_mut();
let actions = &mut state.menu_actions;
app.setMainMenu_(self.create_menu_bar(
menus,
app.delegate(),
actions,
keystroke_matcher,
));
}
}
+17 -8
View File
@@ -15,14 +15,23 @@ pub fn menus() -> Vec<Menu<'static>> {
action: Box::new(auto_update::Check),
},
MenuItem::Separator,
MenuItem::Action {
name: "Open Settings",
action: Box::new(super::OpenSettings),
},
MenuItem::Action {
name: "Open Key Bindings",
action: Box::new(super::OpenKeymap),
},
MenuItem::Submenu(Menu {
name: "Preferences",
items: vec![
MenuItem::Action {
name: "Open Settings",
action: Box::new(super::OpenSettings),
},
MenuItem::Action {
name: "Open Key Bindings",
action: Box::new(super::OpenKeymap),
},
MenuItem::Action {
name: "Select Theme",
action: Box::new(theme_selector::Toggle),
},
],
}),
MenuItem::Action {
name: "Install CLI",
action: Box::new(super::InstallCommandLineInterface),