summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBastien Nocera <hadess@hadess.net>2013-05-21 17:26:37 +0200
committerBastien Nocera <hadess@hadess.net>2013-05-31 17:09:11 +0200
commitdbbea8b97e1db0d001db99316f67483730c786e1 (patch)
tree4eb3e66784d2fb789ff51d6abea7ed4dcd97ad95
parent154a5e55ab4b9c0b5a8b0d665148a93a5d4ebfec (diff)
downloadgtk+-dbbea8b97e1db0d001db99316f67483730c786e1.tar.gz
Add GtkSearchBar widget
This widget is a toolbar that will popup automatically when searches should be started, and dismissed when they are finished. https://bugzilla.gnome.org/show_bug.cgi?id=700787
-rw-r--r--demos/gtk-demo/search_entry2.c68
-rw-r--r--docs/reference/gtk/gtk-docs.sgml1
-rw-r--r--docs/reference/gtk/gtk3-sections.txt17
-rw-r--r--docs/reference/gtk/gtk3.types.in1
-rw-r--r--gtk/Makefile.am3
-rw-r--r--gtk/gtk.gresource.xml1
-rw-r--r--gtk/gtk.h1
-rw-r--r--gtk/gtksearchbar.c552
-rw-r--r--gtk/gtksearchbar.h98
-rw-r--r--gtk/gtksearchbar.ui118
10 files changed, 834 insertions, 26 deletions
diff --git a/demos/gtk-demo/search_entry2.c b/demos/gtk-demo/search_entry2.c
index cdadc78ba4..a53c82f1b7 100644
--- a/demos/gtk-demo/search_entry2.c
+++ b/demos/gtk-demo/search_entry2.c
@@ -26,44 +26,64 @@ search_changed_cb (GtkSearchEntry *entry,
gtk_label_set_text (result_label, text ? text : "");
}
+static gboolean
+window_key_press_event_cb (GtkWidget *widget,
+ GdkEvent *event,
+ GtkSearchBar *bar)
+{
+ return gtk_search_bar_handle_event (bar, event);
+}
+
GtkWidget *
do_search_entry2 (GtkWidget *do_widget)
{
- GtkWidget *content_area;
GtkWidget *vbox;
GtkWidget *hbox;
GtkWidget *label;
GtkWidget *entry;
+ GtkWidget *container;
+ GtkWidget *searchbar;
GtkWidget *button;
if (!window)
{
- window = gtk_dialog_new_with_buttons ("Search Entry #2",
- GTK_WINDOW (do_widget),
- 0,
- GTK_STOCK_CLOSE,
- GTK_RESPONSE_NONE,
- NULL);
- gtk_window_set_resizable (GTK_WINDOW (window), FALSE);
-
- g_signal_connect (window, "response",
- G_CALLBACK (gtk_widget_destroy), NULL);
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title (GTK_WINDOW (window), "Search Entry #2");
+ gtk_window_set_transient_for (GTK_WINDOW (window), GTK_WINDOW (do_widget));
+ gtk_window_set_resizable (GTK_WINDOW (window), TRUE);
+ gtk_widget_set_size_request (window, 200, -1);
+
g_signal_connect (window, "destroy",
G_CALLBACK (search_entry_destroyed), &window);
- content_area = gtk_dialog_get_content_area (GTK_DIALOG (window));
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+ gtk_container_add (GTK_CONTAINER (window), vbox);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 0);
- vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 5);
- gtk_box_pack_start (GTK_BOX (content_area), vbox, TRUE, TRUE, 0);
- gtk_container_set_border_width (GTK_CONTAINER (vbox), 5);
-
- label = gtk_label_new (NULL);
- gtk_label_set_markup (GTK_LABEL (label), "Search entry demo #2");
- gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
-
- /* Create our entry */
entry = gtk_search_entry_new ();
- gtk_box_pack_start (GTK_BOX (vbox), entry, FALSE, FALSE, 0);
+ container = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 10);
+ gtk_widget_set_halign (container, GTK_ALIGN_CENTER);
+ gtk_box_pack_start (GTK_BOX (container), entry, FALSE, FALSE, 0);
+ searchbar = gtk_search_bar_new ();
+ gtk_search_bar_connect_entry (GTK_SEARCH_BAR (searchbar), GTK_ENTRY (entry));
+ gtk_search_bar_set_show_close_button (GTK_SEARCH_BAR (searchbar), FALSE);
+ gtk_container_add (GTK_CONTAINER (searchbar), container);
+ gtk_box_pack_start (GTK_BOX (vbox), searchbar, FALSE, FALSE, 0);
+
+ /* Hook the search bar to key presses */
+ g_signal_connect (window, "key-press-event",
+ G_CALLBACK (window_key_press_event_cb), searchbar);
+
+ /* Help */
+ label = gtk_label_new ("Start Typing to search");
+ gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0);
+
+ /* Toggle button */
+ button = gtk_toggle_button_new_with_label ("Search");
+ g_object_bind_property (button, "active",
+ searchbar, "search-mode-enabled",
+ G_BINDING_BIDIRECTIONAL);
+ gtk_box_pack_start (GTK_BOX (vbox), button, TRUE, TRUE, 0);
/* Result */
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 10);
@@ -78,10 +98,6 @@ do_search_entry2 (GtkWidget *do_widget)
g_signal_connect (entry, "changed",
G_CALLBACK (search_changed_cb), label);
-
- /* Give the focus to the close button */
- button = gtk_dialog_get_widget_for_response (GTK_DIALOG (window), GTK_RESPONSE_NONE);
- gtk_widget_grab_focus (button);
}
if (!gtk_widget_get_visible (window))
diff --git a/docs/reference/gtk/gtk-docs.sgml b/docs/reference/gtk/gtk-docs.sgml
index b067e5e849..ffd070368a 100644
--- a/docs/reference/gtk/gtk-docs.sgml
+++ b/docs/reference/gtk/gtk-docs.sgml
@@ -126,6 +126,7 @@
<xi:include href="xml/gtkscale.xml" />
<xi:include href="xml/gtkspinbutton.xml" />
<xi:include href="xml/gtksearchentry.xml" />
+ <xi:include href="xml/gtksearchbar.xml" />
<xi:include href="xml/gtkeditable.xml" />
</chapter>
diff --git a/docs/reference/gtk/gtk3-sections.txt b/docs/reference/gtk/gtk3-sections.txt
index a4db1d7196..aa93baa6df 100644
--- a/docs/reference/gtk/gtk3-sections.txt
+++ b/docs/reference/gtk/gtk3-sections.txt
@@ -3039,6 +3039,23 @@ GtkScrolledWindowPrivate
</SECTION>
<SECTION>
+<FILE>gtksearchbar</FILE>
+<TITLE>GtkSearchBar</TITLE>
+GtkSearchBar
+gtk_search_bar_new
+gtk_search_bar_handle_event
+<SUBSECTION Standard>
+GTK_TYPE_SEARCH_BAR
+GTK_SEARCH_BAR
+GTK_SEARCH_BAR_CLASS
+GTK_IS_SEARCH_BAR
+GTK_IS_SEARCH_BAR_CLASS
+GTK_SEARCH_BAR_GET_CLASS
+<SUBSECTION Private>
+gtk_search_bar_get_type
+</SECTION>
+
+<SECTION>
<FILE>gtksearchentry</FILE>
<TITLE>GtkSearchEntry</TITLE>
GtkSearchEntry
diff --git a/docs/reference/gtk/gtk3.types.in b/docs/reference/gtk/gtk3.types.in
index df3a451b32..f9f9d267c6 100644
--- a/docs/reference/gtk/gtk3.types.in
+++ b/docs/reference/gtk/gtk3.types.in
@@ -147,6 +147,7 @@ gtk_scale_get_type
gtk_scrollable_get_type
gtk_scrollbar_get_type
gtk_scrolled_window_get_type
+gtk_search_bar_get_type
gtk_search_entry_get_type
gtk_separator_get_type
gtk_separator_menu_item_get_type
diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index 4085014cb2..6884575502 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -311,6 +311,7 @@ gtk_public_h_sources = \
gtkscrollable.h \
gtkscrollbar.h \
gtkscrolledwindow.h \
+ gtksearchbar.h \
gtksearchentry.h \
gtkselection.h \
gtkseparator.h \
@@ -590,6 +591,7 @@ gtk_base_c_sources = \
gtkactionobservable.c \
gtkactionable.c \
gtkquery.c \
+ gtksearchbar.c \
gtksearchentry.c \
gtksearchengine.c \
gtksearchenginesimple.c \
@@ -1122,6 +1124,7 @@ COMPOSITE_TEMPLATES = \
gtkpathbar.ui \
gtkprintunixdialog.ui \
gtkrecentchooserdefault.ui \
+ gtksearchbar.ui \
gtkscalebutton.ui \
gtkstatusbar.ui \
gtkvolumebutton.ui
diff --git a/gtk/gtk.gresource.xml b/gtk/gtk.gresource.xml
index b93d83742a..549385f2c1 100644
--- a/gtk/gtk.gresource.xml
+++ b/gtk/gtk.gresource.xml
@@ -31,6 +31,7 @@
<file compressed="true">gtkpathbar.ui</file>
<file compressed="true">gtkprintunixdialog.ui</file>
<file compressed="true">gtkrecentchooserdefault.ui</file>
+ <file compressed="true">gtksearchbar.ui</file>
<file compressed="true">gtkscalebutton.ui</file>
<file compressed="true">gtkstatusbar.ui</file>
<file compressed="true">gtkvolumebutton.ui</file>
diff --git a/gtk/gtk.h b/gtk/gtk.h
index e8a7af94cb..a0d2b4288e 100644
--- a/gtk/gtk.h
+++ b/gtk/gtk.h
@@ -169,6 +169,7 @@
#include <gtk/gtkscrollable.h>
#include <gtk/gtkscrollbar.h>
#include <gtk/gtkscrolledwindow.h>
+#include <gtk/gtksearchbar.h>
#include <gtk/gtksearchentry.h>
#include <gtk/gtkselection.h>
#include <gtk/gtkseparator.h>
diff --git a/gtk/gtksearchbar.c b/gtk/gtksearchbar.c
new file mode 100644
index 0000000000..db0c5545d3
--- /dev/null
+++ b/gtk/gtksearchbar.c
@@ -0,0 +1,552 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2013 Red Hat, Inc.
+ *
+ * Authors:
+ * - Bastien Nocera <bnocera@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Modified by the GTK+ Team and others 2013. See the AUTHORS
+ * file for a list of people on the GTK+ Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+#include "config.h"
+
+#include "gtkentry.h"
+#include "gtkentryprivate.h"
+#include "gtkintl.h"
+#include "gtktoolbar.h"
+#include "gtktoolitem.h"
+#include "gtkstylecontext.h"
+#include "gtksearchbar.h"
+
+/**
+ * SECTION:gtksearchbar
+ * @Short_description: An toolbar to integrate a search entry with
+ * @Title: GtkSearchBar
+ *
+ * #GtkSearchBar is a container made to have a search entry (possibly
+ * with additional connex widgets, such as drop-down menus, or buttons) built-in.
+ * The search bar would appear when a search is started through typing on the
+ * keyboard, or the application's search mode is toggled on.
+ *
+ * For keyboard presses to start a search, events will need to be forwarded
+ * from the top-level window that contains the search bar. See
+ * gtk_search_bar_handle_event() for example code.
+ *
+ * You will also need to tell the search bar about which entry you are
+ * using as your search entry using gtk_search_bar_connect_entry().
+ * The following example shows you how to create a more complex
+ * search entry.
+ *
+ * <example>
+ * <title>Creating a search bar</title>
+ * <programlisting><![CDATA[
+ * bar = gtk_search_bar_new ();
+ *
+ * /<!---->* Create a box for the search entry and related widgets *<---->/
+ * entry = gtk_search_entry_new ();
+ * box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ * gtk_box_pack_start (GTK_BOX (box), entry, TRUE, TRUE, 0);
+ * /<!---->* Add a menu button to select the category of the search *<---->/
+ * menu_button = gtk_menu_button_new ();
+ * gtk_box_pack_start (GTK_BOX (box), menu_button, FALSE, FALSE, 0);
+ * gtk_container_add (GTK_CONTAINER (searchbar), box);
+ *
+ * /<!---->* And tell the search bar about the search entry *<---->/
+ * gtk_search_bar_set_search_entry (GTK_SEARCH_BAR (bar), entry);
+ * ]]></programlisting>
+ * </example>
+ *
+ * Since: 3.10
+ */
+
+G_DEFINE_TYPE (GtkSearchBar, gtk_search_bar, GTK_TYPE_BIN)
+
+struct _GtkSearchBarPrivate {
+ /* Template widgets */
+ GtkWidget *revealer;
+ GtkWidget *toolbar;
+ GtkWidget *box_center;
+ GtkWidget *close_button;
+
+ GtkWidget *entry;
+ gboolean reveal_child;
+};
+
+enum {
+ PROP_0,
+ PROP_SEARCH_MODE_ENABLED,
+ PROP_SHOW_CLOSE_BUTTON,
+ LAST_PROPERTY
+};
+
+static GParamSpec *widget_props[LAST_PROPERTY] = { NULL, };
+
+static gboolean
+is_keynav_event (GdkEvent *event,
+ guint keyval)
+{
+ GdkModifierType state = 0;
+
+ gdk_event_get_state (event, &state);
+
+ if (keyval == GDK_KEY_Tab ||
+ keyval == GDK_KEY_KP_Tab ||
+ keyval == GDK_KEY_Up ||
+ keyval == GDK_KEY_KP_Up ||
+ keyval == GDK_KEY_Down ||
+ keyval == GDK_KEY_KP_Down ||
+ keyval == GDK_KEY_Left ||
+ keyval == GDK_KEY_KP_Left ||
+ keyval == GDK_KEY_Right ||
+ keyval == GDK_KEY_KP_Right ||
+ keyval == GDK_KEY_Home ||
+ keyval == GDK_KEY_KP_Home ||
+ keyval == GDK_KEY_End ||
+ keyval == GDK_KEY_KP_End ||
+ keyval == GDK_KEY_Page_Up ||
+ keyval == GDK_KEY_KP_Page_Up ||
+ keyval == GDK_KEY_Page_Down ||
+ keyval == GDK_KEY_KP_Page_Down ||
+ ((state & (GDK_CONTROL_MASK | GDK_MOD1_MASK)) != 0))
+ return TRUE;
+
+ /* Other navigation events should get automatically
+ * ignored as they will not change the content of the entry
+ */
+
+ return FALSE;
+}
+
+static gboolean
+entry_key_pressed_event_cb (GtkWidget *widget,
+ GdkEvent *event,
+ GtkSearchBar *bar)
+{
+ guint keyval;
+
+ if (!gdk_event_get_keyval (event, &keyval) ||
+ keyval != GDK_KEY_Escape)
+ return GDK_EVENT_PROPAGATE;
+
+ gtk_revealer_set_reveal_child (GTK_REVEALER (bar->priv->revealer), FALSE);
+ return GDK_EVENT_STOP;
+}
+
+static void
+preedit_changed_cb (GtkEntry *entry,
+ GtkWidget *popup,
+ gboolean *preedit_changed)
+{
+ *preedit_changed = TRUE;
+}
+
+/**
+ * gtk_search_bar_handle_event:
+ * @bar: a #GtkSearchBar
+ * @event: a #GdkEvent containing key press events
+ *
+ * This function should be called when the top-level
+ * window which contains the search bar received a key event.
+ *
+ * If the key event is handled by the search bar, the bar will
+ * be shown, the entry populated with the entered text and %GDK_EVENT_STOP
+ * will be returned. The caller should ensure that events are
+ * not propagated further.
+ *
+ * If no entry has been connected to the search bar, using
+ * gtk_search_bar_connect_entry(), this function will return immediately with
+ * a warning.
+ *
+ * <example>
+ * <title>Showing the search bar on key presses</title>
+ * <programlisting><![CDATA[
+ * static gboolean
+ * window_key_press_event_cb (GtkWidget *widget,
+ * GdkEvent *event,
+ * gpointer user_data)
+ * {
+ * return gtk_search_bar_handle_event (GTK_SEARCH_BAR (user_data), event);
+ * }
+ *
+ * g_signal_connect (window, "key-press-event",
+ * G_CALLBACK (window_key_press_event_cb), search_bar);
+ * ]]></programlisting>
+ * </example>
+ *
+ * Return value: %GDK_EVENT_STOP if the key press event resulted in text
+ * being * entered in the search entry (and revealing the search bar if
+ * necessary), %GDK_EVENT_PROPAGATE otherwise.
+ *
+ * Since: 3.10
+ **/
+gboolean
+gtk_search_bar_handle_event (GtkSearchBar *bar,
+ GdkEvent *event)
+{
+ guint keyval;
+ gboolean handled;
+ gboolean preedit_changed;
+ guint preedit_change_id;
+ gboolean res;
+ char *old_text, *new_text;
+
+ if (!bar->priv->entry)
+ {
+ g_warning ("The search bar does not have an entry connected to it. Call gtk_search_bar_connect_entry() to connect one.");
+ return GDK_EVENT_PROPAGATE;
+ }
+
+ /* Exit early if the search bar is already shown,
+ * the event doesn't contain a key press,
+ * or the event is a navigation or space bar key press
+ */
+ if (bar->priv->reveal_child ||
+ !gdk_event_get_keyval (event, &keyval) ||
+ is_keynav_event (event, keyval) ||
+ keyval == GDK_KEY_space)
+ return GDK_EVENT_PROPAGATE;
+
+ if (!gtk_widget_get_realized (bar->priv->entry))
+ gtk_widget_realize (bar->priv->entry);
+
+ handled = GDK_EVENT_PROPAGATE;
+ preedit_changed = FALSE;
+ preedit_change_id = g_signal_connect (bar->priv->entry, "preedit-changed",
+ G_CALLBACK (preedit_changed_cb), &preedit_changed);
+
+ old_text = g_strdup (gtk_entry_get_text (GTK_ENTRY (bar->priv->entry)));
+ res = gtk_widget_event (bar->priv->entry, event);
+ new_text = g_strdup (gtk_entry_get_text (GTK_ENTRY (bar->priv->entry)));
+
+ g_signal_handler_disconnect (bar->priv->entry, preedit_change_id);
+
+ if ((res && g_strcmp0 (new_text, old_text) != 0) || preedit_changed)
+ {
+ handled = GDK_EVENT_STOP;
+ gtk_revealer_set_reveal_child (GTK_REVEALER (bar->priv->revealer), TRUE);
+ }
+
+ g_free (old_text);
+ g_free (new_text);
+
+ return handled;
+}
+
+static void
+reveal_child_changed_cb (GObject *object,
+ GParamSpec *pspec,
+ GtkSearchBar *bar)
+{
+ gboolean reveal_child;
+
+ g_object_get (object, "reveal-child", &reveal_child, NULL);
+ if (reveal_child == bar->priv->reveal_child)
+ return;
+
+ bar->priv->reveal_child = reveal_child;
+
+ if (reveal_child)
+ _gtk_entry_grab_focus (GTK_ENTRY (bar->priv->entry), FALSE);
+ else
+ gtk_entry_set_text (GTK_ENTRY (bar->priv->entry), "");
+
+ g_object_notify (G_OBJECT (bar), "search-mode-enabled");
+}
+
+static void
+close_button_clicked_cb (GtkWidget *button,
+ GtkSearchBar *bar)
+{
+ gtk_revealer_set_reveal_child (GTK_REVEALER (bar->priv->revealer), FALSE);
+}
+
+static void
+gtk_search_bar_add (GtkContainer *container,
+ GtkWidget *child)
+{
+ GtkSearchBar *bar = GTK_SEARCH_BAR (container);
+
+ /* When constructing the widget, we want the revealer to be added
+ * as the first child of the search bar, as an implementation detail.
+ * After that, the child added by the application should be added
+ * to the toolbar's box_center.
+ */
+ if (bar->priv->box_center == NULL)
+ {
+ GTK_CONTAINER_CLASS (gtk_search_bar_parent_class)->add (container, child);
+ /* If an entry is the only child, save the developer a couple of
+ * lines of code */
+ if (GTK_IS_ENTRY (child))
+ gtk_search_bar_connect_entry (bar, GTK_ENTRY (child));
+ }
+ else
+ {
+ gtk_container_add (GTK_CONTAINER (bar->priv->box_center), child);
+ }
+}
+
+static void
+gtk_search_bar_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GtkSearchBar *bar = GTK_SEARCH_BAR (object);
+
+ switch (prop_id)
+ {
+ case PROP_SEARCH_MODE_ENABLED:
+ gtk_search_bar_set_search_mode (bar, g_value_get_boolean (value));
+ break;
+ case PROP_SHOW_CLOSE_BUTTON:
+ gtk_search_bar_set_show_close_button (bar, g_value_get_boolean (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gtk_search_bar_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GtkSearchBar *bar = GTK_SEARCH_BAR (object);
+
+ switch (prop_id)
+ {
+ case PROP_SEARCH_MODE_ENABLED:
+ g_value_set_boolean (value, bar->priv->reveal_child);
+ break;
+ case PROP_SHOW_CLOSE_BUTTON:
+ g_value_set_boolean (value, gtk_search_bar_get_show_close_button (bar));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gtk_search_bar_dispose (GObject *object)
+{
+ GtkSearchBar *bar = GTK_SEARCH_BAR (object);
+
+ if (bar->priv->entry)
+ {
+ g_signal_handlers_disconnect_by_func (bar->priv->entry, entry_key_pressed_event_cb, bar);
+ g_object_remove_weak_pointer (G_OBJECT (bar->priv->entry), (gpointer *) &bar->priv->entry);
+ bar->priv->entry = NULL;
+ }
+
+ G_OBJECT_CLASS (gtk_search_bar_parent_class)->dispose (object);
+}
+
+static void
+gtk_search_bar_class_init (GtkSearchBarClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+ GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
+
+ object_class->dispose = gtk_search_bar_dispose;
+ object_class->set_property = gtk_search_bar_set_property;
+ object_class->get_property = gtk_search_bar_get_property;
+
+ container_class->add = gtk_search_bar_add;
+
+ /**
+ * GtkEntry:search-mode-enabled:
+ *
+ * Whether the search mode is on and the search bar shown.
+ *
+ * See gtk_search_bar_set_search_mode() for details.
+ *
+ **/
+ widget_props[PROP_SEARCH_MODE_ENABLED] = g_param_spec_boolean ("search-mode-enabled",
+ P_("Search Mode Enabled"),
+ P_("Whether the search mode is on and the search bar shown"),
+ FALSE,
+ G_PARAM_READWRITE);
+ /**
+ * GtkEntry:show-close-button:
+ *
+ * Whether to show the close button in the toolbar.
+ *
+ **/
+ widget_props[PROP_SHOW_CLOSE_BUTTON] = g_param_spec_boolean ("show-close-button",
+ P_("Show Close Button"),
+ P_("Whether to show the close button in the toolbar"),
+ TRUE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
+
+ g_object_class_install_properties (object_class, LAST_PROPERTY, widget_props);
+
+ gtk_widget_class_set_template_from_resource (widget_class, "/org/gtk/libgtk/gtksearchbar.ui");
+ gtk_widget_class_bind_child_internal (widget_class, GtkSearchBarPrivate, toolbar);
+ gtk_widget_class_bind_child_internal (widget_class, GtkSearchBarPrivate, revealer);
+ gtk_widget_class_bind_child_internal (widget_class, GtkSearchBarPrivate, box_center);
+ gtk_widget_class_bind_child_internal (widget_class, GtkSearchBarPrivate, close_button);
+
+ g_type_class_add_private (klass, sizeof (GtkSearchBarPrivate));
+}
+
+static void
+gtk_search_bar_init (GtkSearchBar *bar)
+{
+ bar->priv = G_TYPE_INSTANCE_GET_PRIVATE (bar, GTK_TYPE_SEARCH_BAR, GtkSearchBarPrivate);
+
+ gtk_widget_init_template (GTK_WIDGET (bar));
+
+ gtk_widget_show_all (bar->priv->toolbar);
+
+ g_signal_connect (bar->priv->revealer, "notify::reveal-child",
+ G_CALLBACK (reveal_child_changed_cb), bar);
+
+ gtk_widget_set_no_show_all (bar->priv->close_button, TRUE);
+ g_signal_connect (bar->priv->close_button, "clicked",
+ G_CALLBACK (close_button_clicked_cb), bar);
+};
+
+/**
+ * gtk_search_bar_new:
+ *
+ * Creates a #GtkSearchBar. You will need to tell it about
+ * which widget is going to be your text entry using
+ * gtk_search_bar_set_entry().
+ *
+ * Return value: a new #GtkSearchBar
+ *
+ * Since: 3.10
+ */
+GtkWidget *
+gtk_search_bar_new (void)
+{
+ return g_object_new (GTK_TYPE_SEARCH_BAR, NULL);
+}
+
+/**
+ * gtk_search_bar_connect_entry:
+ * @bar: a #GtkSearchBar
+ * @entry: a #GtkEntry
+ *
+ * Connects the #GtkEntry widget passed as the one to be used in
+ * this search bar. The entry should be a descendant of the search bar.
+ * This is only required if the entry isn't the direct child of the
+ * search bar (as in our main example).
+ *
+ * Since: 3.10
+ **/
+void
+gtk_search_bar_connect_entry (GtkSearchBar *bar,
+ GtkEntry *entry)
+{
+ g_return_if_fail (GTK_IS_SEARCH_BAR (bar));
+ g_return_if_fail (entry == NULL || GTK_IS_ENTRY (entry));
+
+ if (bar->priv->entry != NULL)
+ {
+ g_signal_handlers_disconnect_by_func (bar->priv->entry, entry_key_pressed_event_cb, bar);
+ g_object_remove_weak_pointer (G_OBJECT (bar->priv->entry), (gpointer *) &bar->priv->entry);
+ bar->priv->entry = NULL;
+ }
+
+ if (entry != NULL)
+ {
+ bar->priv->entry = GTK_WIDGET (entry);
+ g_object_add_weak_pointer (G_OBJECT (bar->priv->entry), (gpointer *) &bar->priv->entry);
+ g_signal_connect (bar->priv->entry, "key-press-event",
+ G_CALLBACK (entry_key_pressed_event_cb), bar);
+ }
+}
+
+/**
+ * gtk_search_bar_get_search_mode:
+ * @bar: a #GtkSearchBar
+ *
+ * Returns whether the search mode is on or off.
+ *
+ * Return value: whether search mode is toggled on
+ *
+ * Since: 3.10
+ **/
+gboolean
+gtk_search_bar_get_search_mode (GtkSearchBar *bar)
+{
+ g_return_val_if_fail (GTK_IS_SEARCH_BAR (bar), FALSE);
+
+ return bar->priv->reveal_child;
+}
+
+/**
+ * gtk_search_bar_set_search_mode:
+ * @bar: a #GtkSearchBar
+ * @search_mode: the new state of the search mode
+ *
+ * Switches the search mode on or off.
+ *
+ * Since: 3.10
+ **/
+void
+gtk_search_bar_set_search_mode (GtkSearchBar *bar,
+ gboolean search_mode)
+{
+ g_return_if_fail (GTK_IS_SEARCH_BAR (bar));
+
+ gtk_revealer_set_reveal_child (GTK_REVEALER (bar->priv->revealer), search_mode);
+}
+
+/**
+ * gtk_search_bar_get_show_close_button:
+ * @bar: a #GtkSearchBar
+ *
+ * Returns whether the close button is shown.
+ *
+ * Return value: whether the close button is shown
+ *
+ * Since: 3.10
+ **/
+gboolean
+gtk_search_bar_get_show_close_button (GtkSearchBar *bar)
+{
+ g_return_val_if_fail (GTK_IS_SEARCH_BAR (bar), FALSE);
+
+ return gtk_widget_get_visible (bar->priv->close_button);
+}
+
+/**
+ * gtk_search_bar_set_show_close_button:
+ * @bar: a #GtkSearchBar
+ * @shown: whether the close button will be shown or not.
+ *
+ * Shows or hides the close button.
+ *
+ * Since: 3.10
+ **/
+void
+gtk_search_bar_set_show_close_button (GtkSearchBar *bar,
+ gboolean visible)
+{
+ g_return_if_fail (GTK_IS_SEARCH_BAR (bar));
+
+ gtk_widget_set_visible (bar->priv->close_button, visible);
+}
diff --git a/gtk/gtksearchbar.h b/gtk/gtksearchbar.h
new file mode 100644
index 0000000000..93112d28fe
--- /dev/null
+++ b/gtk/gtksearchbar.h
@@ -0,0 +1,98 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2013 Red Hat, Inc.
+ *
+ * Authors:
+ * - Bastien Nocera <bnocera@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Modified by the GTK+ Team and others 2013. See the AUTHORS
+ * file for a list of people on the GTK+ Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+#ifndef __GTK_SEARCH_BAR_H__
+#define __GTK_SEARCH_BAR_H__
+
+#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
+#error "Only <gtk/gtk.h> can be included directly."
+#endif
+
+#include <gtk/gtkrevealer.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_SEARCH_BAR (gtk_search_bar_get_type ())
+#define GTK_SEARCH_BAR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_SEARCH_BAR, GtkSearchBar))
+#define GTK_SEARCH_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_SEARCH_BAR, GtkSearchBarClass))
+#define GTK_IS_SEARCH_BAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_SEARCH_BAR))
+#define GTK_IS_SEARCH_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_SEARCH_BAR))
+#define GTK_SEARCH_BAR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_SEARCH_BAR, GtkSearchBarClass))
+
+typedef struct _GtkSearchBar GtkSearchBar;
+typedef struct _GtkSearchBarPrivate GtkSearchBarPrivate;
+typedef struct _GtkSearchBarClass GtkSearchBarClass;
+
+struct _GtkSearchBar
+{
+ /*< private >*/
+ GtkBin parent;
+
+ GtkSearchBarPrivate *priv;
+};
+
+struct _GtkSearchBarClass
+{
+ /*< private >*/
+ GtkBinClass parent_class;
+
+ /* Padding for future expansion */
+ void (*_gtk_reserved1) (void);
+ void (*_gtk_reserved2) (void);
+ void (*_gtk_reserved3) (void);
+ void (*_gtk_reserved4) (void);
+};
+
+GDK_AVAILABLE_IN_3_10
+GType gtk_search_bar_get_type (void) G_GNUC_CONST;
+
+GDK_AVAILABLE_IN_3_10
+GtkWidget* gtk_search_bar_new (void);
+
+GDK_AVAILABLE_IN_3_10
+void gtk_search_bar_connect_entry (GtkSearchBar *bar,
+ GtkEntry *entry);
+
+GDK_AVAILABLE_IN_3_10
+gboolean gtk_search_bar_get_search_mode (GtkSearchBar *bar);
+GDK_AVAILABLE_IN_3_10
+void gtk_search_bar_set_search_mode (GtkSearchBar *bar,
+ gboolean search_mode);
+
+GDK_AVAILABLE_IN_3_10
+gboolean gtk_search_bar_get_show_close_button (GtkSearchBar *bar);
+GDK_AVAILABLE_IN_3_10
+void gtk_search_bar_set_show_close_button (GtkSearchBar *bar,
+ gboolean visible);
+
+GDK_AVAILABLE_IN_3_10
+gboolean gtk_search_bar_handle_event (GtkSearchBar *bar,
+ GdkEvent *event);
+
+G_END_DECLS
+
+#endif /* __GTK_SEARCH_BAR_H__ */
diff --git a/gtk/gtksearchbar.ui b/gtk/gtksearchbar.ui
new file mode 100644
index 0000000000..ac02b4b5fe
--- /dev/null
+++ b/gtk/gtksearchbar.ui
@@ -0,0 +1,118 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface domain="gtk30">
+ <!-- interface-requires gtk+ 3.10 -->
+ <template class="GtkSearchBar" parent="GtkBox">
+ <property name="app_paintable">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkRevealer" id="revealer">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="vexpand">False</property>
+ <property name="hexpand">True</property>
+ <child>
+ <object class="GtkToolbar" id="toolbar">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <style>
+ <class name="primary-toolbar"/>
+ </style>
+ <child>
+ <object class="GtkToolItem" id="tool_item">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkBox" id="tool_box">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkBox" id="box_left">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="halign">start</property>
+ <property name="hexpand">True</property>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="box_center">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="halign">center</property>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="box_right">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="halign">end</property>
+ <property name="hexpand">True</property>
+ <child>
+ <object class="GtkButton" id="close_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="relief">none</property>
+ <style>
+ <class name="raised"/>
+ <class name="close"/>
+ </style>
+ <child>
+ <object class="GtkImage" id="close_image">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon_name">window-close-symbolic</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+ <object class="GtkSizeGroup" id="sizegroup">
+ <property name="mode">both</property>
+ <widgets>
+ <widget name="box_left"/>
+ <widget name="box_right"/>
+ </widgets>
+ </object>
+</interface>