From 1ca33bdd9d7832ee6a803a623d72b25cbb54e70f Mon Sep 17 00:00:00 2001 From: Gergely Polonkai Date: Fri, 27 Jul 2018 18:32:37 +0200 Subject: [PATCH] Add the initial version of the water level widget --- data/gwr-window.ui | 38 +++++--- src/gwr-water-level.c | 197 ++++++++++++++++++++++++++++++++++++------ src/gwr-water-level.h | 4 + src/gwr-window.c | 2 + 4 files changed, 206 insertions(+), 35 deletions(-) diff --git a/data/gwr-window.ui b/data/gwr-window.ui index 6760e43..68f6dee 100644 --- a/data/gwr-window.ui +++ b/data/gwr-window.ui @@ -1,19 +1,37 @@ + + - \ No newline at end of file + diff --git a/src/gwr-water-level.c b/src/gwr-water-level.c index 95d1683..d514e27 100644 --- a/src/gwr-water-level.c +++ b/src/gwr-water-level.c @@ -1,60 +1,207 @@ #include "gwr-water-level.h" struct _GwrWaterLevel { - GtkWidget parent_instance; + GtkWidget parent_instance; + + gfloat level; }; +/** + * GwrWaterLevel: + * + * A widget displaying a glass filled with water. + */ G_DEFINE_TYPE(GwrWaterLevel, gwr_water_level, GTK_TYPE_WIDGET) +enum { + PROP_0, + PROP_LEVEL, + N_PROPS +}; + +static GParamSpec *props[N_PROPS] = { NULL, }; + +/** + * gwr_water_level_set_level: + * @self: a #GwrWaterLevel object + * @level: the level to set + * + * Set the water level for this object. + */ +void +gwr_water_level_set_level(GwrWaterLevel *self, + gfloat level) +{ + self->level = MAX(0, MIN(level, 1.0)); + + g_object_notify_by_pspec(G_OBJECT(self), props[PROP_LEVEL]); +} + +/** + * gwr_water_level_get_level: + * @self: a #GwrWaterLevel object + * + * @returns: the current water level + */ +gfloat +gwr_water_level_get_level(GwrWaterLevel *self) +{ + return self->level; +} + static gboolean gwr_water_level_draw(GtkWidget *widget, cairo_t *cr) { - GtkStyleContext *context = gtk_widget_get_style_context(widget); - gint width = gtk_widget_get_allocated_width(widget); - gint height = gtk_widget_get_allocated_height(widget); - GdkRGBA color; + GwrWaterLevel *self = GWR_WATER_LEVEL(widget); + GtkStyleContext *context = gtk_widget_get_style_context(widget); + gint width = gtk_widget_get_allocated_width(widget); + gint height = gtk_widget_get_allocated_height(widget); + GdkRGBA color; + gint left, right; - gtk_render_background(context, cr, 0, 0, width, height); - gtk_render_frame(context, cr, 0, 0, width, height); + gtk_render_background(context, cr, 0, 0, width, height); + gtk_render_frame(context, cr, 0, 0, width, height); - cairo_arc(cr, width / 2.0, height / 2.0, MIN(width, height) / 2.0, 0, 2 * G_PI); - gtk_style_context_get_color(context, gtk_style_context_get_state(context), &color); - gdk_cairo_set_source_rgba(cr, &color); - gdk_cairo_set_source_rgba(cr, &color); - cairo_fill (cr); + left = right = width / 2.0; + left -= 3.0 * height / 8.0; + right += 3.0 * height / 8.0; + width = right - left; - return FALSE; + cairo_save(cr); + + gint top_left_x = left + (width * 0.1); + gint top_y = width * 0.1; + gint top_right_x = right - (width * 0.1); + gint bottom_right_x = right - (width * 0.2); + gint bottom_y = height - (width * 0.1); + gint bottom_left_x = left + (width * 0.2); + float level_y = top_y + (bottom_y - top_y) * (1.0 - self->level); + + cairo_move_to(cr, top_left_x, top_y); + cairo_line_to(cr, top_right_x, top_y); + cairo_line_to(cr, bottom_right_x, bottom_y); + cairo_line_to(cr, bottom_left_x, bottom_y); + cairo_close_path(cr); + cairo_stroke(cr); + + cairo_move_to(cr, bottom_right_x, bottom_y); + cairo_line_to(cr, bottom_left_x, bottom_y); + cairo_line_to(cr, bottom_left_x - ((top_right_x - bottom_right_x) * self->level), level_y); + cairo_line_to(cr, bottom_right_x + ((top_right_x - bottom_right_x) * self->level), level_y); + cairo_close_path(cr); + cairo_restore(cr); + + gtk_style_context_get_color(context, gtk_style_context_get_state(context), &color); + + gdk_cairo_set_source_rgba(cr, &color); + cairo_fill (cr); + + return FALSE; } static void gwr_water_level_finalize(GObject *gobject) { - // Here, you free memory that is associated with your instance. - // - // References to other objects should be freed in dispose instead. + // Here, you free memory that is associated with your instance. + // + // References to other objects should be freed in dispose instead. - G_OBJECT_CLASS(gwr_water_level_parent_class)->finalize(gobject); + G_OBJECT_CLASS(gwr_water_level_parent_class)->finalize(gobject); } static void gwr_water_level_destroy(GtkWidget *widget) { - // Here you drop references to other objects. + // Here you drop references to other objects. - GTK_WIDGET_CLASS(gwr_water_level_parent_class)->destroy(widget); + GTK_WIDGET_CLASS(gwr_water_level_parent_class)->destroy(widget); +} + +static void +gwr_water_level_get_preferred_height(GtkWidget *widget, gint *minimum_height, gint *natural_height) +{ + if (minimum_height != NULL) { + *minimum_height = 200; + } + + if (natural_height != NULL) { + *natural_height = 400; + } +} + +static void +gwr_water_level_get_preferred_width(GtkWidget *widget, gint *minimum_width, gint *natural_width) +{ + if (minimum_width != NULL) { + *minimum_width = 100; + } + + if (natural_width != NULL) { + *natural_width = 200; + } +} + +static void +gwr_water_level_set_property(GObject *gobject, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (prop_id) { + case PROP_LEVEL: + gwr_water_level_set_level(GWR_WATER_LEVEL(gobject), g_value_get_float(value)); + + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, prop_id, pspec); + + break; + } +} + +static void +gwr_water_level_get_property(GObject *gobject, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + switch (prop_id) { + case PROP_LEVEL: + g_value_set_float(value, gwr_water_level_get_level(GWR_WATER_LEVEL(gobject))); + + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, prop_id, pspec); + + break; + } } static void gwr_water_level_class_init(GwrWaterLevelClass *klass) { - GObjectClass *gobject_class = G_OBJECT_CLASS(klass); - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass); + GObjectClass *gobject_class = G_OBJECT_CLASS(klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass); - gobject_class->finalize = gwr_water_level_finalize; - widget_class->destroy = gwr_water_level_destroy; - widget_class->draw = gwr_water_level_draw; + gobject_class->set_property = gwr_water_level_set_property; + gobject_class->get_property = gwr_water_level_get_property; + gobject_class->finalize = gwr_water_level_finalize; + + widget_class->destroy = gwr_water_level_destroy; + widget_class->get_preferred_width = gwr_water_level_get_preferred_width; + widget_class->get_preferred_height = gwr_water_level_get_preferred_height; + widget_class->draw = gwr_water_level_draw; + + props[PROP_LEVEL] = g_param_spec_float( + "level", "level", "The current water level", + 0.0, 1.0, 0.0, + G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE); + g_object_class_install_properties(gobject_class, N_PROPS, props); } static void gwr_water_level_init(GwrWaterLevel *self) -{} +{ + self->level = 0.0; + gtk_widget_set_has_window(GTK_WIDGET(self), FALSE); +} diff --git a/src/gwr-water-level.h b/src/gwr-water-level.h index cb7fe4a..eaca336 100644 --- a/src/gwr-water-level.h +++ b/src/gwr-water-level.h @@ -8,6 +8,10 @@ G_BEGIN_DECLS # define GWR_TYPE_WATER_LEVEL (gwr_water_level_get_type()) G_DECLARE_FINAL_TYPE(GwrWaterLevel, gwr_water_level, GWR, WATER_LEVEL, GtkWidget) +void gwr_water_level_set_level(GwrWaterLevel *water_level, + gfloat level); +gfloat gwr_water_level_get_level(GwrWaterLevel *water_level); + G_END_DECLS #endif /* __GWR_WATER_LEVEL_H__ */ diff --git a/src/gwr-window.c b/src/gwr-window.c index 5e9bec1..b45602c 100644 --- a/src/gwr-window.c +++ b/src/gwr-window.c @@ -44,5 +44,7 @@ gwr_window_class_init (GwrWindowClass *klass) static void gwr_window_init (GwrWindow *self) { + g_type_ensure(GWR_TYPE_WATER_LEVEL); + gtk_widget_init_template (GTK_WIDGET (self)); }