gnome-water-reminder/src/gwr-water-level.c

283 lines
7.8 KiB
C

#include "gwr-water-level.h"
#include "gwr-enums.h"
struct _GwrWaterLevel {
GtkWidget parent_instance;
gfloat level;
GwrWaterLevelStyle style;
};
/**
* GwrWaterLevel:
*
* A widget displaying a glass filled with water.
*/
G_DEFINE_TYPE(GwrWaterLevel, gwr_water_level, GTK_TYPE_WIDGET)
enum {
PROP_0,
PROP_LEVEL,
PROP_STYLE,
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)
{
g_debug("Setting level to %.2f", level);
if (G_UNLIKELY(level > 1.0)) {
g_warning("Value (%.2f) is too large. Scaling down to 1.0", level);
level = 1.0;
} else if (G_UNLIKELY(level < 0.0)) {
g_warning("Value (%.2f) is too small. Scaling up to 0.0", level);
level = 0.0;
}
self->level = level;
gtk_widget_queue_draw(GTK_WIDGET(self));
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;
}
void
gwr_water_level_set_style(GwrWaterLevel *self,
GwrWaterLevelStyle style)
{
g_debug("Setting style to %d", style);
self->style = style;
gtk_widget_queue_draw(GTK_WIDGET(self));
g_object_notify_by_pspec(G_OBJECT(self), props[PROP_LEVEL]);
}
GwrWaterLevelStyle
gwr_water_level_get_style(GwrWaterLevel *self)
{
return self->style;
}
static gboolean
gwr_water_level_draw(GtkWidget *widget, cairo_t *cr)
{
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, center;
gfloat level_y;
gtk_render_background(context, cr, 0, 0, width, height);
gtk_render_frame(context, cr, 0, 0, width, height);
center = left = right = width / 2.0;
left -= 3.0 * height / 8.0;
right += 3.0 * height / 8.0;
width = right - left;
gfloat top_y = width * 0.1;
gint bottom_y = height - (width * 0.1);
cairo_save(cr);
switch (self->style) {
case GWR_WATER_LEVEL_STYLE_BOTTLE:
{
gfloat left_x = left + (width * 0.2);
gfloat right_x = right - (width * 0.2);
cairo_move_to(cr, left_x, bottom_y);
cairo_line_to(cr, right_x, bottom_y);
cairo_line_to(cr, right_x, top_y + (width * 0.2));
cairo_line_to(cr, center + (width * 0.1), top_y + (width * 0.1));
cairo_line_to(cr, center + (width * 0.1), top_y);
cairo_line_to(cr, center - (width * 0.1), top_y);
cairo_line_to(cr, center - (width * 0.1), top_y + (width * 0.1));
cairo_line_to(cr, left_x, top_y + (width * 0.2));
cairo_close_path(cr);
cairo_stroke(cr);
top_y = top_y + (width * 0.2);
level_y = top_y + (bottom_y - top_y) * (1.0 - self->level);
cairo_rectangle(cr, left_x, level_y, right_x - left_x, bottom_y - level_y);
}
break;
default:
{
gint top_left_x = left + (width * 0.1);
gint top_right_x = right - (width * 0.1);
gint bottom_right_x = right - (width * 0.2);
gint bottom_left_x = left + (width * 0.2);
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);
}
break;
}
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.
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.
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;
case PROP_STYLE:
gwr_water_level_set_style(GWR_WATER_LEVEL(gobject), g_value_get_enum(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;
case PROP_STYLE:
g_value_set_enum(value, gwr_water_level_get_style(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);
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);
props[PROP_STYLE] = g_param_spec_enum(
"style", "style", "The style of the water level",
GWR_TYPE_WATER_LEVEL_STYLE, GWR_WATER_LEVEL_STYLE_GLASS,
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);
}