[gtk+/wip/gbsneto/other-locations] entrycompletion: enable changing popup position
- From: Georges Basile Stavracas Neto <gbsneto src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk+/wip/gbsneto/other-locations] entrycompletion: enable changing popup position
- Date: Fri, 3 Jul 2015 17:49:32 +0000 (UTC)
commit 2212329279c9e0986a1ff96510867c338764e1c2
Author: Georges Basile Stavracas Neto <georges stavracas gmail com>
Date: Fri Jul 3 14:20:30 2015 -0300
entrycompletion: enable changing popup position
GtkEntryCompletion only handles bottom popups, and it's
enought for most cases. There is, however, a considerable
number of situations where it is good to change the position
of the popup window.
The new GtkPlacesView widget, for example, requires that
the popup window is placed above the entry, not below. Since
the widget has a minimal space below, the popup window is
narrowed down to a ridiculous height, only to fit the space
below the entry.
By adding a GtkEntryCompletion::popup-position property,
GtkEntryCompletion is able to know where the user wants to
place the popup window, and reposition it properly.
https://bugzilla.gnome.org/show_bug.cgi?id=751913
gtk/gtkentrycompletion.c | 146 +++++++++++++++++++++++++++++++++++++++----
gtk/gtkentrycompletion.h | 8 +++
gtk/gtkentryprivate.h | 2 +
tests/testentrycompletion.c | 100 ++++++++++++++++++++---------
4 files changed, 212 insertions(+), 44 deletions(-)
---
diff --git a/gtk/gtkentrycompletion.c b/gtk/gtkentrycompletion.c
index 6a8cbf4..798ba9c 100644
--- a/gtk/gtkentrycompletion.c
+++ b/gtk/gtkentrycompletion.c
@@ -112,6 +112,7 @@ enum
PROP_TEXT_COLUMN,
PROP_INLINE_COMPLETION,
PROP_POPUP_COMPLETION,
+ PROP_POPUP_POSITION,
PROP_POPUP_SET_WIDTH,
PROP_POPUP_SINGLE_MATCH,
PROP_INLINE_SELECTION,
@@ -409,6 +410,24 @@ gtk_entry_completion_class_init (GtkEntryCompletionClass *klass)
GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
/**
+ * GtkEntryCompletion:popup-position:
+ *
+ * Determines where the popup window should be positioned
+ * regarding its parent entry.
+ *
+ * Since: 3.18
+ **/
+ g_object_class_install_property (object_class,
+ PROP_POPUP_POSITION,
+ g_param_spec_int ("popup-position",
+ P_("Popup position"),
+ P_("Where the completions window should be positioned"),
+ GTK_POS_LEFT,
+ GTK_POS_BOTTOM,
+ GTK_POS_BOTTOM,
+ GTK_PARAM_READWRITE));
+
+ /**
* GtkEntryCompletion:popup-set-width:
*
* Determines whether the completions popup window will be
@@ -520,6 +539,7 @@ gtk_entry_completion_init (GtkEntryCompletion *completion)
priv->popup_set_width = TRUE;
priv->popup_single_match = TRUE;
priv->inline_selection = FALSE;
+ priv->popup_position = GTK_POS_BOTTOM;
priv->filter_model = NULL;
}
@@ -681,6 +701,11 @@ gtk_entry_completion_set_property (GObject *object,
g_value_get_boolean (value));
break;
+ case PROP_POPUP_POSITION:
+ gtk_entry_completion_set_popup_position (completion,
+ g_value_get_int (value));
+ break;
+
case PROP_POPUP_SET_WIDTH:
gtk_entry_completion_set_popup_set_width (completion,
g_value_get_boolean (value));
@@ -749,6 +774,10 @@ gtk_entry_completion_get_property (GObject *object,
g_value_set_boolean (value, gtk_entry_completion_get_popup_completion (completion));
break;
+ case PROP_POPUP_POSITION:
+ g_value_set_int (value, gtk_entry_completion_get_popup_position (completion));
+ break;
+
case PROP_POPUP_SET_WIDTH:
g_value_set_boolean (value, gtk_entry_completion_get_popup_set_width (completion));
break;
@@ -1484,7 +1513,6 @@ gtk_entry_completion_list_motion_notify (GtkWidget *widget,
return FALSE;
}
-
/* some nasty size requisition */
gboolean
_gtk_entry_completion_resize_popup (GtkEntryCompletion *completion)
@@ -1505,8 +1533,10 @@ _gtk_entry_completion_resize_popup (GtkEntryCompletion *completion)
gint width;
GtkTreeViewColumn *action_column;
gint action_height;
+ GtkPositionType pos;
window = gtk_widget_get_window (completion->priv->entry);
+ pos = completion->priv->popup_position;
if (!window)
return FALSE;
@@ -1514,17 +1544,15 @@ _gtk_entry_completion_resize_popup (GtkEntryCompletion *completion)
if (!completion->priv->filter_model)
return FALSE;
+ matches = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (completion->priv->filter_model), NULL);
+ actions = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (completion->priv->actions), NULL);
+ action_column = gtk_tree_view_get_column (GTK_TREE_VIEW (completion->priv->action_view), 0);
+
gtk_widget_get_allocation (completion->priv->entry, &allocation);
gtk_widget_get_preferred_size (completion->priv->entry,
&entry_req, NULL);
gdk_window_get_origin (window, &x, &y);
- x += allocation.x;
- y += allocation.y + (allocation.height - entry_req.height) / 2;
-
- matches = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (completion->priv->filter_model), NULL);
- actions = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (completion->priv->actions), NULL);
- action_column = gtk_tree_view_get_column (GTK_TREE_VIEW (completion->priv->action_view), 0);
/* Call get preferred size on the on the tree view to force it to validate its
* cells before calling into the cell size functions.
@@ -1578,21 +1606,65 @@ _gtk_entry_completion_resize_popup (GtkEntryCompletion *completion)
gtk_widget_get_preferred_size (completion->priv->popup_window,
&popup_req, NULL);
+ above = FALSE;
+
+ switch (pos)
+ {
+ case GTK_POS_TOP:
+ x += allocation.x;
+ y -= popup_req.height;
+ above = TRUE;
+ break;
+
+ case GTK_POS_RIGHT:
+ x += allocation.x + width;
+ break;
+
+ case GTK_POS_BOTTOM:
+ x += allocation.x;
+ y += allocation.y + (allocation.height - entry_req.height) / 2;
+ break;
+
+ case GTK_POS_LEFT:
+ x += allocation.x - width;
+ break;
+
+ default:
+ g_assert_not_reached ();
+ }
+
+
if (x < monitor.x)
x = monitor.x;
else if (x + popup_req.width > monitor.x + monitor.width)
x = monitor.x + monitor.width - popup_req.width;
- if (y + entry_req.height + popup_req.height <= monitor.y + monitor.height ||
- y - monitor.y < (monitor.y + monitor.height) - (y + entry_req.height))
+ if (pos == GTK_POS_TOP)
{
- y += entry_req.height;
- above = FALSE;
+ if (y < monitor.y)
+ {
+ y += popup_req.height + allocation.y + (allocation.height - entry_req.height) / 2 +
entry_req.height;
+ above = FALSE;
+ }
+ else
+ {
+ y += entry_req.height;
+ above = TRUE;
+ }
}
else
{
- y -= popup_req.height;
- above = TRUE;
+ if (y + entry_req.height + popup_req.height <= monitor.y + monitor.height ||
+ y - monitor.y < (monitor.y + monitor.height) - (y + entry_req.height))
+ {
+ y += entry_req.height;
+ above = FALSE;
+ }
+ else
+ {
+ y -= popup_req.height;
+ above = TRUE;
+ }
}
if (matches > 0)
@@ -2149,6 +2221,54 @@ gtk_entry_completion_get_inline_selection (GtkEntryCompletion *completion)
return completion->priv->inline_selection;
}
+/**
+ * gtk_entry_completion_get_popup_position:
+ * @completion: a #GtkEntryCompletion
+ *
+ * Returns the position relative to the #GtkEntry where
+ * the popup window is placed.
+ *
+ * Returns: a #GtkPositionType.
+ *
+ * Since: 3.18
+ */
+GtkPositionType
+gtk_entry_completion_get_popup_position (GtkEntryCompletion *completion)
+{
+ g_return_val_if_fail (GTK_IS_ENTRY_COMPLETION (completion), 0);
+
+ return completion->priv->popup_position;
+}
+
+/**
+ * gtk_entry_completion_set_popup_position:
+ * @completion: a #GtkEntryCompletion
+ * @position: a #GtkPositionType
+ *
+ * Sets where the popup window will be placed relatively
+ * to the #GtkEntry of @completion.
+ *
+ * Since: 3.18
+ */
+void
+gtk_entry_completion_set_popup_position (GtkEntryCompletion *completion,
+ GtkPositionType position)
+{
+ g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
+
+ if (completion->priv->popup_position != position)
+ {
+ completion->priv->popup_position = position;
+
+ if (gtk_widget_get_visible (completion->priv->popup_window))
+ {
+ _gtk_entry_completion_popdown (completion);
+ gtk_entry_completion_popup(completion);
+ }
+
+ g_object_notify (G_OBJECT (completion), "popup-position");
+ }
+}
static gint
gtk_entry_completion_timeout (gpointer data)
diff --git a/gtk/gtkentrycompletion.h b/gtk/gtkentrycompletion.h
index d0f5d08..df22d99 100644
--- a/gtk/gtkentrycompletion.h
+++ b/gtk/gtkentrycompletion.h
@@ -23,6 +23,7 @@
#endif
#include <gdk/gdk.h>
+#include <gtk/gtkenums.h>
#include <gtk/gtktreemodel.h>
#include <gtk/gtkliststore.h>
#include <gtk/gtkcellarea.h>
@@ -177,6 +178,13 @@ void gtk_entry_completion_set_text_column (GtkEntryComplet
GDK_AVAILABLE_IN_ALL
gint gtk_entry_completion_get_text_column (GtkEntryCompletion *completion);
+GDK_AVAILABLE_IN_3_18
+GtkPositionType gtk_entry_completion_get_popup_position (GtkEntryCompletion *completion);
+
+GDK_AVAILABLE_IN_3_18
+void gtk_entry_completion_set_popup_position (GtkEntryCompletion *completion,
+ GtkPositionType position);
+
G_END_DECLS
#endif /* __GTK_ENTRY_COMPLETION_H__ */
diff --git a/gtk/gtkentryprivate.h b/gtk/gtkentryprivate.h
index 7e84d06..f55ac45 100644
--- a/gtk/gtkentryprivate.h
+++ b/gtk/gtkentryprivate.h
@@ -51,6 +51,8 @@ struct _GtkEntryCompletionPrivate
GtkWidget *scrolled_window;
GtkWidget *action_view;
+ GtkPositionType popup_position;
+
gulong completion_timeout;
gulong changed_id;
gulong insert_text_id;
diff --git a/tests/testentrycompletion.c b/tests/testentrycompletion.c
index 16f7f10..dfc358f 100644
--- a/tests/testentrycompletion.c
+++ b/tests/testentrycompletion.c
@@ -286,13 +286,26 @@ match_selected_cb (GtkEntryCompletion *completion,
return TRUE;
}
-int
+void
+position_radio_activated_cb (GtkRadioButton *button,
+ GtkEntryCompletion *completion)
+{
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
+ {
+ GtkPositionType pos = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (button), "position"));
+ gtk_entry_completion_set_popup_position (completion, pos);
+ }
+}
+
+int
main (int argc, char *argv[])
{
GtkWidget *vbox;
+ GtkWidget *hbox;
GtkWidget *label;
GtkWidget *entry;
- GtkEntryCompletion *completion;
+ GtkWidget *button;
+ GtkEntryCompletion *completion[4];
GtkTreeModel *completion_model;
GtkCellRenderer *cell;
@@ -316,83 +329,83 @@ main (int argc, char *argv[])
entry = gtk_entry_new ();
/* Create the completion object */
- completion = gtk_entry_completion_new ();
- gtk_entry_completion_set_inline_completion (completion, TRUE);
+ completion[0] = gtk_entry_completion_new ();
+ gtk_entry_completion_set_inline_completion (completion[0], TRUE);
/* Assign the completion to the entry */
- gtk_entry_set_completion (GTK_ENTRY (entry), completion);
- g_object_unref (completion);
+ gtk_entry_set_completion (GTK_ENTRY (entry), completion[0]);
+ g_object_unref (completion[0]);
gtk_container_add (GTK_CONTAINER (vbox), entry);
/* Create a tree model and use it as the completion model */
completion_model = create_simple_completion_model ();
- gtk_entry_completion_set_model (completion, completion_model);
+ gtk_entry_completion_set_model (completion[0], completion_model);
g_object_unref (completion_model);
/* Use model column 0 as the text column */
- gtk_entry_completion_set_text_column (completion, 0);
+ gtk_entry_completion_set_text_column (completion[0], 0);
/* Create our second entry */
entry = gtk_entry_new ();
/* Create the completion object */
- completion = gtk_entry_completion_new ();
+ completion[1] = gtk_entry_completion_new ();
/* Assign the completion to the entry */
- gtk_entry_set_completion (GTK_ENTRY (entry), completion);
- g_object_unref (completion);
+ gtk_entry_set_completion (GTK_ENTRY (entry), completion[1]);
+ g_object_unref (completion[1]);
gtk_container_add (GTK_CONTAINER (vbox), entry);
/* Create a tree model and use it as the completion model */
completion_model = create_completion_model ();
- gtk_entry_completion_set_model (completion, completion_model);
- gtk_entry_completion_set_minimum_key_length (completion, 2);
+ gtk_entry_completion_set_model (completion[1], completion_model);
+ gtk_entry_completion_set_minimum_key_length (completion[1], 2);
g_object_unref (completion_model);
/* Use model column 1 as the text column */
cell = gtk_cell_renderer_pixbuf_new ();
- gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (completion), cell, FALSE);
- gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (completion), cell,
+ gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (completion[1]), cell, FALSE);
+ gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (completion[1]), cell,
"pixbuf", 0, NULL);
cell = gtk_cell_renderer_text_new ();
- gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (completion), cell, FALSE);
- gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (completion), cell,
+ gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (completion[1]), cell, FALSE);
+ gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (completion[1]), cell,
"text", 1, NULL);
- gtk_entry_completion_set_match_func (completion, match_func, NULL, NULL);
- g_signal_connect (completion, "match-selected",
+ gtk_entry_completion_set_match_func (completion[1], match_func, NULL, NULL);
+ g_signal_connect (completion[1], "match-selected",
G_CALLBACK (match_selected_cb), NULL);
- gtk_entry_completion_insert_action_text (completion, 100, "action!");
- gtk_entry_completion_insert_action_text (completion, 101, "'nother action!");
- g_signal_connect (completion, "action_activated", G_CALLBACK (activated_cb), NULL);
+ gtk_entry_completion_insert_action_text (completion[1], 100, "action!");
+ gtk_entry_completion_insert_action_text (completion[1], 101, "'nother action!");
+ g_signal_connect (completion[1], "action_activated", G_CALLBACK (activated_cb), NULL);
/* Create our third entry */
entry = gtk_entry_new ();
/* Create the completion object */
- completion = gtk_entry_completion_new ();
+ completion[2] = gtk_entry_completion_new ();
/* Assign the completion to the entry */
- gtk_entry_set_completion (GTK_ENTRY (entry), completion);
- g_object_unref (completion);
+ gtk_entry_set_completion (GTK_ENTRY (entry), completion[2]);
+ g_object_unref (completion[2]);
gtk_container_add (GTK_CONTAINER (vbox), entry);
/* Create a tree model and use it as the completion model */
completion_model = GTK_TREE_MODEL (gtk_list_store_new (1, G_TYPE_STRING));
- gtk_entry_completion_set_model (completion, completion_model);
+ gtk_entry_completion_set_model (completion[2], completion_model);
g_object_unref (completion_model);
/* Use model column 0 as the text column */
- gtk_entry_completion_set_text_column (completion, 0);
+ gtk_entry_completion_set_text_column (completion[2], 0);
/* Fill the completion dynamically */
- gdk_threads_add_timeout (1000, (GSourceFunc) animation_timer, completion);
+ gdk_threads_add_timeout (1000, (GSourceFunc) animation_timer, completion[2]);
/* Fourth entry */
gtk_box_pack_start (GTK_BOX (vbox), gtk_label_new ("Model-less entry completion"), FALSE, FALSE, 0);
@@ -400,14 +413,39 @@ main (int argc, char *argv[])
entry = gtk_entry_new ();
/* Create the completion object */
- completion = gtk_entry_completion_new ();
+ completion[3] = gtk_entry_completion_new ();
/* Assign the completion to the entry */
- gtk_entry_set_completion (GTK_ENTRY (entry), completion);
- g_object_unref (completion);
+ gtk_entry_set_completion (GTK_ENTRY (entry), completion[3]);
+ g_object_unref (completion[3]);
gtk_container_add (GTK_CONTAINER (vbox), entry);
+ /* Position selector */
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+ button = gtk_radio_button_new_with_label (NULL, "Bottom");
+ g_object_set_data (G_OBJECT (button), "position", GINT_TO_POINTER (GTK_POS_BOTTOM));
+ g_signal_connect (button, "toggled", G_CALLBACK (position_radio_activated_cb), completion[0]);
+ gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
+
+ button = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON (button), "Top");
+ g_object_set_data (G_OBJECT (button), "position", GINT_TO_POINTER (GTK_POS_TOP));
+ g_signal_connect (button, "toggled", G_CALLBACK (position_radio_activated_cb), completion[0]);
+ gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
+
+ button = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON (button), "Left");
+ g_object_set_data (G_OBJECT (button), "position", GINT_TO_POINTER (GTK_POS_LEFT));
+ g_signal_connect (button, "toggled", G_CALLBACK (position_radio_activated_cb), completion[0]);
+ gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
+
+ button = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON (button), "Right");
+ g_object_set_data (G_OBJECT (button), "position", GINT_TO_POINTER (GTK_POS_RIGHT));
+ g_signal_connect (button, "toggled", G_CALLBACK (position_radio_activated_cb), completion[0]);
+ gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
+
+ gtk_container_add (GTK_CONTAINER (vbox), gtk_label_new ("Position of the completion popup"));
+ gtk_container_add (GTK_CONTAINER (vbox), hbox);
+
gtk_widget_show_all (window);
gtk_main ();
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]