Sophie

Sophie

distrib > Mageia > 7 > i586 > by-pkgid > e40b0b2b839eccdb3c85993a7b738115 > files > 156

gtkmm-documentation-3.24.0-1.mga7.noarch.rpm

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Adding a side bar</title>
<link rel="stylesheet" type="text/css" href="style.css">
<meta name="generator" content="DocBook XSL Stylesheets V1.79.1">
<link rel="home" href="index.html" title="Programming with gtkmm 3">
<link rel="up" href="chapter-building-applications.html" title="Chapter 31. Building applications">
<link rel="prev" href="sec-buildapp-search-bar.html" title="Adding a search bar">
<link rel="next" href="sec-buildapp-properties.html" title="Properties">
</head>
<body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF">
<div class="navheader">
<table width="100%" summary="Navigation header">
<tr><th colspan="3" align="center">Adding a side bar</th></tr>
<tr>
<td width="20%" align="left">
<a accesskey="p" href="sec-buildapp-search-bar.html"><img src="icons/prev.png" alt="Prev"></a> </td>
<th width="60%" align="center">Chapter 31. Building applications</th>
<td width="20%" align="right"> <a accesskey="n" href="sec-buildapp-properties.html"><img src="icons/next.png" alt="Next"></a>
</td>
</tr>
</table>
<hr>
</div>
<div class="sect1">
<div class="titlepage"><div><div><h2 class="title" style="clear: both">
<a name="sec-buildapp-side-bar"></a>Adding a side bar</h2></div></div></div>
<p>
As another piece of functionality, we are adding a sidebar, which demonstrates
<code class="classname">Gtk::MenuButton</code>, <code class="classname">Gtk::Revealer</code> and
<code class="classname">Gtk::ListBox</code>. The new widgets are added in the <code class="filename">window.ui</code> file.
</p>
<p>
The code to populate the sidebar with buttons for the words found in each file is a
little too involved to go into here. But we'll look at the code to add the gears menu.
As expected by now, the gears menu is specified in a <code class="classname">Gtk::Builder</code>
ui file, <code class="filename">gears_menu.ui</code>
</p>
<p>
To connect the menu item to the new <code class="literal">show-words</code> setting, we use a
<code class="classname">Gio::Action</code> corresponding to the given <code class="classname">Gio::Settings</code>
key. In <code class="classname">ExampleAppWindow</code>'s constructor:
</p>
<pre class="programlisting">
// 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 object = menu_builder-&gt;get_object("menu");
auto menu = Glib::RefPtr&lt;Gio::MenuModel&gt;::cast_dynamic(object);
m_gears-&gt;set_menu_model(menu);
add_action(m_settings-&gt;create_action("show-words"));
</pre>
<p>
</p>
<p>
What our application looks like now:
</p>
<div class="figure">
<a name="figure-buildapp-side-bar"></a><p class="title"><b>Figure 31.7. Adding a side bar</b></p>
<div class="figure-contents"><div class="screenshot"><div><img src="figures/buildapp_side_bar.png" alt="Adding a side bar"></div></div></div>
</div>
<br class="figure-break"><p><a class="ulink" href="http://git.gnome.org/browse/gtkmm-documentation/tree/examples/book/buildapp/step7?h=gtkmm-3-24" target="_top">Source Code</a></p>
<p>File: <code class="filename">exampleappwindow.h</code> (For use with gtkmm 3, not gtkmm 2)
</p>
<pre class="programlisting">
#ifndef GTKMM_EXAMPLEAPPWINDOW_H_
#define GTKMM_EXAMPLEAPPWINDOW_H_

#include &lt;gtkmm.h&gt;

class ExampleAppWindow : public Gtk::ApplicationWindow
{
public:
  ExampleAppWindow(BaseObjectType* cobject,
    const Glib::RefPtr&lt;Gtk::Builder&gt;&amp; refBuilder);

  static ExampleAppWindow* create();

  void open_file_view(const Glib::RefPtr&lt;Gio::File&gt;&amp; file);

protected:
  // Signal handlers
  void on_search_text_changed();
  void on_visible_child_changed();
  void on_find_word(const Gtk::Button* button);
  void on_reveal_child_changed();

  void update_words();

  Glib::RefPtr&lt;Gtk::Builder&gt; m_refBuilder;
  Glib::RefPtr&lt;Gio::Settings&gt; m_settings;
  Gtk::Stack* m_stack;
  Gtk::ToggleButton* m_search;
  Gtk::SearchBar* m_searchbar;
  Gtk::SearchEntry* m_searchentry;
  Gtk::MenuButton* m_gears;
  Gtk::Revealer* m_sidebar;
  Gtk::ListBox* m_words;
  Glib::RefPtr&lt;Glib::Binding&gt; m_prop_binding;
};

