2
* Copyright 2012 Canonical Ltd.
4
* This program is free software: you can redistribute it and/or modify it
5
* under the terms of the GNU General Public License version 3, as published
6
* by the Free Software Foundation.
8
* This program is distributed in the hope that it will be useful, but
9
* WITHOUT ANY WARRANTY; without even the implied warranties of
10
* MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11
* PURPOSE. See the GNU General Public License for more details.
13
* You should have received a copy of the GNU General Public License along
14
* with this program. If not, see <http://www.gnu.org/licenses/>.
17
* Lars Uebernickel <lars.uebernickel@canonical.com>
20
#include "ido-detail-label.h"
24
G_DEFINE_TYPE (IdoDetailLabel, ido_detail_label, GTK_TYPE_WIDGET)
26
struct _IdoDetailLabelPrivate
30
gboolean draw_lozenge;
40
static GParamSpec *properties[NUM_PROPERTIES];
43
ido_detail_label_get_property (GObject *object,
48
IdoDetailLabel *self = IDO_DETAIL_LABEL (object);
53
g_value_set_string (value, self->priv->text);
57
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
62
ido_detail_label_set_property (GObject *object,
67
IdoDetailLabel *self = IDO_DETAIL_LABEL (object);
72
ido_detail_label_set_text (self, g_value_get_string (value));
76
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
82
ido_detail_label_finalize (GObject *object)
84
IdoDetailLabelPrivate *priv = IDO_DETAIL_LABEL (object)->priv;
88
G_OBJECT_CLASS (ido_detail_label_parent_class)->finalize (object);
92
ido_detail_label_dispose (GObject *object)
94
IdoDetailLabelPrivate *priv = IDO_DETAIL_LABEL (object)->priv;
96
g_clear_object (&priv->layout);
98
G_OBJECT_CLASS (ido_detail_label_parent_class)->dispose (object);
102
ido_detail_label_ensure_layout (IdoDetailLabel *label)
104
IdoDetailLabelPrivate *priv = label->priv;
106
if (priv->layout == NULL)
108
priv->layout = gtk_widget_create_pango_layout (GTK_WIDGET (label), priv->text);
109
pango_layout_set_alignment (priv->layout, PANGO_ALIGN_CENTER);
110
pango_layout_set_ellipsize (priv->layout, PANGO_ELLIPSIZE_END);
111
pango_layout_set_height (priv->layout, -1);
113
// TODO update layout on "style-updated" and "direction-changed"
118
cairo_lozenge (cairo_t *cr,
125
double x1 = x + w - radius;
126
double x2 = x + radius;
127
double y1 = y + radius;
128
double y2 = y + h - radius;
130
cairo_move_to (cr, x + radius, y);
131
cairo_arc (cr, x1, y1, radius, G_PI * 1.5, G_PI * 2);
132
cairo_arc (cr, x1, y2, radius, 0, G_PI * 0.5);
133
cairo_arc (cr, x2, y2, radius, G_PI * 0.5, G_PI);
134
cairo_arc (cr, x2, y1, radius, G_PI, G_PI * 1.5);
137
static PangoFontMetrics *
138
gtk_widget_get_font_metrics (GtkWidget *widget,
139
PangoContext *context)
141
PangoFontDescription *font;
142
PangoFontMetrics *metrics;
144
gtk_style_context_get (gtk_widget_get_style_context (widget),
145
gtk_widget_get_state_flags (widget),
146
"font", &font, NULL);
148
metrics = pango_context_get_metrics (context,
150
pango_context_get_language (context));
152
pango_font_description_free (font);
157
ido_detail_label_get_minimum_text_width (IdoDetailLabel *label)
159
IdoDetailLabelPrivate *priv = label->priv;
160
PangoContext *context;
161
PangoFontMetrics *metrics;
165
context = pango_layout_get_context (priv->layout);
166
metrics = gtk_widget_get_font_metrics (GTK_WIDGET (label), context);
167
char_width = pango_font_metrics_get_approximate_digit_width (metrics);
169
w = 2 * char_width / PANGO_SCALE;
170
pango_font_metrics_unref (metrics);
175
ido_detail_label_draw (GtkWidget *widget,
178
IdoDetailLabel *label = IDO_DETAIL_LABEL (widget);
179
IdoDetailLabelPrivate *priv = IDO_DETAIL_LABEL (widget)->priv;
180
PangoRectangle extents;
181
GtkAllocation allocation;
182
double x, w, h, radius;
185
if (!priv->text || !*priv->text)
188
gtk_widget_get_allocation (widget, &allocation);
190
ido_detail_label_ensure_layout (IDO_DETAIL_LABEL (widget));
192
pango_layout_get_extents (priv->layout, NULL, &extents);
193
pango_extents_to_pixels (&extents, NULL);
195
h = MIN (allocation.height, extents.height);
196
radius = floor (h / 2.0);
197
w = MAX (ido_detail_label_get_minimum_text_width (label), extents.width) + 2.0 * radius;
198
x = allocation.width - w;
200
pango_layout_set_width (priv->layout, (allocation.width - 2 * radius) * PANGO_SCALE);
201
pango_layout_get_extents (priv->layout, NULL, &extents);
202
pango_extents_to_pixels (&extents, NULL);
204
gtk_style_context_get_color (gtk_widget_get_style_context (widget),
205
gtk_widget_get_state_flags (widget),
207
gdk_cairo_set_source_rgba (cr, &color);
209
cairo_set_line_width (cr, 1.0);
210
cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
212
if (priv->draw_lozenge)
213
cairo_lozenge (cr, x, 0.0, w, h, radius);
215
cairo_move_to (cr, x + radius, (allocation.height - extents.height) / 2.0);
216
pango_cairo_layout_path (cr, priv->layout);
223
ido_detail_label_get_preferred_width (GtkWidget *widget,
227
IdoDetailLabelPrivate *priv = IDO_DETAIL_LABEL (widget)->priv;
228
PangoRectangle extents;
231
ido_detail_label_ensure_layout (IDO_DETAIL_LABEL (widget));
233
pango_layout_get_extents (priv->layout, NULL, &extents);
234
pango_extents_to_pixels (&extents, NULL);
236
radius = floor (extents.height / 2.0);
238
*minimum = ido_detail_label_get_minimum_text_width (IDO_DETAIL_LABEL (widget)) + 2.0 * radius;
239
*natural = MAX (*minimum, extents.width + 2.0 * radius);
243
ido_detail_label_get_preferred_height (GtkWidget *widget,
247
IdoDetailLabelPrivate *priv = IDO_DETAIL_LABEL (widget)->priv;
248
PangoContext *context;
249
PangoFontMetrics *metrics;
250
PangoRectangle extents;
252
ido_detail_label_ensure_layout (IDO_DETAIL_LABEL (widget));
254
pango_layout_get_extents (priv->layout, NULL, &extents);
255
pango_extents_to_pixels (&extents, NULL);
256
context = pango_layout_get_context (priv->layout);
257
metrics = gtk_widget_get_font_metrics (widget, context);
259
*minimum = *natural = (pango_font_metrics_get_ascent (metrics) +
260
pango_font_metrics_get_descent (metrics)) / PANGO_SCALE;
262
pango_font_metrics_unref (metrics);
266
ido_detail_label_class_init (IdoDetailLabelClass *klass)
268
GObjectClass *object_class = G_OBJECT_CLASS (klass);
269
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
271
object_class->get_property = ido_detail_label_get_property;
272
object_class->set_property = ido_detail_label_set_property;
273
object_class->finalize = ido_detail_label_finalize;
274
object_class->dispose = ido_detail_label_dispose;
276
widget_class->draw = ido_detail_label_draw;
277
widget_class->get_preferred_width = ido_detail_label_get_preferred_width;
278
widget_class->get_preferred_height = ido_detail_label_get_preferred_height;
280
g_type_class_add_private (klass, sizeof (IdoDetailLabelPrivate));
282
properties[PROP_TEXT] = g_param_spec_string ("text",
284
"The text of the label",
287
G_PARAM_STATIC_STRINGS);
289
g_object_class_install_properties (object_class, NUM_PROPERTIES, properties);
293
ido_detail_label_init (IdoDetailLabel *self)
295
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
296
IDO_TYPE_DETAIL_LABEL,
297
IdoDetailLabelPrivate);
299
gtk_widget_set_has_window (GTK_WIDGET (self), FALSE);
303
ido_detail_label_new (const gchar *label)
305
return g_object_new (IDO_TYPE_DETAIL_LABEL,
311
ido_detail_label_get_text (IdoDetailLabel *label)
313
g_return_val_if_fail (IDO_IS_DETAIL_LABEL (label), NULL);
314
return label->priv->text;
317
/* collapse_whitespace:
318
* @str: the source string
320
* Collapses all occurences of consecutive whitespace charactes in @str
321
* into a single space.
323
* Returns: (transfer full): a newly-allocated string
326
collapse_whitespace (const gchar *str)
329
gboolean in_space = FALSE;
334
result = g_string_new ("");
338
gunichar c = g_utf8_get_char_validated (str, -1);
343
if (!g_unichar_isspace (c))
345
g_string_append_unichar (result, c);
350
g_string_append_c (result, ' ');
354
str = g_utf8_next_char (str);
357
return g_string_free (result, FALSE);
361
ido_detail_label_set_text_impl (IdoDetailLabel *label,
363
gboolean draw_lozenge)
365
IdoDetailLabelPrivate * priv = label->priv;
367
g_clear_object (&priv->layout);
370
priv->text = g_strdup (text);
371
priv->draw_lozenge = draw_lozenge;
373
g_object_notify_by_pspec (G_OBJECT (label), properties[PROP_TEXT]);
374
gtk_widget_queue_resize (GTK_WIDGET (label));
378
ido_detail_label_set_text (IdoDetailLabel *label,
383
g_return_if_fail (IDO_IS_DETAIL_LABEL (label));
385
str = collapse_whitespace (text);
386
ido_detail_label_set_text_impl (label, str, FALSE);
391
ido_detail_label_set_count (IdoDetailLabel *label,
396
g_return_if_fail (IDO_IS_DETAIL_LABEL (label));
398
text = g_strdup_printf ("%d", count);
399
ido_detail_label_set_text_impl (label, text, TRUE);