标题栏
我们的应用程序使用了Gtk::HeaderBar而不是普通的窗口标题栏。标题栏是窗口的直接子项,其类型为titlebar。这些由window.ui文件进行设置。
在这里我们对标题栏进行了两个小的更改。decoration-layout属性在window.ui文件中设置,使标题栏只显示关闭按钮,并隐藏最小化和最大化按钮。我们还在资源文件中包含了一个图标,并将其设置为窗口图标。在ExampleAppWindow的构造函数中:
Gtk::IconTheme::get_for_display(get_display())->add_resource_path("/org/gtkmm/exampleapp"); set_icon_name("exampleapp");
现在应用程序看起来如下所示:
window.ui文件设置了标题栏标题,但是此标题未显示。这是因为堆切换器是title的子类型。此时堆切换器成为了自定义标题并将正常的标题标签隐藏。
File: exampleapplication.h (For use with gtkmm 4)
#include "../step4/exampleapplication.h" // Equal to the corresponding file in step4
File: exampleappprefs.h (For use with gtkmm 4)
#include "../step5/exampleappprefs.h" // Equal to the corresponding file in step5
File: exampleappwindow.h (For use with gtkmm 4)
#include "../step8/exampleappwindow.h" // Equal to the corresponding file in step8
File: main.cc (For use with gtkmm 4)
#include "../step5/main.cc" // Equal to the corresponding file in step5
File: exampleappwindow.cc (For use with gtkmm 4)
#include "exampleappwindow.h" #include <iostream> #include <stdexcept> #include <set> ExampleAppWindow::ExampleAppWindow(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& refBuilder) : Gtk::ApplicationWindow(cobject), m_refBuilder(refBuilder), m_settings(), m_stack(nullptr), m_search(nullptr), m_searchbar(nullptr), m_searchentry(nullptr), m_gears(nullptr), m_sidebar(nullptr), m_words(nullptr), m_lines(nullptr), m_lines_label(nullptr), m_binding_search_enabled(), m_binding_lines_visible() { // 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_search = m_refBuilder->get_widget<Gtk::ToggleButton>("search"); if (!m_search) throw std::runtime_error("No \"search\" object in window.ui"); m_searchbar = m_refBuilder->get_widget<Gtk::SearchBar>("searchbar"); if (!m_searchbar) throw std::runtime_error("No \"searchbar\" object in window.ui"); m_searchentry = m_refBuilder->get_widget<Gtk::SearchEntry>("searchentry"); if (!m_searchentry) throw std::runtime_error("No \"searchentry\" 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"); m_sidebar = m_refBuilder->get_widget<Gtk::Revealer>("sidebar"); if (!m_sidebar) throw std::runtime_error("No \"sidebar\" object in window.ui"); m_words = m_refBuilder->get_widget<Gtk::ListBox>("words"); if (!m_words) throw std::runtime_error("No \"words\" object in window.ui"); m_lines = m_refBuilder->get_widget<Gtk::Label>("lines"); if (!m_lines) throw std::runtime_error("No \"lines\" object in window.ui"); m_lines_label = m_refBuilder->get_widget<Gtk::Label>("lines_label"); if (!m_lines_label) throw std::runtime_error("No \"lines_label\" object in window.ui"); // Bind settings. m_settings = Gio::Settings::create("org.gtkmm.exampleapp"); m_settings->bind("transition", m_stack->property_transition_type()); m_settings->bind("show-words", m_sidebar->property_reveal_child()); // Bind properties of the search button to the search bar. m_binding_search_enabled = Glib::Binding::bind_property(m_search->property_active(), m_searchbar->property_search_mode_enabled(), Glib::Binding::Flags::BIDIRECTIONAL); // Connect signal handlers. m_searchentry->signal_search_changed().connect( sigc::mem_fun(*this, &ExampleAppWindow::on_search_text_changed)); m_stack->property_visible_child().signal_changed().connect( sigc::mem_fun(*this, &ExampleAppWindow::on_visible_child_changed)); m_sidebar->property_reveal_child().signal_changed().connect( sigc::mem_fun(*this, &ExampleAppWindow::on_reveal_child_changed)); // Connect the menu to the MenuButton m_gears, and bind the show-words setting // to the win.show-words action and the "Words" menu item. // (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); add_action(m_settings->create_action("show-words")); // Bind the "visible" property of m_lines to the win.show-lines action, to // the "Lines" menu item and to the "visible" property of m_lines_label. add_action(Gio::PropertyAction::create("show-lines", m_lines->property_visible())); m_binding_lines_visible = Glib::Binding::bind_property(m_lines->property_visible(), m_lines_label->property_visible()); // Set the window icon. Gtk::IconTheme::get_for_display(get_display())->add_resource_path("/org/gtkmm/exampleapp"); set_icon_name("exampleapp"); } //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); auto buffer = view->get_buffer(); try { char* contents = nullptr; gsize length = 0; file->load_contents(contents, length); 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; return; } auto tag = buffer->create_tag(); m_settings->bind("font", tag->property_font()); buffer->apply_tag(tag, buffer->begin(), buffer->end()); m_search->set_sensitive(true); update_words(); update_lines(); } void ExampleAppWindow::on_search_text_changed() { const auto text = m_searchentry->get_text(); if (text.empty()) return; auto tab = dynamic_cast<Gtk::ScrolledWindow*>(m_stack->get_visible_child()); if (!tab) { std::cout << "ExampleAppWindow::on_search_text_changed(): No visible child." << std::endl; return; } auto view = dynamic_cast<Gtk::TextView*>(tab->get_child()); if (!view) { std::cout << "ExampleAppWindow::on_search_text_changed(): No visible text view." << std::endl; return; } // Very simple-minded search implementation. auto buffer = view->get_buffer(); Gtk::TextIter match_start; Gtk::TextIter match_end; if (buffer->begin().forward_search(text, Gtk::TextSearchFlags::CASE_INSENSITIVE, match_start, match_end)) { buffer->select_range(match_start, match_end); view->scroll_to(match_start); } } void ExampleAppWindow::on_visible_child_changed() { m_searchbar->set_search_mode(false); update_words(); update_lines(); } void ExampleAppWindow::on_find_word(const Gtk::Button* button) { m_searchentry->set_text(button->get_label()); } void ExampleAppWindow::on_reveal_child_changed() { update_words(); } void ExampleAppWindow::update_words() { auto tab = dynamic_cast<Gtk::ScrolledWindow*>(m_stack->get_visible_child()); if (!tab) return; auto view = dynamic_cast<Gtk::TextView*>(tab->get_child()); if (!view) { std::cout << "ExampleAppWindow::update_words(): No visible text view." << std::endl; return; } auto buffer = view->get_buffer(); // Find all words in the text buffer. std::set<Glib::ustring> words; auto start = buffer->begin(); Gtk::TextIter end; while (start) { while (start && !start.starts_word()) ++start; if (!start) break; end = start; end.forward_word_end(); if (start == end) break; auto word = buffer->get_text(start, end, false); words.insert(word.lowercase()); start = end; } // Remove old children from the ListBox. while (auto child = m_words->get_first_child()) m_words->remove(*child); // Add new child buttons, one per unique word. for (const auto& word : words) { auto row = Gtk::make_managed<Gtk::Button>(word); row->signal_clicked().connect(sigc::bind(sigc::mem_fun(*this, &ExampleAppWindow::on_find_word), row)); m_words->append(*row); } } void ExampleAppWindow::update_lines() { auto tab = dynamic_cast<Gtk::ScrolledWindow*>(m_stack->get_visible_child()); if (!tab) return; auto view = dynamic_cast<Gtk::TextView*>(tab->get_child()); if (!view) { std::cout << "ExampleAppWindow::update_lines(): No visible text view." << std::endl; return; } auto buffer = view->get_buffer(); int count = 0; auto iter = buffer->begin(); while (iter) { ++count; if (!iter.forward_line()) break; } m_lines->set_text(Glib::ustring::format(count)); }
File: exampleappprefs.cc (For use with gtkmm 4)
#include "../step5/exampleappprefs.cc" // Equal to the corresponding file in step5
File: exampleapplication.cc (For use with gtkmm 4)
#include "../step5/exampleapplication.cc" // Equal to the corresponding file in step5
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> <file preprocess="xml-stripblanks">prefs.ui</file> <file>exampleapp.png</file> </gresource> </gresources>
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"> <property name="decoration-layout">icon:close</property> <child> <object class="GtkLabel" id="lines_label"> <property name="visible">False</property> <property name="label" translatable="yes">Lines:</property> </object> </child> <child> <object class="GtkLabel" id="lines"> <property name="visible">False</property> </object> </child> <child type="title"> <object class="GtkStackSwitcher" id="tabs"> <property name="stack">stack</property> </object> </child> <child type="end"> <object class="GtkToggleButton" id="search"> <property name="sensitive">False</property> <property name="icon-name">edit-find-symbolic</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="GtkSearchBar" id="searchbar"> <child> <object class="GtkSearchEntry" id="searchentry"> </object> </child> </object> </child> <child> <object class="GtkBox" id="hbox"> <child> <object class="GtkRevealer" id="sidebar"> <property name="transition-type">slide-right</property> <child> <object class="GtkScrolledWindow" id="sidebar-sw"> <property name="hscrollbar-policy">never</property> <child> <object class="GtkListBox" id="words"> <property name="selection-mode">none</property> </object> </child> </object> </child> </object> </child> <child> <object class="GtkStack" id="stack"> <property name="transition-duration">500</property> </object> </child> </object> </child> </object> </child> </object> </interface>