Commit d51336c2 authored by Carlos Garnacho's avatar Carlos Garnacho Committed by Georges Basile Stavracas Neto

wacom: Port calibrator UI to GTK+

Same dog, different collar. The UI has been ported 1:1 to GTK+, using
GtkBuilder, CSS and event controllers fairly reduced the amount of code
needed for this.

It also allows us to stop initializing clutter-gtk across the several
executables.
parent 94b218a4
......@@ -198,15 +198,10 @@ if host_is_linux_not_s390
# gnome-bluetooth
gnome_bluetooth_dep = dependency('gnome-bluetooth-1.0', version: '>= 3.18.2')
# Wacom
assert(clutter_gtk_dep.found(), 'clutter-gtk library is required for wacom support, but is not available.')
libwacom_dep = dependency('libwacom', version: '>= 0.7')
wacom_deps = [
clutter_gtk_dep,
libwacom_dep,
dependency('clutter-1.0', version: '>= 1.11.3'),
]
config_h.set('HAVE_WACOM_3D_STYLUS', libwacom_dep.version().version_compare('>= 0.27'),
description: 'Define to 1 if libwacom provides definition for 3D styli')
......
This diff is collapsed.
window {
background-color: #000;
}
* {
color: #fff;
}
label {
font-size: larger;
}
#title {
font-weight: bold;
color: #888;
}
#error {
font-weight: bold;
}
#target {
background-image: url('target.svg');
background-repeat: no-repeat;
background-position: 50% 50%;
}
@keyframes target-enabled-animation {
0% { background-size: 0px }
90% { background-size: 120px }
100% { background-size: 100px }
}
@keyframes target-disabled-animation {
0% { background-size: 100px }
100% { background-size: 0px }
}
#target:not(disabled) {
animation: target-enabled-animation 1 ease 0.5s;
background-size: 100px;
}
#target:disabled {
animation: target-disabled-animation 1 ease 0.2s;
background-size: 0px;
}
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<requires lib="gtk+" version="3.20"/>
<object class="GtkWindow" id="window">
<child>
<object class="GtkStack" id="stack">
<property name="visible">True</property>
<property name="transition_duration">0</property>
<child>
<object class="GtkGrid">
<property name="visible">True</property>
<property name="row_homogeneous">True</property>
<property name="column_homogeneous">True</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkBox" id="box1">
<property name="visible">True</property>
<property name="orientation">vertical</property>
</object>
<packing>
<property name="expand">True</property>
</packing>
</child>
<child>
<object class="CcClock" id="clock">
<property name="visible">True</property>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkBox" id="box2">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkRevealer" id="title_revealer">
<property name="visible">True</property>
<property name="transition_duration">300</property>
<child>
<object class="GtkLabel">
<property name="name">title</property>
<property name="visible">True</property>
<property name="label" translatable="yes">Screen Calibration</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkRevealer" id="subtitle_revealer">
<property name="visible">True</property>
<property name="transition_duration">300</property>
<child>
<object class="GtkLabel">
<property name="name">subtitle</property>
<property name="visible">True</property>
<property name="label" translatable="yes">Please tap the target markers as they appear on screen to calibrate the tablet.</property>
</object>
</child>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkRevealer" id="error_revealer">
<property name="visible">True</property>
<property name="transition_type">crossfade</property>
<property name="transition_duration">500</property>
<child>
<object class="GtkLabel">
<property name="name">error</property>
<property name="visible">True</property>
<property name="label" translatable="yes">Mis-click detected, restarting…</property>
</object>
</child>
</object>
<packing>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
<property name="width">8</property>
<property name="height">8</property>
</packing>
</child>
<child>
<object class="GtkEventBox" id="target1">
<property name="name">target</property>
<property name="width_request">100</property>
<property name="height_request">100</property>
<property name="visible">True</property>
<property name="visible_window">True</property>
<property name="sensitive">False</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
<property name="width">2</property>
<property name="height">2</property>
</packing>
</child>
<child>
<object class="GtkEventBox" id="target2">
<property name="name">target</property>
<property name="width_request">100</property>
<property name="height_request">100</property>
<property name="visible">True</property>
<property name="visible_window">True</property>
<property name="sensitive">False</property>
</object>
<packing>
<property name="left_attach">6</property>
<property name="top_attach">0</property>
<property name="width">2</property>
<property name="height">2</property>
</packing>
</child>
<child>
<object class="GtkEventBox" id="target3">
<property name="name">target</property>
<property name="width_request">100</property>
<property name="height_request">100</property>
<property name="visible">True</property>
<property name="visible_window">True</property>
<property name="sensitive">False</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">6</property>
<property name="width">2</property>
<property name="height">2</property>
</packing>
</child>
<child>
<object class="GtkEventBox" id="target4">
<property name="name">target</property>
<property name="width_request">100</property>
<property name="height_request">100</property>
<property name="visible">True</property>
<property name="visible_window">True</property>
<property name="sensitive">False</property>
</object>
<packing>
<property name="left_attach">6</property>
<property name="top_attach">6</property>
<property name="width">2</property>
<property name="height">2</property>
</packing>
</child>
<child>
<object class="GtkRevealer">
<property name="visible">True</property>
<property name="transition_type">none</property>
<property name="transition_duration">0</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
<property name="width">8</property>
<property name="height">8</property>
</packing>
</child>
</object>
<packing>
<property name="name">page0</property>
</packing>
</child>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="pixel_size">300</property>
<property name="icon_name">emblem-ok-symbolic</property>
</object>
<packing>
<property name="name">page1</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
</object>
<object class="GtkSizeGroup">
<property name="mode">vertical</property>
<widgets>
<widget name="box1"/>
<widget name="box2"/>
</widgets>
</object>
</interface>
/*
* Copyright © 2013 Red Hat, Inc.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* Author: Joaquim Rocha <jrocha@redhat.com>
*/
#include <math.h>
#include "cc-clock-actor.h"
#define CLOCK_RADIUS 50
#define CLOCK_LINE_WIDTH 10
#define CLOCK_LINE_PADDING 10
#define CLOCK_SIZE_EXTRA
#define ANGLE "angle"
#define EXTRA_SPACE 2
struct _CcClockActor
{
ClutterActor parent_instance;
gfloat angle;
};
G_DEFINE_TYPE (CcClockActor, cc_clock_actor, CLUTTER_TYPE_ACTOR);
enum {
PROP_0,
PROP_ANGLE,
N_PROPERTIES
};
static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, };
static void
draw_clock (ClutterCairoTexture *texture,
cairo_t *cr,
gint width,
gint height,
CcClockActor *self)
{
cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
cairo_paint (cr);
cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
/* Draw the clock background */
cairo_arc (cr, width / 2, height / 2, CLOCK_RADIUS / 2, 0.0, 2.0 * M_PI);
cairo_set_source_rgb (cr, 0.5, 0.5, 0.5);
cairo_fill_preserve (cr);
cairo_stroke (cr);
cairo_set_line_width (cr, CLOCK_LINE_WIDTH);
cairo_arc (cr,
width / 2,
height / 2,
(CLOCK_RADIUS - CLOCK_LINE_WIDTH - CLOCK_LINE_PADDING) / 2,
3 * M_PI_2,
3 * M_PI_2 + self->angle * M_PI / 180.0);
cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
cairo_stroke (cr);
}
static void
cc_clock_actor_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
CcClockActor *self = CC_CLOCK_ACTOR (object);
ClutterContent *content;
content = clutter_actor_get_content (CLUTTER_ACTOR (self));
switch (property_id)
{
case PROP_ANGLE:
self->angle = g_value_get_float (value);
if (content)
clutter_content_invalidate (content);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
cc_clock_actor_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
CcClockActor *self = CC_CLOCK_ACTOR (object);
switch (property_id)
{
case PROP_ANGLE:
g_value_set_float (value, self->angle);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
cc_clock_actor_init (CcClockActor *self)
{
self->angle = 0;
ClutterContent *content;
content = clutter_canvas_new ();
/* Extra space is needed because when drawing without it,
it will miss 1 pixel in each of the edges */
clutter_canvas_set_size (CLUTTER_CANVAS (content),
CLOCK_RADIUS + EXTRA_SPACE,
CLOCK_RADIUS + EXTRA_SPACE);
clutter_actor_set_content (CLUTTER_ACTOR (self), content);
g_signal_connect (CLUTTER_CANVAS (content),
"draw",
G_CALLBACK (draw_clock),
self);
g_object_unref (content);
}
static void
cc_clock_actor_get_preferred_width (ClutterActor *actor,
gfloat for_height,
gfloat *min_width_p,
gfloat *natural_width_p)
{
*min_width_p = CLOCK_RADIUS + EXTRA_SPACE;
*natural_width_p = CLOCK_RADIUS + EXTRA_SPACE;
}
static void
cc_clock_actor_get_preferred_height (ClutterActor *actor,
gfloat for_width,
gfloat *min_height_p,
gfloat *natural_height_p)
{
*min_height_p = CLOCK_RADIUS + EXTRA_SPACE;
*natural_height_p = CLOCK_RADIUS + EXTRA_SPACE;
}
static void
cc_clock_actor_class_init (CcClockActorClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
ClutterActorClass *clutter_actor_class = CLUTTER_ACTOR_CLASS (klass);
gobject_class->set_property = cc_clock_actor_set_property;
gobject_class->get_property = cc_clock_actor_get_property;
clutter_actor_class->get_preferred_width = cc_clock_actor_get_preferred_width;
clutter_actor_class->get_preferred_height = cc_clock_actor_get_preferred_height;
obj_properties[PROP_ANGLE] =
g_param_spec_float (ANGLE,
"The angle of the clock's progress",
"Set the angle of the clock's progress",
.0,
360.0,
.0,
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class,
N_PROPERTIES,
obj_properties);
}
ClutterActor *
cc_clock_actor_new (void)
{
return g_object_new (CC_CLOCK_ACTOR_TYPE, NULL);
}
/*
* Copyright © 2018 Red Hat, Inc.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* Authors: Joaquim Rocha <jrocha@redhat.com>
* Carlos Garnacho <carlosg@gnome.org>
*/
#include "config.h"
#include "cc-clock.h"
#include <math.h>
#define CLOCK_RADIUS 50
#define CLOCK_LINE_WIDTH 10
#define CLOCK_LINE_PADDING 10
#define EXTRA_SPACE 2
typedef struct _CcClock CcClock;
struct _CcClock
{
GtkWidget parent_instance;
guint duration;
gint64 start_time;
gboolean running;
};
enum
{
PROP_DURATION = 1,
N_PROPS
};
static GParamSpec *props[N_PROPS] = { 0, };
enum {
FINISHED,
N_SIGNALS
};
static guint signals[N_SIGNALS] = { 0, };
G_DEFINE_TYPE (CcClock, cc_clock, GTK_TYPE_WIDGET)
static gint64
cc_clock_get_time_diff (CcClock *clock)
{
GdkFrameClock *frame_clock;
gint64 current_time;
frame_clock = gtk_widget_get_frame_clock (GTK_WIDGET (clock));
current_time = gdk_frame_clock_get_frame_time (frame_clock);
return current_time - clock->start_time;
}
static gdouble
cc_clock_get_angle (CcClock *clock)
{
gint64 time_diff;
time_diff = cc_clock_get_time_diff (clock);
if (time_diff > clock->duration * 1000)
return 360;
return ((gdouble) time_diff / (clock->duration * 1000)) * 360;
}
static gboolean
cc_clock_draw (GtkWidget *widget,
cairo_t *cr)
{
GtkAllocation allocation;
gdouble angle;
gtk_widget_get_allocation (widget, &allocation);
angle = cc_clock_get_angle (CC_CLOCK (widget));
cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
cairo_paint (cr);
cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
/* Draw the clock background */
cairo_arc (cr, allocation.width / 2, allocation.height / 2, CLOCK_RADIUS / 2, 0.0, 2.0 * M_PI);
cairo_set_source_rgb (cr, 0.5, 0.5, 0.5);
cairo_fill_preserve (cr);
cairo_stroke (cr);
cairo_set_line_width (cr, CLOCK_LINE_WIDTH);
cairo_arc (cr,
allocation.width / 2,
allocation.height / 2,
(CLOCK_RADIUS - CLOCK_LINE_WIDTH - CLOCK_LINE_PADDING) / 2,
3 * M_PI_2,
3 * M_PI_2 + angle * M_PI / 180.0);
cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
cairo_stroke (cr);
return TRUE;
}
static void
cc_clock_stop (CcClock *clock)
{
GdkFrameClock *frame_clock;
if (!clock->running)
return;
frame_clock = gtk_widget_get_frame_clock (GTK_WIDGET (clock));
gdk_frame_clock_end_updating (frame_clock);
clock->running = FALSE;
}
static void
on_frame_clock_update (CcClock *clock)
{
gint64 time_diff;
if (!clock->running)
return;
time_diff = cc_clock_get_time_diff (clock);
if (time_diff > clock->duration * 1000)
{
g_signal_emit (clock, signals[FINISHED], 0);
cc_clock_stop (clock);
}
gtk_widget_queue_draw (GTK_WIDGET (clock));
}
static void
cc_clock_map (GtkWidget *widget)
{
GdkFrameClock *frame_clock;
GTK_WIDGET_CLASS (cc_clock_parent_class)->map (widget);
frame_clock = gtk_widget_get_frame_clock (widget);
g_signal_connect_object (frame_clock, "update",
G_CALLBACK (on_frame_clock_update),
widget, G_CONNECT_SWAPPED);
cc_clock_reset (CC_CLOCK (widget));
}
static void
cc_clock_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
CcClock *clock = CC_CLOCK (object);
switch (prop_id)
{
case PROP_DURATION:
clock->duration = g_value_get_uint (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
cc_clock_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
CcClock *clock = CC_CLOCK (object);
switch (prop_id)
{
case PROP_DURATION:
g_value_set_uint (value, clock->duration);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
cc_clock_get_preferred_width (GtkWidget *widget,
gint *minimum,
gint *natural)
{
if (minimum)
*minimum = CLOCK_RADIUS + EXTRA_SPACE;
if (natural)
*natural = CLOCK_RADIUS + EXTRA_SPACE;
}
static void
cc_clock_get_preferred_height (GtkWidget *widget,
gint *minimum,
gint *natural)
{
if (minimum)
*minimum = CLOCK_RADIUS + EXTRA_SPACE;
if (natural)
*natural = CLOCK_RADIUS + EXTRA_SPACE;
}
static void
cc_clock_class_init (CcClockClass *klass)
{
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->set_property = cc_clock_set_property;
object_class->get_property = cc_clock_get_property;
widget_class->map = cc_clock_map;
widget_class->draw = cc_clock_draw;
widget_class->get_preferred_width = cc_clock_get_preferred_width;
widget_class->get_preferred_height = cc_clock_get_preferred_height;
signals[FINISHED] =
g_signal_new ("finished",
CC_TYPE_CLOCK,
G_SIGNAL_RUN_LAST,
0, NULL, NULL, NULL,
G_TYPE_NONE, 0);
props[PROP_DURATION] =
g_param_spec_uint ("duration",
"Duration",
"Duration",
0, G_MAXUINT, 0,
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS |
G_PARAM_CONSTRUCT_ONLY);
g_object_class_install_properties (object_class, N_PROPS, props);
}
static void
cc_clock_init (CcClock *clock)
{
gtk_widget_set_has_window (GTK_WIDGET (clock), FALSE);
}
GtkWidget *
cc_clock_new (guint duration)
{
return g_object_new (CC_TYPE_CLOCK,