Chapter 8. Container Widgets

# Chapter 8. Container Widgets

All container widgets derive from `Gtk::Container`, not always directly. Some container widgets, such as `Gtk::Grid` can hold many child widgets, so these typically have more complex interfaces. Others, such as `Gtk::Frame` contain only one child widget.

## Single-item Containers

The single-item container widgets derive from `Gtk::Bin`, which provides the `add()` and `remove()` methods for the child widget. Note that `Gtk::Button` and `Gtk::Window` are technically single-item containers, but we have discussed them already elsewhere.

We also discuss the `Gtk::Paned` widget, which allows you to divide a window into two separate "panes". This widget actually contains two child widgets, but the number is fixed so it seems appropriate.

### Frame

Frames can enclose one or a group of widgets within a box, optionally with a title. For instance, you might place a group of `RadioButton`s or `CheckButton`s in a `Frame`.

Reference

#### Example

Figure 8.1. Frame

Source Code

File: `examplewindow.h` (For use with gtkmm 3, not gtkmm 2)

    #ifndef GTKMM_EXAMPLEWINDOW_H
    #define GTKMM_EXAMPLEWINDOW_H

    #include <gtkmm.h>

    class ExampleWindow : public Gtk::Window
    {
    public:
      ExampleWindow();
      virtual ~ExampleWindow();

    protected:

      //Child widgets:
      Gtk::Frame m_Frame;
    };

    #endif //GTKMM_EXAMPLEWINDOW_H

File: `main.cc` (For use with gtkmm 3, not gtkmm 2)

    #include "examplewindow.h"
    #include <gtkmm/application.h>

    int main(int argc, char *argv[])
    {
      auto app = Gtk::Application::create(argc, argv, "org.gtkmm.example");

      ExampleWindow window;

      //Shows the window and returns when it is closed.
      return app->run(window);
    }

File: `examplewindow.cc` (For use with gtkmm 3, not gtkmm 2)

    #include "examplewindow.h"

    ExampleWindow::ExampleWindow()
    {
     /* Set some window properties */
      set_title("Frame Example");
      set_size_request(300, 300);

      /* Sets the border width of the window. */
      set_border_width(10);

      add(m_Frame);

      /* Set the frames label */
      m_Frame.set_label("Gtk::Frame Widget");

      /* Align the label at the right of the frame */
      //m_Frame.set_label_align(Gtk::ALIGN_END, Gtk::ALIGN_START);

      /* Set the style of the frame */
      m_Frame.set_shadow_type(Gtk::SHADOW_ETCHED_OUT);

      show_all_children();
    }

    ExampleWindow::~ExampleWindow()
    {
    }

### Paned

Panes divide a widget into two halves, separated by a moveable divider. The two halves (panes) can be oriented either horizontally (side by side) or vertically (one above the other).

Unlike the other widgets in this section, pane widgets contain not one but two child widgets, one in each pane. Therefore, you should use `add1()` and `add2()` instead of the `add()` method.

You can adjust the position of the divider using the `set_position()` method, and you will probably need to do so.

Reference

#### Example

Figure 8.2. Paned

Source Code

File: `examplewindow.h` (For use with gtkmm 3, not gtkmm 2)

    #ifndef GTKMM_EXAMPLEWINDOW_H
    #define GTKMM_EXAMPLEWINDOW_H

    #include "messageslist.h"
    #include "messagetext.h"
    #include <gtkmm.h>

    class ExampleWindow : public Gtk::Window
    {
    public:
      ExampleWindow();
      virtual ~ExampleWindow();

    protected:

      //Child widgets:
      Gtk::Paned m_VPaned;
      MessagesList m_MessagesList;
      MessageText m_MessageText;
    };

    #endif //GTKMM_EXAMPLEWINDOW_H

File: `messageslist.h` (For use with gtkmm 3, not gtkmm 2)

    #ifndef GTKMM_EXAMPLE_MESSAGESLIST_H
    #define GTKMM_EXAMPLE_MESSAGESLIST_H

    #include <gtkmm.h>

    class MessagesList: public Gtk::ScrolledWindow
    {
    public:
      MessagesList();
      virtual ~MessagesList();

      class ModelColumns : public Gtk::TreeModel::ColumnRecord
      {
      public:

        ModelColumns()
        { add(m_col_text); }

        Gtk::TreeModelColumn<Glib::ustring> m_col_text;
      };

      ModelColumns m_Columns;

    protected:
      Glib::RefPtr<Gtk::ListStore> m_refListStore; //The Tree Model.
      Gtk::TreeView m_TreeView; //The Tree View.
    };
    #endif //GTKMM_EXAMPLE_MESSAGESLIST_H

