feat: settings for menu shortcuts on macOS (#3481)

* feat: settings for menu shortcuts on macOS

---------

Co-authored-by: Alexsander Falcucci <alex.falcucci@gmail.com>
This commit is contained in:
Ole 2026-04-22 10:26:25 +01:00 committed by GitHub
parent b5363a36b7
commit c8b801493a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 355 additions and 46 deletions

View file

@ -118,6 +118,69 @@ pub struct CmdLineSettings {
#[arg(long = "no-system-native-tabs", action = ArgAction::SetTrue, value_parser = FalseyValueParser::new())]
_no_system_native_tabs: bool,
/// Set the Window > New Window shortcut
#[cfg(target_os = "macos")]
#[arg(
long = "system-new-window-hotkey",
env = "NEOVIDE_SYSTEM_NEW_WINDOW_HOTKEY",
default_value = "cmd+n"
)]
pub system_new_window_hotkey: String,
/// Set the Neovide > Hide shortcut
#[cfg(target_os = "macos")]
#[arg(
long = "system-hide-hotkey",
env = "NEOVIDE_SYSTEM_HIDE_HOTKEY",
default_value = "cmd+h"
)]
pub system_hide_hotkey: String,
/// Set the Neovide > Hide Others shortcut
#[cfg(target_os = "macos")]
#[arg(
long = "system-hide-others-hotkey",
env = "NEOVIDE_SYSTEM_HIDE_OTHERS_HOTKEY",
default_value = "cmd+alt+h"
)]
pub system_hide_others_hotkey: String,
/// Set the Neovide > Quit shortcut
#[cfg(target_os = "macos")]
#[arg(
long = "system-quit-hotkey",
env = "NEOVIDE_SYSTEM_QUIT_HOTKEY",
default_value = "cmd+q"
)]
pub system_quit_hotkey: String,
/// Set the Window > Minimize shortcut
#[cfg(target_os = "macos")]
#[arg(
long = "system-minimize-hotkey",
env = "NEOVIDE_SYSTEM_MINIMIZE_HOTKEY",
default_value = "cmd+m"
)]
pub system_minimize_hotkey: String,
/// Set the Window > Enter Full Screen shortcut
#[cfg(target_os = "macos")]
#[arg(
long = "system-fullscreen-hotkey",
env = "NEOVIDE_SYSTEM_FULLSCREEN_HOTKEY",
default_value = "cmd+ctrl+f"
)]
pub system_fullscreen_hotkey: String,
/// Set the Window > Editors shortcut when native tabs are visible
#[cfg(target_os = "macos")]
#[arg(
long = "system-show-all-tabs-hotkey",
env = "NEOVIDE_SYSTEM_SHOW_ALL_TABS_HOTKEY",
default_value = "cmd+shift+e"
)]
pub system_show_all_tabs_hotkey: String,
/// Cycle to the previous system tab when pressed inside Neovide
#[cfg(target_os = "macos")]
#[arg(
@ -458,6 +521,86 @@ mod tests {
assert!(handle_command_line_arguments(args, &settings).is_err());
}
#[test]
#[cfg(target_os = "macos")]
fn test_system_new_window_hotkey_flag() {
let settings = Settings::new();
let args: Vec<String> = ["neovide", "--system-new-window-hotkey", "cmd+shift+n"]
.iter()
.map(|s| s.to_string())
.collect();
handle_command_line_arguments(args, &settings).expect("Could not parse arguments");
assert_eq!(settings.get::<CmdLineSettings>().system_new_window_hotkey, "cmd+shift+n");
}
#[test]
#[cfg(target_os = "macos")]
fn test_system_new_window_hotkey_environment_variable() {
let settings = Settings::new();
let args: Vec<String> = ["neovide"].iter().map(|s| s.to_string()).collect();
let _env = ScopedEnv::set("NEOVIDE_SYSTEM_NEW_WINDOW_HOTKEY", "ctrl+shift+n");
handle_command_line_arguments(args, &settings).expect("Could not parse arguments");
assert_eq!(settings.get::<CmdLineSettings>().system_new_window_hotkey, "ctrl+shift+n");
}
#[test]
#[cfg(target_os = "macos")]
fn test_system_menu_hotkey_flags() {
let settings = Settings::new();
let args: Vec<String> = [
"neovide",
"--system-hide-hotkey",
"ctrl+h",
"--system-hide-others-hotkey",
"ctrl+alt+h",
"--system-quit-hotkey",
"cmd+shift+q",
"--system-minimize-hotkey",
"cmd+shift+m",
"--system-fullscreen-hotkey",
"ctrl+alt+f",
"--system-show-all-tabs-hotkey",
"cmd+e",
]
.iter()
.map(|s| s.to_string())
.collect();
handle_command_line_arguments(args, &settings).expect("Could not parse arguments");
let cmdline = settings.get::<CmdLineSettings>();
assert_eq!(cmdline.system_hide_hotkey, "ctrl+h");
assert_eq!(cmdline.system_hide_others_hotkey, "ctrl+alt+h");
assert_eq!(cmdline.system_quit_hotkey, "cmd+shift+q");
assert_eq!(cmdline.system_minimize_hotkey, "cmd+shift+m");
assert_eq!(cmdline.system_fullscreen_hotkey, "ctrl+alt+f");
assert_eq!(cmdline.system_show_all_tabs_hotkey, "cmd+e");
}
#[test]
#[cfg(target_os = "macos")]
fn test_system_menu_hotkey_environment_variables() {
let settings = Settings::new();
let args: Vec<String> = ["neovide"].iter().map(|s| s.to_string()).collect();
let _hide = ScopedEnv::set("NEOVIDE_SYSTEM_HIDE_HOTKEY", "ctrl+h");
let _hide_others = ScopedEnv::set("NEOVIDE_SYSTEM_HIDE_OTHERS_HOTKEY", "ctrl+alt+h");
let _quit = ScopedEnv::set("NEOVIDE_SYSTEM_QUIT_HOTKEY", "ctrl+q");
let _minimize = ScopedEnv::set("NEOVIDE_SYSTEM_MINIMIZE_HOTKEY", "ctrl+m");
let _fullscreen = ScopedEnv::set("NEOVIDE_SYSTEM_FULLSCREEN_HOTKEY", "ctrl+shift+f");
let _show_all_tabs = ScopedEnv::set("NEOVIDE_SYSTEM_SHOW_ALL_TABS_HOTKEY", "ctrl+shift+e");
handle_command_line_arguments(args, &settings).expect("Could not parse arguments");
let cmdline = settings.get::<CmdLineSettings>();
assert_eq!(cmdline.system_hide_hotkey, "ctrl+h");
assert_eq!(cmdline.system_hide_others_hotkey, "ctrl+alt+h");
assert_eq!(cmdline.system_quit_hotkey, "ctrl+q");
assert_eq!(cmdline.system_minimize_hotkey, "ctrl+m");
assert_eq!(cmdline.system_fullscreen_hotkey, "ctrl+shift+f");
assert_eq!(cmdline.system_show_all_tabs_hotkey, "ctrl+shift+e");
}
#[test]
fn test_grid() {
let settings = Settings::new();

View file

@ -15,9 +15,9 @@ use objc2::{
};
use objc2_app_kit::{
NSApplication, NSAutoresizingMaskOptions, NSColor, NSEvent, NSEventModifierFlags, NSFont,
NSFontAttributeName, NSFontDescriptor, NSFontWeight, NSFontWeightLight, NSImage, NSMenu,
NSMenuDelegate, NSMenuItem, NSTextView, NSView, NSWindow, NSWindowDidBecomeKeyNotification,
NSApplication, NSAutoresizingMaskOptions, NSColor, NSEvent, NSFont, NSFontAttributeName,
NSFontDescriptor, NSFontWeight, NSFontWeightLight, NSImage, NSMenu, NSMenuDelegate, NSMenuItem,
NSTextView, NSView, NSWindow, NSWindowDidBecomeKeyNotification,
NSWindowDidBecomeMainNotification, NSWindowStyleMask, NSWindowTabbingMode,
NSWindowTitleVisibility, NSWorkspace,
};
@ -36,6 +36,7 @@ use crate::bridge::{
use crate::renderer::fonts::font_options::FontOptions;
use crate::settings::Settings;
use crate::utils::expand_tilde;
use crate::window::macos::tab_navigation::KeyCombo;
use crate::{cmd_line::CmdLineSettings, frame::Frame};
use winit::{event_loop::EventLoopProxy, window::Window};
@ -316,6 +317,22 @@ fn open_external_url(url: &str) {
}
}
fn apply_menu_item_hotkey(item: &NSMenuItem, raw: &str, setting_name: &str) {
let Some(shortcut) = KeyCombo::parse(raw) else {
return;
};
let Some(key) = shortcut.to_key() else {
log::warn!(
"macOS menu shortcut '{raw}' for {setting_name} uses an unsupported named key; ignoring"
);
return;
};
item.setKeyEquivalent(key.as_ref());
item.setKeyEquivalentModifierMask(shortcut.to_modifiers());
}
#[derive(Debug)]
pub struct MacosWindowFeature {
ns_window: Retained<NSWindow>,
@ -1111,7 +1128,7 @@ impl MacosWindowFeature {
return;
}
*menu_cell.borrow_mut() = Some(Menu::new(mtm));
*menu_cell.borrow_mut() = Some(Menu::new(mtm, self.settings.as_ref()));
let app = NSApplication::sharedApplication(mtm);
#[allow(deprecated)]
app.activateIgnoringOtherApps(true);
@ -1516,7 +1533,7 @@ struct Menu {
}
impl Menu {
fn new(mtm: MainThreadMarker) -> Self {
fn new(mtm: MainThreadMarker, settings: &Settings) -> Self {
let menu = Menu {
quit_handler: QuitHandler::new(mtm),
help_menu_handler: HelpMenuHandler::new(mtm),
@ -1526,11 +1543,11 @@ impl Menu {
_window_menu_observer: WindowMenuNotificationHandler::register(mtm),
window_menu_delegate: WindowMenuDelegate::new(mtm),
};
menu.add_menus(mtm);
menu.add_menus(mtm, &settings.get::<CmdLineSettings>());
menu
}
fn add_app_menu(&self, mtm: MainThreadMarker) -> Retained<NSMenu> {
fn add_app_menu(&self, mtm: MainThreadMarker, settings: &CmdLineSettings) -> Retained<NSMenu> {
unsafe {
let app_menu = NSMenu::new(mtm);
let process_name = NSProcessInfo::processInfo().processName();
@ -1551,15 +1568,16 @@ impl Menu {
// application window operations
let hide_item = NSMenuItem::new(mtm);
hide_item.setTitle(&ns_string!("Hide ").stringByAppendingString(&process_name));
hide_item.setKeyEquivalent(ns_string!("h"));
apply_menu_item_hotkey(&hide_item, &settings.system_hide_hotkey, "system_hide_hotkey");
hide_item.setAction(Some(sel!(hide:)));
app_menu.addItem(&hide_item);
let hide_others_item = NSMenuItem::new(mtm);
hide_others_item.setTitle(ns_string!("Hide Others"));
hide_others_item.setKeyEquivalent(ns_string!("h"));
hide_others_item.setKeyEquivalentModifierMask(
NSEventModifierFlags::Option | NSEventModifierFlags::Command,
apply_menu_item_hotkey(
&hide_others_item,
&settings.system_hide_others_hotkey,
"system_hide_others_hotkey",
);
hide_others_item.setAction(Some(sel!(hideOtherApplications:)));
app_menu.addItem(&hide_others_item);
@ -1574,7 +1592,7 @@ impl Menu {
let quit_item = NSMenuItem::new(mtm);
quit_item.setTitle(&ns_string!("Quit ").stringByAppendingString(&process_name));
quit_item.setKeyEquivalent(ns_string!("q"));
apply_menu_item_hotkey(&quit_item, &settings.system_quit_hotkey, "system_quit_hotkey");
quit_item.setAction(Some(sel!(quit:)));
quit_item.setTarget(Some(&self.quit_handler));
app_menu.addItem(&quit_item);
@ -1583,12 +1601,12 @@ impl Menu {
}
}
fn add_menus(&self, mtm: MainThreadMarker) {
fn add_menus(&self, mtm: MainThreadMarker, settings: &CmdLineSettings) {
let app = NSApplication::sharedApplication(mtm);
let main_menu = NSMenu::new(mtm);
let app_menu = self.add_app_menu(mtm);
let app_menu = self.add_app_menu(mtm, settings);
let app_menu_item = NSMenuItem::new(mtm);
app_menu_item.setSubmenu(Some(&app_menu));
if let Some(services_menu) = app_menu.itemWithTitle(ns_string!("Services")) {
@ -1596,7 +1614,7 @@ impl Menu {
}
main_menu.addItem(&app_menu_item);
let win_menu = self.add_window_menu(mtm);
let win_menu = self.add_window_menu(mtm, settings);
let win_menu_item = NSMenuItem::new(mtm);
win_menu_item.setSubmenu(Some(&win_menu));
main_menu.addItem(&win_menu_item);
@ -1612,7 +1630,11 @@ impl Menu {
app.setMainMenu(Some(&main_menu));
}
fn add_window_menu(&self, mtm: MainThreadMarker) -> Retained<NSMenu> {
fn add_window_menu(
&self,
mtm: MainThreadMarker,
settings: &CmdLineSettings,
) -> Retained<NSMenu> {
unsafe {
let menu = NSMenu::new(mtm);
menu.setTitle(ns_string!("Window"));
@ -1622,16 +1644,21 @@ impl Menu {
let full_screen_item = NSMenuItem::new(mtm);
full_screen_item.setTitle(ns_string!("Enter Full Screen"));
full_screen_item.setKeyEquivalent(ns_string!("f"));
full_screen_item.setAction(Some(sel!(toggleFullScreen:)));
full_screen_item.setKeyEquivalentModifierMask(
NSEventModifierFlags::Control | NSEventModifierFlags::Command,
apply_menu_item_hotkey(
&full_screen_item,
&settings.system_fullscreen_hotkey,
"system_fullscreen_hotkey",
);
full_screen_item.setAction(Some(sel!(toggleFullScreen:)));
menu.addItem(&full_screen_item);
let create_new_window = NSMenuItem::new(mtm);
create_new_window.setTitle(ns_string!("New Window"));
create_new_window.setKeyEquivalent(ns_string!("n"));
apply_menu_item_hotkey(
&create_new_window,
&settings.system_new_window_hotkey,
"system_new_window_hotkey",
);
create_new_window.setAction(Some(sel!(neovideCreateWindow:)));
create_new_window.setTarget(Some(&self.new_window_handler));
menu.addItem(&create_new_window);
@ -1639,9 +1666,10 @@ impl Menu {
if should_show_native_tab_bar() {
let show_all_tabs_item = NSMenuItem::new(mtm);
show_all_tabs_item.setTitle(ns_string!("Editors"));
show_all_tabs_item.setKeyEquivalent(ns_string!("e"));
show_all_tabs_item.setKeyEquivalentModifierMask(
NSEventModifierFlags::Command | NSEventModifierFlags::Shift,
apply_menu_item_hotkey(
&show_all_tabs_item,
&settings.system_show_all_tabs_hotkey,
"system_show_all_tabs_hotkey",
);
show_all_tabs_item.setAction(Some(sel!(neovideShowAllTabs:)));
show_all_tabs_item.setTarget(Some(&self.tab_overview_handler));
@ -1650,7 +1678,11 @@ impl Menu {
let min_item = NSMenuItem::new(mtm);
min_item.setTitle(ns_string!("Minimize"));
min_item.setKeyEquivalent(ns_string!("m"));
apply_menu_item_hotkey(
&min_item,
&settings.system_minimize_hotkey,
"system_minimize_hotkey",
);
min_item.setAction(Some(sel!(performMiniaturize:)));
menu.addItem(&min_item);
menu

View file

@ -3,7 +3,10 @@
use std::{env, fs, sync::mpsc, time::Duration};
use notify_debouncer_full::{new_debouncer, notify::RecursiveMode};
use serde::Deserialize;
use serde::{
Deserialize, Deserializer,
de::{Error as DeError, Unexpected},
};
use winit::event_loop::EventLoopProxy;
use crate::{
@ -45,6 +48,27 @@ pub fn config_path() -> PathBuf {
})
}
#[derive(Debug, Deserialize)]
#[serde(untagged)]
enum HotkeyConfigValue {
String(String),
Bool(bool),
}
fn deserialize_optional_hotkey<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
where
D: Deserializer<'de>,
{
match Option::<HotkeyConfigValue>::deserialize(deserializer)? {
None => Ok(None),
Some(HotkeyConfigValue::String(value)) => Ok(Some(value)),
Some(HotkeyConfigValue::Bool(false)) => Ok(Some("false".to_string())),
Some(HotkeyConfigValue::Bool(true)) => {
Err(D::Error::invalid_value(Unexpected::Bool(true), &"a shortcut string or false"))
}
}
}
#[derive(Debug, Deserialize, Default, Clone)]
#[serde(rename_all = "kebab-case")]
pub struct Config {
@ -67,9 +91,27 @@ pub struct Config {
pub vsync: Option<bool>,
pub wsl: Option<bool>,
pub backtraces_path: Option<PathBuf>,
#[serde(default, deserialize_with = "deserialize_optional_hotkey")]
pub system_pinned_hotkey: Option<String>,
#[serde(default, deserialize_with = "deserialize_optional_hotkey")]
pub system_switcher_hotkey: Option<String>,
#[serde(default, deserialize_with = "deserialize_optional_hotkey")]
pub system_new_window_hotkey: Option<String>,
#[serde(default, deserialize_with = "deserialize_optional_hotkey")]
pub system_hide_hotkey: Option<String>,
#[serde(default, deserialize_with = "deserialize_optional_hotkey")]
pub system_hide_others_hotkey: Option<String>,
#[serde(default, deserialize_with = "deserialize_optional_hotkey")]
pub system_quit_hotkey: Option<String>,
#[serde(default, deserialize_with = "deserialize_optional_hotkey")]
pub system_minimize_hotkey: Option<String>,
#[serde(default, deserialize_with = "deserialize_optional_hotkey")]
pub system_fullscreen_hotkey: Option<String>,
#[serde(default, deserialize_with = "deserialize_optional_hotkey")]
pub system_show_all_tabs_hotkey: Option<String>,
#[serde(default, deserialize_with = "deserialize_optional_hotkey")]
pub system_tab_prev_hotkey: Option<String>,
#[serde(default, deserialize_with = "deserialize_optional_hotkey")]
pub system_tab_next_hotkey: Option<String>,
pub icon: Option<String>,
pub chdir: Option<PathBuf>,
@ -179,6 +221,27 @@ impl Config {
if let Some(switcher_hotkey) = &self.system_switcher_hotkey {
unsafe { env::set_var("NEOVIDE_SYSTEM_SWITCHER_HOTKEY", switcher_hotkey) };
}
if let Some(new_window_hotkey) = &self.system_new_window_hotkey {
unsafe { env::set_var("NEOVIDE_SYSTEM_NEW_WINDOW_HOTKEY", new_window_hotkey) };
}
if let Some(hide_hotkey) = &self.system_hide_hotkey {
unsafe { env::set_var("NEOVIDE_SYSTEM_HIDE_HOTKEY", hide_hotkey) };
}
if let Some(hide_others_hotkey) = &self.system_hide_others_hotkey {
unsafe { env::set_var("NEOVIDE_SYSTEM_HIDE_OTHERS_HOTKEY", hide_others_hotkey) };
}
if let Some(quit_hotkey) = &self.system_quit_hotkey {
unsafe { env::set_var("NEOVIDE_SYSTEM_QUIT_HOTKEY", quit_hotkey) };
}
if let Some(minimize_hotkey) = &self.system_minimize_hotkey {
unsafe { env::set_var("NEOVIDE_SYSTEM_MINIMIZE_HOTKEY", minimize_hotkey) };
}
if let Some(fullscreen_hotkey) = &self.system_fullscreen_hotkey {
unsafe { env::set_var("NEOVIDE_SYSTEM_FULLSCREEN_HOTKEY", fullscreen_hotkey) };
}
if let Some(show_all_tabs_hotkey) = &self.system_show_all_tabs_hotkey {
unsafe { env::set_var("NEOVIDE_SYSTEM_SHOW_ALL_TABS_HOTKEY", show_all_tabs_hotkey) };
}
if let Some(tab_prev_hotkey) = &self.system_tab_prev_hotkey {
unsafe { env::set_var("NEOVIDE_SYSTEM_TAB_PREV_HOTKEY", tab_prev_hotkey) };
}

View file

@ -1,4 +1,7 @@
use log::warn;
use objc2::rc::Retained;
use objc2_app_kit::NSEventModifierFlags;
use objc2_foundation::NSString;
use winit::{
event::{ElementState, KeyEvent, Modifiers},
keyboard::{Key, NamedKey},
@ -7,19 +10,19 @@ use winit::{
use crate::{CmdLineSettings, settings::Settings};
#[derive(Clone, Copy)]
pub(crate) enum TabNavigationAction {
pub enum TabNavigationAction {
Next,
Previous,
}
#[derive(Clone)]
pub(crate) struct TabNavigationHotkeys {
pub struct TabNavigationHotkeys {
next: Option<KeyCombo>,
prev: Option<KeyCombo>,
}
impl TabNavigationHotkeys {
pub(crate) fn new(settings: &Settings) -> Self {
pub fn new(settings: &Settings) -> Self {
let cmdline = settings.get::<CmdLineSettings>();
Self {
next: KeyCombo::parse(&cmdline.system_tab_next_hotkey),
@ -27,7 +30,7 @@ impl TabNavigationHotkeys {
}
}
pub(crate) fn action_for(
pub fn action_for(
&self,
event: &KeyEvent,
modifiers: &Modifiers,
@ -45,8 +48,8 @@ impl TabNavigationHotkeys {
}
}
#[derive(Clone, Copy)]
struct KeyCombo {
#[derive(Debug, Clone, Copy)]
pub struct KeyCombo {
command: bool,
control: bool,
option: bool,
@ -55,7 +58,7 @@ struct KeyCombo {
}
impl KeyCombo {
fn parse(raw: &str) -> Option<Self> {
pub fn parse(raw: &str) -> Option<Self> {
let trimmed = raw.trim();
if trimmed.is_empty() || is_disabled_keyword(trimmed) {
return None;
@ -72,6 +75,41 @@ impl KeyCombo {
.build(raw)
}
pub fn to_modifiers(self) -> NSEventModifierFlags {
let mut flags = NSEventModifierFlags::empty();
if self.command {
flags |= NSEventModifierFlags::Command;
}
if self.control {
flags |= NSEventModifierFlags::Control;
}
if self.option {
flags |= NSEventModifierFlags::Option;
}
if self.shift {
flags |= NSEventModifierFlags::Shift;
}
flags
}
/// Constructs an `NSString` representing the key component of this combo, if it's a character
/// key. Named keys will return `None`.
pub fn to_key(self) -> Option<Retained<NSString>> {
match self.key {
KeyMatch::Char(character) => Some(NSString::from_str(&character.to_string())),
KeyMatch::Named(_named) => {
// TODO: Figure out how to represent named keys in a way that can be used with
// NSEvent. For now, we don't support this.
None
}
}
}
fn matches(&self, event: &KeyEvent, modifiers: &Modifiers) -> bool {
if !self.modifiers_match(modifiers) {
return false;
@ -121,15 +159,12 @@ fn parse_token(value: &str, raw: &str) -> Option<ParsedToken> {
fn parse_character_key(value: &str, raw: &str) -> Option<char> {
let mut chars = value.chars();
let Some(ch) = chars.next() else {
warn!("macOS tab navigation shortcut '{}' has no key; ignoring", raw);
warn!("macOS shortcut '{}' has no key; ignoring", raw);
return None;
};
if chars.next().is_some() {
warn!(
"macOS tab navigation shortcut '{}' must end with a single character key; ignoring",
raw
);
warn!("macOS shortcut '{}' must end with a single character key; ignoring", raw);
return None;
}
@ -166,7 +201,7 @@ impl ParseState {
fn set_key(&mut self, value: KeyMatch, raw: &str) -> Option<()> {
match self.key {
Some(_) => {
warn!("macOS tab navigation shortcut '{}' has multiple keys; ignoring", raw);
warn!("macOS shortcut '{}' has multiple keys; ignoring", raw);
None
}
None => {
@ -183,10 +218,7 @@ impl ParseState {
option: self.option,
shift: self.shift,
key: self.key.or_else(|| {
warn!(
"macOS tab navigation shortcut '{}' is missing a key component; ignoring",
raw
);
warn!("macOS shortcut '{}' is missing a key component; ignoring", raw);
None
})?,
})
@ -197,7 +229,7 @@ fn is_disabled_keyword(value: &str) -> bool {
value.trim().eq_ignore_ascii_case("false")
}
#[derive(Clone, Copy)]
#[derive(Debug, Clone, Copy)]
enum KeyMatch {
Char(char),
Named(NamedKey),

View file

@ -264,6 +264,23 @@ default to mimic a standalone window. Enable this option to keep the tab bar vis
shows up as a tab immediately. The setting applies to windows opened through both global shortcuts
and the Editors menu entry.
### Menu Shortcuts
```sh
--system-hide-hotkey <combo> or $NEOVIDE_SYSTEM_HIDE_HOTKEY
--system-hide-others-hotkey <combo> or $NEOVIDE_SYSTEM_HIDE_OTHERS_HOTKEY
--system-quit-hotkey <combo> or $NEOVIDE_SYSTEM_QUIT_HOTKEY
--system-new-window-hotkey <combo> or $NEOVIDE_SYSTEM_NEW_WINDOW_HOTKEY
--system-minimize-hotkey <combo> or $NEOVIDE_SYSTEM_MINIMIZE_HOTKEY
--system-fullscreen-hotkey <combo> or $NEOVIDE_SYSTEM_FULLSCREEN_HOTKEY
--system-show-all-tabs-hotkey <combo> or $NEOVIDE_SYSTEM_SHOW_ALL_TABS_HOTKEY
```
Remaps the macOS menu shortcuts used by Neovide. The defaults are `cmd+h`, `cmd+alt+h`, `cmd+q`,
`cmd+n`, `cmd+m`, `cmd+ctrl+f`, and `cmd+shift+e` for Hide, Hide Others, Quit, New Window,
Minimize, Enter Full Screen, and Editors respectively. Set any of them to `false` or leave them
empty to remove the shortcut while keeping the menu item available.
### System Tab Navigation
```sh

View file

@ -48,6 +48,13 @@ tabs = true
system-native-tabs = false # macOS only
system-pinned-hotkey = "cmd+ctrl+z" # macOS only
system-switcher-hotkey = "cmd+ctrl+n" # macOS only, requires system-native-tabs = true
system-new-window-hotkey = "cmd+n" # macOS only
system-hide-hotkey = "cmd+h" # macOS only
system-hide-others-hotkey = "cmd+alt+h" # macOS only
system-quit-hotkey = "cmd+q" # macOS only
system-minimize-hotkey = "cmd+m" # macOS only
system-fullscreen-hotkey = "cmd+ctrl+f" # macOS only
system-show-all-tabs-hotkey = "cmd+shift+e" # macOS only
system-tab-prev-hotkey = "cmd+shift+[" # macOS only
system-tab-next-hotkey = "cmd+shift+]" # macOS only
title-hidden = false

View file

@ -918,8 +918,8 @@ single host window.
Set `system-native-tabs = true` to merge windows into a tab group. The native tab bar stays hidden
until more than one tab exists to keep a clean single-window look.
Use Window > New Window (cmd+n) or the Dock menu to open another Neovide window. If native tabs
are enabled, new windows become tabs in the host window.
Use Window > New Window (default: `cmd+n`) or the Dock menu to open another Neovide window. If
native tabs are enabled, new windows become tabs in the host window.
If you have native tabs enabled, the Window menu shows an Editors entry and the Editors hotkey
becomes available. You can also remap the in-app tab cycling shortcuts.
@ -957,6 +957,21 @@ system-pinned-hotkey = "ctrl+shift+z"
system-switcher-hotkey = "ctrl+shift+n"
```
You can also remap the macOS application and Window menu shortcuts:
```toml
system-hide-hotkey = "cmd+h"
system-hide-others-hotkey = "cmd+alt+h"
system-quit-hotkey = "cmd+q"
system-new-window-hotkey = "cmd+n"
system-minimize-hotkey = "cmd+m"
system-fullscreen-hotkey = "cmd+ctrl+f"
system-show-all-tabs-hotkey = "cmd+shift+e"
```
Set any of them to `false` (or an empty value) to remove the menu shortcut while keeping the menu
item.
When `system-native-tabs` is enabled, you can also customize the in-app tab navigation shortcuts:
```toml