ComboBox with an Entry
| Chapter 13. Combo Boxes |

## ComboBox with an Entry

A `ComboBox` may contain an `Entry` widget for entering of arbitrary text, by specifying `true` for the constructor's `has_entry` parameter.

### The text column

So that the `Entry` can interact with the drop-down list of choices, you must specify which of your model columns is the text column, with `set_entry_text_column()`. For instance:

    m_combo.set_entry_text_column(m_columns.m_col_name);

When you select a choice from the drop-down menu, the value from this column will be placed in the `Entry`.

### The entry

Because the user may enter arbitrary text, an active model row isn't enough to tell us what text the user has entered. Therefore, you should retrieve the `Entry` widget with the `ComboBox::get_entry()` method and call `get_text()` on that.

### Responding to changes

When the user enters arbitrary text, it may not be enough to connect to the `changed` signal, which is emitted for every typed character. It is not emitted when the user presses the Enter key. Pressing the Enter key or moving the keyboard focus to another widget may signal that the user has finished entering text. To be notified of these events, connect to the `Entry`'s `activate` signal (available since gtkmm 4.8), and add a `Gtk::EventControllerFocus` and connect to its `leave` signal, like so

    auto entry = m_Combo.get_entry();
    if (entry)
    {
      // Alternatively you can connect to m_Combo.signal_changed().
      entry->signal_changed().connect(sigc::mem_fun(*this,
        &ExampleWindow::on_entry_changed));
      entry->signal_activate().connect(sigc::mem_fun(*this,
        &ExampleWindow::on_entry_activate));
      // The Entry shall receive focus-leave events.
      auto controller = Gtk::EventControllerFocus::create();
      m_ConnectionFocusLeave = controller->signal_leave().connect(
        sigc::mem_fun(*this, &ExampleWindow::on_entry_focus_leave));
      entry->add_controller(controller);
    }

The `changed` signals of `ComboBox` and `Entry` are both emitted for every change. It doesn't matter which one you connect to. But the `EventControllerFocus` must be added to the `Entry`.

### Full Example

Figure 13.3. ComboBox with Entry

Source Code

File: `examplewindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEWINDOW_H
    #define GTKMM_EXAMPLEWINDOW_H

    #include <gtkmm/window.h>
    #include <gtkmm/combobox.h>
    #include <gtkmm/liststore.h>
    #include <gtkmm/version.h>

    #define HAS_SIGNAL_ACTIVATE GTKMM_CHECK_VERSION(4,7,1)

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

    protected:
      //Signal handlers:
      void on_entry_changed();
    #if HAS_SIGNAL_ACTIVATE
      void on_entry_activate();
    #endif
      void on_entry_focus_leave();

      //Signal connection:
      sigc::connection m_ConnectionFocusLeave;

      //Tree model columns:
      class ModelColumns : public Gtk::TreeModel::ColumnRecord
      {
      public:

        ModelColumns()
        { add(m_col_id); add(m_col_name); }

        Gtk::TreeModelColumn<Glib::ustring> m_col_id; //The data to choose - this must be text.
        Gtk::TreeModelColumn<Glib::ustring> m_col_name;
      };

      ModelColumns m_Columns;

      //Child widgets:
      Gtk::ComboBox m_Combo;
      Glib::RefPtr<Gtk::ListStore> m_refTreeModel;
    };

    #endif //GTKMM_EXAMPLEWINDOW_H

File: `examplewindow.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <gtkmm/eventcontrollerfocus.h>
    #include <iostream>

    ExampleWindow::ExampleWindow()
    : m_Combo(true /* has_entry */)
    {
      set_title("ComboBox example");

      //Create the Tree model:
      //m_refTreeModel = Gtk::TreeStore::create(m_Columns);
      m_refTreeModel = Gtk::ListStore::create(m_Columns);
      m_Combo.set_model(m_refTreeModel);

      //Fill the ComboBox's Tree Model:
      auto row = *(m_refTreeModel->append());
      row[m_Columns.m_col_id] = "1";
      row[m_Columns.m_col_name] = "Billy Bob";
      /*
      auto childrow = *(m_refTreeModel->append(row.children()));
      childrow[m_Columns.m_col_id] = 11;
      childrow[m_Columns.m_col_name] = "Billy Bob Junior";

      childrow = *(m_refTreeModel->append(row.children()));
      childrow[m_Columns.m_col_id] = 12;
      childrow[m_Columns.m_col_name] = "Sue Bob";
      */

      row = *(m_refTreeModel->append());
      row[m_Columns.m_col_id] = "2";
      row[m_Columns.m_col_name] = "Joey Jojo";

      row = *(m_refTreeModel->append());
      row[m_Columns.m_col_id] = "3";
      row[m_Columns.m_col_name] = "Rob McRoberts";

      /*
      childrow = *(m_refTreeModel->append(row.children()));
      childrow[m_Columns.m_col_id] = 31;
      childrow[m_Columns.m_col_name] = "Xavier McRoberts";
      */

      //Add the model columns to the Combo (which is a kind of view),
      //rendering them in the default way:
      //This is automatically rendered when we use set_entry_text_column().
      //m_Combo.pack_start(m_Columns.m_col_id, Gtk::PackOptions::EXPAND_WIDGET);
      m_Combo.pack_start(m_Columns.m_col_name);

      m_Combo.set_entry_text_column(m_Columns.m_col_id);
      m_Combo.set_active(1);

      //Add the ComboBox to the window.
      set_child(m_Combo);

      //Connect signal handlers:
      auto entry = m_Combo.get_entry();
      if (entry)
      {
        // Alternatively you can connect to m_Combo.signal_changed().
        entry->signal_changed().connect(sigc::mem_fun(*this,
          &ExampleWindow::on_entry_changed));
    #if HAS_SIGNAL_ACTIVATE
        entry->signal_activate().connect(sigc::mem_fun(*this,
          &ExampleWindow::on_entry_activate));
    #endif
        // The Entry shall receive focus-leave events.
        auto controller = Gtk::EventControllerFocus::create();
        m_ConnectionFocusLeave = controller->signal_leave().connect(
          sigc::mem_fun(*this, &ExampleWindow::on_entry_focus_leave));
        entry->add_controller(controller);
      }
      else
        std::cout << "No Entry ???" << std::endl;
    }

    ExampleWindow::~ExampleWindow()
    {
      // The focus-leave signal may be emitted while m_Combo is being destructed.
      // The signal handler can generate critical messages, if it's called when
      // m_Combo has been partly destructed.
      m_ConnectionFocusLeave.disconnect();
    }

    void ExampleWindow::on_entry_changed()
    {
      auto entry = m_Combo.get_entry();
      if (entry)
      {
        std::cout << "on_entry_changed(): Row=" << m_Combo.get_active_row_number()
          << ", ID=" << entry->get_text() << std::endl;
      }
    }

    #if HAS_SIGNAL_ACTIVATE
    void ExampleWindow::on_entry_activate()
    {
      auto entry = m_Combo.get_entry();
      if (entry)
      {
        std::cout << "on_entry_activate(): Row=" << m_Combo.get_active_row_number()
          << ", ID=" << entry->get_text() << std::endl;
      }
    }
    #endif

    void ExampleWindow::on_entry_focus_leave()
    {
      auto entry = m_Combo.get_entry();
      if (entry)
      {
        std::cout << "on_entry_focus_leave(): Row=" << m_Combo.get_active_row_number()
          << ", ID=" << entry->get_text() << std::endl;
      }
    }

