[gtk/text: 17/29] search entry: Use a GtkText
- From: Matthias Clasen <matthiasc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk/text: 17/29] search entry: Use a GtkText
- Date: Sun, 17 Feb 2019 20:58:09 +0000 (UTC)
commit 4a3ffd3a3fadeba42138f8a1b63ef5e64558afb6
Author: Matthias Clasen <mclasen redhat com>
Date: Sat Feb 16 23:57:18 2019 -0500
search entry: Use a GtkText
gtk/gtksearchentry.c | 297 ++++++++++++++++++++++++++++++++------------
gtk/gtksearchentry.h | 14 +--
gtk/gtksearchentryprivate.h | 2 +
3 files changed, 225 insertions(+), 88 deletions(-)
---
diff --git a/gtk/gtksearchentry.c b/gtk/gtksearchentry.c
index acbd5e110e..e514fc7251 100644
--- a/gtk/gtksearchentry.c
+++ b/gtk/gtksearchentry.c
@@ -31,8 +31,13 @@
#include "gtkaccessible.h"
#include "gtkbindings.h"
-#include "gtkentryprivate.h"
+#include "gtkeditableprivate.h"
+#include "gtkbox.h"
+#include "gtkgesturemultipress.h"
+#include "gtktextprivate.h"
+#include "gtkimage.h"
#include "gtkintl.h"
+#include "gtkprivate.h"
#include "gtkmarshalers.h"
#include "gtkstylecontext.h"
#include "gtkeventcontrollerkey.h"
@@ -70,6 +75,7 @@
*/
enum {
+ ACTIVATE,
SEARCH_CHANGED,
NEXT_MATCH,
PREVIOUS_MATCH,
@@ -77,25 +83,33 @@ enum {
LAST_SIGNAL
};
+enum {
+ PROP_0,
+ PROP_PLACEHOLDER_TEXT,
+ PROP_ACTIVATES_DEFAULT,
+ NUM_PROPERTIES,
+};
+
static guint signals[LAST_SIGNAL] = { 0 };
+static GParamSpec *props[NUM_PROPERTIES] = { NULL, };
+
typedef struct {
GtkWidget *capture_widget;
GtkEventController *capture_widget_controller;
+ GtkWidget *box;
+ GtkWidget *entry;
+ GtkWidget *icon;
+
guint delayed_changed_id;
gboolean content_changed;
gboolean search_stopped;
} GtkSearchEntryPrivate;
-static void gtk_search_entry_icon_release (GtkEntry *entry,
- GtkEntryIconPosition icon_pos);
-static void gtk_search_entry_changed (GtkEditable *editable);
static void gtk_search_entry_editable_init (GtkEditableInterface *iface);
-static GtkEditableInterface *parent_editable_iface;
-
-G_DEFINE_TYPE_WITH_CODE (GtkSearchEntry, gtk_search_entry, GTK_TYPE_ENTRY,
+G_DEFINE_TYPE_WITH_CODE (GtkSearchEntry, gtk_search_entry, GTK_TYPE_WIDGET,
G_ADD_PRIVATE (GtkSearchEntry)
G_IMPLEMENT_INTERFACE (GTK_TYPE_EDITABLE,
gtk_search_entry_editable_init))
@@ -103,36 +117,21 @@ G_DEFINE_TYPE_WITH_CODE (GtkSearchEntry, gtk_search_entry, GTK_TYPE_ENTRY,
/* 150 mseconds of delay */
#define DELAYED_TIMEOUT_ID 150
-/* This widget got created without a private structure, meaning
- * that we cannot now have one without breaking ABI */
-#define GET_PRIV(e) ((GtkSearchEntryPrivate *) gtk_search_entry_get_instance_private ((GtkSearchEntry *)
(e)))
-
static void
-gtk_search_entry_preedit_changed (GtkEntry *entry,
- const gchar *preedit)
+text_changed (GtkSearchEntry *entry)
{
- GtkSearchEntryPrivate *priv = GET_PRIV (entry);
+ GtkSearchEntryPrivate *priv = gtk_search_entry_get_instance_private (entry);
priv->content_changed = TRUE;
}
-static void
-gtk_search_entry_notify (GObject *object,
- GParamSpec *pspec)
-{
- GtkSearchEntryPrivate *priv = GET_PRIV (object);
-
- if (strcmp (pspec->name, "text") == 0)
- priv->content_changed = TRUE;
-
- if (G_OBJECT_CLASS (gtk_search_entry_parent_class)->notify)
- G_OBJECT_CLASS (gtk_search_entry_parent_class)->notify (object, pspec);
-}
-
static void
gtk_search_entry_finalize (GObject *object)
{
- GtkSearchEntryPrivate *priv = GET_PRIV (object);
+ GtkSearchEntry *entry = GTK_SEARCH_ENTRY (object);
+ GtkSearchEntryPrivate *priv = gtk_search_entry_get_instance_private (entry);
+
+ g_clear_pointer (&priv->box, gtk_widget_unparent);
if (priv->delayed_changed_id > 0)
g_source_remove (priv->delayed_changed_id);
@@ -145,25 +144,137 @@ gtk_search_entry_finalize (GObject *object)
static void
gtk_search_entry_stop_search (GtkSearchEntry *entry)
{
- GtkSearchEntryPrivate *priv = GET_PRIV (entry);
+ GtkSearchEntryPrivate *priv = gtk_search_entry_get_instance_private (entry);
priv->search_stopped = TRUE;
}
+static void
+gtk_search_entry_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GtkSearchEntry *entry = GTK_SEARCH_ENTRY (object);
+ GtkSearchEntryPrivate *priv = gtk_search_entry_get_instance_private (entry);
+
+ if (gtk_editable_delegate_set_property (object, prop_id, value, pspec))
+ return;
+
+ switch (prop_id)
+ {
+ case PROP_PLACEHOLDER_TEXT:
+ gtk_text_set_placeholder_text (GTK_TEXT (priv->entry), g_value_get_string (value));
+ break;
+
+ case PROP_ACTIVATES_DEFAULT:
+ gtk_text_set_activates_default (GTK_TEXT (priv->entry), g_value_get_boolean (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gtk_search_entry_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GtkSearchEntry *entry = GTK_SEARCH_ENTRY (object);
+ GtkSearchEntryPrivate *priv = gtk_search_entry_get_instance_private (entry);
+
+ if (gtk_editable_delegate_get_property (object, prop_id, value, pspec))
+ return;
+
+ switch (prop_id)
+ {
+ case PROP_PLACEHOLDER_TEXT:
+ g_value_set_string (value, gtk_text_get_placeholder_text (GTK_TEXT (priv->entry)));
+ break;
+
+ case PROP_ACTIVATES_DEFAULT:
+ g_value_set_boolean (value, gtk_text_get_activates_default (GTK_TEXT (priv->entry)));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gtk_search_entry_measure (GtkWidget *widget,
+ GtkOrientation orientation,
+ int for_size,
+ int *minimum,
+ int *natural,
+ int *minimum_baseline,
+ int *natural_baseline)
+{
+ GtkSearchEntry *entry = GTK_SEARCH_ENTRY (widget);
+ GtkSearchEntryPrivate *priv = gtk_search_entry_get_instance_private (entry);
+
+ gtk_widget_measure (priv->box, orientation, for_size,
+ minimum, natural,
+ minimum_baseline, natural_baseline);
+}
+
+static void
+gtk_search_entry_size_allocate (GtkWidget *widget,
+ int width,
+ int height,
+ int baseline)
+{
+ GtkSearchEntry *entry = GTK_SEARCH_ENTRY (widget);
+ GtkSearchEntryPrivate *priv = gtk_search_entry_get_instance_private (entry);
+
+ gtk_widget_size_allocate (priv->box,
+ &(GtkAllocation) { 0, 0, width, height },
+ baseline);
+}
+
static void
gtk_search_entry_class_init (GtkSearchEntryClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
GtkBindingSet *binding_set;
object_class->finalize = gtk_search_entry_finalize;
- object_class->notify = gtk_search_entry_notify;
+ object_class->get_property = gtk_search_entry_get_property;
+ object_class->set_property = gtk_search_entry_set_property;
+
+ widget_class->measure = gtk_search_entry_measure;
+ widget_class->size_allocate = gtk_search_entry_size_allocate;
klass->stop_search = gtk_search_entry_stop_search;
- g_signal_override_class_handler ("icon-release",
- GTK_TYPE_SEARCH_ENTRY,
- G_CALLBACK (gtk_search_entry_icon_release));
+ props[PROP_PLACEHOLDER_TEXT] =
+ g_param_spec_string ("placeholder-text",
+ P_("Placeholder text"),
+ P_("Show text in the entry when it’s empty and unfocused"),
+ NULL,
+ GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+
+ props[PROP_ACTIVATES_DEFAULT] =
+ g_param_spec_boolean ("activates-default",
+ P_("Activates default"),
+ P_("Whether to activate the default widget (such as the default button in a
dialog) when Enter is pressed"),
+ FALSE,
+ GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+
+ g_object_class_install_properties (object_class, NUM_PROPERTIES, props);
+ gtk_editable_install_properties (object_class);
+
+ signals[ACTIVATE] =
+ g_signal_new (I_("activate"),
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (GtkSearchEntryClass, activate),
+ NULL, NULL,
+ NULL,
+ G_TYPE_NONE, 0);
/**
* GtkSearchEntry::search-changed:
@@ -254,37 +365,33 @@ gtk_search_entry_class_init (GtkSearchEntryClass *klass)
"previous-match", 0);
gtk_binding_entry_add_signal (binding_set, GDK_KEY_Escape, 0,
"stop-search", 0);
+
+ gtk_widget_class_set_css_name (widget_class, I_("entry"));
}
static void
gtk_search_entry_editable_init (GtkEditableInterface *iface)
{
- parent_editable_iface = g_type_interface_peek_parent (iface);
- iface->do_insert_text = parent_editable_iface->do_insert_text;
- iface->do_delete_text = parent_editable_iface->do_delete_text;
- iface->insert_text = parent_editable_iface->insert_text;
- iface->delete_text = parent_editable_iface->delete_text;
- iface->get_chars = parent_editable_iface->get_chars;
- iface->set_selection_bounds = parent_editable_iface->set_selection_bounds;
- iface->get_selection_bounds = parent_editable_iface->get_selection_bounds;
- iface->set_position = parent_editable_iface->set_position;
- iface->get_position = parent_editable_iface->get_position;
- iface->changed = gtk_search_entry_changed;
+ gtk_editable_delegate_iface_init (iface);
}
static void
-gtk_search_entry_icon_release (GtkEntry *entry,
- GtkEntryIconPosition icon_pos)
+gtk_search_entry_icon_release (GtkGestureMultiPress *press,
+ int n_press,
+ double x,
+ double y,
+ GtkSearchEntry *entry)
{
- if (icon_pos == GTK_ENTRY_ICON_SECONDARY)
- gtk_entry_set_text (entry, "");
+ GtkSearchEntryPrivate *priv = gtk_search_entry_get_instance_private (entry);
+
+ gtk_editable_set_text (GTK_EDITABLE (priv->entry), "");
}
static gboolean
gtk_search_entry_changed_timeout_cb (gpointer user_data)
{
GtkSearchEntry *entry = user_data;
- GtkSearchEntryPrivate *priv = GET_PRIV (entry);
+ GtkSearchEntryPrivate *priv = gtk_search_entry_get_instance_private (entry);
g_signal_emit (entry, signals[SEARCH_CHANGED], 0);
priv->delayed_changed_id = 0;
@@ -295,7 +402,7 @@ gtk_search_entry_changed_timeout_cb (gpointer user_data)
static void
reset_timeout (GtkSearchEntry *entry)
{
- GtkSearchEntryPrivate *priv = GET_PRIV (entry);
+ GtkSearchEntryPrivate *priv = gtk_search_entry_get_instance_private (entry);
if (priv->delayed_changed_id > 0)
g_source_remove (priv->delayed_changed_id);
@@ -306,35 +413,19 @@ reset_timeout (GtkSearchEntry *entry)
}
static void
-gtk_search_entry_changed (GtkEditable *editable)
+gtk_search_entry_changed (GtkEditable *editable,
+ GtkSearchEntry *entry)
{
- GtkSearchEntry *entry = GTK_SEARCH_ENTRY (editable);
- GtkSearchEntryPrivate *priv = GET_PRIV (entry);
- const char *str, *icon_name;
- gboolean cleared;
+ GtkSearchEntryPrivate *priv = gtk_search_entry_get_instance_private (entry);
+ const char *str;
/* Update the icons first */
- str = gtk_entry_get_text (GTK_ENTRY (entry));
+ str = gtk_editable_get_text (GTK_EDITABLE (priv->entry));
if (str == NULL || *str == '\0')
{
- icon_name = NULL;
- cleared = TRUE;
- }
- else
- {
- icon_name = "edit-clear-symbolic";
- cleared = FALSE;
- }
-
- g_object_set (entry,
- "secondary-icon-name", icon_name,
- "secondary-icon-activatable", !cleared,
- "secondary-icon-sensitive", !cleared,
- NULL);
+ gtk_widget_hide (priv->icon);
- if (cleared)
- {
if (priv->delayed_changed_id > 0)
{
g_source_remove (priv->delayed_changed_id);
@@ -344,30 +435,67 @@ gtk_search_entry_changed (GtkEditable *editable)
}
else
{
+ gtk_widget_show (priv->icon);
+
/* Queue up the timeout */
reset_timeout (entry);
}
}
+static void
+notify_cb (GObject *object,
+ GParamSpec *pspec,
+ gpointer data)
+{
+ /* The editable interface properties are already forwarded by the editable delegate setup */
+ if (g_str_equal (pspec->name, "placeholder-text") ||
+ g_str_equal (pspec->name, "activates-default"))
+ g_object_notify (data, pspec->name);
+}
+
+static void
+activate_cb (GtkText *text,
+ gpointer data)
+{
+ g_signal_emit (data, signals[ACTIVATE], 0);
+}
+
static void
gtk_search_entry_init (GtkSearchEntry *entry)
{
+ GtkSearchEntryPrivate *priv = gtk_search_entry_get_instance_private (entry);
AtkObject *atk_obj;
+ GtkGesture *press;
- g_object_set (entry,
- "primary-icon-name", "edit-find-symbolic",
- "primary-icon-activatable", FALSE,
- "primary-icon-sensitive", FALSE,
- NULL);
+ gtk_widget_set_has_surface (GTK_WIDGET (entry), FALSE);
+
+ priv->box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+ gtk_widget_set_parent (priv->box, GTK_WIDGET (entry));
+
+ priv->entry = gtk_text_new ();
+ gtk_widget_set_hexpand (priv->entry, TRUE);
+ gtk_widget_set_vexpand (priv->entry, TRUE);
+ gtk_container_add (GTK_CONTAINER (priv->box), GTK_WIDGET (priv->entry));
+ gtk_editable_set_delegate (GTK_EDITABLE (entry), GTK_EDITABLE (priv->entry));
+ g_signal_connect_swapped (priv->entry, "changed", G_CALLBACK (text_changed), entry);
+ g_signal_connect_after (priv->entry, "changed", G_CALLBACK (gtk_search_entry_changed), entry);
+ g_signal_connect_swapped (priv->entry, "preedit-changed", G_CALLBACK (text_changed), entry);
+ g_signal_connect (priv->entry, "notify", G_CALLBACK (notify_cb), entry);
+ g_signal_connect (priv->entry, "activate", G_CALLBACK (activate_cb), entry);
+
+ priv->icon = gtk_image_new_from_icon_name ("edit-clear-symbolic");
+ gtk_container_add (GTK_CONTAINER (priv->box), GTK_WIDGET (priv->icon));
+ gtk_widget_hide (priv->icon);
+
+ press = gtk_gesture_multi_press_new ();
+ g_signal_connect (press, "released", G_CALLBACK (gtk_search_entry_icon_release), entry);
+ gtk_widget_add_controller (priv->icon, GTK_EVENT_CONTROLLER (press));
atk_obj = gtk_widget_get_accessible (GTK_WIDGET (entry));
if (GTK_IS_ACCESSIBLE (atk_obj))
atk_object_set_name (atk_obj, _("Search"));
gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (entry)), I_("search"));
-
- g_signal_connect (gtk_entry_get_text_widget (GTK_ENTRY (entry)), "preedit-changed",
- G_CALLBACK (gtk_search_entry_preedit_changed), NULL);
}
/**
@@ -431,7 +559,7 @@ gboolean
gtk_search_entry_handle_event (GtkSearchEntry *entry,
GdkEvent *event)
{
- GtkSearchEntryPrivate *priv = GET_PRIV (entry);
+ GtkSearchEntryPrivate *priv = gtk_search_entry_get_instance_private (entry);
gboolean handled;
guint keyval, state;
@@ -544,3 +672,10 @@ gtk_search_entry_get_key_capture_widget (GtkSearchEntry *entry)
return priv->capture_widget;
}
+GtkEventController *
+gtk_search_entry_get_key_controller (GtkSearchEntry *entry)
+{
+ GtkSearchEntryPrivate *priv = gtk_search_entry_get_instance_private (entry);
+
+ return gtk_text_get_key_controller (GTK_TEXT (priv->entry));
+}
diff --git a/gtk/gtksearchentry.h b/gtk/gtksearchentry.h
index cdabb9fcda..5d65fc2e83 100644
--- a/gtk/gtksearchentry.h
+++ b/gtk/gtksearchentry.h
@@ -48,17 +48,18 @@ typedef struct _GtkSearchEntryClass GtkSearchEntryClass;
struct _GtkSearchEntry
{
- GtkEntry parent;
+ GtkWidget parent;
};
struct _GtkSearchEntryClass
{
- GtkEntryClass parent_class;
+ GtkWidgetClass parent_class;
- void (*search_changed) (GtkSearchEntry *entry);
- void (*next_match) (GtkSearchEntry *entry);
- void (*previous_match) (GtkSearchEntry *entry);
- void (*stop_search) (GtkSearchEntry *entry);
+ void (* activate) (GtkSearchEntry *entry);
+ void (* search_changed) (GtkSearchEntry *entry);
+ void (* next_match) (GtkSearchEntry *entry);
+ void (* previous_match) (GtkSearchEntry *entry);
+ void (* stop_search) (GtkSearchEntry *entry);
};
GDK_AVAILABLE_IN_ALL
@@ -78,7 +79,6 @@ GDK_AVAILABLE_IN_ALL
GtkWidget* gtk_search_entry_get_key_capture_widget (GtkSearchEntry *entry);
-
G_END_DECLS
#endif /* __GTK_SEARCH_ENTRY_H__ */
diff --git a/gtk/gtksearchentryprivate.h b/gtk/gtksearchentryprivate.h
index 1b0706d412..46b5d9f5b9 100644
--- a/gtk/gtksearchentryprivate.h
+++ b/gtk/gtksearchentryprivate.h
@@ -32,6 +32,8 @@ G_BEGIN_DECLS
gboolean gtk_search_entry_is_keynav (guint keyval,
GdkModifierType state);
+GtkEventController * gtk_search_entry_get_key_controller (GtkSearchEntry *entry);
+
G_END_DECLS
#endif /* __GTK_SEARCH_ENTRY_PRIVATE_H__ */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]