菜单
菜单显示在标题栏的右侧。它旨在放置所有应用程序不常用的动作。
就像应用程序窗口一样,我们在UI文件中指定一个菜单,并将其作为资源添加到二进制中。
为了使菜单出现,我们必须加载UI文件,并将生成的菜单模型与添加到标题栏右侧的菜单按钮相关联。由于菜单通过激活Gio::Action工作,我们还需要向应用程序中添加一组合适的动作。
最好在on_startup()的默认信号处理函数中完成添加动作,并确保每个应用程序实例都只调用一次。
现在我们的首选项菜单还什么都不做,但是退出菜单项是有效的。也可以用常用的Ctrl-Q快捷键激活它。快捷键通过Gtk::Application::set_accel_for_action()添加。
菜单如下所示
File: exampleapplication.h (For use with gtkmm 4)
#ifndef GTKMM_EXAMPLEAPPLICATION_H #define GTKMM_EXAMPLEAPPLICATION_H #include <gtkmm.h> class ExampleAppWindow; class ExampleApplication: public Gtk::Application { protected: ExampleApplication(); public: static Glib::RefPtr<ExampleApplication> create(); protected: // Override default signal handlers: void on_startup() override; void on_activate() override; void on_open(const Gio::Application::type_vec_files& files, const Glib::ustring& hint) override; private: ExampleAppWindow* create_appwindow(); void on_hide_window(Gtk::Window* window); void on_action_preferences(); void on_action_quit(); }; #endif /* GTKMM_EXAMPLEAPPLICATION_H */
File: exampleappwindow.h (For use with gtkmm 4)
#ifndef GTKMM_EXAMPLEAPPWINDOW_H_ #define GTKMM_EXAMPLEAPPWINDOW_H_ #include <gtkmm.h> class ExampleAppWindow : public Gtk::ApplicationWindow { public: ExampleAppWindow(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& refBuilder); static ExampleAppWindow* create(); void open_file_view(const Glib::RefPtr<Gio::File>& file); protected: Glib::RefPtr<Gtk::Builder> m_refBuilder; Gtk::Stack* m_stack; Gtk::MenuButton* m_gears; }; #endif /* GTKMM_EXAMPLEAPPWINDOW_H */
File: main.cc (For use with gtkmm 4)
#include "../step1/main.cc" // Equal to the corresponding file in step1
File: exampleappwindow.cc (For use with gtkmm 4)
#include "exampleappwindow.h" #include <iostream> #include <stdexcept> ExampleAppWindow::ExampleAppWindow(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& refBuilder) : Gtk::ApplicationWindow(cobject), m_refBuilder(refBuilder), m_stack(nullptr), m_gears(nullptr) { // Get widgets from the Gtk::Builder file. m_stack = m_refBuilder->get_widget<Gtk::Stack>("stack"); if (!m_stack) throw std::runtime_error("No \"stack\" object in window.ui"); m_gears = m_refBuilder->get_widget<Gtk::MenuButton>("gears"); if (!m_gears) throw std::runtime_error("No \"gears\" object in window.ui"); // Connect the menu to the MenuButton m_gears. // (The connection between action and menu item is specified in gears_menu.ui.) auto menu_builder = Gtk::Builder::create_from_resource("/org/gtkmm/exampleapp/gears_menu.ui"); auto menu = menu_builder->get_object<Gio::MenuModel>("menu"); if (!menu) throw std::runtime_error("No \"menu\" object in gears_menu.ui"); m_gears->set_menu_model(menu); } //static ExampleAppWindow* ExampleAppWindow::create() { // Load the Builder file and instantiate its widgets. auto refBuilder = Gtk::Builder::create_from_resource("/org/gtkmm/exampleapp/window.ui"); auto window = Gtk::Builder::get_widget_derived<ExampleAppWindow>(refBuilder, "app_window"); if (!window) throw std::runtime_error("No \"app_window\" object in window.ui"); return window; } void ExampleAppWindow::open_file_view(const Glib::RefPtr<Gio::File>& file) { const Glib::ustring basename = file->get_basename(); auto scrolled = Gtk::make_managed<Gtk::ScrolledWindow>(); scrolled->set_expand(true); auto view = Gtk::make_managed<Gtk::TextView>(); view->set_editable(false); view->set_cursor_visible(false); scrolled->set_child(*view); m_stack->add(*scrolled, basename, basename); try { char* contents = nullptr; gsize length = 0; file->load_contents(contents, length); view->get_buffer()->set_text(contents, contents+length); g_free(contents); } catch (const Glib::Error& ex) { std::cout << "ExampleAppWindow::open_file_view(\"" << file->get_parse_name() << "\"):\n " << ex.what() << std::endl; } }
File: exampleapplication.cc (For use with gtkmm 4)
#include "exampleapplication.h" #include "exampleappwindow.h" #include <iostream> #include <exception> ExampleApplication::ExampleApplication() : Gtk::Application("org.gtkmm.examples.application", Gio::Application::Flags::HANDLES_OPEN) { } Glib::RefPtr<ExampleApplication> ExampleApplication::create() { return Glib::make_refptr_for_instance<ExampleApplication>(new ExampleApplication()); } ExampleAppWindow* ExampleApplication::create_appwindow() { auto appwindow = ExampleAppWindow::create(); // Make sure that the application runs for as long this window is still open. add_window(*appwindow); // A window can be added to an application with Gtk::Application::add_window() // or Gtk::Window::set_application(). When all added windows have been hidden // or removed, the application stops running (Gtk::Application::run() returns()), // unless Gio::Application::hold() has been called. // Delete the window when it is hidden. appwindow->signal_hide().connect(sigc::bind(sigc::mem_fun(*this, &ExampleApplication::on_hide_window), appwindow)); return appwindow; } void ExampleApplication::on_startup() { // Call the base class's implementation. Gtk::Application::on_startup(); // Add actions and keyboard accelerators for the menu. add_action("preferences", sigc::mem_fun(*this, &ExampleApplication::on_action_preferences)); add_action("quit", sigc::mem_fun(*this, &ExampleApplication::on_action_quit)); set_accel_for_action("app.quit", "<Ctrl>Q"); } void ExampleApplication::on_activate() { try { // The application has been started, so let's show a window. auto appwindow = create_appwindow(); appwindow->present(); } // If create_appwindow() throws an exception (perhaps from Gtk::Builder), // no window has been created, no window has been added to the application, // and therefore the application will stop running. catch (const Glib::Error& ex) { std::cerr << "ExampleApplication::on_activate(): " << ex.what() << std::endl; } catch (const std::exception& ex) { std::cerr << "ExampleApplication::on_activate(): " << ex.what() << std::endl; } } void ExampleApplication::on_open(const Gio::Application::type_vec_files& files, const Glib::ustring& /* hint */) { // The application has been asked to open some files, // so let's open a new view for each one. ExampleAppWindow* appwindow = nullptr; auto windows = get_windows(); if (windows.size() > 0) appwindow = dynamic_cast<ExampleAppWindow*>(windows[0]); try { if (!appwindow) appwindow = create_appwindow(); for (const auto& file : files) appwindow->open_file_view(file); appwindow->present(); } catch (const Glib::Error& ex) { std::cerr << "ExampleApplication::on_open(): " << ex.what() << std::endl; } catch (const std::exception& ex) { std::cerr << "ExampleApplication::on_open(): " << ex.what() << std::endl; } } void ExampleApplication::on_hide_window(Gtk::Window* window) { delete window; } void ExampleApplication::on_action_preferences() { } void ExampleApplication::on_action_quit() { // Gio::Application::quit() will make Gio::Application::run() return, // but it's a crude way of ending the program. The window is not removed // from the application. Neither the window's nor the application's // destructors will be called, because there will be remaining reference // counts in both of them. If we want the destructors to be called, we // must remove the window from the application. One way of doing this // is to hide the window. See comment in create_appwindow(). auto windows = get_windows(); for (auto window : windows) window->hide(); // Not really necessary, when Gtk::Widget::hide() is called, unless // Gio::Application::hold() has been called without a corresponding call // to Gio::Application::release(). quit(); }
File: exampleapp.gresource.xml (For use with gtkmm 4)
<?xml version="1.0" encoding="UTF-8"?> <gresources> <gresource prefix="/org/gtkmm/exampleapp"> <file preprocess="xml-stripblanks">window.ui</file> <file preprocess="xml-stripblanks">gears_menu.ui</file> </gresource> </gresources>
File: gears_menu.ui (For use with gtkmm 4)
<?xml version="1.0" encoding="UTF-8"?> <interface> <menu id="menu"> <section> <item> <attribute name="label" translatable="yes">_Preferences</attribute> <attribute name="action">app.preferences</attribute> </item> </section> <section> <item> <attribute name="label" translatable="yes">_Quit</attribute> <attribute name="action">app.quit</attribute> </item> </section> </menu> </interface>
File: window.ui (For use with gtkmm 4)
<?xml version="1.0" encoding="UTF-8"?> <interface> <object class="GtkApplicationWindow" id="app_window"> <property name="title" translatable="yes">Example Application</property> <property name="default-width">600</property> <property name="default-height">400</property> <property name="hide-on-close">True</property> <child type="titlebar"> <object class="GtkHeaderBar" id="header"> <child type="title"> <object class="GtkStackSwitcher" id="tabs"> <property name="stack">stack</property> </object> </child> <child type="end"> <object class="GtkMenuButton" id="gears"> <property name="direction">none</property> </object> </child> </object> </child> <child> <object class="GtkBox" id="content_box"> <property name="orientation">vertical</property> <child> <object class="GtkStack" id="stack"/> </child> </object> </child> </object> </interface>