[gnome-builder/editor-layout] command-bar: Implement tab completion
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder/editor-layout] command-bar: Implement tab completion
- Date: Wed, 26 Nov 2014 03:47:59 +0000 (UTC)
commit c5ee616c4081a0a1e7f8c47027af02cd7e86147c
Author: Alexander Larsson <alexl redhat com>
Date: Mon Nov 24 15:42:14 2014 +0100
command-bar: Implement tab completion
When you press tab we expand to the longest common prefix from, and
if no common prefix exists we show the available matches in a list
(multiple tabs scroll in the list).
https://bugzilla.gnome.org/show_bug.cgi?id=740629
src/commands/gb-command-bar.c | 156 ++++++++++++++++++++++++++++++++++++
src/resources/ui/gb-command-bar.ui | 17 ++++
2 files changed, 173 insertions(+), 0 deletions(-)
---
diff --git a/src/commands/gb-command-bar.c b/src/commands/gb-command-bar.c
index cdf7734..bfae313 100644
--- a/src/commands/gb-command-bar.c
+++ b/src/commands/gb-command-bar.c
@@ -31,6 +31,9 @@ struct _GbCommandBarPrivate
GtkEntry *entry;
GtkListBox *list_box;
GtkScrolledWindow *scroller;
+ GtkScrolledWindow *completion_scroller;
+ GtkFlowBox *flow_box;
+ gchar *last_completion;
};
G_DEFINE_TYPE_WITH_PRIVATE (GbCommandBar, gb_command_bar, GTK_TYPE_REVEALER)
@@ -66,6 +69,8 @@ gb_command_bar_show (GbCommandBar *bar)
{
g_return_if_fail (GB_IS_COMMAND_BAR (bar));
+ gtk_widget_hide (GTK_WIDGET (bar->priv->completion_scroller));
+
gtk_revealer_set_reveal_child (GTK_REVEALER (bar), TRUE);
gtk_entry_set_text (bar->priv->entry, "");
gtk_widget_grab_focus (GTK_WIDGET (bar->priv->entry));
@@ -121,6 +126,8 @@ gb_command_bar_on_entry_activate (GbCommandBar *bar,
if (!workbench)
return;
+ gtk_widget_hide (GTK_WIDGET (bar->priv->completion_scroller));
+
if (!gb_str_empty0 (text))
{
GbCommandManager *manager;
@@ -183,6 +190,44 @@ gb_command_bar_grab_focus (GtkWidget *widget)
gtk_widget_grab_focus (GTK_WIDGET (bar->priv->entry));
}
+static gchar *
+find_longest_common_prefix (gchar **strv)
+{
+ gchar *lcp = NULL;
+ gchar *lcp_end = NULL;
+ int i;
+
+ for (i = 0; strv[i] != NULL; i++)
+ {
+ gchar *str = strv[i];
+ if (lcp == NULL)
+ {
+ lcp = str;
+ lcp_end = str + strlen (str);
+ }
+ else
+ {
+ gchar *tmp = lcp;
+
+ while (tmp < lcp_end && *str != 0 && *tmp == *str)
+ {
+ str++;
+ tmp++;
+ }
+
+ lcp_end = tmp;
+ }
+ }
+
+ if (lcp == NULL)
+ return g_strdup ("");
+
+ return g_strndup (lcp, lcp_end - lcp);
+}
+
+#define N_COMPLETION_COLUMS 3
+#define N_UNSCROLLED_COMPLETION_ROWS 4
+
static gboolean
gb_command_bar_on_entry_key_press_event (GbCommandBar *bar,
GdkEventKey *event,
@@ -198,6 +243,111 @@ gb_command_bar_on_entry_key_press_event (GbCommandBar *bar,
return TRUE;
}
+ if (event->keyval == GDK_KEY_Tab)
+ {
+ GbWorkbench *workbench = NULL;
+
+ workbench = GB_WORKBENCH (gtk_widget_get_toplevel (GTK_WIDGET (bar)));
+ if (workbench)
+ {
+ GtkEditable *editable = GTK_EDITABLE (bar->priv->entry);
+ GtkWidget *viewport = gtk_bin_get_child (GTK_BIN (bar->priv->completion_scroller));
+ GbCommandManager *manager;
+ gchar **completions;
+ int pos, i;
+ gchar *current_prefix, *expanded_prefix;
+
+ pos = gtk_editable_get_position (editable);
+ current_prefix = gtk_editable_get_chars (editable, 0, pos);
+
+ /* If we complete again with the same data we scroll the completion instead */
+ if (gtk_widget_is_visible (GTK_WIDGET (bar->priv->completion_scroller)) &&
+ bar->priv->last_completion != NULL &&
+ strcmp (bar->priv->last_completion, current_prefix) == 0)
+ {
+ GtkAdjustment *vadj = gtk_scrolled_window_get_vadjustment (bar->priv->completion_scroller);
+ int viewport_height = gtk_widget_get_allocated_height (viewport);
+ int y = gtk_adjustment_get_value (vadj);
+ int max = gtk_adjustment_get_upper (vadj);
+
+ y += viewport_height;
+ if (y >= max)
+ y = 0;
+
+ gtk_adjustment_set_value (vadj, y);
+ }
+ else
+ {
+ g_clear_pointer (&bar->priv->last_completion, g_free);
+
+ manager = gb_workbench_get_command_manager (workbench);
+ completions = gb_command_manager_complete (manager, current_prefix);
+
+ expanded_prefix = find_longest_common_prefix (completions);
+
+ if (strlen (expanded_prefix) > strlen (current_prefix))
+ {
+ gtk_widget_hide (GTK_WIDGET (bar->priv->completion_scroller));
+ gtk_editable_insert_text (editable, expanded_prefix + strlen (current_prefix), -1, &pos);
+ gtk_editable_set_position (editable, pos);
+ }
+ else if (g_strv_length (completions) > 1)
+ {
+ gint wrapped_height = 0;
+ bar->priv->last_completion = g_strdup (current_prefix);
+
+ gtk_widget_show (GTK_WIDGET (bar->priv->completion_scroller));
+ gtk_container_foreach (GTK_CONTAINER (bar->priv->flow_box),
+ (GtkCallback)gtk_widget_destroy, NULL);
+
+
+ gtk_flow_box_set_min_children_per_line (bar->priv->flow_box, N_COMPLETION_COLUMS);
+ gtk_flow_box_set_max_children_per_line (bar->priv->flow_box, N_COMPLETION_COLUMS);
+
+ for (i = 0; completions[i] != NULL; i++)
+ {
+ GtkWidget *label;
+ char *s;
+
+ label = gtk_label_new ("");
+ s = g_strdup_printf ("<b>%s</b>%s", current_prefix, completions[i] + strlen
(current_prefix));
+ gtk_label_set_markup (GTK_LABEL (label), s);
+ g_free (s);
+
+ gtk_container_add (GTK_CONTAINER (bar->priv->flow_box), label);
+ gtk_widget_show (label);
+
+ if (i == N_COMPLETION_COLUMS * N_UNSCROLLED_COMPLETION_ROWS - 1)
+ gtk_widget_get_preferred_height (GTK_WIDGET (bar->priv->flow_box), &wrapped_height,
NULL);
+ }
+
+ if (i < N_COMPLETION_COLUMS * N_UNSCROLLED_COMPLETION_ROWS)
+ {
+ gtk_widget_set_size_request (GTK_WIDGET (bar->priv->completion_scroller), -1, -1);
+ gtk_scrolled_window_set_policy (bar->priv->completion_scroller,
+ GTK_POLICY_NEVER, GTK_POLICY_NEVER);
+ }
+ else
+ {
+ gtk_widget_set_size_request (GTK_WIDGET (bar->priv->completion_scroller), -1,
wrapped_height);
+ gtk_scrolled_window_set_policy (bar->priv->completion_scroller,
+ GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+ }
+ }
+ else
+ gtk_widget_hide (GTK_WIDGET (bar->priv->completion_scroller));
+
+ g_free (expanded_prefix);
+
+ g_strfreev (completions);
+ }
+
+ g_free (current_prefix);
+
+ return TRUE;
+ }
+ }
+
return GDK_EVENT_PROPAGATE;
}
@@ -259,6 +409,10 @@ gb_command_bar_constructed (GObject *object)
static void
gb_command_bar_finalize (GObject *object)
{
+ GbCommandBar *bar = (GbCommandBar *)object;
+
+ g_clear_pointer (&bar->priv->last_completion, g_free);
+
G_OBJECT_CLASS (gb_command_bar_parent_class)->finalize (object);
}
@@ -280,6 +434,8 @@ gb_command_bar_class_init (GbCommandBarClass *klass)
gtk_widget_class_bind_template_child_private (widget_class, GbCommandBar, list_box);
gtk_widget_class_bind_template_child_private (widget_class, GbCommandBar, scroller);
gtk_widget_class_bind_template_child_private (widget_class, GbCommandBar, result_size_group);
+ gtk_widget_class_bind_template_child_private (widget_class, GbCommandBar, completion_scroller);
+ gtk_widget_class_bind_template_child_private (widget_class, GbCommandBar, flow_box);
}
static void
diff --git a/src/resources/ui/gb-command-bar.ui b/src/resources/ui/gb-command-bar.ui
index 7c9f4c1..e52362b 100644
--- a/src/resources/ui/gb-command-bar.ui
+++ b/src/resources/ui/gb-command-bar.ui
@@ -30,7 +30,24 @@
<child>
<object class="GtkSeparator" id="hsep1">
<property name="orientation">horizontal</property>
+ <property name="visible">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="completion_scroller">
+ <property name="hscrollbar_policy">GTK_POLICY_NEVER</property>
<property name="visible">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">False</property>
+ <child>
+ <object class="GtkFlowBox" id="flow_box">
+ <property name="visible">True</property>
+ <property name="column-spacing">8</property>
+ <property name="halign">start</property>
+ <property name="hexpand">False</property>
+ <property name="selection_mode">GTK_SELECTION_NONE</property>
+ </object>
+ </child>
</object>
</child>
<child>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]