[gtk/suggestion-entry: 2/4] suggestionentry: Add an optional button



commit 33a89cd64868c233100c87487ef46ac6e66f8515
Author: Matthias Clasen <mclasen redhat com>
Date:   Fri Jun 26 17:41:33 2020 -0400

    suggestionentry: Add an optional button
    
    This is useful for comboboxtext-like use cases.
    To simulate comboboxtext behavior, set
    use-filter = FALSE
    show-button = TRUE
    insert-selection = TRUE

 docs/reference/gtk/gtk4-sections.txt |   2 +
 gtk/gtksuggestionentry.c             | 154 +++++++++++++++++++++++++++++++----
 gtk/gtksuggestionentry.h             |   6 ++
 3 files changed, 144 insertions(+), 18 deletions(-)
---
diff --git a/docs/reference/gtk/gtk4-sections.txt b/docs/reference/gtk/gtk4-sections.txt
index f68396dbea..7763c7d6ad 100644
--- a/docs/reference/gtk/gtk4-sections.txt
+++ b/docs/reference/gtk/gtk4-sections.txt
@@ -7617,4 +7617,6 @@ gtk_suggestion_entry_set_insert_selection
 gtk_suggestion_entry_get_insert_selection
 gtk_suggestion_entry_set_insert_prefix
 gtk_suggestion_entry_get_insert_prefix
+gtk_suggestion_entry_set_show_button
+gtk_suggestion_entry_get_show_button
 </SECTION>
diff --git a/gtk/gtksuggestionentry.c b/gtk/gtksuggestionentry.c
index 61d61adc3a..5b1b6d16e5 100644
--- a/gtk/gtksuggestionentry.c
+++ b/gtk/gtksuggestionentry.c
@@ -44,6 +44,9 @@
 #include "gtkscrolledwindow.h"
 #include "gtkeventcontrollerkey.h"
 #include "gtkeventcontrollerfocus.h"
+#include "gtkbox.h"
+#include "gtkgizmoprivate.h"
+#include "gtkactionable.h"
 
 
 /**
@@ -75,14 +78,19 @@
  * # CSS Nodes
  *
  * |[<!-- language="plain" -->
- * entry.suggestion
- * ├── text
- * ╰── popover
+ * widget
+ * ╰── box
+ *     ├── entry.suggestion
+ *     │   ├── text
+ *     │   ╰── popover
+ *     ╰── [button]
  * ]|
  *
  * GtkSuggestionEntry has a single CSS node with name entry that carries
  * a .sugggestion style class, and the text and popover nodes are children
- * of that.
+ * of that. The parent of the entry node is a box node, which also contains
+ * the CSS node for the button (which may be hidden). The parent of the box
+ * node is a widget node.
  */
 
 struct _GtkSuggestionEntry
@@ -96,6 +104,9 @@ struct _GtkSuggestionEntry
   GtkFilterListModel *filter_model;
   GtkSingleSelection *selection;
 
+  GtkWidget *box;
+  GtkWidget *gizmo;
+  GtkWidget *button;
   GtkWidget *entry;
   GtkWidget *popup;
   GtkWidget *list;
@@ -107,6 +118,7 @@ struct _GtkSuggestionEntry
   guint use_filter       : 1;
   guint insert_selection : 1;
   guint insert_prefix    : 1;
+  guint show_button      : 1;
 };
 
 typedef struct _GtkSuggestionEntryClass GtkSuggestionEntryClass;
@@ -127,6 +139,7 @@ enum
   PROP_USE_FILTER,
   PROP_INSERT_PREFIX,
   PROP_INSERT_SELECTION,
+  PROP_SHOW_BUTTON,
 
   N_PROPERTIES,
 };
@@ -162,8 +175,7 @@ gtk_suggestion_entry_dispose (GObject *object)
       g_signal_handler_disconnect (self->entry, self->changed_id);
       self->changed_id = 0;
     }
-  g_clear_pointer (&self->popup, gtk_widget_unparent);
-  g_clear_pointer (&self->entry, gtk_widget_unparent);
+  g_clear_pointer (&self->box, gtk_widget_unparent);
 
   g_clear_pointer (&self->expression, gtk_expression_unref);
   g_clear_object (&self->factory);
