[gnome-builder] search: simplify search implementation
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder] search: simplify search implementation
- Date: Mon, 21 Dec 2015 07:53:46 +0000 (UTC)
commit 947eb631c9debd578ecbf65485ad0c6d69c9c4f7
Author: Christian Hergert <christian hergert me>
Date: Tue Nov 24 17:04:36 2015 -0800
search: simplify search implementation
This removes the Box and simply subclasses GtkEntry to do the things we
want. We still use a popover to control the popdown list, but manually
control that now.
data/ui/ide-omni-search-entry.ui | 53 -----
libide/search/ide-omni-search-entry.c | 391 +++++++++++++--------------------
2 files changed, 149 insertions(+), 295 deletions(-)
---
diff --git a/libide/search/ide-omni-search-entry.c b/libide/search/ide-omni-search-entry.c
index 1248843..6d1651a 100644
--- a/libide/search/ide-omni-search-entry.c
+++ b/libide/search/ide-omni-search-entry.c
@@ -24,27 +24,31 @@
#include "ide-omni-search-entry.h"
#include "ide-omni-search-display.h"
-#define SHORT_DELAY_TIMEOUT_MSEC 30
-#define LONG_DELAY_TIMEOUT_MSEC 30
+#define SHORT_DELAY_TIMEOUT_MSEC 20
+#define LONG_DELAY_TIMEOUT_MSEC 50
+#define LONG_DELAY_MAX_CHARS 3
+#define RESULTS_PER_PROVIDER 7
struct _IdeOmniSearchEntry
{
- GtkBox parent_instance;
-
- /* Weak references */
- IdeWorkbench *workbench;
- gulong set_focus_handler;
+ GtkBox parent_instance;
/* Template references */
- GtkMenuButton *button;
IdeOmniSearchDisplay *display;
- GtkSearchEntry *entry;
- GtkPopover *popover;
+ GtkEntry *entry;
+ GtkPopover *popover;
+
+ guint delay_timeout;
+};
+
+G_DEFINE_TYPE (IdeOmniSearchEntry, ide_omni_search_entry, GTK_TYPE_ENTRY)
- guint delay_timeout;
+enum {
+ CLEAR_SEARCH,
+ LAST_SIGNAL
};
-G_DEFINE_TYPE (IdeOmniSearchEntry, ide_omni_search_entry, GTK_TYPE_BOX)
+static guint signals [LAST_SIGNAL];
GtkWidget *
ide_omni_search_entry_new (void)
@@ -52,24 +56,53 @@ ide_omni_search_entry_new (void)
return g_object_new (IDE_TYPE_OMNI_SEARCH_ENTRY, NULL);
}
+/**
+ * ide_omni_search_entry_get_search_engine:
+ * @self: An #IdeOmniSearchEntry.
+ *
+ * Gets the search engine to use with the current workbench.
+ *
+ * Returns: (transfer none): An #IdeSearchEngine.
+ */
IdeSearchEngine *
ide_omni_search_entry_get_search_engine (IdeOmniSearchEntry *self)
{
+ IdeWorkbench *workbench;
IdeContext *context;
- IdeSearchEngine *search_engine;
g_return_val_if_fail (IDE_IS_OMNI_SEARCH_ENTRY (self), NULL);
- if (self->workbench == NULL)
+ workbench = ide_widget_get_workbench (GTK_WIDGET (self));
+ if (workbench == NULL)
return NULL;
- context = ide_workbench_get_context (self->workbench);
+ context = ide_workbench_get_context (workbench);
if (context == NULL)
return NULL;
- search_engine = ide_context_get_search_engine (context);
+ return ide_context_get_search_engine (context);
+}
- return search_engine;
+static void
+ide_omni_search_entry_completed (IdeOmniSearchEntry *self,
+ IdeSearchContext *context)
+{
+ g_assert (IDE_IS_OMNI_SEARCH_ENTRY (self));
+ g_assert (IDE_IS_SEARCH_CONTEXT (context));
+
+ if (ide_omni_search_display_get_count (self->display) == 0)
+ {
+ gint position;
+
+ /*
+ * Hiding the popover will cause the entry to get focus,
+ * thereby selecting all available text. We don't want
+ * that to happen.
+ */
+ position = gtk_editable_get_position (GTK_EDITABLE (self));
+ gtk_widget_hide (GTK_WIDGET (self->popover));
+ gtk_editable_set_position (GTK_EDITABLE (self), position);
+ }
}
static gboolean
@@ -80,28 +113,29 @@ ide_omni_search_entry_delay_cb (gpointer user_data)
IdeSearchContext *context;
const gchar *search_text;
- g_return_val_if_fail (IDE_IS_OMNI_SEARCH_ENTRY (self), G_SOURCE_REMOVE);
+ g_assert (IDE_IS_OMNI_SEARCH_ENTRY (self));
self->delay_timeout = 0;
if (self->display)
{
context = ide_omni_search_display_get_context (self->display);
- if (context)
+ if (context != NULL)
ide_search_context_cancel (context);
search_engine = ide_omni_search_entry_get_search_engine (self);
- if (!search_engine)
+ search_text = gtk_entry_get_text (GTK_ENTRY (self));
+ if (search_engine == NULL || search_text == NULL)
return G_SOURCE_REMOVE;
- search_text = gtk_entry_get_text (GTK_ENTRY (self->entry));
- if (!search_text)
- return G_SOURCE_REMOVE;
-
- /* TODO: Remove search text */
context = ide_search_engine_search (search_engine, search_text);
+ g_signal_connect_object (context,
+ "completed",
+ G_CALLBACK (ide_omni_search_entry_completed),
+ self,
+ G_CONNECT_SWAPPED);
ide_omni_search_display_set_context (self->display, context);
- ide_search_context_execute (context, search_text, 7);
+ ide_search_context_execute (context, search_text, RESULTS_PER_PROVIDER);
g_object_unref (context);
}
@@ -109,73 +143,58 @@ ide_omni_search_entry_delay_cb (gpointer user_data)
}
static void
-ide_omni_search_entry_popover_set_visible (IdeOmniSearchEntry *self,
- gboolean visible)
+ide_omni_search_entry_clear_search (IdeOmniSearchEntry *self)
{
- gboolean entry_has_text;
-
- g_return_if_fail (IDE_IS_OMNI_SEARCH_ENTRY (self));
+ g_assert (IDE_IS_OMNI_SEARCH_ENTRY (self));
- entry_has_text = !!(gtk_entry_get_text_length (GTK_ENTRY (self->entry)));
-
- if (visible == gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->button)))
- return;
-
- if (visible && entry_has_text)
- {
- if (!gtk_widget_has_focus (GTK_WIDGET (self->entry)))
- gtk_widget_grab_focus (GTK_WIDGET (self->entry));
-
- gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->button), TRUE);
- }
- else if (!visible)
- {
- gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->button), FALSE);
- }
+ gtk_widget_hide (GTK_WIDGET (self->popover));
+ gtk_entry_set_text (GTK_ENTRY (self), "");
}
static void
-ide_omni_search_entry_entry_activate (IdeOmniSearchEntry *self,
- GtkSearchEntry *entry)
+ide_omni_search_entry_activate (IdeOmniSearchEntry *self)
{
- g_return_if_fail (IDE_IS_OMNI_SEARCH_ENTRY (self));
- g_return_if_fail (GTK_IS_SEARCH_ENTRY (entry));
+ g_assert (IDE_IS_OMNI_SEARCH_ENTRY (self));
- ide_omni_search_display_activate (self->display);
- gtk_entry_set_text (GTK_ENTRY (self->entry), "");
+ gtk_widget_activate (GTK_WIDGET (self->display));
+ ide_omni_search_entry_clear_search (self);
}
static void
-ide_omni_search_entry_entry_changed (IdeOmniSearchEntry *self,
- GtkSearchEntry *entry)
+ide_omni_search_entry_changed (IdeOmniSearchEntry *self)
{
- GtkWidget *button;
- gboolean active;
- gboolean sensitive;
- guint delay_msec = SHORT_DELAY_TIMEOUT_MSEC;
+ const gchar *text;
+ gboolean had_focus;
+ guint position;
g_return_if_fail (IDE_IS_OMNI_SEARCH_ENTRY (self));
- g_return_if_fail (GTK_IS_SEARCH_ENTRY (entry));
- button = GTK_WIDGET (self->button);
- active = gtk_widget_has_focus (GTK_WIDGET (entry)) || (self->delay_timeout != 0);
- sensitive = !!(gtk_entry_get_text_length (GTK_ENTRY (self->entry)));
+ text = gtk_entry_get_text (GTK_ENTRY (self));
+ had_focus = gtk_widget_has_focus (GTK_WIDGET (self));
+ position = gtk_editable_get_position (GTK_EDITABLE (self));
- if (gtk_widget_get_sensitive (button) != sensitive)
- gtk_widget_set_sensitive (button, sensitive);
+ gtk_widget_set_visible (GTK_WIDGET (self->popover), (text != NULL));
- if (active)
- ide_omni_search_entry_popover_set_visible (self, TRUE);
+ /*
+ * Showing the popover could steal focus, so reset the focus to the
+ * entry and reset the position which might get mucked up by focus
+ * changes.
+ */
+ if (had_focus)
+ {
+ gtk_widget_grab_focus (GTK_WIDGET (self));
+ gtk_editable_set_position (GTK_EDITABLE (self), position);
+ }
- if (!self->delay_timeout)
+ if (self->delay_timeout == 0)
{
- const gchar *search_text;
+ guint delay_msec = SHORT_DELAY_TIMEOUT_MSEC;
- search_text = gtk_entry_get_text (GTK_ENTRY (entry));
- if (search_text)
+ if (text != NULL)
{
- if (strlen (search_text) < 3)
+ if (strlen (text) <= LONG_DELAY_MAX_CHARS)
delay_msec = LONG_DELAY_TIMEOUT_MSEC;
+
self->delay_timeout = g_timeout_add (delay_msec,
ide_omni_search_entry_delay_cb,
self);
@@ -183,47 +202,6 @@ ide_omni_search_entry_entry_changed (IdeOmniSearchEntry *self,
}
}
-static gboolean
-ide_omni_search_entry_entry_key_press_event (IdeOmniSearchEntry *self,
- GdkEventKey *key,
- GtkSearchEntry *entry)
-{
- g_return_val_if_fail (IDE_IS_OMNI_SEARCH_ENTRY (self), GDK_EVENT_PROPAGATE);
- g_return_val_if_fail (key, GDK_EVENT_PROPAGATE);
- g_return_val_if_fail (GTK_IS_SEARCH_ENTRY (entry), GDK_EVENT_PROPAGATE);
-
- switch (key->keyval)
- {
- case GDK_KEY_Escape:
- {
- ide_omni_search_entry_popover_set_visible (self, FALSE);
- gtk_widget_grab_focus (gtk_widget_get_toplevel (GTK_WIDGET (entry)));
-
- return GDK_EVENT_STOP;
- }
- break;
-
- case GDK_KEY_Tab:
- case GDK_KEY_KP_Tab:
- if ((key->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK)) != 0)
- break;
- /* Fall through */
- case GDK_KEY_Down:
- case GDK_KEY_KP_Down:
- if (gtk_widget_get_visible (GTK_WIDGET (self->popover)))
- {
- gtk_widget_grab_focus (GTK_WIDGET (self->display));
- return GDK_EVENT_STOP;
- }
- break;
-
- default:
- break;
- }
-
- return GDK_EVENT_PROPAGATE;
-}
-
static void
ide_omni_search_entry_display_result_activated (IdeOmniSearchEntry *self,
IdeSearchResult *result,
@@ -233,164 +211,93 @@ ide_omni_search_entry_display_result_activated (IdeOmniSearchEntry *self,
g_return_if_fail (IDE_IS_SEARCH_RESULT (result));
g_return_if_fail (IDE_IS_OMNI_SEARCH_DISPLAY (display));
- gtk_entry_set_text (GTK_ENTRY (self->entry), "");
+ ide_omni_search_entry_clear_search (self);
}
-static void
-ide_omni_search_entry_grab_focus (GtkWidget *widget)
+static gboolean
+ide_omni_search_entry_popover_key_press_event (IdeOmniSearchEntry *self,
+ GdkEventKey *event,
+ GtkPopover *popover)
{
- IdeOmniSearchEntry *self = (IdeOmniSearchEntry *)widget;
+ g_assert (IDE_IS_OMNI_SEARCH_ENTRY (self));
+ g_assert (event != NULL);
+ g_assert (GTK_IS_POPOVER (popover));
- g_return_if_fail (IDE_IS_OMNI_SEARCH_ENTRY (self));
-
- gtk_widget_grab_focus (GTK_WIDGET (self->entry));
+ return GTK_WIDGET_GET_CLASS (self)->key_press_event (GTK_WIDGET (self), event);
}
static void
-ide_omni_search_entry_workbench_set_focus (IdeOmniSearchEntry *self,
- GtkWidget *focus,
- IdeWorkbench *workbench)
-{
- g_return_if_fail (IDE_IS_OMNI_SEARCH_ENTRY (self));
- g_return_if_fail (!focus || GTK_IS_WIDGET (focus));
- g_return_if_fail (IDE_IS_WORKBENCH (workbench));
-
- if (!focus ||
- (!gtk_widget_is_ancestor (focus, GTK_WIDGET (self)) &&
- !gtk_widget_is_ancestor (focus, GTK_WIDGET (self->popover))))
- {
- gtk_entry_set_text (GTK_ENTRY (self->entry), "");
- }
- else
- {
- ide_omni_search_entry_popover_set_visible (self, TRUE);
- }
-}
-
-static void
-ide_omni_search_entry_map (GtkWidget *widget)
+ide_omni_search_entry_destroy (GtkWidget *widget)
{
IdeOmniSearchEntry *self = (IdeOmniSearchEntry *)widget;
- GtkWidget *toplevel;
- g_return_if_fail (IDE_IS_OMNI_SEARCH_ENTRY (self));
+ ide_clear_source (&self->delay_timeout);
+ g_clear_pointer ((GtkWidget **)&self->popover, gtk_widget_destroy);
- GTK_WIDGET_CLASS (ide_omni_search_entry_parent_class)->map (widget);
-
- gtk_widget_set_sensitive (GTK_WIDGET (self->button), FALSE);
- toplevel = gtk_widget_get_toplevel (widget);
-
- if (IDE_IS_WORKBENCH (toplevel))
- {
- ide_set_weak_pointer (&self->workbench, IDE_WORKBENCH (toplevel));
- self->set_focus_handler =
- g_signal_connect_object (toplevel,
- "set-focus",
- G_CALLBACK (ide_omni_search_entry_workbench_set_focus),
- self,
- G_CONNECT_SWAPPED | G_CONNECT_AFTER);
- }
+ GTK_WIDGET_CLASS (ide_omni_search_entry_parent_class)->destroy (widget);
}
static void
-ide_omni_search_entry_unmap (GtkWidget *widget)
+ide_omni_search_entry_class_init (IdeOmniSearchEntryClass *klass)
{
- IdeOmniSearchEntry *self = (IdeOmniSearchEntry *)widget;
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+ GtkBindingSet *binding_set;
- g_return_if_fail (IDE_IS_OMNI_SEARCH_ENTRY (self));
+ widget_class->destroy = ide_omni_search_entry_destroy;
- if (self->workbench)
- {
- ide_clear_signal_handler (self->workbench, &self->set_focus_handler);
- ide_clear_weak_pointer (&self->workbench);
- }
+ g_signal_override_class_handler ("activate",
+ G_TYPE_FROM_CLASS (klass),
+ G_CALLBACK (ide_omni_search_entry_activate));
- GTK_WIDGET_CLASS (ide_omni_search_entry_parent_class)->unmap (widget);
+ signals [CLEAR_SEARCH] =
+ g_signal_new_class_handler ("clear-search",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+ G_CALLBACK (ide_omni_search_entry_clear_search),
+ NULL, NULL, NULL, G_TYPE_NONE, 0);
+
+ binding_set = gtk_binding_set_by_class (klass);
+ gtk_binding_entry_add_signal (binding_set, GDK_KEY_Escape, 0, "clear-search", 0);
+ gtk_binding_entry_add_signal (binding_set, GDK_KEY_Return, 0, "activate", 0);
+ gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Enter, 0, "activate", 0);
}
static void
-ide_omni_search_entry_constructed (GObject *object)
+ide_omni_search_entry_init (IdeOmniSearchEntry *self)
{
- IdeOmniSearchEntry *self = (IdeOmniSearchEntry *)object;
+ g_object_set (self,
+ "max-width-chars", 50,
+ "primary-icon-name", "edit-find-symbolic",
+ "primary-icon-activatable", FALSE,
+ "primary-icon-sensitive", FALSE,
+ NULL);
+
+ self->popover = g_object_new (GTK_TYPE_POPOVER,
+ "width-request", 500,
+ "relative-to", self,
+ "position", GTK_POS_BOTTOM,
+ NULL);
+
+ g_signal_connect_object (self->popover,
+ "key-press-event",
+ G_CALLBACK (ide_omni_search_entry_popover_key_press_event),
+ self,
+ G_CONNECT_SWAPPED);
- g_return_if_fail (IDE_IS_OMNI_SEARCH_ENTRY (self));
+ self->display = g_object_new (IDE_TYPE_OMNI_SEARCH_DISPLAY,
+ "visible", TRUE,
+ NULL);
- G_OBJECT_CLASS (ide_omni_search_entry_parent_class)->constructed (object);
+ gtk_container_add (GTK_CONTAINER (self->popover), GTK_WIDGET (self->display));
- gtk_popover_set_relative_to (self->popover, GTK_WIDGET (self->entry));
+ g_signal_connect (self,
+ "changed",
+ G_CALLBACK (ide_omni_search_entry_changed),
+ NULL);
- g_signal_connect_object (self->entry,
- "activate",
- G_CALLBACK (ide_omni_search_entry_entry_activate),
- self,
- G_CONNECT_SWAPPED);
- g_signal_connect_object (self->entry,
- "changed",
- G_CALLBACK (ide_omni_search_entry_entry_changed),
- self,
- G_CONNECT_SWAPPED);
- g_signal_connect_object (self->entry,
- "key-press-event",
- G_CALLBACK (ide_omni_search_entry_entry_key_press_event),
- self,
- G_CONNECT_SWAPPED);
g_signal_connect_object (self->display,
"result-activated",
G_CALLBACK (ide_omni_search_entry_display_result_activated),
self,
G_CONNECT_SWAPPED);
}
-
-static void
-ide_omni_search_entry_finalize (GObject *object)
-{
- IdeOmniSearchEntry *self = (IdeOmniSearchEntry *)object;
-
- if (self->delay_timeout)
- {
- g_source_remove (self->delay_timeout);
- self->delay_timeout = 0;
- }
-
- G_OBJECT_CLASS (ide_omni_search_entry_parent_class)->finalize (object);
-}
-
-static void
-ide_omni_search_entry_class_init (IdeOmniSearchEntryClass *klass)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
- GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
-
- object_class->constructed = ide_omni_search_entry_constructed;
- object_class->finalize = ide_omni_search_entry_finalize;
-
- widget_class->grab_focus = ide_omni_search_entry_grab_focus;
- widget_class->map = ide_omni_search_entry_map;
- widget_class->unmap = ide_omni_search_entry_unmap;
-
- gtk_widget_class_set_template_from_resource (widget_class,
"/org/gnome/builder/ui/ide-omni-search-entry.ui");
- gtk_widget_class_bind_template_child (widget_class, IdeOmniSearchEntry, button);
- gtk_widget_class_bind_template_child (widget_class, IdeOmniSearchEntry, display);
- gtk_widget_class_bind_template_child (widget_class, IdeOmniSearchEntry, entry);
- gtk_widget_class_bind_template_child (widget_class, IdeOmniSearchEntry, popover);
-
- g_type_ensure (IDE_TYPE_OMNI_SEARCH_DISPLAY);
-}
-
-static void
-ide_omni_search_entry_init (IdeOmniSearchEntry *self)
-{
- gtk_widget_init_template (GTK_WIDGET (self));
-
- /*
- * WORKAROUND:
- *
- * The GtkWidget template things that popover is a child of ours. When in
- * reality it is a child of the GtkMenuButton (since it owns the "popover"
- * property. Both our widget and the menu button try to call
- * gtk_widget_destroy() on it.
- *
- * https://bugzilla.gnome.org/show_bug.cgi?id=741529
- */
- g_object_ref (self->popover);
-}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]