#endif /* GTKMM_EXAMPLEAPPWINDOW_H */
</pre>
<p>File: <code class="filename">exampleappprefs.h</code> (For use with gtkmm 3, not gtkmm 2)
</p>
<pre class="programlisting">
#include "../step5/exampleappprefs.h"
// Equal to the corresponding file in step5
</pre>
<p>File: <code class="filename">exampleapplication.h</code> (For use with gtkmm 3, not gtkmm 2)
</p>
<pre class="programlisting">
#include "../step4/exampleapplication.h"
// Equal to the corresponding file in step4
</pre>
<p>File: <code class="filename">exampleapplication.cc</code> (For use with gtkmm 3, not gtkmm 2)
</p>
<pre class="programlisting">
#include "../step5/exampleapplication.cc"
// Equal to the corresponding file in step5
</pre>
<p>File: <code class="filename">main.cc</code> (For use with gtkmm 3, not gtkmm 2)
</p>
<pre class="programlisting">
#include "../step5/main.cc"
// Equal to the corresponding file in step5
</pre>
<p>File: <code class="filename">exampleappwindow.cc</code> (For use with gtkmm 3, not gtkmm 2)
</p>
<pre class="programlisting">
#include "exampleappwindow.h"
#include &lt;iostream&gt;
#include &lt;stdexcept&gt;
#include &lt;set&gt;