@@ -207,7 +219,7 @@ gtk_suggestion_entry_get_property (GObject    *object,
       break;
 
     case PROP_POPUP_VISIBLE:
-      g_value_set_boolean (value, gtk_widget_get_visible (self->popup));
+      g_value_set_boolean (value, self->popup && gtk_widget_get_visible (self->popup));
       break;
 
     case PROP_USE_FILTER:
@@ -222,6 +234,10 @@ gtk_suggestion_entry_get_property (GObject    *object,
       g_value_set_boolean (value, gtk_suggestion_entry_get_insert_prefix (self));
       break;
 
+    case PROP_SHOW_BUTTON:
+      g_value_set_boolean (value, gtk_suggestion_entry_get_show_button (self));
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
       break;
@@ -273,6 +289,10 @@ gtk_suggestion_entry_set_property (GObject      *object,
       gtk_suggestion_entry_set_insert_prefix (self, g_value_get_boolean (value));
       break;
 
+    case PROP_SHOW_BUTTON:
+      gtk_suggestion_entry_set_show_button (self, g_value_get_boolean (value));
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
       break;
@@ -290,7 +310,7 @@ gtk_suggestion_entry_measure (GtkWidget      *widget,
 {
   GtkSuggestionEntry *self = GTK_SUGGESTION_ENTRY (widget);
 
-  gtk_widget_measure (self->entry,
+  gtk_widget_measure (self->box,
                       orientation,
                       size,
                       minimum, natural,
@@ -305,11 +325,8 @@ gtk_suggestion_entry_size_allocate (GtkWidget *widget,
 {
   GtkSuggestionEntry *self = GTK_SUGGESTION_ENTRY (widget);
 
-  gtk_widget_size_allocate (self->entry, &(GtkAllocation) { 0, 0, width, height }, baseline);
+  gtk_widget_size_allocate (self->box, &(GtkAllocation) { 0, 0, width, height }, baseline);
 
-  gtk_widget_set_size_request (self->popup, gtk_widget_get_allocated_width (widget), -1);
-
-  gtk_native_check_resize (GTK_NATIVE (self->popup));
 }
 
 static gboolean
@@ -431,11 +448,16 @@ gtk_suggestion_entry_class_init (GtkSuggestionEntryClass *klass)
                             FALSE,
                             G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
 
+  properties[PROP_SHOW_BUTTON] =
+      g_param_spec_boolean ("show-button",
+                            P_("Show button"),
+                            P_("Whether to show a button for presenting the popup"),
+                            FALSE,
+                            G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
+
   g_object_class_install_properties (object_class, N_PROPERTIES, properties);
   gtk_editable_install_properties (object_class, N_PROPERTIES);
 
-  gtk_widget_class_set_css_name (widget_class, I_("entry"));
-
   /**
    * GtkSuggestionEntry|popup.show:
    *
@@ -775,6 +797,46 @@ set_default_factory (GtkSuggestionEntry *self)
   g_object_unref (factory);
 }
 
+static void
+measure_entry (GtkGizmo       *gizmo,
+               GtkOrientation  orientation,
+               gint            size,
+               gint           *minimum,
+               gint           *natural,
+               gint           *minimum_baseline,
+               gint           *natural_baseline)
+{
+  gtk_widget_measure (gtk_widget_get_first_child (GTK_WIDGET (gizmo)),
+                      orientation, size,
+                      minimum, natural,
+                      minimum_baseline, natural_baseline);
+}
+
+static void
+allocate_entry (GtkGizmo *gizmo,
+                int       width,
+                int       height,
+                int       baseline)
+{
+  GtkSuggestionEntry *self;
+
+  self = GTK_SUGGESTION_ENTRY (gtk_widget_get_parent (gtk_widget_get_parent (GTK_WIDGET (gizmo))));
+
+  gtk_widget_size_allocate (gtk_widget_get_first_child (GTK_WIDGET (gizmo)),
+                            &(GtkAllocation){ 0, 0, width, height },
+                            baseline);
+
+  gtk_widget_set_size_request (self->popup, gtk_widget_get_allocated_width (GTK_WIDGET (gizmo)), -1);
+
+  gtk_native_check_resize (GTK_NATIVE (self->popup));
+}
+
+static gboolean
+grab_focus_entry (GtkGizmo *gizmo)
+{
+  return gtk_widget_grab_focus (gtk_widget_get_first_child (GTK_WIDGET (gizmo)));
+}
+
 static void
 gtk_suggestion_entry_init (GtkSuggestionEntry *self)
 {
@@ -783,19 +845,38 @@ gtk_suggestion_entry_init (GtkSuggestionEntry *self)
 
   self->use_filter = TRUE;
 
+  self->box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+  gtk_widget_add_css_class (self->box, "linked");
+  gtk_widget_set_hexpand (self->box, TRUE);
+  gtk_widget_set_parent (self->box, GTK_WIDGET (self));
+
+  self->gizmo = gtk_gizmo_new ("entry", measure_entry, allocate_entry, NULL, NULL,
+                               (GtkGizmoFocusFunc)gtk_widget_focus_child,
+                               grab_focus_entry);
+  gtk_widget_add_css_class (self->gizmo, "suggestion");
+  gtk_widget_set_hexpand (self->gizmo, TRUE);
+  gtk_box_append (GTK_BOX (self->box), self->gizmo);
+
   self->entry = gtk_text_new ();
-  gtk_widget_set_parent (self->entry, GTK_WIDGET (self));
+  gtk_widget_set_parent (self->entry, self->gizmo);
   gtk_widget_set_hexpand (self->entry, TRUE);
   gtk_editable_init_delegate (GTK_EDITABLE (self));
   self->changed_id = g_signal_connect (self->entry, "notify::text", G_CALLBACK (text_changed), self);
 
+  self->button = gtk_toggle_button_new ();
+  gtk_button_set_icon_name (GTK_BUTTON (self->button), "pan-down-symbolic");
+  gtk_widget_set_focus_on_click (self->button, FALSE);
+  gtk_actionable_set_action_name (GTK_ACTIONABLE (self->button), "popup.show");
+  gtk_box_append (GTK_BOX (self->box), self->button);
+  gtk_widget_hide (self->button);
+
   self->popup = gtk_popover_new ();
   gtk_popover_set_position (GTK_POPOVER (self->popup), GTK_POS_BOTTOM);
   gtk_popover_set_autohide (GTK_POPOVER (self->popup), FALSE);
   gtk_popover_set_has_arrow (GTK_POPOVER (self->popup), FALSE);
   gtk_widget_set_halign (self->popup, GTK_ALIGN_START);
   gtk_widget_add_css_class (self->popup, "menu");
-  gtk_widget_set_parent (self->popup, GTK_WIDGET (self));
+  gtk_widget_set_parent (self->popup, self->gizmo);
   sw = gtk_scrolled_window_new ();
   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
                                   GTK_POLICY_NEVER,
@@ -812,8 +893,6 @@ gtk_suggestion_entry_init (GtkSuggestionEntry *self)
 
   set_default_factory (self);
 
-  gtk_widget_add_css_class (GTK_WIDGET (self), I_("suggestion"));
-
   controller = gtk_event_controller_key_new ();
   gtk_event_controller_set_name (controller, "gtk-suggestion-entry");
   g_signal_connect (controller, "key-pressed",
@@ -1383,3 +1462,42 @@ gtk_suggestion_entry_get_insert_prefix (GtkSuggestionEntry *self)
 
   return self->insert_prefix;
 }
+
+/**
+ * gtk_suggestion_entry_set_show_button:
+ * @self: a #GtkSuggestionEntry
+ * @show_button: %TRUE to show a button
+ *
+ * Sets whether the GtkSuggestionEntry should show a button
+ * for opening the popup with suggestions.
+ */
+void
+gtk_suggestion_entry_set_show_button (GtkSuggestionEntry *self,
+                                      gboolean            show_button)
+{
+  g_return_if_fail (GTK_IS_SUGGESTION_ENTRY (self));
+
+  if (self->show_button == show_button)
+    return;
+
+  if (self->button)
+    gtk_widget_set_visible (self->button, show_button);
+
+  g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SHOW_BUTTON]);
+}
+
+/**
+ * gtk_suggestion_entry_get_show_button:
+ * @self: a #GtkSuggestionEntry
+ *
+ * Gets the value set by gtk_suggestion_entry_set_show_button().
+ *
+ * Returns: %TRUE if @self is showing a button for suggestions
+ */
+gboolean
+gtk_suggestion_entry_get_show_button (GtkSuggestionEntry *self)
+{
+  g_return_val_if_fail (GTK_IS_SUGGESTION_ENTRY (self), FALSE);
+
+  return self->show_button;
+}
diff --git a/gtk/gtksuggestionentry.h b/gtk/gtksuggestionentry.h
index e04a46e38b..25ad7074da 100644
--- a/gtk/gtksuggestionentry.h
+++ b/gtk/gtksuggestionentry.h
@@ -84,6 +84,12 @@ void            gtk_suggestion_entry_set_insert_prefix    (GtkSuggestionEntry  *
 GDK_AVAILABLE_IN_ALL
 gboolean        gtk_suggestion_entry_get_insert_prefix    (GtkSuggestionEntry  *self);
 
+
+GDK_AVAILABLE_IN_ALL
+void            gtk_suggestion_entry_set_show_button      (GtkSuggestionEntry  *self,
+                                                           gboolean             show_button);
+GDK_AVAILABLE_IN_ALL
+gboolean        gtk_suggestion_entry_get_show_button      (GtkSuggestionEntry  *self);
 G_END_DECLS
 
 #endif /* __GTK_SUGGESTION_ENTRY_H__ */


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]