File: `messagetext.h` (For use with gtkmm 3, not gtkmm 2)

    #ifndef GTKMM_EXAMPLE_MESSAGETEXT_H
    #define GTKMM_EXAMPLE_MESSAGETEXT_H

    #include <gtkmm.h>

    class MessageText : public Gtk::ScrolledWindow
    {
    public:
      MessageText();
      virtual ~MessageText();

      void insert_text();

    protected:
      Gtk::TextView m_TextView;
    };

    #endif //GTKMM_EXAMPLE_MESSAGETEXT_H

File: `messageslist.cc` (For use with gtkmm 3, not gtkmm 2)

    #include "messageslist.h"
    #include <sstream>

    MessagesList::MessagesList()
    {
      /* Create a new scrolled window, with scrollbars only if needed */
      set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);

      add(m_TreeView);

      /* create list store */
      m_refListStore = Gtk::ListStore::create(m_Columns);

      m_TreeView.set_model(m_refListStore);

      /* Add some messages to the window */
      for(int i = 0; i < 10; ++i)
      {
        std::ostringstream text;
        text << "message #" << i;

        Gtk::TreeModel::Row row = *(m_refListStore->append());
        row[m_Columns.m_col_text] = text.str();
      }

      //Add the Model's column to the View's columns:
      m_TreeView.append_column("Messages", m_Columns.m_col_text);

      show_all_children();
    }

    MessagesList::~MessagesList()
    {
    }

File: `main.cc` (For use with gtkmm 3, not gtkmm 2)

    #include "examplewindow.h"
    #include <gtkmm/application.h>

    int main(int argc, char *argv[])
    {
      auto app = Gtk::Application::create(argc, argv, "org.gtkmm.example");

      ExampleWindow window;

      //Shows the window and returns when it is closed.
      return app->run(window);
    }

File: `messagetext.cc` (For use with gtkmm 3, not gtkmm 2)

    #include "messagetext.h"

    MessageText::MessageText()
    {
      set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);

      add(m_TextView);
      insert_text();

      show_all_children();
    }

    MessageText::~MessageText()
    {
    }

    void MessageText::insert_text()
    {
      auto refTextBuffer = m_TextView.get_buffer();

      Gtk::TextBuffer::iterator iter = refTextBuffer->get_iter_at_offset(0);
      refTextBuffer->insert(iter,
        "From: pathfinder@nasa.gov\n"
        "To: mom@nasa.gov\n"
        "Subject: Made it!\n"
        "\n"
        "We just got in this morning. The weather has been\n"
        "great - clear but cold, and there are lots of fun sights.\n"
        "Sojourner says hi. See you soon.\n"
        " -Path\n");
    }

File: `examplewindow.cc` (For use with gtkmm 3, not gtkmm 2)

    #include "examplewindow.h"

    ExampleWindow::ExampleWindow()
    : m_VPaned(Gtk::ORIENTATION_VERTICAL)
    {
      set_title ("Paned Windows");
      set_border_width(10);
      set_default_size(450, 400);

      /* Add a vpaned widget to our toplevel window */
      add(m_VPaned);

      /* Now add the contents of the two halves of the window */
      m_VPaned.add1(m_MessagesList);
      m_VPaned.add2(m_MessageText);

      show_all_children();
    }

    ExampleWindow::~ExampleWindow()
    {
    }

### ScrolledWindow

`ScrolledWindow` widgets create a scrollable area. You can insert any type of widget into a `ScrolledWindow` window, and it will be accessible regardless of its size by using the scrollbars. Note that `ScrolledWindow` is not a `Gtk::Window` despite the slightly misleading name.

