[gtkmm-documentation] Improve the Entry and ComboBox with Entry sections.



commit 217a3420343777130b57ef6125d392044d3a9afc
Author: Kjell Ahlstedt <kjell ahlstedt bredband net>
Date:   Mon Apr 2 10:10:42 2012 +0200

    Improve the Entry and ComboBox with Entry sections.
    
    * docs/tutorial/C/gtkmm-tutorial-in.xml: Don't mention the deprecated
    Entry::signal_activate(). Add description of Entry::signal_key_press_event()
    and Entry::signal_focus_out_event().
    * examples/book/combobox/entry_complex/examplewindow.[h|cc]:
    * examples/book/combobox/entry_text/examplewindow.[h|cc]:
    Add on_entry_key_press_event() and on_entry_focus_out_event(). Bug #655489.

 ChangeLog                                          |   11 ++
 docs/tutorial/C/gtkmm-tutorial-in.xml              |  111 +++++++++++++++++---
 .../book/combobox/entry_complex/examplewindow.cc   |   63 ++++++++++--
 .../book/combobox/entry_complex/examplewindow.h    |    7 +-
 examples/book/combobox/entry_text/examplewindow.cc |   48 ++++++++-
 examples/book/combobox/entry_text/examplewindow.h  |    5 +
 6 files changed, 213 insertions(+), 32 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index 958a3cc..de0795d 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+2012-04-02  Kjell Ahlstedt <kjell ahlstedt bredband net>
+
+	Improve the Entry and ComboBox with Entry sections.
+
+	* docs/tutorial/C/gtkmm-tutorial-in.xml: Don't mention the deprecated
+	Entry::signal_activate(). Add description of Entry::signal_key_press_event()
+	and Entry::signal_focus_out_event().
+	* examples/book/combobox/entry_complex/examplewindow.[h|cc]:
+	* examples/book/combobox/entry_text/examplewindow.[h|cc]:
+	Add on_entry_key_press_event() and on_entry_focus_out_event(). Bug #655489.
+
 2012-03-26  Murray Cumming  <murrayc murrayc com>
 
 	Fix the --enable-warnings=fatal build with latest gtkmm.
diff --git a/docs/tutorial/C/gtkmm-tutorial-in.xml b/docs/tutorial/C/gtkmm-tutorial-in.xml
index 2987846..b4ff15d 100644
--- a/docs/tutorial/C/gtkmm-tutorial-in.xml
+++ b/docs/tutorial/C/gtkmm-tutorial-in.xml
@@ -1221,12 +1221,31 @@ echoed on the screen, calling <methodname>set_visibility()</methodname> with
 
 <para>
 You might want to be notified whenever the user types in a text entry widget.
-<classname>Gtk::Entry</classname> provides two signals,
-<literal>activate</literal> and <literal>changed</literal>, for just this
-purpose. <literal>activate</literal> is emitted when the user presses the
-enter key in a text-entry widget; <literal>changed</literal> is emitted when
-the text in the widget changes. You can use these, for instance, to validate
-or filter the text the user types.
+<classname>Gtk::Entry</classname> provides the <literal>changed</literal> signal
+for this purpose. It is emitted when the text in the widget changes.
+You can use it, for instance, to validate or filter the text the user types.
+</para>
+
+<para>
+The <literal>changed</literal> signal is not emitted when the user presses the
+Enter key. If a pressed Enter key signals that the user has finished entering text,
+you probably want to be notified. For this purpose, use the
+<literal>key_press_event</literal> signal that <classname>Gtk::Entry</classname>
+inherits from <classname>Gtk::Widget</classname>. Moving the keyboard focus to
+another widget may also signal that the user has finished entering text. The
+<literal>focus_out_event</literal> signal can notify you when that happens.
+The <link linkend="sec-comboboxentry">ComboBox with an Entry</link> section
+contains example programs that use these signals.
+</para>
+
+<para>
+If you pass <literal>true</literal> to the <methodname>set_activates_default()</methodname>
+method, pressing Enter in the <classname>Gtk::Entry</classname> will activate
+the default widget for the window containing the <classname>Gtk::Entry</classname>.
+This is especially useful in dialog boxes. The default widget is usually one of
+the dialog buttons, which e.g. will close the dialog box. To set a widget as the
+default widget, use <methodname>Gtk::Widget::set_can_default()</methodname> and
+<methodname>Gtk::Widget::grab_default()</methodname>.
 </para>
 
 <para><ulink url="&url_refdocs_base_gtk;Entry.html">Reference</ulink></para>
