带有条目的组合框

通过指定ComboBox构造函数的has_entry参数为true,可以令其包含一个Entry部件用以输入任意文本。

11.6.1. 文本列

为了使Entry可以与下拉列表的选项进行交互,你必须使用set_entry_text_column()指定哪些模型列是文本列。例如:

m_combo.set_entry_text_column(m_columns.m_col_name);

当你从下拉菜单中选择一个选项的时候,该列的值将被放于Entry中。

11.6.2. 条目

由于用户可以输入任意文本,被使用中的模型行可能不足以告诉我们用户输入了什么,因此,你应该使用ComboBox::get_entry()方法检索组合框中的Entry部件并在其上调用get_text()

11.6.3. 响应变更

当用户输入任意文本时,只连接到为每个键入字符而发出的changed信号可能是不够的。当用户按下Enter键的时候,这个信号不会被发出。而按下Enter或是键盘焦点被移动到另一个部件的时候,可以表示用户已经完成了输入。为了收到这些事件的通知,你需要连接到Entryactivatefocus_out_event信号像是这样

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) );

  entry->signal_focus_out_event().connect(sigc::mem_fun(*this,
    &ExampleWindow::on_entry_focus_out_event) );
}
每次更改ComboBoxEntry都会发出changed信号,你可以连接它们中的任意一个。但是在这里只有Entryfocus_out_event信号才有用。

X事件在附录的X事件信号小节有更详细的描述。

11.6.4. 完整例子

Figure 11-3带条目的组合框

源代码

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>

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

protected:
  //Signal handlers:
  void on_combo_changed();
  void on_entry_changed();
  void on_entry_has_focus_changed();

  //Signal connection:
  sigc::connection m_ConnectionHasFocusChanged;
  bool m_entry_had_focus {false};

  //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: 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);
}

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

#include "examplewindow.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:
  m_Combo.signal_changed().connect(sigc::mem_fun(*this, &ExampleWindow::on_combo_changed));

  auto entry = m_Combo.get_entry();
  if (entry)
  {
    entry->signal_changed().connect(sigc::mem_fun(*this,
      &ExampleWindow::on_entry_changed) );
    m_ConnectionHasFocusChanged = entry->property_has_focus().signal_changed().
      connect(sigc::mem_fun(*this, &ExampleWindow::on_entry_has_focus_changed));
  }
  else
    std::cout << "No Entry ???" << std::endl;
}

ExampleWindow::~ExampleWindow()
{
  // The has_focus changed 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_ConnectionHasFocusChanged.disconnect();
}

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

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;
  }
}

void ExampleWindow::on_entry_has_focus_changed()
{
  auto entry = m_Combo.get_entry();
  if (entry)
  {
    const bool entry_has_focus = entry->has_focus();
    if (m_entry_had_focus && !entry_has_focus)
    {
      // entry->has_focus() has changed from true to false; entry has lost focus.
      std::cout << "on_entry_has_focus_changed() to not focused: Row="
        << m_Combo.get_active_row_number() << ", ID=" << entry->get_text() << std::endl;
    }
    m_entry_had_focus = entry_has_focus;
  }
}

11.6.5. 简单文字示例

Figure 11-4带条目的文本组合框

源代码

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

#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();
  void on_entry_changed();
  void on_entry_has_focus_changed();

  //Signal connection:
  sigc::connection m_ConnectionHasFocusChanged;
  bool m_entry_had_focus {false};

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

#endif //GTKMM_EXAMPLEWINDOW_H

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);
}

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

#include "examplewindow.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:
  m_Combo.signal_changed().connect(sigc::mem_fun(*this,
    &ExampleWindow::on_combo_changed) );

  auto entry = m_Combo.get_entry();
  if (entry)
  {
    entry->signal_changed().connect(sigc::mem_fun(*this,
      &ExampleWindow::on_entry_changed));
    m_ConnectionHasFocusChanged = entry->property_has_focus().signal_changed().
      connect(sigc::mem_fun(*this, &ExampleWindow::on_entry_has_focus_changed));
  }
  else
    std::cout << "No Entry ???" << std::endl;

  m_Combo.property_has_frame() = false;
}

ExampleWindow::~ExampleWindow()
{
  // The has_focus changed 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_ConnectionHasFocusChanged.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;
}

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

void ExampleWindow::on_entry_has_focus_changed()
{
  auto entry = m_Combo.get_entry();
  if (entry)
  {
    const bool entry_has_focus = entry->has_focus();
    if (m_entry_had_focus && !entry_has_focus)
    {
      // entry->has_focus() has changed from true to false; entry has lost focus.
      std::cout << "on_entry_has_focus_changed() to not focused: Row="
        << m_Combo.get_active_row_number() << ", ID=" << entry->get_text() << std::endl;
    }
    m_entry_had_focus = entry_has_focus;
  }
}