Scrolled windows have scrollbar policies which determine whether the `Scrollbar`s will be displayed. The policies can be set with the `set_policy()` method. The policy may be one of `Gtk::POLICY_AUTOMATIC` or `Gtk::POLICY_ALWAYS`. `Gtk::POLICY_AUTOMATIC` will cause the scrolled window to display the scrollbar only if the contained widget is larger than the visible area. `Gtk::POLICY_ALWAYS` will cause the scrollbar to be displayed always.

Reference

#### Example

Here is a simple example that packs 100 toggle buttons into a ScrolledWindow. Try resizing the window to see the scrollbars react.

Figure 8.3. ScrolledWindow

Source Code

File: `examplewindow.h` (For use with gtkmm 3, not gtkmm 2)

    #ifndef GTKMM_EXAMPLEWINDOW_H
    #define GTKMM_EXAMPLEWINDOW_H

    #include <gtkmm.h>

    class ExampleWindow : public Gtk::Dialog
    {
    public:
      ExampleWindow();
      virtual ~ExampleWindow();

    protected:
      //Signal handlers:
      void on_dialog_response(int response_id);

      //Child widgets:
      Gtk::ScrolledWindow m_ScrolledWindow;
      Gtk::Grid m_Grid;
    };

    #endif //GTKMM_EXAMPLEWINDOW_H

File: `main.cc` (For use with gtkmm 3, not gtkmm 2)

    #include "examplewindow.h"
    #include <gtkmm/application.h>

    int main(int argc, char *argv[])
    {
      auto app = Gtk::Application::create(argc, argv, "org.gtkmm.example");

      ExampleWindow window;

      //Shows the window and returns when it is closed.
      return app->run(window);
    }

File: `examplewindow.cc` (For use with gtkmm 3, not gtkmm 2)

    #include "examplewindow.h"
    #include <iostream>

    ExampleWindow::ExampleWindow()
    {
      set_title("Gtk::ScrolledWindow example");
      set_border_width(0);
      set_size_request(300, 300);

      m_ScrolledWindow.set_border_width(10);

      /* the policy is one of Gtk::POLICY AUTOMATIC, or Gtk::POLICY_ALWAYS.
       * Gtk::POLICY_AUTOMATIC will automatically decide whether you need
       * scrollbars, whereas Gtk::POLICY_ALWAYS will always leave the scrollbars
       * there.  The first one is the horizontal scrollbar, the second,
       * the vertical. */
      m_ScrolledWindow.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_ALWAYS);

      get_content_area()->pack_start(m_ScrolledWindow);

      /* set the spacing to 10 on x and 10 on y */
      m_Grid.set_row_spacing(10);
      m_Grid.set_column_spacing(10);

      /* pack the grid into the scrolled window */
      m_ScrolledWindow.add(m_Grid);

      /* this simply creates a grid of toggle buttons
       * to demonstrate the scrolled window. */
      for(int i = 0; i < 10; i++)
      {
         for(int j = 0; j < 10; j++)
         {
            char buffer[32];
            sprintf(buffer, "button (%d,%d)\n", i, j);
            auto pButton = Gtk::make_managed<Gtk::ToggleButton>(buffer);
            m_Grid.attach(*pButton, i, j);
         }
      }

      /* Add a "close" button to the bottom of the dialog */
      add_button("_Close", Gtk::RESPONSE_CLOSE);
      signal_response().connect(sigc::mem_fun(*this, &ExampleWindow::on_dialog_response));

      /* This makes it so the button is the default.
       * Simply hitting the "Enter" key will cause this button to activate. */
      set_default_response(Gtk::RESPONSE_CLOSE);

      show_all_children();
    }

    ExampleWindow::~ExampleWindow()
    {
    }

    void ExampleWindow::on_dialog_response(int response_id)
    {
      switch (response_id)
      {
      case Gtk::RESPONSE_CLOSE:
      case Gtk::RESPONSE_DELETE_EVENT:
        hide();
        break;
      default:
        std::cout << "Unexpected response_id=" << response_id << std::endl;
        break;
      }
    }

### AspectFrame

The `AspectFrame` widget looks like a `Frame` widget, but it also enforces the aspect ratio (the ratio of the width to the height) of the child widget, adding extra space if necessary. For instance, this would allow you to display a photograph without allowing the user to distort it horizontally or vertically while resizing.

Reference

#### Example

The following program uses a `Gtk::AspectFrame` to present a drawing area whose aspect ratio will always be 2:1, no matter how the user resizes the top-level window.