@@ -2960,7 +2979,7 @@ section.
 <chapter id="chapter-combobox">
 <title>Combo Boxes</title>
 
-<para>The <classname>ComboBox</classname> widgets offers a list (or tree) of choices in a dropdown menu. If appropriate, it can show extra information about each item, such as text, a picture, a checkbox, or a progress bar. The <classname>ComboBox</classname> widget usually restricts the user to the available choices, but it can optionally have an <classname>Entry</classname>, allowing the user to enter arbitrary text if the none of the available choices are suitable.
+<para>The <classname>ComboBox</classname> widget offers a list (or tree) of choices in a dropdown menu. If appropriate, it can show extra information about each item, such as text, a picture, a checkbox, or a progress bar. The <classname>ComboBox</classname> widget usually restricts the user to the available choices, but it can optionally have an <classname>Entry</classname>, allowing the user to enter arbitrary text if none of the available choices are suitable.
 </para>
 
 <para>The list is provided via a <classname>TreeModel</classname>, and columns from this model are added to the ComboBox's view with the <methodname>ComboBox::pack_start()</methodname> method. This provides flexibility and compile-time type-safety, but the <classname>ComboBoxText</classname> class provides a simpler text-based specialization in case that flexibility is not required.
@@ -2970,7 +2989,7 @@ section.
 
 <sect1 id="sec-combobox-model">
 <title>The model</title>
-<para>The model for a ComboBox can be defined and filled exactly as for a <classname>TreeView</classname>. For instance, you might derive a ComboBox class with one integer and one text columns, like so:
+<para>The model for a ComboBox can be defined and filled exactly as for a <classname>TreeView</classname>. For instance, you might derive a ComboBox class with one integer and one text column, like so:
 </para>
 <programlisting>ModelColumns()
 { add(m_col_id); add(m_col_name); }
@@ -2981,7 +3000,7 @@ section.
 
 ModelColumns m_columns;</programlisting>
 
-<para>After appending rows to this model, you should provide the model to the <classname>ComboBox</classname> with the <methodname>set_model()</methodname> method. Then use the <methodname>pack_start()</methodname> or <methodname>pack_end()</methodname> methods to specify what methods will be displayed in the ComboBox. As with the TreeView you may either use the default cell renderer by passing the <classname>TreeModelColumn</classname> to the pack methods, or you may instantiate a specific <classname>CellRenderer</classname> and specify a particular mapping with either <methodname>add_attribute()</methodname> or <methodname>set_cell_data_func()</methodname>. Note that these methods are in the <classname>CellLayout</classname> base class.</para>
+<para>After appending rows to this model, you should provide the model to the <classname>ComboBox</classname> with the <methodname>set_model()</methodname> method. Then use the <methodname>pack_start()</methodname> or <methodname>pack_end()</methodname> methods to specify what columns will be displayed in the ComboBox. As with the TreeView you may either use the default cell renderer by passing the <classname>TreeModelColumn</classname> to the pack methods, or you may instantiate a specific <classname>CellRenderer</classname> and specify a particular mapping with either <methodname>add_attribute()</methodname> or <methodname>set_cell_data_func()</methodname>. Note that these methods are in the <classname>CellLayout</classname> base class.</para>
 </sect1>
 
 <sect1 id="sec-combobox-get">
@@ -3005,7 +3024,7 @@ else
 <sect1 id="sec-combobox-changes">
 <title>Responding to changes</title>
 <para>
