[gnome-shell] st-entry: Add support for a "hint actor"



commit 6ed7034a6bb5afa1acb003183eb5bdd1ca1542f6
Author: Mario Sanchez Prada <mario endlessm com>
Date:   Mon Jun 5 18:36:41 2017 +0100

    st-entry: Add support for a "hint actor"
    
    This allows a full ClutterActor to be used as hint in the entry, instead
    of a simple string.
    
    The string case has been now re-implemented on top of the hint actor.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=783484

 src/st/st-entry.c |  214 +++++++++++++++++++++++++++++++++++++----------------
 src/st/st-entry.h |    4 +
 2 files changed, 154 insertions(+), 64 deletions(-)
---
diff --git a/src/st/st-entry.c b/src/st/st-entry.c
index c92991e..fc9ffbf 100644
--- a/src/st/st-entry.c
+++ b/src/st/st-entry.c
@@ -34,7 +34,7 @@
  *   <para>focus: the widget has focus</para>
  *  </listitem>
  *  <listitem>
- *   <para>indeterminate: the widget is showing the hint text</para>
+ *   <para>indeterminate: the widget is showing the hint text or actor</para>
  *  </listitem>
  * </itemizedlist>
  */
@@ -56,6 +56,7 @@
 
 #include "st-im-text.h"
 #include "st-icon.h"
+#include "st-label.h"
 #include "st-widget.h"
 #include "st-texture-cache.h"
 #include "st-clipboard.h"
@@ -75,6 +76,7 @@ enum
   PROP_PRIMARY_ICON,
   PROP_SECONDARY_ICON,
   PROP_HINT_TEXT,
+  PROP_HINT_ACTOR,
   PROP_TEXT,
   PROP_INPUT_PURPOSE,
   PROP_INPUT_HINTS,
