/* -*- 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 . */ /** * 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 #include #include #include #include "st-label.h" #include "st-private.h" #include "st-widget.h" #include 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_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) /* State is defunct */ name = NULL; else name = st_label_get_text (ST_LABEL (actor)); } return name; }