-You might need to react to every change of selection in the ComboBox, for instance to update other widgets. To do so, you should handle the &quot;changed&quot; signal. For instance:
+You might need to react to every change of selection in the ComboBox, for instance to update other widgets. To do so, you should handle the <literal>changed</literal> signal. For instance:
 </para>
 <programlisting>m_combo.signal_changed().connect( sigc::mem_fun(*this,
       &amp;ExampleWindow::on_combo_changed) );</programlisting>
@@ -3027,7 +3046,7 @@ You might need to react to every change of selection in the ComboBox, for instan
 <sect1 id="combobox-example-simple"><title>Simple Text Example</title>
 
 <figure id="figure-combobox-text">
-  <title>ComboBox</title>
+  <title>ComboBoxText</title>
   <screenshot>
     <graphic format="PNG" fileref="&url_figures_base;combobox_text.png"/>
   </screenshot>
@@ -3040,12 +3059,12 @@ You might need to react to every change of selection in the ComboBox, for instan
 <sect1 id="sec-comboboxentry">
 <title>ComboBox with an Entry</title>
 
-<para>A <classname>ComboBox</classname> may contain an <classname>Entry</classname> widget for entering of arbitrary text, by specifying true for the constructor's <literal>has_entry</literal> parameter.</para>
+<para>A <classname>ComboBox</classname> may contain an <classname>Entry</classname> widget for entering of arbitrary text, by specifying <literal>true</literal> for the constructor's <literal>has_entry</literal> parameter.</para>
 
 <sect2 id="sec-comboboxentry-text-column">
 <title>The text column</title>
-<para>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 <methodname>set_text_column()</methodname>. For instance:
-<programlisting>m_combo.set_text_column(m_columns.m_col_name);</programlisting>
+<para>So that the <classname>Entry</classname> can interact with the drop-down list of choices, you must specify which of your model columns is the text column, with <methodname>set_entry_text_column()</methodname>. For instance:
+<programlisting>m_combo.set_entry_text_column(m_columns.m_col_name);</programlisting>
 </para>
 <para>
 When you select a choice from the drop-down menu, the value from this column will be placed in the <classname>Entry</classname>.
@@ -3054,14 +3073,72 @@ When you select a choice from the drop-down menu, the value from this column wil
 
 <sect2 id="sec-comboboxentry-model">
 <title>The entry</title>
-<para>Because the user may enter arbitrary text, an active model row isn't enough to tell us what text the user has inputted. Therefore, you should retrieve the <classname>Entry</classname> widget with the <methodname>ComboBoxEntry::get_entry()</methodname> method and call <methodname>get_text()</methodname> on that.
+<para>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 <classname>Entry</classname> widget with the <methodname>ComboBox::get_entry()</methodname> method and call <methodname>get_text()</methodname> on that.
+</para>
+</sect2>
+
+<sect2 id="sec-comboboxentry-changes">
+<title>Responding to changes</title>
+<para>
+When the user enters arbitrary text, it may not be enough to connect to the
+<literal>changed</literal> 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
+<classname>Entry</classname>'s <literal>key_press_event</literal> and
+<literal>focus_out_event</literal> signals, like so
+<programlisting>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,
+    &amp;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,
+    &amp;ExampleWindow::on_entry_key_press_event), /* after= */ false );
+
+  entry->signal_focus_out_event().connect(sigc::mem_fun(*this,
+    &amp;ExampleWindow::on_entry_focus_out_event) );
+}</programlisting>
+The <literal>changed</literal> signals of <classname>ComboBox</classname> and
+<classname>Entry</classname> are both emitted for every change. It doesn't matter
+which one you connect to. But only <classname>Entry</classname>'s
+<literal>key_press_event</literal> and <literal>focus_out_event</literal> signals
+are useful here.
+</para>
+<para>
+In <literal>key_press_event</literal>'s signal handler you must check which key
+has been pressed, e.g. like so
+<programlisting>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;
+}</programlisting>
+X events are described in more detail in the
+<link linkend="chapter-keyboardevents">Keyboard Events</link> chapter and the
+<link linkend="sec-xeventsignals">X Event signals</link> section in the appendix.
 </para>
 </sect2>
 
 <sect2 id="comboboxentry-example-full"><title>Full Example</title>
 
 <figure id="figure-comboboxentry-complex">
