diff options
Diffstat (limited to '')
-rw-r--r-- | src/gs-lozenge.c | 470 |
1 files changed, 470 insertions, 0 deletions
diff --git a/src/gs-lozenge.c b/src/gs-lozenge.c new file mode 100644 index 0000000..6e7cff2 --- /dev/null +++ b/src/gs-lozenge.c @@ -0,0 +1,470 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * vi:set noexpandtab tabstop=8 shiftwidth=8: + * + * Copyright (C) 2022 Red Hat (www.redhat.com) + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include "config.h" + +#include "gs-lozenge.h" +#include "gs-layout-manager.h" + +#define GS_TYPE_LOZENGE_LAYOUT (gs_lozenge_layout_get_type ()) +G_DECLARE_FINAL_TYPE (GsLozengeLayout, gs_lozenge_layout, GS, LOZENGE_LAYOUT, GsLayoutManager) + +struct _GsLozengeLayout +{ + GsLayoutManager parent_instance; + + gboolean circular; +}; + +G_DEFINE_TYPE (GsLozengeLayout, gs_lozenge_layout, GS_TYPE_LAYOUT_MANAGER) + +static void +gs_lozenge_layout_measure (GtkLayoutManager *layout_manager, + GtkWidget *widget, + GtkOrientation orientation, + gint for_size, + gint *minimum, + gint *natural, + gint *minimum_baseline, + gint *natural_baseline) +{ + GsLozengeLayout *self = GS_LOZENGE_LAYOUT (layout_manager); + + GTK_LAYOUT_MANAGER_CLASS (gs_lozenge_layout_parent_class)->measure (layout_manager, + widget, orientation, for_size, minimum, natural, minimum_baseline, natural_baseline); + + if (self->circular) { + *minimum = MAX (for_size, *minimum); + *natural = *minimum; + *natural_baseline = *minimum_baseline; + } + + if (*natural_baseline > *natural) + *natural_baseline = *natural; + if (*minimum_baseline > *minimum) + *minimum_baseline = *minimum; +} + +static void +gs_lozenge_layout_class_init (GsLozengeLayoutClass *klass) +{ + GtkLayoutManagerClass *layout_manager_class = GTK_LAYOUT_MANAGER_CLASS (klass); + + layout_manager_class->measure = gs_lozenge_layout_measure; +} + +static void +gs_lozenge_layout_init (GsLozengeLayout *self) +{ +} + +/* ********************************************************************* */ + +struct _GsLozenge +{ + GtkBox parent_instance; + + GtkWidget *image; /* (unowned) */ + GtkWidget *label; /* (unowned) */ + + gchar *icon_name; + gchar *text; + gchar *markup; + gboolean circular; + gint pixel_size; +}; + +G_DEFINE_TYPE (GsLozenge, gs_lozenge, GTK_TYPE_BOX) + +typedef enum { + PROP_CIRCULAR = 1, + PROP_ICON_NAME, + PROP_PIXEL_SIZE, + PROP_TEXT, + PROP_MARKUP +} GsLozengeProperty; + +static GParamSpec *obj_props[PROP_MARKUP + 1] = { NULL, }; + +static void +gs_lozenge_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GsLozenge *self = GS_LOZENGE (object); + + switch ((GsLozengeProperty) prop_id) { + case PROP_CIRCULAR: + g_value_set_boolean (value, gs_lozenge_get_circular (self)); + break; + case PROP_ICON_NAME: + g_value_set_string (value, gs_lozenge_get_icon_name (self)); + break; + case PROP_PIXEL_SIZE: + g_value_set_int (value, gs_lozenge_get_pixel_size (self)); + break; + case PROP_TEXT: + g_value_set_string (value, gs_lozenge_get_text (self)); + break; + case PROP_MARKUP: + g_value_set_string (value, gs_lozenge_get_markup (self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gs_lozenge_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GsLozenge *self = GS_LOZENGE (object); + + switch ((GsLozengeProperty) prop_id) { + case PROP_CIRCULAR: + gs_lozenge_set_circular (self, g_value_get_boolean (value)); + break; + case PROP_ICON_NAME: + gs_lozenge_set_icon_name (self, g_value_get_string (value)); + break; + case PROP_PIXEL_SIZE: + gs_lozenge_set_pixel_size (self, g_value_get_int (value)); + break; + case PROP_TEXT: + gs_lozenge_set_text (self, g_value_get_string (value)); + break; + case PROP_MARKUP: + gs_lozenge_set_markup (self, g_value_get_string (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gs_lozenge_dispose (GObject *object) +{ + GsLozenge *self = GS_LOZENGE (object); + + g_clear_pointer (&self->icon_name, g_free); + g_clear_pointer (&self->text, g_free); + g_clear_pointer (&self->markup, g_free); + + G_OBJECT_CLASS (gs_lozenge_parent_class)->dispose (object); +} + +static void +gs_lozenge_class_init (GsLozengeClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->get_property = gs_lozenge_get_property; + object_class->set_property = gs_lozenge_set_property; + object_class->dispose = gs_lozenge_dispose; + + /** + * GsLozenge:circular: + * + * Whether the lozenge should be circular/square widget. + * + * Since: 43 + */ + obj_props[PROP_CIRCULAR] = + g_param_spec_boolean ("circular", NULL, NULL, + FALSE, + G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); + + /** + * GsLozenge:icon-name: + * + * An icon name for the lozenge. Setting this property turns + * the lozenge into the icon mode, which mean showing the icon, + * not the markup. + * + * Since: 43 + */ + obj_props[PROP_ICON_NAME] = + g_param_spec_string ("icon-name", NULL, NULL, + NULL, + G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); + + /** + * GsLozenge:pixel-size: + * + * An icon pixel size for the lozenge. + * + * Since: 43 + */ + obj_props[PROP_PIXEL_SIZE] = + g_param_spec_int ("pixel-size", NULL, NULL, + 0, G_MAXINT, 16, + G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); + + /** + * GsLozenge:text: + * + * A plain text for the lozenge. Setting this property turns + * the lozenge into the text mode, which mean showing the text, + * not the icon. + * + * Since: 43 + */ + obj_props[PROP_TEXT] = + g_param_spec_string ("text", NULL, NULL, + NULL, + G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); + + /** + * GsLozenge:markup: + * + * A markup text for the lozenge. Setting this property turns + * the lozenge into the text mode, which mean showing the markup, + * not the icon. + * + * Since: 43 + */ + obj_props[PROP_MARKUP] = + g_param_spec_string ("markup", NULL, NULL, + NULL, + G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, G_N_ELEMENTS (obj_props), obj_props); + + gtk_widget_class_set_layout_manager_type (widget_class, GS_TYPE_LOZENGE_LAYOUT); + gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/Software/gs-lozenge.ui"); + + gtk_widget_class_bind_template_child (widget_class, GsLozenge, image); + gtk_widget_class_bind_template_child (widget_class, GsLozenge, label); +} + +static void +gs_lozenge_init (GsLozenge *self) +{ + gtk_widget_init_template (GTK_WIDGET (self)); + + self->pixel_size = 16; +} + +/** + * gs_lozenge_new: + * + * Returns: (transfer full): a new #GsLozenge + * + * Since: 43 + **/ +GtkWidget * +gs_lozenge_new (void) +{ + return g_object_new (GS_TYPE_LOZENGE, NULL); +} + +const gchar * +gs_lozenge_get_icon_name (GsLozenge *self) +{ + g_return_val_if_fail (GS_IS_LOZENGE (self), NULL); + + return self->icon_name; +} + +gboolean +gs_lozenge_get_circular (GsLozenge *self) +{ + g_return_val_if_fail (GS_IS_LOZENGE (self), FALSE); + + return self->circular; +} + +void +gs_lozenge_set_circular (GsLozenge *self, + gboolean value) +{ + GtkLayoutManager *layout_manager; + + g_return_if_fail (GS_IS_LOZENGE (self)); + + if ((!self->circular) == (!value)) + return; + + self->circular = value; + + layout_manager = gtk_widget_get_layout_manager (GTK_WIDGET (self)); + GS_LOZENGE_LAYOUT (layout_manager)->circular = self->circular; + gtk_layout_manager_layout_changed (layout_manager); + + g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CIRCULAR]); +} + +void +gs_lozenge_set_icon_name (GsLozenge *self, + const gchar *value) +{ + g_return_if_fail (GS_IS_LOZENGE (self)); + + if (value != NULL && *value == '\0') + value = NULL; + + if (g_strcmp0 (self->icon_name, value) == 0) + return; + + g_clear_pointer (&self->icon_name, g_free); + self->icon_name = g_strdup (value); + + if (self->icon_name == NULL) { + gtk_widget_hide (self->image); + gtk_widget_show (self->label); + } else { + gtk_image_set_from_icon_name (GTK_IMAGE (self->image), self->icon_name); + gtk_widget_hide (self->label); + gtk_widget_show (self->image); + } + + /* Clean up the other properties before notifying of the changed property name */ + + if (self->text != NULL) { + g_clear_pointer (&self->text, g_free); + g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_TEXT]); + } + + if (self->markup != NULL) { + g_clear_pointer (&self->markup, g_free); + g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_MARKUP]); + } + + g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_ICON_NAME]); +} + +gint +gs_lozenge_get_pixel_size (GsLozenge *self) +{ + g_return_val_if_fail (GS_IS_LOZENGE (self), 0); + + return self->pixel_size; +} + +void +gs_lozenge_set_pixel_size (GsLozenge *self, + gint value) +{ + g_return_if_fail (GS_IS_LOZENGE (self)); + + if (self->pixel_size == value) + return; + + self->pixel_size = value; + + gtk_image_set_pixel_size (GTK_IMAGE (self->image), self->pixel_size); + + g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_PIXEL_SIZE]); +} + +gboolean +gs_lozenge_get_use_markup (GsLozenge *self) +{ + g_return_val_if_fail (GS_IS_LOZENGE (self), FALSE); + return gtk_label_get_use_markup (GTK_LABEL (self->label)); +} + +const gchar * +gs_lozenge_get_text (GsLozenge *self) +{ + g_return_val_if_fail (GS_IS_LOZENGE (self), NULL); + + return self->text; +} + +void +gs_lozenge_set_text (GsLozenge *self, + const gchar *value) +{ + g_return_if_fail (GS_IS_LOZENGE (self)); + + if (value != NULL && *value == '\0') + value = NULL; + + if (g_strcmp0 (self->text, value) == 0) + return; + + g_clear_pointer (&self->text, g_free); + self->text = g_strdup (value); + + if (self->text == NULL) { + gtk_widget_hide (self->label); + gtk_widget_show (self->image); + } else { + gtk_label_set_text (GTK_LABEL (self->label), self->text); + gtk_widget_hide (self->image); + gtk_widget_show (self->label); + } + + /* Clean up the other properties before notifying of the changed property name */ + + if (self->icon_name != NULL) { + g_clear_pointer (&self->icon_name, g_free); + g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_ICON_NAME]); + } + + if (self->markup != NULL) { + g_clear_pointer (&self->markup, g_free); + g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_MARKUP]); + } + + g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_TEXT]); +} + +const gchar * +gs_lozenge_get_markup (GsLozenge *self) +{ + g_return_val_if_fail (GS_IS_LOZENGE (self), NULL); + + return self->markup; +} + +void +gs_lozenge_set_markup (GsLozenge *self, + const gchar *value) +{ + g_return_if_fail (GS_IS_LOZENGE (self)); + + if (value != NULL && *value == '\0') + value = NULL; + + if (g_strcmp0 (self->markup, value) == 0) + return; + + g_clear_pointer (&self->markup, g_free); + self->markup = g_strdup (value); + + if (self->markup == NULL) { + gtk_widget_hide (self->label); + gtk_widget_show (self->image); + } else { + gtk_label_set_markup (GTK_LABEL (self->label), self->markup); + gtk_widget_hide (self->image); + gtk_widget_show (self->label); + } + + /* Clean up the other properties before notifying of the changed property name */ + + if (self->icon_name != NULL) { + g_clear_pointer (&self->icon_name, g_free); + g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_ICON_NAME]); + } + + if (self->text != NULL) { + g_clear_pointer (&self->text, g_free); + g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_TEXT]); + } + + g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_MARKUP]); +} |