ExampleAppWindow::ExampleAppWindow(BaseObjectType* cobject,
  const Glib::RefPtr&lt;Gtk::Builder&gt;&amp; 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_prop_binding()
{
  // Get widgets from the Gtk::Builder file.
  m_refBuilder-&gt;get_widget("stack", m_stack);
  if (!m_stack)
    throw std::runtime_error("No \"stack\" object in window.ui");

  m_refBuilder-&gt;get_widget("search", m_search);
  if (!m_search)
    throw std::runtime_error("No \"search\" object in window.ui");

  m_refBuilder-&gt;get_widget("searchbar", m_searchbar);
  if (!m_searchbar)
    throw std::runtime_error("No \"searchbar\" object in window.ui");

  m_refBuilder-&gt;get_widget("searchentry", m_searchentry);
  if (!m_searchentry)
    throw std::runtime_error("No \"searchentry\" object in window.ui");

  m_refBuilder-&gt;get_widget("gears", m_gears);
  if (!m_gears)
    throw std::runtime_error("No \"gears\" object in window.ui");

  m_refBuilder-&gt;get_widget("sidebar", m_sidebar);
  if (!m_sidebar)
    throw std::runtime_error("No \"sidebar\" object in window.ui");

  m_refBuilder-&gt;get_widget("words", m_words);
  if (!m_words)
    throw std::runtime_error("No \"words\" object in window.ui");

  // Bind settings.
  m_settings = Gio::Settings::create("org.gtkmm.exampleapp");
  m_settings-&gt;bind("transition", m_stack-&gt;property_transition_type());
  m_settings-&gt;bind("show-words", m_sidebar-&gt;property_reveal_child());

  // Bind properties.
  m_prop_binding = Glib::Binding::bind_property(m_search-&gt;property_active(),
    m_searchbar-&gt;property_search_mode_enabled(), Glib::BINDING_BIDIRECTIONAL);

  // Connect signal handlers.
  m_searchentry-&gt;signal_search_changed().connect(
    sigc::mem_fun(*this, &amp;ExampleAppWindow::on_search_text_changed));
  m_stack-&gt;property_visible_child().signal_changed().connect(
    sigc::mem_fun(*this, &amp;ExampleAppWindow::on_visible_child_changed));
  m_sidebar-&gt;property_reveal_child().signal_changed().connect(
    sigc::mem_fun(*this, &amp;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 object = menu_builder-&gt;get_object("menu");
  auto menu = Glib::RefPtr&lt;Gio::MenuModel&gt;::cast_dynamic(object);
  if (!menu)
    throw std::runtime_error("No \"menu\" object in gears_menu.ui");

  m_gears-&gt;set_menu_model(menu);
  add_action(m_settings-&gt;create_action("show-words"));
}

//static
ExampleAppWindow* ExampleAppWindow::create()
{
  // Load the Builder file and instantiate its widgets.
  auto refBuilder = Gtk::Builder::create_from_resource("/org/gtkmm/exampleapp/window.ui");

  ExampleAppWindow* window = nullptr;
  refBuilder-&gt;get_widget_derived("app_window", window);
  if (!window)
    throw std::runtime_error("No \"app_window\" object in window.ui");

  return window;
}

void ExampleAppWindow::open_file_view(const Glib::RefPtr&lt;Gio::File&gt;&amp; file)
{
  const auto basename = file-&gt;get_basename();

  auto scrolled = Gtk::make_managed&lt;Gtk::ScrolledWindow&gt;();
  scrolled-&gt;set_hexpand(true);
  scrolled-&gt;set_vexpand(true);
  scrolled-&gt;show();
  auto view = Gtk::make_managed&lt;Gtk::TextView&gt;();
  view-&gt;set_editable(false);
  view-&gt;set_cursor_visible(false);
  view-&gt;show();
  scrolled-&gt;add(*view);
  m_stack-&gt;add(*scrolled, basename, basename);

  auto buffer = view-&gt;get_buffer();
  try
  {
    char* contents = nullptr;
    gsize length = 0;
    
    file-&gt;load_contents(contents, length);
    buffer-&gt;set_text(contents, contents+length);
    g_free(contents);
  }
  catch (const Glib::Error&amp; ex)
  {
    std::cout &lt;&lt; "ExampleAppWindow::open_file_view(\"" &lt;&lt; file-&gt;get_parse_name()
      &lt;&lt; "\"):\n  " &lt;&lt; ex.what() &lt;&lt; std::endl;
    return;
  }

  auto tag = buffer-&gt;create_tag();
  m_settings-&gt;bind("font", tag-&gt;property_font());
  buffer-&gt;apply_tag(tag, buffer-&gt;begin(), buffer-&gt;end());

  m_search-&gt;set_sensitive(true);
  update_words();
}

void ExampleAppWindow::on_search_text_changed()
{
  const auto text = m_searchentry-&gt;get_text();
  if (text.empty())
    return;

  auto tab = dynamic_cast&lt;Gtk::ScrolledWindow*&gt;(m_stack-&gt;get_visible_child());
  if (!tab)
  {
    std::cout &lt;&lt; "ExampleAppWindow::on_search_text_changed(): No visible child." &lt;&lt; std::endl;
    return;
  }

  auto view = dynamic_cast&lt;Gtk::TextView*&gt;(tab-&gt;get_child());
  if (!view)
  {
    std::cout &lt;&lt; "ExampleAppWindow::on_search_text_changed(): No visible text view." &lt;&lt; std::endl;
    return;
  }

  // Very simple-minded search implementation.
  auto buffer = view-&gt;get_buffer();
  Gtk::TextIter match_start;
  Gtk::TextIter match_end;
  if (buffer-&gt;begin().forward_search(text, Gtk::TEXT_SEARCH_CASE_INSENSITIVE,
      match_start, match_end))
  {
    buffer-&gt;select_range(match_start, match_end);
    view-&gt;scroll_to(match_start);
  }
}

void ExampleAppWindow::on_visible_child_changed()
{
  m_searchbar-&gt;set_search_mode(false);
  update_words();
}

void ExampleAppWindow::on_find_word(const Gtk::Button* button)
{
  m_searchentry-&gt;set_text(button-&gt;get_label());
}

void ExampleAppWindow::on_reveal_child_changed()
{
  update_words();
}

void ExampleAppWindow::update_words()
{
  auto tab = dynamic_cast&lt;Gtk::ScrolledWindow*&gt;(m_stack-&gt;get_visible_child());
  if (!tab)
    return;

  auto view = dynamic_cast&lt;Gtk::TextView*&gt;(tab-&gt;get_child());
  if (!view)
  {
    std::cout &lt;&lt; "ExampleAppWindow::update_words(): No visible text view." &lt;&lt; std::endl;
    return;
  }
  auto buffer = view-&gt;get_buffer();

  // Find all words in the text buffer.
  std::set&lt;Glib::ustring&gt; words;
  auto start = buffer-&gt;begin();
  Gtk::TextIter end;
  while (start)
  {
    while (start &amp;&amp; !start.starts_word())
      ++start;

    if (!start)
      break;

    end = start;
    end.forward_word_end();
    if (start == end)
      break;

    auto word = buffer-&gt;get_text(start, end, false);
    words.insert(word.lowercase());
    start = end;
  }

  // Remove old children from the ListBox.
  auto old_children = m_words-&gt;get_children();
  for (auto child : old_children)
  {
    m_words-&gt;remove(*child);
    delete child;
  }

  // Add new child buttons, one per unique word.
  for (const auto&amp; word : words)
  {
    auto row = Gtk::make_managed&lt;Gtk::Button&gt;(word);
    row-&gt;signal_clicked().connect(sigc::bind(sigc::mem_fun(*this,
      &amp;ExampleAppWindow::on_find_word), row));
    row-&gt;show();
    m_words-&gt;add(*row);
  }
}
</pre>
<p>File: <code class="filename">exampleappprefs.cc</code> (For use with gtkmm 3, not gtkmm 2)
</p>
<pre class="programlisting">
#include "../step5/exampleappprefs.cc"
// Equal to the corresponding file in step5
</pre>
<p>File: <code class="filename">exampleapp.gresource.xml</code> (For use with gtkmm 3, not gtkmm 2)
</p>
<pre class="programlisting">
&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;gresources&gt;
  &lt;gresource prefix="/org/gtkmm/exampleapp"&gt;
    &lt;file preprocess="xml-stripblanks"&gt;window.ui&lt;/file&gt;
    &lt;file preprocess="xml-stripblanks"&gt;app_menu.ui&lt;/file&gt;
    &lt;file preprocess="xml-stripblanks"&gt;gears_menu.ui&lt;/file&gt;
    &lt;file preprocess="xml-stripblanks"&gt;prefs.ui&lt;/file&gt;
  &lt;/gresource&gt;
&lt;/gresources&gt;
</pre>
<p>File: <code class="filename">gears_menu.ui</code> (For use with gtkmm 3, not gtkmm 2)
</p>
<pre class="programlisting">
&lt;?xml version="1.0"?&gt;
&lt;interface&gt;
  &lt;!-- interface-requires gtk+ 3.0 --&gt;
  &lt;menu id="menu"&gt;
    &lt;section&gt;
      &lt;item&gt;
        &lt;attribute name="label" translatable="yes"&gt;_Words&lt;/attribute&gt;
        &lt;attribute name="action"&gt;win.show-words&lt;/attribute&gt;
      &lt;/item&gt;
    &lt;/section&gt;
  &lt;/menu&gt;
&lt;/interface&gt;
</pre>
<p>File: <code class="filename">window.ui</code> (For use with gtkmm 3, not gtkmm 2)
</p>
<pre class="programlisting">
&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;interface&gt;
  &lt;!-- interface-requires gtk+ 3.8 --&gt;
  &lt;object class="GtkApplicationWindow" id="app_window"&gt;
    &lt;property name="title" translatable="yes"&gt;Example Application&lt;/property&gt;
    &lt;property name="default-width"&gt;600&lt;/property&gt;
    &lt;property name="default-height"&gt;400&lt;/property&gt;
    &lt;child&gt;
      &lt;object class="GtkBox" id="content_box"&gt;
        &lt;property name="visible"&gt;True&lt;/property&gt;
        &lt;property name="orientation"&gt;vertical&lt;/property&gt;
        &lt;child&gt;
          &lt;object class="GtkHeaderBar" id="header"&gt;
            &lt;property name="visible"&gt;True&lt;/property&gt;
            &lt;child type="title"&gt;
              &lt;object class="GtkStackSwitcher" id="tabs"&gt;
                &lt;property name="visible"&gt;True&lt;/property&gt;
                &lt;property name="stack"&gt;stack&lt;/property&gt;
              &lt;/object&gt;
            &lt;/child&gt;
            &lt;child&gt;
              &lt;object class="GtkToggleButton" id="search"&gt;
                &lt;property name="visible"&gt;True&lt;/property&gt;
                &lt;property name="sensitive"&gt;False&lt;/property&gt;
                &lt;style&gt;
                  &lt;class name="image-button"/&gt;
                &lt;/style&gt;
                &lt;child&gt;
                  &lt;object class="GtkImage" id="search-icon"&gt;
                    &lt;property name="visible"&gt;True&lt;/property&gt;
                    &lt;property name="icon-name"&gt;edit-find-symbolic&lt;/property&gt;
                    &lt;property name="icon-size"&gt;1&lt;/property&gt;
                  &lt;/object&gt;
                &lt;/child&gt;
              &lt;/object&gt;
              &lt;packing&gt;
                &lt;property name="pack-type"&gt;end&lt;/property&gt;
              &lt;/packing&gt;
            &lt;/child&gt;
            &lt;child&gt;
              &lt;object class="GtkMenuButton" id="gears"&gt;
                &lt;property name="visible"&gt;True&lt;/property&gt;
                &lt;property name="direction"&gt;none&lt;/property&gt;
                &lt;property name="use-popover"&gt;True&lt;/property&gt;
                &lt;style&gt;
                  &lt;class name="image-button"/&gt;
                &lt;/style&gt;
              &lt;/object&gt;
              &lt;packing&gt;
                &lt;property name="pack-type"&gt;end&lt;/property&gt;
              &lt;/packing&gt;
            &lt;/child&gt;
          &lt;/object&gt;
        &lt;/child&gt;
        &lt;child&gt;
          &lt;object class="GtkSearchBar" id="searchbar"&gt;
            &lt;property name="visible"&gt;True&lt;/property&gt;
            &lt;child&gt;
              &lt;object class="GtkSearchEntry" id="searchentry"&gt;
                &lt;property name="visible"&gt;True&lt;/property&gt;
              &lt;/object&gt;
            &lt;/child&gt;
          &lt;/object&gt;
        &lt;/child&gt;
        &lt;child&gt;
          &lt;object class="GtkBox" id="hbox"&gt;
            &lt;property name="visible"&gt;True&lt;/property&gt;
            &lt;child&gt;
              &lt;object class="GtkRevealer" id="sidebar"&gt;
                &lt;property name="visible"&gt;True&lt;/property&gt;
                &lt;property name="transition-type"&gt;slide-right&lt;/property&gt;
                &lt;child&gt;
                 &lt;object class="GtkScrolledWindow" id="sidebar-sw"&gt;
                   &lt;property name="visible"&gt;True&lt;/property&gt;
                   &lt;property name="hscrollbar-policy"&gt;never&lt;/property&gt;
                   &lt;property name="vscrollbar-policy"&gt;automatic&lt;/property&gt;
                   &lt;child&gt;
                     &lt;object class="GtkListBox" id="words"&gt;
                       &lt;property name="visible"&gt;True&lt;/property&gt;
                       &lt;property name="selection-mode"&gt;none&lt;/property&gt;
                     &lt;/object&gt;
                   &lt;/child&gt;
                 &lt;/object&gt;
                &lt;/child&gt;
              &lt;/object&gt;
            &lt;/child&gt;
            &lt;child&gt;
              &lt;object class="GtkStack" id="stack"&gt;
                &lt;property name="visible"&gt;True&lt;/property&gt;
                &lt;property name="transition-duration"&gt;500&lt;/property&gt;
              &lt;/object&gt;
            &lt;/child&gt;
          &lt;/object&gt;
        &lt;/child&gt;
      &lt;/object&gt;
    &lt;/child&gt;
  &lt;/object&gt;
&lt;/interface&gt;
</pre>
<p>File: <code class="filename">org.gtkmm.exampleapp.gschema.xml</code> (For use with gtkmm 3, not gtkmm 2)
</p>
<pre class="programlisting">
&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;schemalist&gt;
  &lt;schema path="/org/gtkmm/exampleapp/" id="org.gtkmm.exampleapp"&gt;
    &lt;key name="font" type="s"&gt;
      &lt;default&gt;'Monospace 12'&lt;/default&gt;
      &lt;summary&gt;Font&lt;/summary&gt;
      &lt;description&gt;The font to be used for content.&lt;/description&gt;
    &lt;/key&gt;
    &lt;key name="transition" type="s"&gt;
      &lt;choices&gt;
        &lt;choice value='none'/&gt;
        &lt;choice value='crossfade'/&gt;
        &lt;choice value='slide-left-right'/&gt;
      &lt;/choices&gt;
      &lt;default&gt;'none'&lt;/default&gt;
      &lt;summary&gt;Transition&lt;/summary&gt;
      &lt;description&gt;The transition to use when switching tabs.&lt;/description&gt;
    &lt;/key&gt;
    &lt;key name="show-words" type="b"&gt;
      &lt;default&gt;false&lt;/default&gt;
      &lt;summary&gt;Show words&lt;/summary&gt;
      &lt;description&gt;Whether to show a word list in the sidebar.&lt;/description&gt;
    &lt;/key&gt;
  &lt;/schema&gt;
&lt;/schemalist&gt;
</pre>
</div>
<div class="navfooter">
<hr>
<table width="100%" summary="Navigation footer">
<tr>
<td width="40%" align="left">
<a accesskey="p" href="sec-buildapp-search-bar.html"><img src="icons/prev.png" alt="Prev"></a> </td>
<td width="20%" align="center"><a accesskey="u" href="chapter-building-applications.html"><img src="icons/up.png" alt="Up"></a></td>
<td width="40%" align="right"> <a accesskey="n" href="sec-buildapp-properties.html"><img src="icons/next.png" alt="Next"></a>
</td>
</tr>
<tr>
<td width="40%" align="left" valign="top">Adding a search bar </td>
<td width="20%" align="center"><a accesskey="h" href="index.html"><img src="icons/home.png" alt="Home"></a></td>
<td width="40%" align="right" valign="top"> Properties</td>
</tr>
</table>
</div>
</body>
</html>