Figure 8.4. AspectFrame

Source Code

File: `examplewindow.h` (For use with gtkmm 3, not gtkmm 2)

    #ifndef GTKMM_EXAMPLEWINDOW_H
    #define GTKMM_EXAMPLEWINDOW_H

    #include <gtkmm.h>

    class ExampleWindow : public Gtk::Window
    {
    public:
      ExampleWindow();
      virtual ~ExampleWindow();

    protected:

      //Child widgets:
      Gtk::AspectFrame m_AspectFrame;
      Gtk::DrawingArea m_DrawingArea;
    };

    #endif //GTKMM_EXAMPLEWINDOW_H

File: `main.cc` (For use with gtkmm 3, not gtkmm 2)

    #include "examplewindow.h"
    #include <gtkmm/application.h>

    int main(int argc, char *argv[])
    {
      auto app = Gtk::Application::create(argc, argv, "org.gtkmm.example");

      ExampleWindow window;

      //Shows the window and returns when it is closed.
      return app->run(window);
    }

File: `examplewindow.cc` (For use with gtkmm 3, not gtkmm 2)

    #include "examplewindow.h"

    ExampleWindow::ExampleWindow()
    : m_AspectFrame("2x1", /* label */
        Gtk::ALIGN_CENTER, /* center x */
        Gtk::ALIGN_CENTER, /* center y */
        2.0, /* xsize/ysize = 2 */
        false /* ignore child's aspect */)
    {
      set_title("Aspect Frame");
      set_border_width(10);

      // Add a child widget to the aspect frame */
      // Ask for a 200x200 window, but the AspectFrame will give us a 200x100
      // window since we are forcing a 2x1 aspect ratio */
      m_DrawingArea.set_size_request(200, 200);
      m_AspectFrame.add(m_DrawingArea);

      // Add the aspect frame to our toplevel window:
      add(m_AspectFrame);

      show_all_children();
    }

    ExampleWindow::~ExampleWindow()
    {
    }

### Alignment

The `Alignment` widget allows you to place a widget at a position and size relative to the size of the `Alignment` widget itself. For instance, it might be used to center a widget.

The `Alignment` widget is deprecated from gtkmm version 3.14 and should not be used in newly-written code. Use `Gtk::Widget`'s alignment and margin methods instead.

#### Example

This example right-aligns a button in a window by using `Gtk::Widget::set_halign()`.

Figure 8.5. Alignment

Source Code

File: `examplewindow.h` (For use with gtkmm 3, not gtkmm 2)

    #ifndef GTKMM_EXAMPLEWINDOW_H
    #define GTKMM_EXAMPLEWINDOW_H

    #include <gtkmm.h>

    class ExampleWindow : public Gtk::Window
    {
    public:
      ExampleWindow();
      virtual ~ExampleWindow();

    protected:
      //Signal handlers:
      void on_button_clicked();

      //Child widgets:
      Gtk::Button m_Button;
    };

    #endif //GTKMM_EXAMPLEWINDOW_H

File: `main.cc` (For use with gtkmm 3, not gtkmm 2)

    #include "examplewindow.h"
    #include <gtkmm/application.h>

    int main(int argc, char *argv[])
    {
      auto app = Gtk::Application::create(argc, argv, "org.gtkmm.example");

      ExampleWindow window;

      //Shows the window and returns when it is closed.
      return app->run(window);
    }

File: `examplewindow.cc` (For use with gtkmm 3, not gtkmm 2)

    #include "examplewindow.h"

    ExampleWindow::ExampleWindow()
    : m_Button("_Close", /* mnemonic= */ true)
    {
      set_title("Alignment");
      set_border_width(10);
      set_default_size(200, 50);

      m_Button.set_halign(Gtk::ALIGN_END);
      m_Button.set_valign(Gtk::ALIGN_CENTER);
      add(m_Button);

      m_Button.signal_clicked().connect( sigc::mem_fun(*this,
                  &ExampleWindow::on_button_clicked) );

      show_all_children();
    }

    ExampleWindow::~ExampleWindow()
    {
    }

    void ExampleWindow::on_button_clicked()
    {
      hide();
    }

See the ProgressBar section for another example that uses `set_halign()`.

Tooltips |  | Multiple-item widgets