A ComboBox
may contain an Entry
widget for entering of arbitrary text, by specifying true
for the constructor's has_entry
parameter.
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
.
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.
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 key_press_event
and
focus_out_event
signals, like so
Gtk::Entry* entry = m_Combo.get_entry(); if (entry) { // The Entry shall receive key-press events and focus-out events. entry->add_events(Gdk::KEY_PRESS_MASK | Gdk::FOCUS_CHANGE_MASK); // Alternatively you can connect to m_Combo.signal_changed(). entry->signal_changed().connect(sigc::mem_fun(*this, &ExampleWindow::on_entry_changed) ); // This signal handler must be called before the default signal handler, // or else it will not be called, if the default signal handler returns true. entry->signal_key_press_event().connect(sigc::mem_fun(*this, &ExampleWindow::on_entry_key_press_event), /* after= */ false ); entry->signal_focus_out_event().connect(sigc::mem_fun(*this, &ExampleWindow::on_entry_focus_out_event) ); }
The changed
signals of ComboBox
and
Entry
are both emitted for every change. It doesn't matter
which one you connect to. But only Entry
's
key_press_event
and focus_out_event
signals
are useful here.
In key_press_event
's signal handler you must check which key
has been pressed, e.g. like so
bool ExampleWindow::on_entry_key_press_event(GdkEventKey* event) { Gtk::Entry* entry = m_Combo.get_entry(); if (entry) { if (event->keyval == GDK_KEY_Return || event->keyval == GDK_KEY_ISO_Enter || event->keyval == GDK_KEY_KP_Enter) { input_finished(); //Your own function. return true; } } return false; }
X events are described in more detail in the Keyboard Events chapter and the X Event signals section in the appendix.
File: examplewindow.h
(For use with gtkmm 3, not gtkmm 2)
#ifndef GTKMM_EXAMPLEWINDOW_H #define GTKMM_EXAMPLEWINDOW_H #include <gtkmm/window.h> #include <gtkmm/combobox.h> #include <gtkmm/liststore.h> class ExampleWindow : public Gtk::Window { public: ExampleWindow(); virtual ~ExampleWindow(); protected: //Signal handlers: void on_entry_changed(); bool on_entry_key_press_event(GdkEventKey* event); bool on_entry_focus_out_event(GdkEventFocus* event); //Signal connection: sigc::connection m_ConnectionFocusOut; //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 3, not gtkmm 2)
#include "examplewindow.h" #include <gtkmm/stock.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: Gtk::TreeModel::Row row = *(m_refTreeModel->append()); row[m_Columns.m_col_id] = "1"; row[m_Columns.m_col_name] = "Billy Bob"; /* Gtk::TreeModel::Row 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); m_Combo.pack_start(m_Columns.m_col_name); m_Combo.set_entry_text_column(m_Columns.m_col_id); //Add the ComboBox to the window. add(m_Combo); //Connect signal handlers: Gtk::Entry* entry = m_Combo.get_entry(); if (entry) { // The Entry shall receive key-press events and focus-out events. entry->add_events(Gdk::KEY_PRESS_MASK | Gdk::FOCUS_CHANGE_MASK); // Alternatively you can connect to m_Combo.signal_changed(). entry->signal_changed().connect(sigc::mem_fun(*this, &ExampleWindow::on_entry_changed) ); // This signal handler must be called before the default signal handler, // or else it will not be called, if the default signal handler returns true. entry->signal_key_press_event().connect(sigc::mem_fun(*this, &ExampleWindow::on_entry_key_press_event), false ); m_ConnectionFocusOut = entry->signal_focus_out_event(). connect(sigc::mem_fun(*this, &ExampleWindow::on_entry_focus_out_event) ); } else std::cout << "No Entry ???" << std::endl; show_all_children(); } ExampleWindow::~ExampleWindow() { // The focus_out 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_ConnectionFocusOut.disconnect(); } void ExampleWindow::on_entry_changed() { Gtk::Entry* 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; } } bool ExampleWindow::on_entry_key_press_event(GdkEventKey* event) { Gtk::Entry* entry = m_Combo.get_entry(); if (entry) { if (event->keyval == GDK_KEY_Return || event->keyval == GDK_KEY_ISO_Enter || event->keyval == GDK_KEY_KP_Enter) { std::cout << "on_entry_key_press_event(): Row=" << m_Combo.get_active_row_number() << ", ID=" << entry->get_text() << std::endl; return true; } } return false; } bool ExampleWindow::on_entry_focus_out_event(GdkEventFocus* /* event */) { Gtk::Entry* entry = m_Combo.get_entry(); if (entry) { std::cout << "on_entry_focus_out_event(): Row=" << m_Combo.get_active_row_number() << ", ID=" << entry->get_text() << std::endl; return true; } return false; }
File: main.cc
(For use with gtkmm 3, not gtkmm 2)
#include "examplewindow.h" #include <gtkmm/application.h> int main(int argc, char *argv[]) { Glib::RefPtr<Gtk::Application> 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.h
(For use with gtkmm 3, not gtkmm 2)
#ifndef GTKMM_EXAMPLEWINDOW_H #define GTKMM_EXAMPLEWINDOW_H #include <gtkmm/window.h> #include <gtkmm/comboboxtext.h> class ExampleWindow : public Gtk::Window { public: ExampleWindow(); virtual ~ExampleWindow(); protected: //Signal handlers: void on_combo_changed(); bool on_entry_key_press_event(GdkEventKey* event); bool on_entry_focus_out_event(GdkEventFocus* event); //Signal connection: sigc::connection m_ConnectionFocusOut; //Child widgets: Gtk::ComboBoxText m_Combo; }; #endif //GTKMM_EXAMPLEWINDOW_H
File: examplewindow.cc
(For use with gtkmm 3, not gtkmm 2)
#include "examplewindow.h" #include <gtkmm/stock.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"); add(m_Combo); //Connect signal handlers: Gtk::Entry* 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) { // The Entry shall receive key-press events and focus-out events. entry->add_events(Gdk::KEY_PRESS_MASK | Gdk::FOCUS_CHANGE_MASK); // This signal handler must be called before the default signal handler, // or else it will not be called, if the default signal handler returns true. entry->signal_key_press_event().connect(sigc::mem_fun(*this, &ExampleWindow::on_entry_key_press_event), false ); m_ConnectionFocusOut = entry->signal_focus_out_event(). connect(sigc::mem_fun(*this, &ExampleWindow::on_entry_focus_out_event) ); } else std::cout << "No Entry ???" << std::endl; m_Combo.property_has_frame() = false; show_all_children(); } ExampleWindow::~ExampleWindow() { // The focus_out 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_ConnectionFocusOut.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; } bool ExampleWindow::on_entry_key_press_event(GdkEventKey* event) { if (event->keyval == GDK_KEY_Return || event->keyval == GDK_KEY_ISO_Enter || event->keyval == GDK_KEY_KP_Enter) { std::cout << "on_entry_key_press_event(): Row=" << m_Combo.get_active_row_number() << ", Text=" << m_Combo.get_active_text() << std::endl; return true; } return false; } bool ExampleWindow::on_entry_focus_out_event(GdkEventFocus* /* event */) { std::cout << "on_entry_focus_out_event(): Row=" << m_Combo.get_active_row_number() << ", Text=" << m_Combo.get_active_text() << std::endl; return true; }
File: main.cc
(For use with gtkmm 3, not gtkmm 2)
#include "examplewindow.h" #include <gtkmm/application.h> int main(int argc, char *argv[]) { Glib::RefPtr<Gtk::Application> app = Gtk::Application::create(argc, argv, "org.gtkmm.example"); ExampleWindow window; //Shows the window and returns when it is closed. return app->run(window); }