d6fe008b2c
The shadows are currently rendered by painting the actor we want to apply shadow on, in an offscreen buffer. The problem is that when this actor has an allocation padding (ie allocation that isn't at 0x0 relatively to its parent), this padding is added within the offscreen buffer and as a result the shadow rendering is truncated because the offscreen buffer size is the size of the allocation box, not the allocation box + padding. This patch reposition the actor at 0x0 with rendering it by changing the initial transformation matrix when rendering the actor offscreen. https://bugzilla.gnome.org/show_bug.cgi?id=698301
497 lines
14 KiB
C
497 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));
|
|
ClutterActorBox content_box;
|
|
|
|
clutter_actor_set_allocation (actor, box, flags);
|
|
|
|
st_theme_node_get_content_box (theme_node, box, &content_box);
|
|
|
|
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;
|
|
StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
|
|
StShadow *shadow_spec = st_theme_node_get_text_shadow (theme_node);
|
|
|
|
st_widget_paint_background (ST_WIDGET (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);
|
|
|
|
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_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;
|
|
|
|
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_add_child (CLUTTER_ACTOR (label), priv->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
|
|
label_text_notify_cb (StLabel *label,
|
|
GParamSpec *pspec,
|
|
AtkObject *accessible)
|
|
{
|
|
g_object_notify (G_OBJECT (accessible), "accessible-name");
|
|
}
|
|
|
|
static void
|
|
st_label_accessible_initialize (AtkObject *obj,
|
|
gpointer data)
|
|
{
|
|
ATK_OBJECT_CLASS (st_label_accessible_parent_class)->initialize (obj, data);
|
|
|
|
g_signal_connect (data, "notify::text",
|
|
G_CALLBACK (label_text_notify_cb),
|
|
obj);
|
|
|
|
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 || st_widget_has_style_class_name (ST_WIDGET (actor), "hidden"))
|
|
name = NULL;
|
|
else
|
|
name = st_label_get_text (ST_LABEL (actor));
|
|
}
|
|
|
|
return name;
|
|
}
|