From 9bbfdddde046c151b4ebaa06de10b2d51216cbec Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Sun, 13 Mar 2016 12:04:17 -0700 Subject: [PATCH] Add the standard OS X "Window" menu --- src/command/app.cpp | 40 ++++++++++++++++++++++++++++ src/hotkey.cpp | 12 +++++++++ src/libresrc/osx/default_hotkey.json | 3 +++ src/libresrc/osx/default_menu.json | 9 +++++++ src/menu.cpp | 39 ++++++++++++++++++++++++--- src/osx/osx_utils.mm | 22 +++++++++++++++ src/utils.h | 13 +++++++-- 7 files changed, 133 insertions(+), 5 deletions(-) diff --git a/src/command/app.cpp b/src/command/app.cpp index edd27bdab..1239242ba 100644 --- a/src/command/app.cpp +++ b/src/command/app.cpp @@ -265,6 +265,41 @@ struct app_updates final : public Command { } }; +#ifdef __WXMAC__ +struct app_minimize final : public Command { + CMD_NAME("app/minimize") + STR_MENU("Minimize") + STR_DISP("Minimize") + STR_HELP("Minimize the active window") + + void operator()(agi::Context *c) override { + c->frame->Iconize(); + } +}; + +struct app_maximize final : public Command { + CMD_NAME("app/maximize") + STR_MENU("Zoom") + STR_DISP("Zoom") + STR_HELP("Maximize the active window") + + void operator()(agi::Context *c) override { + c->frame->Maximize(!c->frame->IsMaximized()); + } +}; + +struct app_bring_to_front final : public Command { + CMD_NAME("app/bring_to_front") + STR_MENU("Bring All to Front") + STR_DISP("Bring All to Front") + STR_HELP("Bring forward all open documents to the front") + + void operator()(agi::Context *) override { + osx::bring_to_front(); + } +}; +#endif + } namespace cmd { @@ -281,6 +316,11 @@ namespace cmd { reg(agi::make_unique()); reg(agi::make_unique()); reg(agi::make_unique()); +#ifdef __WXMAC__ + reg(agi::make_unique()); + reg(agi::make_unique()); + reg(agi::make_unique()); +#endif #ifdef WITH_UPDATE_CHECKER reg(agi::make_unique()); #endif diff --git a/src/hotkey.cpp b/src/hotkey.cpp index a3d7ce715..2ec8f50e4 100644 --- a/src/hotkey.cpp +++ b/src/hotkey.cpp @@ -47,6 +47,11 @@ namespace { {nullptr} }; + const char *added_hotkeys_minimize[][3] = { + {"app/minimize", "Default", "Ctrl-M"}, + {nullptr} + }; + void migrate_hotkeys(const char *added[][3]) { auto hk_map = hotkey::inst->GetHotkeyMap(); bool changed = false; @@ -109,6 +114,13 @@ void init() { migrations.emplace_back("duplicate -> split"); } +#ifdef __WXMAC__ + if (boost::find(migrations, "app/minimize") == end(migrations)) { + migrate_hotkeys(added_hotkeys_minimize); + migrations.emplace_back("app/minimize"); + } +#endif + OPT_SET("App/Hotkey Migrations")->SetListString(std::move(migrations)); } diff --git a/src/libresrc/osx/default_hotkey.json b/src/libresrc/osx/default_hotkey.json index b7050cf52..829adb88e 100644 --- a/src/libresrc/osx/default_hotkey.json +++ b/src/libresrc/osx/default_hotkey.json @@ -107,6 +107,9 @@ ] }, "Default" : { + "app/minimize" : [ + "Ctrl-M" + ], "app/exit" : [ "Ctrl-Q" ], diff --git a/src/libresrc/osx/default_menu.json b/src/libresrc/osx/default_menu.json index 0f504b26d..036753c94 100644 --- a/src/libresrc/osx/default_menu.json +++ b/src/libresrc/osx/default_menu.json @@ -36,6 +36,7 @@ { "submenu" : "main/video", "text" : "&Video" }, { "submenu" : "main/audio", "text" : "&Audio" }, { "special" : "automation", "text" : "A&utomation" }, + { "submenu" : "main/window", "text" : "Window" }, { "submenu" : "main/help", "text" : "&Help", "special" : "help" } ], "main/file" : [ @@ -200,6 +201,14 @@ {}, { "command" : "app/toggle/toolbar" } ], + "main/window" : [ + { "command" : "app/minimize" }, + { "command" : "app/maximize" }, + {}, + { "command" : "app/bring_to_front" }, + {}, + { "special" : "window" } + ], "main/help" : [ { "command" : "help/contents" }, {}, diff --git a/src/menu.cpp b/src/menu.cpp index 4c3848e50..13e054baa 100644 --- a/src/menu.cpp +++ b/src/menu.cpp @@ -27,6 +27,7 @@ #include "format.h" #include "libresrc/libresrc.h" #include "options.h" +#include "utils.h" #include #include @@ -170,6 +171,10 @@ public: { } + void SetContext(agi::Context *c) { + context = c; + } + int AddCommand(cmd::Command *co, wxMenu *parent, std::string const& text = "") { return AddCommand(co, parent, text.empty() ? co->StrMenu(context) : _(to_wx(text))); } @@ -229,6 +234,8 @@ public: } void OnMenuOpen(wxMenuEvent &) { + if (!context) + return; for (auto const& item : dynamic_items) UpdateItem(item); for (auto item : mru) item->Update(); } @@ -237,7 +244,7 @@ public: // This also gets clicks on unrelated things such as the toolbar, so // the window ID ranges really need to be unique size_t id = static_cast(evt.GetId() - MENU_ID_BASE); - if (id < items.size()) + if (id < items.size() && context) cmd::call(items[id], context); #ifdef __WXMAC__ @@ -336,6 +343,11 @@ void process_menu_item(wxMenu *parent, agi::Context *c, json::Object const& ele, std::string submenu, recent, command, text, special; read_entry(ele, "special", &special); +#ifdef __WXMAC__ + if (special == "window") + osx::make_windows_menu(parent); +#endif + if (read_entry(ele, "submenu", &submenu) && read_entry(ele, "text", &text)) { wxString tl_text = _(to_wx(text)); parent->AppendSubMenu(build_menu(submenu, c, cm), tl_text); @@ -487,6 +499,21 @@ public: namespace menu { void GetMenuBar(std::string const& name, wxFrame *window, agi::Context *c) { +#ifdef __WXMAC__ + auto bind_events = [&](CommandMenuBar *menu) { + window->Bind(wxEVT_ACTIVATE, [=](wxActivateEvent&) { menu->cm.SetContext(c); }); + window->Bind(wxEVT_DESTROY, [=](wxWindowDestroyEvent&) { + if (!osx::activate_top_window_other_than(window)) + menu->cm.SetContext(nullptr); + }); + }; + + if (wxMenuBar *menu = wxMenuBar::MacGetCommonMenuBar()) { + bind_events(static_cast(menu)); + return; + } +#endif + auto menu = agi::make_unique(c); for (auto const& item : get_menu(name)) { std::string submenu, disp; @@ -502,9 +529,15 @@ namespace menu { } } - window->Bind(wxEVT_MENU_OPEN, &CommandManager::OnMenuOpen, &menu->cm); - window->Bind(wxEVT_MENU, &CommandManager::OnMenuClick, &menu->cm); + menu->Bind(wxEVT_MENU_OPEN, &CommandManager::OnMenuOpen, &menu->cm); + menu->Bind(wxEVT_MENU, &CommandManager::OnMenuClick, &menu->cm); + +#ifdef __WXMAC__ + bind_events(menu.get()); + wxMenuBar::MacSetCommonMenuBar(menu.get()); +#else window->SetMenuBar(menu.get()); +#endif menu.release(); } diff --git a/src/osx/osx_utils.mm b/src/osx/osx_utils.mm index ff83b4deb..945773b9e 100644 --- a/src/osx/osx_utils.mm +++ b/src/osx/osx_utils.mm @@ -66,3 +66,25 @@ void RestartAegisub() { [NSTask launchedTaskWithLaunchPath:helperPath arguments:@[NSBundle.mainBundle.executablePath]]; } + +namespace osx { +void make_windows_menu(wxMenu* wxmenu) { + NSApp.windowsMenu = wxmenu->GetHMenu(); +} + +bool activate_top_window_other_than(wxFrame *wx) { + NSWindow *w = wx->GetWXWindow(); + for (NSNumber *windowNumber in [NSWindow windowNumbersWithOptions:0]) { + NSWindow *window = [NSApp windowWithWindowNumber:windowNumber.integerValue]; + if (window && window.canBecomeMainWindow && window != w) { + [window makeMainWindow]; + return true; + } + } + return false; +} + +void bring_to_front() { + [NSApp arrangeInFront:nil]; +} +} diff --git a/src/utils.h b/src/utils.h index 5d9ebe0e2..269100de8 100644 --- a/src/utils.h +++ b/src/utils.h @@ -99,11 +99,20 @@ agi::fs::path SaveFileSelector(wxString const& message, std::string const& optio wxString LocalizedLanguageName(wxString const& lang); -namespace osx { namespace ime { +namespace osx { + /// Make the given menu the OS X Window menu + void make_windows_menu(wxMenu *wxmenu); + /// Activate a top-level document window other than the given one + bool activate_top_window_other_than(wxFrame *wx); + // Bring all windows to the front, maintaining relative z-order + void bring_to_front(); + +namespace ime { /// Inject the IME helper into the given wxSTC void inject(wxStyledTextCtrl *ctrl); /// Invalidate any pending text from the IME void invalidate(wxStyledTextCtrl *ctrl); /// Give the IME a chance to process a key event and return whether it did bool process_key_event(wxStyledTextCtrl *ctrl, wxKeyEvent &); -} } +} +}