diff options
| author | Joel Klinghed <the_jk@yahoo.com> | 2017-07-30 00:36:18 +0200 |
|---|---|---|
| committer | Joel Klinghed <the_jk@yahoo.com> | 2017-07-30 00:36:18 +0200 |
| commit | 124429985be8033a398b891f400f70a058330c87 (patch) | |
| tree | 6f2e8ba3bd4044a1c8252d17e837fe83c44125d8 /src/gui_gtk.cc | |
| parent | e7a0bf680f3d805b190b89bfeb1f1f8a4c3ec982 (diff) | |
Add menu shortcuts
Diffstat (limited to 'src/gui_gtk.cc')
| -rw-r--r-- | src/gui_gtk.cc | 190 |
1 files changed, 168 insertions, 22 deletions
diff --git a/src/gui_gtk.cc b/src/gui_gtk.cc index 7dd2d42..2056034 100644 --- a/src/gui_gtk.cc +++ b/src/gui_gtk.cc @@ -822,10 +822,11 @@ public: return G_MENU_MODEL(menu_.get()); } - void set_map(GActionMap* map); + void set_app(GtkApplication* app); void activate(GSimpleAction* action); - void add_item(std::string const& id, std::string const& label) override; + void add_item(std::string const& id, std::string const& label, + Shortcut const& shortcut) override; GuiMenu* add_menu(std::string const& label) override; void add_separator() override; bool enable_item(std::string const& id, bool enable) override; @@ -833,7 +834,7 @@ public: private: explicit GtkGuiMenu(GtkGuiMenu* parent) : parent_(parent), menu_(g_menu_new()), section_(g_menu_new()), - map_(nullptr) { + app_(nullptr) { g_menu_append_section(menu_.get(), nullptr, G_MENU_MODEL(section_.get())); } @@ -847,14 +848,16 @@ private: std::string add_action(std::string const& id); std::string escape(std::string const& id); std::string unescape(std::string const& action); + void add_shortcut(std::string const& action, Shortcut const& shortcut); GtkGuiMenu* const parent_; shared_gobject<GMenu> menu_; shared_gobject<GMenu> section_; std::unordered_map<std::string, shared_gobject<GSimpleAction>> action_; - GActionMap* map_; + GtkApplication* app_; std::vector<std::unique_ptr<GtkGuiMenu>> submenus_; Observers<Listener*> observers_; + std::unordered_map<std::string, std::string> shortcut_; }; class GtkGuiStatusBar : public GuiStatusBar { @@ -1852,13 +1855,17 @@ MainAppWindow* main_app_window_new(MainApp *app) { return ret; } -void main_app_activate(GApplication* g_app) { - auto app = MAIN_APP(g_app); +void setup_menubar(MainApp* app) { auto menu = static_cast<GtkGuiMenu*>(app->main_->menu()); if (menu) { - menu->set_map(G_ACTION_MAP(app)); + menu->set_app(GTK_APPLICATION(app)); gtk_application_set_menubar(GTK_APPLICATION(app), menu->model()); } +} + +void main_app_activate(GApplication* g_app) { + auto app = MAIN_APP(g_app); + setup_menubar(app); auto win = main_app_window_new(app); gtk_window_set_title(GTK_WINDOW(win), app->main_->title().c_str()); gtk_window_present(GTK_WINDOW(win)); @@ -1876,11 +1883,7 @@ bool main_app_window_open(MainApp* app, MainAppWindow*, GFile* file) { void main_app_open(GApplication* app, GFile** files, gint n_files, gchar const*) { - auto menu = static_cast<GtkGuiMenu*>(MAIN_APP(app)->main_->menu()); - if (menu) { - menu->set_map(G_ACTION_MAP(app)); - gtk_application_set_menubar(GTK_APPLICATION(app), menu->model()); - } + setup_menubar(MAIN_APP(app)); auto windows = gtk_application_get_windows(GTK_APPLICATION(app)); auto win = windows ? MAIN_APP_WINDOW(windows->data) : main_app_window_new(MAIN_APP(app)); @@ -2010,8 +2013,12 @@ double GtkGuiMain::split() const { return split_; } -void GtkGuiMenu::add_item(std::string const& id, std::string const& label) { +void GtkGuiMenu::add_item(std::string const& id, std::string const& label, + Shortcut const& shortcut) { auto action = add_action(id); + if (shortcut.key) { + add_shortcut(action, shortcut); + } g_menu_append(section_.get(), label.c_str(), action.c_str()); } @@ -2027,23 +2034,148 @@ void GtkGuiMenu::add_separator() { g_menu_append_section(menu_.get(), nullptr, G_MENU_MODEL(section_.get())); } -void GtkGuiMenu::set_map(GActionMap* map) { +void GtkGuiMenu::set_app(GtkApplication* app) { assert(!parent_); - assert(!map_); - assert(map); - map_ = map; + assert(!app_); + assert(app); + app_ = app; for (auto const& pair : action_) { - g_action_map_add_action(map_, G_ACTION(pair.second.get())); + g_action_map_add_action(G_ACTION_MAP(app_), G_ACTION(pair.second.get())); + } + for (auto const& pair : shortcut_) { + gchar const* accels[2]; + accels[0] = pair.second.c_str(); + accels[1] = nullptr; + gtk_application_set_accels_for_action(app_, pair.first.c_str(), accels); } } +void GtkGuiMenu::add_shortcut(std::string const& action, + Shortcut const& shortcut) { + if (parent_) return parent_->add_shortcut(action, shortcut); + assert(shortcut.key); + std::string accel; + if (shortcut.mask & CTRL) accel.append("<Ctrl>"); + if (shortcut.mask & ALT) accel.append("<Alt>"); + if (shortcut.mask & SHIFT) accel.append("<Shift>"); + if (shortcut.mask & META) accel.append("<Meta>"); + guint key; + if (shortcut.function) { + if (shortcut.key >= 1 && shortcut.key <= 35) { + key = GDK_KEY_F1 + (shortcut.key - 1); + } else { + assert(false); + return; + } + } else { + if (shortcut.key == ESC) { + key = GDK_KEY_Escape; + } else if (shortcut.key == RETURN) { + key = GDK_KEY_Return; + } else if (shortcut.key == UP) { + key = GDK_KEY_Up; + } else if (shortcut.key == LEFT) { + key = GDK_KEY_Left; + } else if (shortcut.key == RIGHT) { + key = GDK_KEY_Right; + } else if (shortcut.key == DOWN) { + key = GDK_KEY_Down; + } else if (shortcut.key == '\t') { + key = GDK_KEY_Tab; + } else if (shortcut.key == 0x08) { + key = GDK_KEY_BackSpace; + } else if (shortcut.key >= 0x20 && shortcut.key < 0x7f) { + key = GDK_KEY_space + (shortcut.key - ' '); + } else { + assert(false); + return; + } + } + auto name = gdk_keyval_name(key); + if (!name) { + assert(false); + return; + } + accel.append(name); + shortcut_.emplace(action, accel); + if (app_) { + gchar const* accels[2]; + accels[0] = accel.c_str(); + accels[1] = nullptr; + gtk_application_set_accels_for_action(app_, action.c_str(), accels); + } +} + +static char hex[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'a', 'b', 'c', 'd', 'e', 'f' }; + +static int8_t unhex(char c) { + if (c >= '0' && c <= '9') return (c - '0'); + if (c >= 'a' && c <= 'f') return 10 + (c - 'a'); + return -1; +} + std::string GtkGuiMenu::escape(std::string const& action) { - // TODO - return action; + size_t last = 0; + std::string ret; + for (size_t i = 0; i < action.size(); ++i) { + auto c = action[i]; + if ((c >= '0' && c <= '9') + || (c >= 'a' && c <= 'z') + || (c >= 'A' && c <= 'Z') + || c == '-') { + continue; + } + ret.append(action, last, i - last); + if (c == '.') { + ret.append(".."); + } else { + char tmp[3]; + tmp[0] = '.'; + tmp[1] = hex[c >> 4]; + tmp[2] = hex[c & 0xf]; + ret.append(tmp, 3); + } + last = i + 1; + } + if (last == 0) return action; + ret.append(action, last, action.size() - last); + return ret; } std::string GtkGuiMenu::unescape(std::string const& action) { - return action; + size_t last = 0; + std::string ret; + for (size_t i = 0; i < action.size(); ++i) { + if (action[i] != '.') continue; + ++i; + if (i == action.size()) { + assert(false); + return ""; + } + ret.append(action, last, i - last); + if (action[i] == '.') { + last = i + 1; + ret.push_back('.'); + continue; + } + auto a = unhex(action[i]); + ++i; + if (a < 0 || i == action.size()) { + assert(false); + return ""; + } + auto b = unhex(action[i]); + if (b < 0) { + assert(false); + return ""; + } + ret.push_back((a << 4) | b); + last = i + 1; + } + if (last == 0) return action; + ret.append(action, last, action.size() - last); + return ret; } void menu_item_activate(GSimpleAction* action, GVariant*, gpointer data) { @@ -2061,7 +2193,7 @@ std::string GtkGuiMenu::add_action(std::string const& id) { auto action = g_simple_action_new(name.c_str(), nullptr); action_[id].reset(action); g_signal_connect(action, "activate", G_CALLBACK(menu_item_activate), this); - if (map_) g_action_map_add_action(map_, G_ACTION(action)); + if (app_) g_action_map_add_action(G_ACTION_MAP(app_), G_ACTION(action)); return "app." + name; } @@ -2235,6 +2367,20 @@ GuiMenu* GuiMenu::create() { } // static +const uint32_t GuiMenu::CTRL = 1 << 0; +const uint32_t GuiMenu::ALT = 1 << 1; +const uint32_t GuiMenu::SHIFT = 1 << 2; +const uint32_t GuiMenu::META = 1 << 3; + +// static +const char GuiMenu::ESC = 0x1b; +const char GuiMenu::UP = 1; +const char GuiMenu::LEFT = 2; +const char GuiMenu::RIGHT = 3; +const char GuiMenu::DOWN = 4; +const char GuiMenu::RETURN = '\n'; + +// static GuiStatusBar* GuiStatusBar::create() { return new GtkGuiStatusBar(); } |