@@ -96,14 +98,14 @@ typedef struct _StEntryPrivate StEntryPrivate;
 struct _StEntryPrivate
 {
   ClutterActor *entry;
-  gchar        *hint;
 
   ClutterActor *primary_icon;
   ClutterActor *secondary_icon;
 
+  ClutterActor *hint_actor;
+
   gfloat        spacing;
 
-  gboolean      hint_visible;
   gboolean      capslock_warning_shown;
   gboolean      has_ibeam;
 
@@ -140,6 +142,10 @@ st_entry_set_property (GObject      *gobject,
       st_entry_set_hint_text (entry, g_value_get_string (value));
       break;
 
+    case PROP_HINT_ACTOR:
+      st_entry_set_hint_actor (entry, g_value_get_object (value));
+      break;
+
     case PROP_TEXT:
       st_entry_set_text (entry, g_value_get_string (value));
       break;
@@ -181,7 +187,11 @@ st_entry_get_property (GObject    *gobject,
       break;
 
     case PROP_HINT_TEXT:
-      g_value_set_string (value, priv->hint);
+      g_value_set_string (value, st_entry_get_hint_text (ST_ENTRY (gobject)));
+      break;
+
+    case PROP_HINT_ACTOR:
+      g_value_set_object (value, priv->hint_actor);
       break;
 
     case PROP_TEXT:
@@ -265,14 +275,21 @@ st_entry_dispose (GObject *object)
 }
 
 static void
-st_entry_finalize (GObject *object)
+st_entry_update_hint_visibility (StEntry *self)
 {
-  StEntryPrivate *priv = ST_ENTRY_PRIV (object);
+  StEntryPrivate *priv = ST_ENTRY_PRIV (self);
+  gboolean hint_visible =
+    priv->hint_actor != NULL &&
+    strcmp (clutter_text_get_text (CLUTTER_TEXT (priv->entry)), "") == 0 &&
+    !HAS_FOCUS (priv->entry);
 
-  g_free (priv->hint);
-  priv->hint = NULL;
+  if (priv->hint_actor)
+    g_object_set (priv->hint_actor, "visible", hint_visible, NULL);
 
-  G_OBJECT_CLASS (st_entry_parent_class)->finalize (object);
+  if (hint_visible)
+    st_widget_add_style_pseudo_class (ST_WIDGET (self), "indeterminate");
+  else
+    st_widget_remove_style_pseudo_class (ST_WIDGET (self), "indeterminate");
 }
 
 static void
@@ -352,7 +369,7 @@ st_entry_get_preferred_width (ClutterActor *actor,
 {
   StEntryPrivate *priv = ST_ENTRY_PRIV (actor);
   StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
-  gfloat icon_w;
+  gfloat hint_w, icon_w;
 
   st_theme_node_adjust_for_height (theme_node, &for_height);
 
@@ -360,6 +377,17 @@ st_entry_get_preferred_width (ClutterActor *actor,
                                      min_width_p,
                                      natural_width_p);
 
+  if (priv->hint_actor)
+    {
+      clutter_actor_get_preferred_width (priv->hint_actor, -1, NULL, &hint_w);
+
+      if (min_width_p && hint_w > *min_width_p)
+        *min_width_p = hint_w;
+
+      if (natural_width_p && hint_w > *natural_width_p)
+        *natural_width_p = hint_w;
+    }
+
   if (priv->primary_icon)
     {
       clutter_actor_get_preferred_width (priv->primary_icon, -1, NULL, &icon_w);
@@ -394,7 +422,7 @@ st_entry_get_preferred_height (ClutterActor *actor,
 {
   StEntryPrivate *priv = ST_ENTRY_PRIV (actor);
   StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
-  gfloat icon_h;
+  gfloat hint_h, icon_h;
 
   st_theme_node_adjust_for_width (theme_node, &for_width);
 
@@ -402,6 +430,17 @@ st_entry_get_preferred_height (ClutterActor *actor,
                                       min_height_p,
                                       natural_height_p);
 
+  if (priv->hint_actor)
+    {
+      clutter_actor_get_preferred_height (priv->hint_actor, -1, NULL, &hint_h);
+
+      if (min_height_p && hint_h > *min_height_p)
+        *min_height_p = hint_h;
+
+      if (natural_height_p && hint_h > *natural_height_p)
+        *natural_height_p = hint_h;
+    }
+
   if (priv->primary_icon)
     {
       clutter_actor_get_preferred_height (priv->primary_icon,
@@ -436,12 +475,16 @@ st_entry_allocate (ClutterActor          *actor,
 {
   StEntryPrivate *priv = ST_ENTRY_PRIV (actor);
   StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
-  ClutterActorBox content_box, child_box, icon_box;
+  ClutterActorBox content_box, child_box, icon_box, hint_box;
   gfloat icon_w, icon_h;
+  gfloat hint_w, hint_h;
   gfloat entry_h, min_h, pref_h, avail_h;
   ClutterActor *left_icon, *right_icon;
+  gboolean is_rtl;
+
+  is_rtl = clutter_actor_get_text_direction (actor) == CLUTTER_TEXT_DIRECTION_RTL;
 
-  if (clutter_actor_get_text_direction (actor) == CLUTTER_TEXT_DIRECTION_RTL)
+  if (is_rtl)
     {
       right_icon = priv->primary_icon;
       left_icon = priv->secondary_icon;
@@ -495,6 +538,25 @@ st_entry_allocate (ClutterActor          *actor,
       child_box.x2 = MAX (child_box.x1, child_box.x2 - icon_w - priv->spacing);
     }
 
+  if (priv->hint_actor)
+    {
+      /* now allocate the hint actor */
+      hint_box = child_box;
+
+      clutter_actor_get_preferred_width (priv->hint_actor, -1, NULL, &hint_w);
+      clutter_actor_get_preferred_height (priv->hint_actor, -1, NULL, &hint_h);
+
+      if (is_rtl)
+        hint_box.x1 = hint_box.x2 - hint_w;
+      else
+        hint_box.x2 = hint_box.x1 + hint_w;
+
+      hint_box.y1 = ceil (content_box.y1 + avail_h / 2 - hint_h / 2);
+      hint_box.y2 = hint_box.y1 + hint_h;
+
+      clutter_actor_allocate (priv->hint_actor, &hint_box, flags);
+    }
+
   clutter_actor_get_preferred_height (priv->entry, child_box.x2 - child_box.x1,
                                       &min_h, &pref_h);
 
@@ -511,23 +573,15 @@ clutter_text_focus_in_cb (ClutterText  *text,
                           ClutterActor *actor)
 {
   StEntry *entry = ST_ENTRY (actor);
-  StEntryPrivate *priv = ST_ENTRY_PRIV (entry);
   GdkKeymap *keymap;
 
-  /* remove the hint if visible */
-  if (priv->hint && priv->hint_visible)
-    {
-      priv->hint_visible = FALSE;
-
-      clutter_text_set_text (text, "");
-    }
+  st_entry_update_hint_visibility (entry);
 
   keymap = gdk_keymap_get_for_display (gdk_display_get_default ());
   keymap_state_changed (keymap, entry);
   g_signal_connect (keymap, "state-changed",
                     G_CALLBACK (keymap_state_changed), entry);
 
-  st_widget_remove_style_pseudo_class (ST_WIDGET (actor), "indeterminate");
   st_widget_add_style_pseudo_class (ST_WIDGET (actor), "focus");
   clutter_text_set_cursor_visible (text, TRUE);
 }
@@ -537,19 +591,12 @@ clutter_text_focus_out_cb (ClutterText  *text,
                            ClutterActor *actor)
 {
   StEntry *entry = ST_ENTRY (actor);
-  StEntryPrivate *priv = ST_ENTRY_PRIV (entry);
   GdkKeymap *keymap;
 
   st_widget_remove_style_pseudo_class (ST_WIDGET (actor), "focus");
 
-  /* add a hint if the entry is empty */
-  if (priv->hint && !strcmp (clutter_text_get_text (text), ""))
-    {
-      priv->hint_visible = TRUE;
+  st_entry_update_hint_visibility (entry);
 
-      clutter_text_set_text (text, priv->hint);
-      st_widget_add_style_pseudo_class (ST_WIDGET (actor), "indeterminate");
-    }
   clutter_text_set_cursor_visible (text, FALSE);
   remove_capslock_feedback (entry);
 
@@ -870,7 +917,6 @@ st_entry_class_init (StEntryClass *klass)
 
   gobject_class->set_property = st_entry_set_property;
   gobject_class->get_property = st_entry_get_property;
-  gobject_class->finalize = st_entry_finalize;
   gobject_class->dispose = st_entry_dispose;
 
   actor_class->get_preferred_width = st_entry_get_preferred_width;
@@ -917,6 +963,14 @@ st_entry_class_init (StEntryClass *klass)
                                NULL, G_PARAM_READWRITE);
   g_object_class_install_property (gobject_class, PROP_HINT_TEXT, pspec);
 
+  pspec = g_param_spec_object ("hint-actor",
+                               "Hint Actor",
+                               "An actor to display when the entry is not focused "
+                               "and the text property is empty",
+                               CLUTTER_TYPE_ACTOR,
+                               G_PARAM_READWRITE);
+  g_object_class_install_property (gobject_class, PROP_HINT_ACTOR, pspec);
+
   pspec = g_param_spec_string ("text",
                                "Text",
                                "Text of the entry",
@@ -1052,7 +1106,7 @@ st_entry_get_text (StEntry *entry)
   g_return_val_if_fail (ST_IS_ENTRY (entry), NULL);
 
   priv = st_entry_get_instance_private (entry);
-  if (priv->hint_visible)
+  if (clutter_actor_is_visible (priv->hint_actor))
     return "";
   else
     return clutter_text_get_text (CLUTTER_TEXT (priv->entry));
@@ -1075,24 +1129,10 @@ st_entry_set_text (StEntry     *entry,
 
   priv = st_entry_get_instance_private (entry);
 
-  /* set a hint if we are blanking the entry */
-  if (priv->hint
-      && text && !strcmp ("", text)
-      && !HAS_FOCUS (priv->entry))
-    {
-      text = priv->hint;
-      priv->hint_visible = TRUE;
-      st_widget_add_style_pseudo_class (ST_WIDGET (entry), "indeterminate");
-    }
-  else
-    {
-      st_widget_remove_style_pseudo_class (ST_WIDGET (entry), "indeterminate");
-
-      priv->hint_visible = FALSE;
-    }
-
   clutter_text_set_text (CLUTTER_TEXT (priv->entry), text);
 
+  st_entry_update_hint_visibility (entry);
+
   g_object_notify (G_OBJECT (entry), "text");
 }
 
@@ -1126,24 +1166,12 @@ void
 st_entry_set_hint_text (StEntry     *entry,
                         const gchar *text)
 {
-  StEntryPrivate *priv;
+  StWidget *label;
 
   g_return_if_fail (ST_IS_ENTRY (entry));
 
-  priv = st_entry_get_instance_private (entry);
-
-  g_free (priv->hint);
-
-  priv->hint = g_strdup (text);
-
-  if (!strcmp (clutter_text_get_text (CLUTTER_TEXT (priv->entry)), "")
-      && !HAS_FOCUS (priv->entry))
-    {
-      priv->hint_visible = TRUE;
-
-      clutter_text_set_text (CLUTTER_TEXT (priv->entry), priv->hint);
-      st_widget_add_style_pseudo_class (ST_WIDGET (entry), "indeterminate");
-    }
+  label = st_label_new (text);
+  st_entry_set_hint_actor (ST_ENTRY (entry), CLUTTER_ACTOR (label));
 }
 
 /**
@@ -1158,9 +1186,16 @@ st_entry_set_hint_text (StEntry     *entry,
 const gchar *
 st_entry_get_hint_text (StEntry *entry)
 {
+  StEntryPrivate *priv;
+
   g_return_val_if_fail (ST_IS_ENTRY (entry), NULL);
 
-  return ((StEntryPrivate *)ST_ENTRY_PRIV (entry))->hint;
+  priv = ST_ENTRY_PRIV (entry);
+
+  if (priv->hint_actor != NULL && ST_IS_LABEL (priv->hint_actor))
+    return st_label_get_text (ST_LABEL (priv->hint_actor));
+
+  return NULL;
 }
 
 /**
@@ -1370,6 +1405,57 @@ st_entry_get_secondary_icon (StEntry *entry)
   return priv->secondary_icon;
 }
 
+/**
+ * st_entry_set_hint_actor:
+ * @entry: a #StEntry
+ * @hint_actor: (allow-none): a #ClutterActor
+ *
+ * Set the hint actor of the entry to @hint_actor
+ */
+void
+st_entry_set_hint_actor (StEntry      *entry,
+                         ClutterActor *hint_actor)
+{
+  StEntryPrivate *priv;
+
+  g_return_if_fail (ST_IS_ENTRY (entry));
+
+  priv = ST_ENTRY_PRIV (entry);
+
+  if (priv->hint_actor != NULL)
+    {
+      clutter_actor_remove_child (CLUTTER_ACTOR (entry), priv->hint_actor);
+      priv->hint_actor = NULL;
+    }
+
+  if (hint_actor != NULL)
+    {
+      priv->hint_actor = hint_actor;
+      clutter_actor_add_child (CLUTTER_ACTOR (entry), priv->hint_actor);
+    }
+
+  st_entry_update_hint_visibility (entry);
+
+  clutter_actor_queue_relayout (CLUTTER_ACTOR (entry));
+}
+
+/**
+ * st_entry_get_hint_actor:
+ * @entry: a #StEntry
+ *
+ * Returns: (transfer none): a #ClutterActor
+ */
+ClutterActor *
+st_entry_get_hint_actor (StEntry *entry)
+{
+  StEntryPrivate *priv;
+
+  g_return_val_if_fail (ST_IS_ENTRY (entry), NULL);
+
+  priv = ST_ENTRY_PRIV (entry);
+  return priv->hint_actor;
+}
+
 /******************************************************************************/
 /*************************** ACCESSIBILITY SUPPORT ****************************/
 /******************************************************************************/
diff --git a/src/st/st-entry.h b/src/st/st-entry.h
index 1fbc556..43ae0fa 100644
--- a/src/st/st-entry.h
+++ b/src/st/st-entry.h
@@ -65,6 +65,10 @@ void            st_entry_set_secondary_icon (StEntry      *entry,
                                              ClutterActor *icon);
 ClutterActor *  st_entry_get_secondary_icon (StEntry      *entry);
 
+void            st_entry_set_hint_actor    (StEntry      *entry,
+                                            ClutterActor *hint_actor);
+ClutterActor *  st_entry_get_hint_actor    (StEntry      *entry);
+
 typedef void (*StEntryCursorFunc) (StEntry *entry, gboolean use_ibeam, gpointer data);
 void            st_entry_set_cursor_func    (StEntryCursorFunc func,
                                              gpointer          user_data);


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