gnome-shell/src/st/st-label.c
Dan Winship a56bc9d933 st_label_set_text: no-op if the text is unchanged
If a caller sets an StLabel's text to what it already is (as, eg, the
clock menu does), do nothing. Unless the label is editable, in which
case, setting the text has a visible side effect (dropping the
selection), so we don't optimize that out.

https://bugzilla.gnome.org/show_bug.cgi?id=645648
2011-04-11 14:00:16 -04:00

515 lines
14 KiB
C

/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* st-label.c: Plain label actor
*
* Copyright 2008,2009 Intel Corporation
* Copyright 2009 Red Hat, Inc.
* Copyright 2010 Florian Müllner
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
* more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* SECTION:st-label
* @short_description: Widget for displaying text
*
* #StLabel is a simple widget for displaying text. It derives from
* #StWidget to add extra style and placement functionality over
* #ClutterText. The internal #ClutterText is publicly accessibly to allow
* applications to set further properties.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <glib.h>
#include <clutter/clutter.h>
#include "st-label.h"
#include "st-private.h"
#include "st-widget.h"
#include <st/st-widget-accessible.h>
enum
{
PROP_0,
PROP_CLUTTER_TEXT,
PROP_TEXT
};
#define ST_LABEL_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ST_TYPE_LABEL, StLabelPrivate))
struct _StLabelPrivate
{
ClutterActor *label;
CoglHandle text_shadow_material;
float shadow_width;
float shadow_height;
};
G_DEFINE_TYPE (StLabel, st_label, ST_TYPE_WIDGET);
static GType st_label_accessible_get_type (void) G_GNUC_CONST;
static void
st_label_set_property (GObject *gobject,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
StLabel *label = ST_LABEL (gobject);
switch (prop_id)
{
case PROP_TEXT:
st_label_set_text (label, g_value_get_string (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break;
}
}
static void
st_label_get_property (GObject *gobject,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
StLabelPrivate *priv = ST_LABEL (gobject)->priv;
switch (prop_id)
{
case PROP_CLUTTER_TEXT:
g_value_set_object (value, priv->label);
break;
case PROP_TEXT:
g_value_set_string (value, clutter_text_get_text (CLUTTER_TEXT (priv->label)));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break;
}
}
static void
st_label_style_changed (StWidget *self)
{
StLabelPrivate *priv = ST_LABEL(self)->priv;
if (priv->text_shadow_material != COGL_INVALID_HANDLE)
{
cogl_handle_unref (priv->text_shadow_material);
priv->text_shadow_material = COGL_INVALID_HANDLE;
}
_st_set_text_from_style ((ClutterText *)priv->label, st_widget_get_theme_node (self));
ST_WIDGET_CLASS (st_label_parent_class)->style_changed (self);
}
static void
st_label_get_preferred_width (ClutterActor *actor,
gfloat for_height,
gfloat *min_width_p,
gfloat *natural_width_p)
{
StLabelPrivate *priv = ST_LABEL (actor)->priv;
StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
st_theme_node_adjust_for_height (theme_node, &for_height);
clutter_actor_get_preferred_width (priv->label, for_height,
min_width_p,
natural_width_p);
st_theme_node_adjust_preferred_width (theme_node, min_width_p, natural_width_p);
}
static void
st_label_get_preferred_height (ClutterActor *actor,
gfloat for_width,
gfloat *min_height_p,
gfloat *natural_height_p)
{
StLabelPrivate *priv = ST_LABEL (actor)->priv;
StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
st_theme_node_adjust_for_width (theme_node, &for_width);
clutter_actor_get_preferred_height (priv->label, for_width,
min_height_p,
natural_height_p);
st_theme_node_adjust_preferred_height (theme_node, min_height_p, natural_height_p);
}
static void
st_label_allocate (ClutterActor *actor,
const ClutterActorBox *box,
ClutterAllocationFlags flags)
{
StLabelPrivate *priv = ST_LABEL (actor)->priv;
StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
ClutterActorClass *parent_class;
ClutterActorBox content_box;
st_theme_node_get_content_box (theme_node, box, &content_box);
parent_class = CLUTTER_ACTOR_CLASS (st_label_parent_class);
parent_class->allocate (actor, box, flags);
clutter_actor_allocate (priv->label, &content_box, flags);
}
static void
st_label_dispose (GObject *object)
{
StLabelPrivate *priv = ST_LABEL (object)->priv;
if (priv->label)
{
clutter_actor_destroy (priv->label);
priv->label = NULL;
}
if (priv->text_shadow_material != COGL_INVALID_HANDLE)
{
cogl_handle_unref (priv->text_shadow_material);
priv->text_shadow_material = COGL_INVALID_HANDLE;
}
G_OBJECT_CLASS (st_label_parent_class)->dispose (object);
}
static void
st_label_paint (ClutterActor *actor)
{
StLabelPrivate *priv = ST_LABEL (actor)->priv;
ClutterActorClass *parent_class;
StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
StShadow *shadow_spec = st_theme_node_get_text_shadow (theme_node);
parent_class = CLUTTER_ACTOR_CLASS (st_label_parent_class);
parent_class->paint (actor);
if (shadow_spec)
{
ClutterActorBox allocation;
float width, height;
clutter_actor_get_allocation_box (priv->label, &allocation);
clutter_actor_box_get_size (&allocation, &width, &height);
allocation.x1 = allocation.y1 = 0;
allocation.x2 = width;
allocation.y2 = height;
if (priv->text_shadow_material == COGL_INVALID_HANDLE ||
width != priv->shadow_width ||
height != priv->shadow_height)
{
CoglHandle material;
if (priv->text_shadow_material != COGL_INVALID_HANDLE)
cogl_handle_unref (priv->text_shadow_material);
material = _st_create_shadow_material_from_actor (shadow_spec,
priv->label);
priv->shadow_width = width;
priv->shadow_height = height;
priv->text_shadow_material = material;
}
if (priv->text_shadow_material != COGL_INVALID_HANDLE)
_st_paint_shadow_with_opacity (shadow_spec,
priv->text_shadow_material,
&allocation,
clutter_actor_get_paint_opacity (priv->label));
}
clutter_actor_paint (priv->label);
}
static void
st_label_map (ClutterActor *actor)
{
StLabelPrivate *priv = ST_LABEL (actor)->priv;
CLUTTER_ACTOR_CLASS (st_label_parent_class)->map (actor);
clutter_actor_map (priv->label);
}
static void
st_label_unmap (ClutterActor *actor)
{
StLabelPrivate *priv = ST_LABEL (actor)->priv;
CLUTTER_ACTOR_CLASS (st_label_parent_class)->unmap (actor);
clutter_actor_unmap (priv->label);
}
static void
st_label_class_init (StLabelClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
StWidgetClass *widget_class = ST_WIDGET_CLASS (klass);
GParamSpec *pspec;
g_type_class_add_private (klass, sizeof (StLabelPrivate));
gobject_class->set_property = st_label_set_property;
gobject_class->get_property = st_label_get_property;
gobject_class->dispose = st_label_dispose;
actor_class->paint = st_label_paint;
actor_class->allocate = st_label_allocate;
actor_class->get_preferred_width = st_label_get_preferred_width;
actor_class->get_preferred_height = st_label_get_preferred_height;
actor_class->map = st_label_map;
actor_class->unmap = st_label_unmap;
widget_class->style_changed = st_label_style_changed;
widget_class->get_accessible_type = st_label_accessible_get_type;
pspec = g_param_spec_object ("clutter-text",
"Clutter Text",
"Internal ClutterText actor",
CLUTTER_TYPE_TEXT,
G_PARAM_READABLE);
g_object_class_install_property (gobject_class, PROP_CLUTTER_TEXT, pspec);
pspec = g_param_spec_string ("text",
"Text",
"Text of the label",
NULL, G_PARAM_READWRITE);
g_object_class_install_property (gobject_class, PROP_TEXT, pspec);
}
static void
st_label_init (StLabel *label)
{
StLabelPrivate *priv;
label->priv = priv = ST_LABEL_GET_PRIVATE (label);
label->priv->label = g_object_new (CLUTTER_TYPE_TEXT,
"ellipsize", PANGO_ELLIPSIZE_END,
NULL);
label->priv->text_shadow_material = COGL_INVALID_HANDLE;
label->priv->shadow_width = -1.;
label->priv->shadow_height = -1.;
clutter_actor_set_parent (priv->label, CLUTTER_ACTOR (label));
}
/**
* st_label_new:
* @text: text to set the label to
*
* Create a new #StLabel with the specified label
*
* Returns: a new #StLabel
*/
StWidget *
st_label_new (const gchar *text)
{
if (text == NULL || *text == '\0')
return g_object_new (ST_TYPE_LABEL, NULL);
else
return g_object_new (ST_TYPE_LABEL,
"text", text,
NULL);
}
/**
* st_label_get_text:
* @label: a #StLabel
*
* Get the text displayed on the label
*
* Returns: the text for the label. This must not be freed by the application
*/
const gchar *
st_label_get_text (StLabel *label)
{
g_return_val_if_fail (ST_IS_LABEL (label), NULL);
return clutter_text_get_text (CLUTTER_TEXT (label->priv->label));
}
/**
* st_label_set_text:
* @label: a #StLabel
* @text: text to set the label to
*
* Sets the text displayed on the label
*/
void
st_label_set_text (StLabel *label,
const gchar *text)
{
StLabelPrivate *priv;
ClutterText *ctext;
g_return_if_fail (ST_IS_LABEL (label));
g_return_if_fail (text != NULL);
priv = label->priv;
ctext = CLUTTER_TEXT (priv->label);
if (clutter_text_get_editable (ctext) ||
g_strcmp0 (clutter_text_get_text (ctext), text) != 0)
{
if (priv->text_shadow_material != COGL_INVALID_HANDLE)
{
cogl_handle_unref (priv->text_shadow_material);
priv->text_shadow_material = COGL_INVALID_HANDLE;
}
clutter_text_set_text (ctext, text);
g_object_notify (G_OBJECT (label), "text");
}
}
/**
* st_label_get_clutter_text:
* @label: a #StLabel
*
* Retrieve the internal #ClutterText so that extra parameters can be set
*
* Returns: (transfer none): ethe #ClutterText used by #StLabel. The label
* is owned by the #StLabel and should not be unref'ed by the application.
*/
ClutterActor*
st_label_get_clutter_text (StLabel *label)
{
g_return_val_if_fail (ST_LABEL (label), NULL);
return label->priv->label;
}
/******************************************************************************/
/*************************** ACCESSIBILITY SUPPORT ****************************/
/******************************************************************************/
#define ST_TYPE_LABEL_ACCESSIBLE st_label_accessible_get_type ()
#define ST_LABEL_ACCESSIBLE(obj) \
(G_TYPE_CHECK_INSTANCE_CAST ((obj), \
ST_TYPE_LABEL_ACCESSIBLE, StLabelAccessible))
#define ST_IS_LABEL_ACCESSIBLE(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
ST_TYPE_LABEL_ACCESSIBLE))
#define ST_LABEL_ACCESSIBLE_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST ((klass), \
ST_TYPE_LABEL_ACCESSIBLE, StLabelAccessibleClass))
#define ST_IS_LABEL_ACCESSIBLE_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE ((klass), \
ST_TYPE_LABEL_ACCESSIBLE))
#define ST_LABEL_ACCESSIBLE_GET_CLASS(obj) \
(G_TYPE_INSTANCE_GET_CLASS ((obj), \
ST_TYPE_LABEL_ACCESSIBLE, StLabelAccessibleClass))
typedef struct _StLabelAccessible StLabelAccessible;
typedef struct _StLabelAccessibleClass StLabelAccessibleClass;
struct _StLabelAccessible
{
StWidgetAccessible parent;
};
struct _StLabelAccessibleClass
{
StWidgetAccessibleClass parent_class;
};
static void st_label_accessible_class_init (StLabelAccessibleClass *klass);
static void st_label_accessible_init (StLabelAccessible *label);
/* AtkObject */
static void st_label_accessible_initialize (AtkObject *obj,
gpointer data);
static const gchar * st_label_accessible_get_name (AtkObject *obj);
G_DEFINE_TYPE (StLabelAccessible, st_label_accessible, ST_TYPE_WIDGET_ACCESSIBLE)
static void
st_label_accessible_class_init (StLabelAccessibleClass *klass)
{
AtkObjectClass *atk_class = ATK_OBJECT_CLASS (klass);
atk_class->initialize = st_label_accessible_initialize;
atk_class->get_name = st_label_accessible_get_name;
}
static void
st_label_accessible_init (StLabelAccessible *self)
{
/* initialization done on AtkObject->initialize */
}
static void
st_label_accessible_initialize (AtkObject *obj,
gpointer data)
{
ATK_OBJECT_CLASS (st_label_accessible_parent_class)->initialize (obj, data);
obj->role = ATK_ROLE_LABEL;
}
static const gchar *
st_label_accessible_get_name (AtkObject *obj)
{
const gchar *name = NULL;
g_return_val_if_fail (ST_IS_LABEL_ACCESSIBLE (obj), NULL);
name = ATK_OBJECT_CLASS (st_label_accessible_parent_class)->get_name (obj);
if (name == NULL)
{
ClutterActor *actor = NULL;
actor = CLUTTER_ACTOR (atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (obj)));
if (actor == NULL) /* State is defunct */
name = NULL;
else
name = st_label_get_text (ST_LABEL (actor));
}
return name;
}