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
This commit is contained in:
Mario Sanchez Prada 2017-06-05 18:36:41 +01:00 committed by Cosimo Cecchi
parent 4e07d0b073
commit 6ed7034a6b
2 changed files with 154 additions and 64 deletions

View File

@ -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;
if (clutter_actor_get_text_direction (actor) == CLUTTER_TEXT_DIRECTION_RTL)
is_rtl = 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 ****************************/
/******************************************************************************/

View File

@ -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);