diff --git a/apps/firefox/firefox_mac.py b/apps/firefox/firefox_mac.py index d8418c8f1d..742ff4f4c6 100644 --- a/apps/firefox/firefox_mac.py +++ b/apps/firefox/firefox_mac.py @@ -17,6 +17,10 @@ def firefox_bookmarks_sidebar(): def firefox_history_sidebar(): actions.key("cmd-shift-h") + def window_reopen(): + # Note that as of Firefox 138.0.1, this command does not appear in any Firefox’s menus and only works if there is already an existing Firefox window open. + actions.key("cmd-shift-n") + @ctx.action_class("browser") class BrowserActions: diff --git a/apps/firefox/firefox_win_linux.py b/apps/firefox/firefox_win_linux.py index f93eebf44d..228bbf24d1 100644 --- a/apps/firefox/firefox_win_linux.py +++ b/apps/firefox/firefox_win_linux.py @@ -18,6 +18,9 @@ def firefox_bookmarks_sidebar(): def firefox_history_sidebar(): actions.key("ctrl-h") + def window_reopen(): + actions.key("ctrl-shift-n") + @ctx.action_class("browser") class BrowserActions: diff --git a/core/windows_and_tabs/window_management.talon b/core/windows_and_tabs/window_management.talon index 9179d3dcbe..3e56e5e990 100644 --- a/core/windows_and_tabs/window_management.talon +++ b/core/windows_and_tabs/window_management.talon @@ -1,8 +1,9 @@ window (new | open): app.window_open() +window reopen: user.window_reopen() window next: app.window_next() window last: app.window_previous() window close: app.window_close() -window hide: app.window_hide() +window (min | minimize): app.window_hide() app (preferences | prefs | settings): app.preferences() focus : user.switcher_focus(running_applications) # following only works on windows. Can't figure out how to make it work for mac. No idea what the equivalent for linux would be. @@ -21,3 +22,7 @@ snap : snap [screen] : user.move_app_to_screen(running_applications, number) + +# DEPRECATED +window hide: + user.deprecate_command("2025-05-11", "window hide", "window minimize/app hide") diff --git a/core/windows_and_tabs/window_management_mac.talon b/core/windows_and_tabs/window_management_mac.talon new file mode 100644 index 0000000000..82d8fa3658 --- /dev/null +++ b/core/windows_and_tabs/window_management_mac.talon @@ -0,0 +1,4 @@ +os: mac +- +app hide: user.app_hide() +app hide others: user.app_hide_others() diff --git a/core/windows_and_tabs/window_management_win_linux.talon b/core/windows_and_tabs/window_management_win_linux.talon new file mode 100644 index 0000000000..c5ff9561f3 --- /dev/null +++ b/core/windows_and_tabs/window_management_win_linux.talon @@ -0,0 +1,5 @@ +os: windows +os: linux +- +window (max | maximize): user.window_maximize() +window restore: user.window_restore() diff --git a/core/windows_and_tabs/windows_and_tabs.py b/core/windows_and_tabs/windows_and_tabs.py index a496fce22a..3a1965c6a2 100644 --- a/core/windows_and_tabs/windows_and_tabs.py +++ b/core/windows_and_tabs/windows_and_tabs.py @@ -1,5 +1,6 @@ -from talon import Context, actions, ui +from talon import Context, Module, actions, ui +mod = Module() ctx = Context() @@ -12,6 +13,18 @@ def window_next(): cycle_windows(ui.active_app(), 1) +@mod.action_class +class Actions: + def window_maximize(): + """Maximize the current window""" + + def window_reopen(): + """Reopen the last-closed window""" + + def window_restore(): + """Restore (unmaximize) the current window""" + + def cycle_windows(app: ui.App, diff: int): """Cycle windows backwards or forwards for the given application""" active = app.active_window diff --git a/core/windows_and_tabs/windows_and_tabs_linux.py b/core/windows_and_tabs/windows_and_tabs_linux.py index 78b10c1fff..f1512c3e94 100644 --- a/core/windows_and_tabs/windows_and_tabs_linux.py +++ b/core/windows_and_tabs/windows_and_tabs_linux.py @@ -1,6 +1,6 @@ # defines the default app actions for linux -from talon import Context, actions +from talon import Context, actions, ui ctx = Context() ctx.matches = r""" @@ -8,6 +8,7 @@ """ +# TODO: Some keyboard shortcuts were obviously just copied from the Windows implementation. Correct what doesn't work. @ctx.action_class("app") class AppActions: def tab_close(): @@ -26,10 +27,18 @@ def tab_reopen(): actions.key("ctrl-shift-t") def window_close(): - actions.key("alt-f4") + if window := ui.active_window(): + # TODO: Does this work on Linux? + window.close() + else: + actions.key("alt-f4") def window_hide(): - actions.key("alt-space n") + if window := ui.active_window(): + # TODO: Does this work on Linux? + window.minimized = True + else: + actions.key("alt-space n") def window_hide_others(): actions.key("win-d alt-tab") diff --git a/core/windows_and_tabs/windows_and_tabs_mac.py b/core/windows_and_tabs/windows_and_tabs_mac.py index 3fac8ab4cc..2a96ee5643 100644 --- a/core/windows_and_tabs/windows_and_tabs_mac.py +++ b/core/windows_and_tabs/windows_and_tabs_mac.py @@ -1,11 +1,24 @@ -from talon import Context, actions +from talon import Context, Module, actions, ui +from talon.mac import applescript +mod = Module() ctx = Context() ctx.matches = r""" os: mac """ +@mod.action_class +class Actions: + def app_hide(): + """Hide the current app""" + ui.active_app().element.AXHidden = True + + def app_hide_others(): + """Hide all other apps""" + actions.key("cmd-alt-h") + + @ctx.action_class("app") class AppActions: def preferences(): @@ -27,12 +40,19 @@ def tab_reopen(): actions.key("cmd-shift-t") def window_close(): - actions.key("cmd-w") + if window := ui.active_window(): + window.close() + else: + actions.key("cmd-w") def window_hide(): - actions.key("cmd-m") + if window := ui.active_window(): + window.minimized = True + else: + actions.key("cmd-m") def window_hide_others(): + # TODO: Currently hides all apps, like `actions.user.app_hide_others()` already does. Correct this to hide windows instead, if useful, or remove it. actions.key("cmd-alt-h") def window_open(): diff --git a/core/windows_and_tabs/windows_and_tabs_win.py b/core/windows_and_tabs/windows_and_tabs_win.py index 2070ac2d58..5d3af58b70 100644 --- a/core/windows_and_tabs/windows_and_tabs_win.py +++ b/core/windows_and_tabs/windows_and_tabs_win.py @@ -1,12 +1,14 @@ # defines the default app actions for windows -from talon import Context, actions +from talon import Context, actions, ui ctx = Context() ctx.matches = r""" os: windows """ +SYSTEM_MENU_SHORTCUT_MULTISTEP_DELAY = "50ms" + @ctx.action_class("app") class AppActions: @@ -26,13 +28,21 @@ def tab_reopen(): actions.key("ctrl-shift-t") def window_close(): - actions.key("alt-f4") + if window := ui.active_window(): + window.close() + else: + actions.key("alt-f4") def window_hide(): - actions.key("alt-space n") + if window := ui.active_window(): + window.minimized = True + else: + actions.key("alt-space") + actions.sleep(SYSTEM_MENU_SHORTCUT_MULTISTEP_DELAY) + # TODO: This and the other OS-language-dependent mnemonics in this file should be made to depend on a new Windows-only dictionary `OS_LANG_SYSTEM_MENU_MNEMONICS` that's defined per OS language. The current OS language decides what variant will be effective. + actions.key("n") # Depends on English OS language. - def window_hide_others(): - actions.key("win-d alt-tab") + # Note that 2x Win+Down not only minimizes the window, but also restores it before that. It would be contrary to user expectations if a window that was previously maximized is in restored state after unminimizing it again. The shortcut also unexpectedly arranges the window differently, if it's in an arranged state like covering an upper quarter or half of the work area. def window_open(): actions.key("ctrl-n") @@ -42,3 +52,23 @@ def window_open(): class UserActions: def switcher_focus_last(): actions.key("alt-tab") + + def window_maximize(): + if window := ui.active_window(): + window.maximized = True + else: + actions.key("alt-space") + actions.sleep(SYSTEM_MENU_SHORTCUT_MULTISTEP_DELAY) + actions.key("x") # Depends on English OS language. + + # Note that Win+Up arranges the window differently instead of maximizing, if it's in an arranged state like covering a lower quarter or half of the work area. This would be contrary to user expectations. + + def window_restore(): + if window := ui.active_window(): + window.maximized = False + else: + actions.key("alt-space") + actions.sleep(SYSTEM_MENU_SHORTCUT_MULTISTEP_DELAY) + actions.key("r") # Depends on English OS language. + + # Note that Win+Down on a restored window minimizes it instead of restoring it. This can happen with apps that previously saved the maximized window placement, and then applied it to the window's restored state, e.g., when restarting the app. Besides *possible* tiny differences in the appearance of the window border, the only hint that the window isn't in maximized state, even though it covers the whole work area, will be the title bar's restore button symbol that's only slightly different to the maximize symbol. It would be contrary to user expectations if the respective voice command minimized a window that the user intended to restore. (The shortcut also arranges the window differently or minimizes it instead of being a no-op, if it's in any arranged state.)