gnome-control-center/panels/display/cc-night-light-widget.c
Emmanuele Bassi e7d0716d14 display: Load appropriate assets for the night light panel
We hard-code a 16x16 asset for sunrise and sunset icons, which makes
them look blurry at higher scaling factors.

Let's look in the icon theme to see if there's an icon for sunrise and
sunset; if not, we can still use the assets we ship, but we also attempt
to load them from different resource paths, depending on the scale
factor.

We will need to update the assets to include a 32x32 rendering of the
sunrise and sunset icons.

https://bugzilla.gnome.org/show_bug.cgi?id=781029
2017-04-07 14:38:10 +01:00

375 lines
12 KiB
C

/*
* Copyright (C) 2017 Richard Hughes <richard@hughsie.com>
*
* Licensed under the GNU General Public License Version 2
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include <gtk/gtk.h>
#include <colord.h>
#include "cc-night-light-widget.h"
#include "cc-display-resources.h"
struct _CcNightLightWidget {
GtkDrawingArea parent;
gdouble to;
gdouble from;
gdouble now;
cairo_surface_t *surface_sunrise;
cairo_surface_t *surface_sunset;
CcNightLightWidgetMode mode;
};
G_DEFINE_TYPE (CcNightLightWidget, cc_night_light_widget, GTK_TYPE_DRAWING_AREA);
static gboolean cc_night_light_widget_draw (GtkWidget *widget, cairo_t *cr);
void
cc_night_light_widget_set_to (CcNightLightWidget *self, gdouble to)
{
self->to = to;
gtk_widget_queue_draw (GTK_WIDGET (self));
}
void
cc_night_light_widget_set_from (CcNightLightWidget *self, gdouble from)
{
self->from = from;
gtk_widget_queue_draw (GTK_WIDGET (self));
}
void
cc_night_light_widget_set_now (CcNightLightWidget *self, gdouble now)
{
self->now = now;
gtk_widget_queue_draw (GTK_WIDGET (self));
}
void
cc_night_light_widget_set_mode (CcNightLightWidget *self,
CcNightLightWidgetMode mode)
{
self->mode = mode;
gtk_widget_queue_draw (GTK_WIDGET (self));
}
static cairo_status_t
_png_read_func (void *closure, unsigned char *data, unsigned int length)
{
GInputStream *stream = G_INPUT_STREAM (closure);
gssize read;
g_autoptr(GError) error = NULL;
read = g_input_stream_read (stream, data, (gsize) length, NULL, &error);
if (read < 0)
{
g_warning ("failed to read form stream: %s", error->message);
return CAIRO_STATUS_READ_ERROR;
}
return CAIRO_STATUS_SUCCESS;
}
static const char *scaled_sizes[] = {
NULL,
"16x16",
};
static cairo_surface_t *
read_surface_from_resource (const gchar *dirname,
int scale_factor,
const gchar *basename)
{
/* Always ensure that we have a resource for the scale factor */
scale_factor = CLAMP (scale_factor, 1, G_N_ELEMENTS (scaled_sizes) - 1);
g_autoptr(GError) error = NULL;
g_autoptr(GInputStream) stream = NULL;
g_autofree char *path = g_build_filename (dirname, scaled_sizes[scale_factor], basename, NULL);
stream = g_resource_open_stream (cc_display_get_resource (), path,
G_RESOURCE_LOOKUP_FLAGS_NONE, &error);
if (stream == NULL)
{
g_error ("failed to load PNG data: %s", error->message);
return NULL;
}
return cairo_image_surface_create_from_png_stream (_png_read_func, stream);
}
static void
cc_night_light_widget_style_updated (GtkWidget *widget)
{
GTK_WIDGET_CLASS (cc_night_light_widget_parent_class)->style_updated (widget);
CcNightLightWidget *self = CC_NIGHT_LIGHT_WIDGET (widget);
GtkIconTheme *icon_theme = gtk_icon_theme_get_default ();
g_clear_pointer (&self->surface_sunrise, (GDestroyNotify) cairo_surface_destroy);
g_clear_pointer (&self->surface_sunset, (GDestroyNotify) cairo_surface_destroy);
self->surface_sunrise =
gtk_icon_theme_load_surface (icon_theme, "daytime-sunrise-symbolic",
16,
gtk_widget_get_scale_factor (widget),
gtk_widget_get_window (widget),
0,
NULL);
if (self->surface_sunrise == NULL)
self->surface_sunrise = read_surface_from_resource ("/org/gnome/control-center/display",
gtk_widget_get_scale_factor (widget),
"sunrise.png");
self->surface_sunset =
gtk_icon_theme_load_surface (icon_theme, "daytime-sunset-symbolic",
16,
gtk_widget_get_scale_factor (widget),
gtk_widget_get_window (widget),
0,
NULL);
if (self->surface_sunset == NULL)
self->surface_sunset = read_surface_from_resource ("/org/gnome/control-center/display",
gtk_widget_get_scale_factor (widget),
"sunset.png");
}
static void
cc_night_light_widget_finalize (GObject *object)
{
CcNightLightWidget *self = CC_NIGHT_LIGHT_WIDGET (object);
g_clear_pointer (&self->surface_sunrise, (GDestroyNotify) cairo_surface_destroy);
g_clear_pointer (&self->surface_sunset, (GDestroyNotify) cairo_surface_destroy);
G_OBJECT_CLASS (cc_night_light_widget_parent_class)->finalize (object);
}
static void
cc_night_light_widget_class_init (CcNightLightWidgetClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
object_class->finalize = cc_night_light_widget_finalize;
widget_class->draw = cc_night_light_widget_draw;
widget_class->style_updated = cc_night_light_widget_style_updated;
}
static void
cc_night_light_widget_init (CcNightLightWidget *self)
{
self->to = 8;
self->from = 16;
self->now = 11;
}
static gboolean
is_frac_day_between (gdouble value, gdouble to, gdouble from)
{
/* wraparound to the next day */
if (from < to)
from += 24;
/* wraparound to the previous day */
if (value < from && value < to)
value += 24;
/* test limits */
return value > to && value <= from;
}
static void
rounded_rectangle (cairo_t *cr,
gdouble x,
gdouble y,
gdouble radius,
gdouble width,
gdouble height)
{
gdouble degrees = G_PI / 180.0;
cairo_new_sub_path (cr);
cairo_arc (cr,
x + width - radius,
y + radius,
radius,
-90 * degrees,
0 * degrees);
cairo_arc (cr,
x + width - radius,
y + height - radius,
radius,
0 * degrees,
90 * degrees);
cairo_arc (cr,
x + radius,
y + height - radius,
radius,
90 * degrees,
180 * degrees);
cairo_arc (cr,
x + radius,
y + radius,
radius,
180 * degrees,
270 * degrees);
cairo_close_path (cr);
}
static gboolean
cc_night_light_widget_draw (GtkWidget *widget, cairo_t *cr)
{
CdColorRGB color;
CdColorRGB color_temperature;
CdColorRGB color_unity;
GtkAllocation rect;
const guint arrow_sz = 5; /* px */
const guint icon_sz = 16; /* px */
const guint pad_upper_sz = 6; /* px */
const guint pad_lower_sz = 4; /* px */
guint line_x = 0; /* px */
guint bar_sz;
CcNightLightWidget *self = (CcNightLightWidget*) widget;
g_return_val_if_fail (self != NULL, FALSE);
g_return_val_if_fail (CC_IS_NIGHT_LIGHT_WIDGET (self), FALSE);
cd_color_rgb_set (&color_temperature, 0.992, 0.796, 0.612);
cd_color_rgb_set (&color_unity, 0.773, 0.862, 0.953);
/*
* /
* | icon_sz
* \
* <- pad_upper_sz
* /
* | bar_sz (calculated)
* \
* <- pad_lower_sz
* /
* | arrow_sz
* \
*/
gtk_widget_get_allocation (widget, &rect);
bar_sz = rect.height - (icon_sz + pad_upper_sz + pad_lower_sz + arrow_sz);
/* clip to a rounded rectangle */
cairo_save (cr);
cairo_set_line_width (cr, 1);
rounded_rectangle (cr, 0, icon_sz + pad_upper_sz, 6,
rect.width, bar_sz);
cairo_clip (cr);
/* draw each color line */
gdouble subsect = 24.f / (gdouble) rect.width;
if (gtk_widget_is_sensitive (widget))
{
cairo_set_line_width (cr, 1);
for (guint x = 0; x < rect.width; x += 1)
{
gdouble frac_hour = subsect * x;
if (is_frac_day_between (frac_hour, self->to - 1, self->to))
{
gdouble frac = 1.f - (self->to - frac_hour);
cd_color_rgb_interpolate (&color_temperature,
&color_unity,
frac,
&color);
}
else if (is_frac_day_between (frac_hour, self->from - 1, self->from))
{
gdouble frac = self->from - frac_hour;
cd_color_rgb_interpolate (&color_temperature,
&color_unity,
frac,
&color);
}
else if (is_frac_day_between (frac_hour, self->to, self->from))
{
cd_color_rgb_copy (&color_unity, &color);
}
else
{
cd_color_rgb_copy (&color_temperature, &color);
}
cairo_set_source_rgb (cr, color.R, color.G, color.B);
cairo_move_to (cr, x + 0.5, icon_sz + pad_upper_sz);
cairo_line_to (cr, x + 0.5, icon_sz + pad_upper_sz + bar_sz);
cairo_stroke (cr);
}
}
else
{
rounded_rectangle (cr, 0, icon_sz + pad_upper_sz, 6,
rect.width, bar_sz);
cairo_set_source_rgb (cr, 0.95f, 0.95f, 0.95f);
cairo_fill (cr);
}
/* apply border */
rounded_rectangle (cr, 0, icon_sz + pad_upper_sz, 6,
rect.width, bar_sz);
cairo_set_source_rgb (cr, 0.65, 0.65, 0.65);
cairo_set_line_width (cr, 1);
cairo_stroke (cr);
cairo_restore (cr);
/* apply arrow */
if (gtk_widget_is_sensitive (widget))
{
line_x = self->now / subsect;
cairo_move_to (cr,
line_x - arrow_sz + 0.5,
icon_sz + pad_upper_sz + bar_sz + pad_lower_sz + arrow_sz);
cairo_line_to (cr,
line_x + arrow_sz + 0.5,
icon_sz + pad_upper_sz + bar_sz + pad_lower_sz + arrow_sz);
cairo_line_to (cr,
line_x + 0.5,
icon_sz + pad_upper_sz + bar_sz + pad_lower_sz);
cairo_close_path (cr);
cairo_set_source_rgb (cr, 0.333, 0.333, 0.333);
cairo_fill (cr);
}
/* draw icons */
if (gtk_widget_is_sensitive (widget) &&
self->mode == CC_NIGHT_LIGHT_WIDGET_MODE_AUTOMATIC)
{
if (self->to <= 0)
line_x = rect.width - icon_sz;
else
line_x = MIN (MAX ((self->to / subsect) - (icon_sz / 2), 0), rect.width - icon_sz);
cairo_set_source_surface (cr, self->surface_sunrise, line_x, 0);
cairo_paint (cr);
if (self->from <= 0)
line_x = rect.width - icon_sz;
else
line_x = MIN (MAX ((self->from / subsect) - (icon_sz / 2), 0), rect.width - icon_sz);
cairo_set_source_surface (cr, self->surface_sunset, line_x, 0);
cairo_paint (cr);
}
return FALSE;
}
GtkWidget *
cc_night_light_widget_new (void)
{
return g_object_new (CC_TYPE_NIGHT_LIGHT_WIDGET, NULL);
}