-  <title>ComboBoxEntry</title>
+  <title>ComboBox with Entry</title>
   <screenshot>
     <graphic format="PNG" fileref="&url_figures_base;comboboxentry_complex.png"/>
   </screenshot>
@@ -3074,7 +3151,7 @@ When you select a choice from the drop-down menu, the value from this column wil
 <sect2 id="comboboxentry-example-simple"><title>Simple Text Example</title>
 
 <figure id="figure-comboboxentry-text">
-  <title>ComboBoxEntryText</title>
+  <title>ComboBoxText with Entry</title>
   <screenshot>
     <graphic format="PNG" fileref="&url_figures_base;comboboxentry_text.png"/>
   </screenshot>
diff --git a/examples/book/combobox/entry_complex/examplewindow.cc b/examples/book/combobox/entry_complex/examplewindow.cc
index b2b3a7b..0609ea3 100644
--- a/examples/book/combobox/entry_complex/examplewindow.cc
+++ b/examples/book/combobox/entry_complex/examplewindow.cc
@@ -70,26 +70,71 @@ ExampleWindow::ExampleWindow()
   //Add the ComboBox to the window.
   add(m_Combo);
 
-  //Connect signal handler:
-  m_Combo.signal_changed().connect(sigc::mem_fun(*this,
-              &ExampleWindow::on_combo_changed) );
+  //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;
+  }
 }
 
-void ExampleWindow::on_combo_changed()
+bool ExampleWindow::on_entry_key_press_event(GdkEventKey* event)
 {
   Gtk::Entry* entry = m_Combo.get_entry();
-  //Note: to get changes only when the entry has been completed,
-  //instead of on every key press, connect to Entry::signal_changed()
-  //instead of ComboBoxEntry::signal_changed.
+  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;
+}
 
-  if(entry)
+bool ExampleWindow::on_entry_focus_out_event(GdkEventFocus* /* event */)
+{
+  Gtk::Entry* entry = m_Combo.get_entry();
+  if (entry)
   {
-    std::cout << " ID=" << entry->get_text() << std::endl;
+    std::cout << "on_entry_focus_out_event(): Row=" << m_Combo.get_active_row_number()
+      << ", ID=" << entry->get_text() << std::endl;
+    return true;
   }
+  return false;
 }
diff --git a/examples/book/combobox/entry_complex/examplewindow.h b/examples/book/combobox/entry_complex/examplewindow.h
index 335ca26..ca5fcc7 100644
--- a/examples/book/combobox/entry_complex/examplewindow.h
+++ b/examples/book/combobox/entry_complex/examplewindow.h
@@ -31,7 +31,12 @@ public:
 
 protected:
   //Signal handlers:
-  void on_combo_changed();
+  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
diff --git a/examples/book/combobox/entry_text/examplewindow.cc b/examples/book/combobox/entry_text/examplewindow.cc
index 325f52e..bd866b2 100644
--- a/examples/book/combobox/entry_text/examplewindow.cc
+++ b/examples/book/combobox/entry_text/examplewindow.cc
@@ -32,9 +32,24 @@ ExampleWindow::ExampleWindow()
 
   add(m_Combo);
 
-  //Connect signal handler:
+  //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) );
+    &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();
@@ -42,11 +57,34 @@ ExampleWindow::ExampleWindow()
 
 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()
 {
-  Glib::ustring text = m_Combo.get_active_text();
-  if(!(text.empty()))
-    std::cout << "Combo changed: " << text << std::endl;
+  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;
 }
diff --git a/examples/book/combobox/entry_text/examplewindow.h b/examples/book/combobox/entry_text/examplewindow.h
index a0c58f0..a3cda62 100644
--- a/examples/book/combobox/entry_text/examplewindow.h
+++ b/examples/book/combobox/entry_text/examplewindow.h
@@ -31,6 +31,11 @@ public:
 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;



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]