File: `main.cc` (For use with gtkmm 4)

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

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

      //Shows the window and returns when it is closed.
      return app->make_window_and_run<ExampleWindow>(argc, argv);
    }

### Simple Text Example

Figure 13.4. ComboBoxText with Entry

Source Code

File: `examplewindow.h` (For use with gtkmm 4)

    #ifndef GTKMM_EXAMPLEWINDOW_H
    #define GTKMM_EXAMPLEWINDOW_H

    #include <gtkmm/window.h>
    #include <gtkmm/comboboxtext.h>
    #include <gtkmm/version.h>

    #define HAS_SIGNAL_ACTIVATE GTKMM_CHECK_VERSION(4,7,1)

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

    protected:
      //Signal handlers:
      void on_combo_changed();
    #if HAS_SIGNAL_ACTIVATE
      void on_entry_activate();
    #endif
      void on_entry_focus_leave();

      //Signal connection:
      sigc::connection m_ConnectionFocusLeave;

      //Child widgets:
      Gtk::ComboBoxText m_Combo;
    };

    #endif //GTKMM_EXAMPLEWINDOW_H

File: `examplewindow.cc` (For use with gtkmm 4)

    #include "examplewindow.h"
    #include <gtkmm/eventcontrollerfocus.h>
    #include <iostream>

    ExampleWindow::ExampleWindow()
    : m_Combo(true /* has_entry */)
    {
      set_title("ComboBoxText example");

      //Fill the combo:
      m_Combo.append("something");
      m_Combo.append("something else");
      m_Combo.append("something or other");
      m_Combo.set_active(0);

      set_child(m_Combo);

      //Connect signal handlers:
      auto entry = m_Combo.get_entry();
      // Alternatively you can connect to entry->signal_changed().
      m_Combo.signal_changed().connect(sigc::mem_fun(*this,
        &ExampleWindow::on_combo_changed) );

      if (entry)
      {
    #if HAS_SIGNAL_ACTIVATE
        entry->signal_activate().connect(sigc::mem_fun(*this,
          &ExampleWindow::on_entry_activate));
    #endif
        // The Entry shall receive focus-leave events.
        auto controller = Gtk::EventControllerFocus::create();
        m_ConnectionFocusLeave = controller->signal_leave().connect(
          sigc::mem_fun(*this, &ExampleWindow::on_entry_focus_leave));
        entry->add_controller(controller);
      }
      else
        std::cout << "No Entry ???" << std::endl;

      m_Combo.property_has_frame() = false;
    }

    ExampleWindow::~ExampleWindow()
    {
      // The focus-leave signal may be emitted while m_Combo is being destructed.
      // The signal handler can generate critical messages, if it's called when
      // m_Combo has been partly destructed.
      m_ConnectionFocusLeave.disconnect();
    }

    void ExampleWindow::on_combo_changed()
    {
      std::cout << "on_combo_changed(): Row=" << m_Combo.get_active_row_number()
        << ", Text=" << m_Combo.get_active_text() << std::endl;
    }

    #if HAS_SIGNAL_ACTIVATE
    void ExampleWindow::on_entry_activate()
    {
      std::cout << "on_entry_activate(): Row=" << m_Combo.get_active_row_number()
        << ", Text=" << m_Combo.get_active_text() << std::endl;
    }
    #endif

    void ExampleWindow::on_entry_focus_leave()
    {
      std::cout << "on_entry_focus_leave(): Row=" << m_Combo.get_active_row_number()
        << ", Text=" << m_Combo.get_active_text() << std::endl;
    }

File: `main.cc` (For use with gtkmm 4)

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

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

      //Shows the window and returns when it is closed.
      return app->make_window_and_run<ExampleWindow>(argc, argv);
    }

Simple Text Example |  | Chapter 14. TextView