mirror of
https://github.com/kovidgoyal/kitty
synced 2026-07-22 08:18:08 +02:00
macOS: Add menu items to close the OS window and the current tab
Fixes #3246
This commit is contained in:
@@ -27,7 +27,7 @@ To update |kitty|, :doc:`follow the instructions <binary>`.
|
|||||||
|
|
||||||
- Panel kitten: Allow setting WM_CLASS (:iss:`3233`)
|
- Panel kitten: Allow setting WM_CLASS (:iss:`3233`)
|
||||||
|
|
||||||
- macOS: Add a menu item to close the OS window (:pull:`3240`)
|
- macOS: Add menu items to close the OS window and the current tab (:pull:`3240`, :iss:`3246`)
|
||||||
|
|
||||||
|
|
||||||
0.19.3 [2020-12-19]
|
0.19.3 [2020-12-19]
|
||||||
|
|||||||
@@ -141,7 +141,7 @@ class Boss:
|
|||||||
opts: Options,
|
opts: Options,
|
||||||
args: CLIOptions,
|
args: CLIOptions,
|
||||||
cached_values: Dict[str, Any],
|
cached_values: Dict[str, Any],
|
||||||
new_os_window_trigger: Optional[SingleKey]
|
global_shortcuts: Dict[str, SingleKey]
|
||||||
):
|
):
|
||||||
set_layout_options(opts)
|
set_layout_options(opts)
|
||||||
self.clipboard_buffers: Dict[str, str] = {}
|
self.clipboard_buffers: Dict[str, str] = {}
|
||||||
@@ -168,8 +168,8 @@ class Boss:
|
|||||||
set_boss(self)
|
set_boss(self)
|
||||||
self.opts, self.args = opts, args
|
self.opts, self.args = opts, args
|
||||||
self.keymap = self.opts.keymap.copy()
|
self.keymap = self.opts.keymap.copy()
|
||||||
if new_os_window_trigger is not None:
|
for sc in global_shortcuts.values():
|
||||||
self.keymap.pop(new_os_window_trigger, None)
|
self.keymap.pop(sc, None)
|
||||||
if is_macos:
|
if is_macos:
|
||||||
from .fast_data_types import (
|
from .fast_data_types import (
|
||||||
cocoa_set_notification_activated_callback
|
cocoa_set_notification_activated_callback
|
||||||
|
|||||||
@@ -968,6 +968,8 @@ process_global_state(void *data) {
|
|||||||
if (cocoa_pending_actions) {
|
if (cocoa_pending_actions) {
|
||||||
if (cocoa_pending_actions & PREFERENCES_WINDOW) { call_boss(edit_config_file, NULL); }
|
if (cocoa_pending_actions & PREFERENCES_WINDOW) { call_boss(edit_config_file, NULL); }
|
||||||
if (cocoa_pending_actions & NEW_OS_WINDOW) { call_boss(new_os_window, NULL); }
|
if (cocoa_pending_actions & NEW_OS_WINDOW) { call_boss(new_os_window, NULL); }
|
||||||
|
if (cocoa_pending_actions & CLOSE_OS_WINDOW) { call_boss(close_os_window, NULL); }
|
||||||
|
if (cocoa_pending_actions & CLOSE_TAB) { call_boss(close_tab, NULL); }
|
||||||
if (cocoa_pending_actions_wd) {
|
if (cocoa_pending_actions_wd) {
|
||||||
if (cocoa_pending_actions & NEW_OS_WINDOW_WITH_WD) { call_boss(new_os_window_with_wd, "s", cocoa_pending_actions_wd); }
|
if (cocoa_pending_actions & NEW_OS_WINDOW_WITH_WD) { call_boss(new_os_window_with_wd, "s", cocoa_pending_actions_wd); }
|
||||||
if (cocoa_pending_actions & NEW_TAB_WITH_WD) { call_boss(new_tab_with_wd, "s", cocoa_pending_actions_wd); }
|
if (cocoa_pending_actions & NEW_TAB_WITH_WD) { call_boss(new_tab_with_wd, "s", cocoa_pending_actions_wd); }
|
||||||
|
|||||||
@@ -87,6 +87,16 @@ find_app_name(void) {
|
|||||||
set_cocoa_pending_action(NEW_OS_WINDOW, NULL);
|
set_cocoa_pending_action(NEW_OS_WINDOW, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void) close_os_window:(id)sender {
|
||||||
|
(void)sender;
|
||||||
|
set_cocoa_pending_action(CLOSE_OS_WINDOW, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)close_tab:(id)sender {
|
||||||
|
(void)sender;
|
||||||
|
set_cocoa_pending_action(CLOSE_TAB, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
- (void)open_kitty_website_url:(id)sender {
|
- (void)open_kitty_website_url:(id)sender {
|
||||||
(void)sender;
|
(void)sender;
|
||||||
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"https://sw.kovidgoyal.net/kitty/"]];
|
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"https://sw.kovidgoyal.net/kitty/"]];
|
||||||
@@ -106,18 +116,30 @@ find_app_name(void) {
|
|||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
static char new_window_key[32] = {0};
|
typedef struct {
|
||||||
static NSEventModifierFlags new_window_mods = 0;
|
char key[32];
|
||||||
|
NSEventModifierFlags mods;
|
||||||
|
} GlobalShortcut;
|
||||||
|
typedef struct {
|
||||||
|
GlobalShortcut new_os_window, close_os_window, close_tab;
|
||||||
|
} GlobalShortcuts;
|
||||||
|
static GlobalShortcuts global_shortcuts;
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
cocoa_set_new_window_trigger(PyObject *self UNUSED, PyObject *args) {
|
cocoa_set_global_shortcut(PyObject *self UNUSED, PyObject *args) {
|
||||||
int mods;
|
int mods;
|
||||||
unsigned int key;
|
unsigned int key;
|
||||||
if (!PyArg_ParseTuple(args, "iI", &mods, &key)) return NULL;
|
const char *name;
|
||||||
int nwm;
|
if (!PyArg_ParseTuple(args, "siI", &name, &mods, &key)) return NULL;
|
||||||
get_cocoa_key_equivalent(key, mods, new_window_key, sizeof(new_window_key), &nwm);
|
GlobalShortcut *gs = NULL;
|
||||||
new_window_mods = nwm;
|
if (strcmp(name, "new_os_window") == 0) gs = &global_shortcuts.new_os_window;
|
||||||
if (new_window_key[0]) Py_RETURN_TRUE;
|
else if (strcmp(name, "close_os_window") == 0) gs = &global_shortcuts.close_os_window;
|
||||||
|
else if (strcmp(name, "close_tab") == 0) gs = &global_shortcuts.close_tab;
|
||||||
|
if (gs == NULL) { PyErr_SetString(PyExc_KeyError, "Unknown shortcut name"); return NULL; }
|
||||||
|
int cocoa_mods;
|
||||||
|
get_cocoa_key_equivalent(key, mods, gs->key, 32, &cocoa_mods);
|
||||||
|
gs->mods = cocoa_mods;
|
||||||
|
if (gs->key[0]) Py_RETURN_TRUE;
|
||||||
Py_RETURN_FALSE;
|
Py_RETURN_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -358,8 +380,8 @@ cocoa_create_global_menu(void) {
|
|||||||
NSMenuItem* new_os_window_menu_item =
|
NSMenuItem* new_os_window_menu_item =
|
||||||
[appMenu addItemWithTitle:@"New OS window"
|
[appMenu addItemWithTitle:@"New OS window"
|
||||||
action:@selector(new_os_window:)
|
action:@selector(new_os_window:)
|
||||||
keyEquivalent:@(new_window_key)];
|
keyEquivalent:@(global_shortcuts.new_os_window.key)];
|
||||||
[new_os_window_menu_item setKeyEquivalentModifierMask:new_window_mods];
|
[new_os_window_menu_item setKeyEquivalentModifierMask:global_shortcuts.new_os_window.mods];
|
||||||
[new_os_window_menu_item setTarget:global_menu_target];
|
[new_os_window_menu_item setTarget:global_menu_target];
|
||||||
|
|
||||||
|
|
||||||
@@ -396,10 +418,6 @@ cocoa_create_global_menu(void) {
|
|||||||
NSMenu* windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
|
NSMenu* windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
|
||||||
[windowMenuItem setSubmenu:windowMenu];
|
[windowMenuItem setSubmenu:windowMenu];
|
||||||
|
|
||||||
[windowMenu addItemWithTitle:@"Close"
|
|
||||||
action:@selector(performClose:)
|
|
||||||
keyEquivalent:@"w"];
|
|
||||||
|
|
||||||
[windowMenu addItemWithTitle:@"Minimize"
|
[windowMenu addItemWithTitle:@"Minimize"
|
||||||
action:@selector(performMiniaturize:)
|
action:@selector(performMiniaturize:)
|
||||||
keyEquivalent:@"m"];
|
keyEquivalent:@"m"];
|
||||||
@@ -411,6 +429,20 @@ cocoa_create_global_menu(void) {
|
|||||||
action:@selector(arrangeInFront:)
|
action:@selector(arrangeInFront:)
|
||||||
keyEquivalent:@""];
|
keyEquivalent:@""];
|
||||||
|
|
||||||
|
[windowMenu addItem:[NSMenuItem separatorItem]];
|
||||||
|
NSMenuItem* close_tab_item =
|
||||||
|
[windowMenu addItemWithTitle:@"Close Tab"
|
||||||
|
action:@selector(close_tab:)
|
||||||
|
keyEquivalent:@(global_shortcuts.close_tab.key)];
|
||||||
|
[close_tab_item setKeyEquivalentModifierMask:global_shortcuts.close_tab.mods];
|
||||||
|
[close_tab_item setTarget:global_menu_target];
|
||||||
|
NSMenuItem* close_os_window_menu_item =
|
||||||
|
[windowMenu addItemWithTitle:@"Close OS Window"
|
||||||
|
action:@selector(close_os_window:)
|
||||||
|
keyEquivalent:@(global_shortcuts.close_os_window.key)];
|
||||||
|
[close_os_window_menu_item setKeyEquivalentModifierMask:global_shortcuts.close_os_window.mods];
|
||||||
|
[close_os_window_menu_item setTarget:global_menu_target];
|
||||||
|
|
||||||
[windowMenu addItem:[NSMenuItem separatorItem]];
|
[windowMenu addItem:[NSMenuItem separatorItem]];
|
||||||
[[windowMenu addItemWithTitle:@"Enter Full Screen"
|
[[windowMenu addItemWithTitle:@"Enter Full Screen"
|
||||||
action:@selector(toggleFullScreen:)
|
action:@selector(toggleFullScreen:)
|
||||||
@@ -627,7 +659,7 @@ cocoa_system_beep(void) {
|
|||||||
|
|
||||||
static PyMethodDef module_methods[] = {
|
static PyMethodDef module_methods[] = {
|
||||||
{"cocoa_get_lang", (PyCFunction)cocoa_get_lang, METH_NOARGS, ""},
|
{"cocoa_get_lang", (PyCFunction)cocoa_get_lang, METH_NOARGS, ""},
|
||||||
{"cocoa_set_new_window_trigger", (PyCFunction)cocoa_set_new_window_trigger, METH_VARARGS, ""},
|
{"cocoa_set_global_shortcut", (PyCFunction)cocoa_set_global_shortcut, METH_VARARGS, ""},
|
||||||
{"cocoa_send_notification", (PyCFunction)cocoa_send_notification, METH_VARARGS, ""},
|
{"cocoa_send_notification", (PyCFunction)cocoa_send_notification, METH_VARARGS, ""},
|
||||||
{"cocoa_set_notification_activated_callback", (PyCFunction)set_notification_activated_callback, METH_O, ""},
|
{"cocoa_set_notification_activated_callback", (PyCFunction)set_notification_activated_callback, METH_O, ""},
|
||||||
{NULL, NULL, 0, NULL} /* Sentinel */
|
{NULL, NULL, 0, NULL} /* Sentinel */
|
||||||
@@ -635,6 +667,7 @@ static PyMethodDef module_methods[] = {
|
|||||||
|
|
||||||
bool
|
bool
|
||||||
init_cocoa(PyObject *module) {
|
init_cocoa(PyObject *module) {
|
||||||
|
memset(&global_shortcuts, 0, sizeof(global_shortcuts));
|
||||||
if (PyModule_AddFunctions(module, module_methods) != 0) return false;
|
if (PyModule_AddFunctions(module, module_methods) != 0) return false;
|
||||||
if (Py_AtExit(cleanup) != 0) {
|
if (Py_AtExit(cleanup) != 0) {
|
||||||
PyErr_SetString(PyExc_RuntimeError, "Failed to register the cocoa_window at exit handler");
|
PyErr_SetString(PyExc_RuntimeError, "Failed to register the cocoa_window at exit handler");
|
||||||
|
|||||||
@@ -640,7 +640,7 @@ def cocoa_set_notification_activated_callback(identifier: Callable[[str], None])
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def cocoa_set_new_window_trigger(mods: int, key: int) -> bool:
|
def cocoa_set_global_shortcut(name: str, mods: int, key: int) -> bool:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import os
|
|||||||
import shutil
|
import shutil
|
||||||
import sys
|
import sys
|
||||||
from contextlib import contextmanager, suppress
|
from contextlib import contextmanager, suppress
|
||||||
from typing import Generator, List, Mapping, Optional, Sequence
|
from typing import Dict, Generator, List, Mapping, Optional, Sequence
|
||||||
|
|
||||||
from .borders import load_borders_program
|
from .borders import load_borders_program
|
||||||
from .boss import Boss
|
from .boss import Boss
|
||||||
@@ -104,26 +104,30 @@ def init_glfw(opts: OptionsStub, debug_keyboard: bool = False) -> str:
|
|||||||
return glfw_module
|
return glfw_module
|
||||||
|
|
||||||
|
|
||||||
def get_new_os_window_trigger(opts: OptionsStub) -> Optional[SingleKey]:
|
def get_macos_shortcut_for(opts: OptionsStub, function: str = 'new_os_window') -> Optional[SingleKey]:
|
||||||
new_os_window_trigger = None
|
ans = None
|
||||||
if is_macos:
|
candidates = []
|
||||||
new_os_window_shortcuts = []
|
for k, v in opts.keymap.items():
|
||||||
for k, v in opts.keymap.items():
|
if v.func == function:
|
||||||
if v.func == 'new_os_window':
|
candidates.append(k)
|
||||||
new_os_window_shortcuts.append(k)
|
if candidates:
|
||||||
if new_os_window_shortcuts:
|
from .fast_data_types import cocoa_set_global_shortcut
|
||||||
from .fast_data_types import cocoa_set_new_window_trigger
|
|
||||||
|
|
||||||
# Reverse list so that later defined keyboard shortcuts take priority over earlier defined ones
|
# Reverse list so that later defined keyboard shortcuts take priority over earlier defined ones
|
||||||
for candidate in reversed(new_os_window_shortcuts):
|
for candidate in reversed(candidates):
|
||||||
if cocoa_set_new_window_trigger(candidate[0], candidate[2]):
|
if cocoa_set_global_shortcut(function, candidate[0], candidate[2]):
|
||||||
new_os_window_trigger = candidate
|
ans = candidate
|
||||||
break
|
break
|
||||||
return new_os_window_trigger
|
return ans
|
||||||
|
|
||||||
|
|
||||||
def _run_app(opts: OptionsStub, args: CLIOptions, bad_lines: Sequence[BadLine] = ()) -> None:
|
def _run_app(opts: OptionsStub, args: CLIOptions, bad_lines: Sequence[BadLine] = ()) -> None:
|
||||||
new_os_window_trigger = get_new_os_window_trigger(opts)
|
global_shortcuts: Dict[str, SingleKey] = {}
|
||||||
|
if is_macos:
|
||||||
|
for ac in ('new_os_window', 'close_os_window', 'close_tab'):
|
||||||
|
val = get_macos_shortcut_for(opts, ac)
|
||||||
|
if val is not None:
|
||||||
|
global_shortcuts[ac] = val
|
||||||
if is_macos and opts.macos_custom_beam_cursor:
|
if is_macos and opts.macos_custom_beam_cursor:
|
||||||
set_custom_ibeam_cursor()
|
set_custom_ibeam_cursor()
|
||||||
if not is_wayland() and not is_macos: # no window icons on wayland
|
if not is_wayland() and not is_macos: # no window icons on wayland
|
||||||
@@ -137,7 +141,7 @@ def _run_app(opts: OptionsStub, args: CLIOptions, bad_lines: Sequence[BadLine] =
|
|||||||
pre_show_callback,
|
pre_show_callback,
|
||||||
args.title or appname, args.name or args.cls or appname,
|
args.title or appname, args.name or args.cls or appname,
|
||||||
args.cls or appname, load_all_shaders)
|
args.cls or appname, load_all_shaders)
|
||||||
boss = Boss(opts, args, cached_values, new_os_window_trigger)
|
boss = Boss(opts, args, cached_values, global_shortcuts)
|
||||||
boss.start(window_id)
|
boss.start(window_id)
|
||||||
if bad_lines:
|
if bad_lines:
|
||||||
boss.show_bad_config_lines(bad_lines)
|
boss.show_bad_config_lines(bad_lines)
|
||||||
|
|||||||
@@ -263,7 +263,9 @@ typedef enum {
|
|||||||
PREFERENCES_WINDOW = 1,
|
PREFERENCES_WINDOW = 1,
|
||||||
NEW_OS_WINDOW = 2,
|
NEW_OS_WINDOW = 2,
|
||||||
NEW_OS_WINDOW_WITH_WD = 4,
|
NEW_OS_WINDOW_WITH_WD = 4,
|
||||||
NEW_TAB_WITH_WD = 8
|
NEW_TAB_WITH_WD = 8,
|
||||||
|
CLOSE_OS_WINDOW = 16,
|
||||||
|
CLOSE_TAB = 32
|
||||||
} CocoaPendingAction;
|
} CocoaPendingAction;
|
||||||
void set_cocoa_pending_action(CocoaPendingAction action, const char*);
|
void set_cocoa_pending_action(CocoaPendingAction action, const char*);
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Reference in New Issue
Block a user