2
* mx-toggle: toggle switch actor
4
* Copyright 2009 Intel Corporation.
6
* This program is free software; you can redistribute it and/or modify it
7
* under the terms and conditions of the GNU Lesser General Public License,
8
* version 2.1, as published by the Free Software Foundation.
10
* This program is distributed in the hope it will be useful, but WITHOUT ANY
11
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
15
* You should have received a copy of the GNU Lesser General Public License
16
* along with this program; if not, write to the Free Software Foundation,
17
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
19
* Written by: Thomas Wood <thomas.wood@intel.com>
23
#include "mx-toggle.h"
24
#include "mx-private.h"
25
#include "mx-stylable.h"
28
/* mx-toggle-handle */
29
#define MX_TYPE_TOGGLE_HANDLE mx_toggle_handle_get_type()
31
#define MX_TOGGLE_HANDLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MX_TYPE_TOGGLE_HANDLE, MxToggleHandle))
33
#define MX_IS_TOGGLE_HANDLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MX_TYPE_TOGGLE_HANDLE))
42
MxWidgetClass parent_class;
43
} MxToggleHandleClass;
45
GType mx_toggle_handle_get_type (void) G_GNUC_CONST;
47
G_DEFINE_TYPE (MxToggleHandle, mx_toggle_handle, MX_TYPE_WIDGET)
50
mx_toggle_get_preferred_width (ClutterActor *actor,
55
ClutterActor *background;
58
background = mx_widget_get_border_image (MX_WIDGET (actor));
60
background = mx_widget_get_background_image (MX_WIDGET (actor));
72
clutter_actor_get_preferred_width (background, -1, NULL, &pref_w);
75
*min_width_p = pref_w;
78
*pref_width_p = pref_w;
82
mx_toggle_get_preferred_height (ClutterActor *actor,
85
gfloat *pref_height_p)
87
ClutterActor *background;
90
background = mx_widget_get_border_image (MX_WIDGET (actor));
92
background = mx_widget_get_background_image (MX_WIDGET (actor));
104
clutter_actor_get_preferred_height (background, -1, NULL, &pref_h);
107
*min_height_p = pref_h;
110
*pref_height_p = pref_h;
116
mx_toggle_handle_class_init (MxToggleHandleClass *klass)
121
mx_toggle_handle_init (MxToggleHandle *self)
127
static void mx_toggle_stylable_iface_init (MxStylableIface *iface);
129
G_DEFINE_TYPE_WITH_CODE (MxToggle, mx_toggle, MX_TYPE_WIDGET,
130
G_IMPLEMENT_INTERFACE (MX_TYPE_STYLABLE,
131
mx_toggle_stylable_iface_init))
133
#define TOGGLE_PRIVATE(o) \
134
(G_TYPE_INSTANCE_GET_PRIVATE ((o), MX_TYPE_TOGGLE, MxTogglePrivate))
136
struct _MxTogglePrivate
140
ClutterActor *handle;
141
gchar *handle_filename;
158
mx_toggle_stylable_iface_init (MxStylableIface *iface)
162
pspec = g_param_spec_string ("handle-image",
164
"Image used for the handle of the toggle",
167
mx_stylable_iface_install_property (iface, MX_TYPE_TOGGLE, pspec);
171
mx_toggle_get_property (GObject *object,
176
MxTogglePrivate *priv = MX_TOGGLE (object)->priv;
181
g_value_set_boolean (value, priv->active);
185
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
190
mx_toggle_set_property (GObject *object,
195
MxToggle *toggle = MX_TOGGLE (object);
200
mx_toggle_set_active (toggle, g_value_get_boolean (value));
204
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
209
mx_toggle_dispose (GObject *object)
211
MxTogglePrivate *priv = MX_TOGGLE (object)->priv;
215
clutter_actor_destroy (priv->handle);
219
G_OBJECT_CLASS (mx_toggle_parent_class)->dispose (object);
223
mx_toggle_finalize (GObject *object)
225
G_OBJECT_CLASS (mx_toggle_parent_class)->finalize (object);
229
mx_toggle_pick (ClutterActor *actor,
230
const ClutterColor *color)
232
CLUTTER_ACTOR_CLASS (mx_toggle_parent_class)->pick (actor, color);
234
clutter_actor_paint (MX_TOGGLE (actor)->priv->handle);
238
mx_toggle_paint (ClutterActor *actor)
240
CLUTTER_ACTOR_CLASS (mx_toggle_parent_class)->paint (actor);
242
clutter_actor_paint (MX_TOGGLE (actor)->priv->handle);
246
mx_toggle_map (ClutterActor *actor)
248
MxTogglePrivate *priv = MX_TOGGLE (actor)->priv;
250
CLUTTER_ACTOR_CLASS (mx_toggle_parent_class)->map (actor);
252
clutter_actor_map (priv->handle);
256
mx_toggle_unmap (ClutterActor *actor)
258
MxTogglePrivate *priv = MX_TOGGLE (actor)->priv;
260
clutter_actor_unmap (priv->handle);
262
CLUTTER_ACTOR_CLASS (mx_toggle_parent_class)->unmap (actor);
266
mx_toggle_allocate (ClutterActor *actor,
267
const ClutterActorBox *box,
268
ClutterAllocationFlags flags)
270
MxTogglePrivate *priv = MX_TOGGLE (actor)->priv;
271
ClutterActorBox handle_box, child_box;
272
ClutterActor *background;
276
CLUTTER_ACTOR_CLASS (mx_toggle_parent_class)->allocate (actor, box, flags);
278
mx_widget_get_available_area (MX_WIDGET (actor), box, &child_box);
280
/* background-image don't get stretched, so adjust the child box so that the
281
* handle appears in the correct place.
283
background = mx_widget_get_background_image (MX_WIDGET (actor));
289
mx_widget_get_padding (MX_WIDGET (actor), &padding);
290
clutter_actor_get_preferred_width (background, -1, NULL, &width);
291
width -= padding.left + padding.right;
293
child_box.x1 += (child_box.x2 - child_box.x1) / 2.f;
294
child_box.x1 -= width / 2.f;
295
child_box.x2 = child_box.x1 + width;
298
handle_w = (int)((child_box.x2 - child_box.x1) / 2.f);
299
toggle_pos = child_box.x2 - handle_w - child_box.x1;
300
priv->slide_length = toggle_pos;
302
toggle_pos = toggle_pos * priv->position;
304
handle_box.x1 = (gint) (child_box.x1 + toggle_pos);
305
handle_box.y1 = child_box.y1;
306
handle_box.x2 = handle_box.x1 + handle_w;
307
handle_box.y2 = child_box.y2;
309
clutter_actor_allocate (priv->handle, &handle_box, flags);
313
mx_toggle_button_release_event (ClutterActor *actor,
314
ClutterButtonEvent *event)
316
MxToggle *toggle = MX_TOGGLE (actor);
318
if (mx_widget_get_disabled (MX_WIDGET (actor)))
321
mx_toggle_set_active (toggle, !toggle->priv->active);
327
mx_toggle_handle_button_press_event (ClutterActor *actor,
328
ClutterButtonEvent *event,
331
if (mx_widget_get_disabled (MX_WIDGET (actor)))
334
clutter_grab_pointer (actor);
336
toggle->priv->drag_offset = event->x;
342
mx_toggle_handle_button_release_event (ClutterActor *actor,
343
ClutterButtonEvent *event,
346
if (mx_widget_get_disabled (MX_WIDGET (actor)))
349
if (toggle->priv->last_move == 0)
350
mx_toggle_set_active (toggle, !toggle->priv->active);
352
mx_toggle_set_active (toggle, (toggle->priv->last_move > 0.0));
354
toggle->priv->drag_offset = -1;
356
toggle->priv->last_move = 0;
358
clutter_ungrab_pointer ();
364
mx_toggle_handle_motion_event (ClutterActor *actor,
365
ClutterMotionEvent *event,
368
MxTogglePrivate *priv = toggle->priv;
370
if (mx_widget_get_disabled (MX_WIDGET (actor)))
374
if (priv->drag_offset > -1)
376
if (priv->slide_length)
381
pos = 1 - ((priv->drag_offset - event->x) / priv->slide_length);
383
pos = (event->x - priv->drag_offset) / priv->slide_length;
385
if (pos - priv->position)
386
priv->last_move = (pos - priv->position);
388
priv->position = CLAMP (pos, 0, 1);
393
clutter_actor_queue_relayout (actor);
400
mx_toggle_button_press_event (ClutterActor *actor,
401
ClutterButtonEvent *event)
407
mx_toggle_leave_event (ClutterActor *actor,
408
ClutterCrossingEvent *event)
414
mx_toggle_enter_event (ClutterActor *actor,
415
ClutterCrossingEvent *event)
421
mx_toggle_class_init (MxToggleClass *klass)
423
GObjectClass *object_class = G_OBJECT_CLASS (klass);
424
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
427
g_type_class_add_private (klass, sizeof (MxTogglePrivate));
429
object_class->get_property = mx_toggle_get_property;
430
object_class->set_property = mx_toggle_set_property;
431
object_class->dispose = mx_toggle_dispose;
432
object_class->finalize = mx_toggle_finalize;
434
actor_class->pick = mx_toggle_pick;
435
actor_class->paint = mx_toggle_paint;
436
actor_class->map = mx_toggle_map;
437
actor_class->unmap = mx_toggle_unmap;
438
actor_class->get_preferred_width = mx_toggle_get_preferred_width;
439
actor_class->get_preferred_height = mx_toggle_get_preferred_height;
440
actor_class->allocate = mx_toggle_allocate;
441
actor_class->button_release_event = mx_toggle_button_release_event;
443
actor_class->button_press_event = mx_toggle_button_press_event;
444
actor_class->leave_event = mx_toggle_leave_event;
445
actor_class->enter_event = mx_toggle_enter_event;
447
pspec = g_param_spec_boolean ("active",
449
"Whether the toggle switch is activated",
452
g_object_class_install_property (object_class, PROP_ACTIVE, pspec);
456
mx_toggle_update_position (ClutterTimeline *timeline,
460
MxTogglePrivate *priv = toggle->priv;
462
priv->position = clutter_alpha_get_alpha (priv->alpha);
464
clutter_actor_queue_relayout (CLUTTER_ACTOR (toggle));
468
mx_toggle_style_changed (MxToggle *toggle)
470
mx_stylable_style_changed (MX_STYLABLE (toggle->priv->handle), 0);
474
mx_toggle_init (MxToggle *self)
476
ClutterTimeline *timeline;
478
self->priv = TOGGLE_PRIVATE (self);
480
self->priv->handle = g_object_new (MX_TYPE_TOGGLE_HANDLE,
481
"reactive", TRUE, NULL);
482
clutter_actor_set_parent (self->priv->handle, CLUTTER_ACTOR (self));
484
timeline = clutter_timeline_new (300);
485
g_signal_connect (timeline, "new-frame",
486
G_CALLBACK (mx_toggle_update_position), self);
488
self->priv->alpha = clutter_alpha_new_full (timeline,
489
CLUTTER_EASE_IN_OUT_CUBIC);
491
clutter_actor_set_reactive (CLUTTER_ACTOR (self), TRUE);
492
clutter_actor_set_reactive (CLUTTER_ACTOR (self->priv->handle), TRUE);
494
self->priv->drag_offset = -1;
495
g_signal_connect (self->priv->handle, "button-press-event",
496
G_CALLBACK (mx_toggle_handle_button_press_event), self);
497
g_signal_connect (self->priv->handle, "button-release-event",
498
G_CALLBACK (mx_toggle_handle_button_release_event), self);
499
g_signal_connect (self->priv->handle, "motion-event",
500
G_CALLBACK (mx_toggle_handle_motion_event), self);
502
g_signal_connect (self, "style-changed", G_CALLBACK (mx_toggle_style_changed),
510
return g_object_new (MX_TYPE_TOGGLE, NULL);
514
mx_toggle_set_active (MxToggle *toggle,
517
MxTogglePrivate *priv;
519
g_return_if_fail (MX_IS_TOGGLE (toggle));
523
if (priv->active != active
524
|| (priv->position > 0 && priv->position < 1))
526
ClutterTimeline *timeline;
528
timeline = clutter_alpha_get_timeline (priv->alpha);
530
if (clutter_timeline_is_playing (timeline))
534
clutter_timeline_set_direction (timeline, CLUTTER_TIMELINE_FORWARD);
536
clutter_timeline_set_direction (timeline, CLUTTER_TIMELINE_BACKWARD);
538
clutter_timeline_rewind (timeline);
540
if (priv->drag_offset > -1)
542
clutter_alpha_set_mode (priv->alpha, CLUTTER_LINEAR);
543
clutter_timeline_advance (timeline, priv->position * 300);
547
clutter_alpha_set_mode (priv->alpha, CLUTTER_EASE_IN_OUT_CUBIC);
550
clutter_timeline_start (timeline);
552
priv->active = active;
555
mx_stylable_set_style_pseudo_class (MX_STYLABLE (toggle), "checked");
557
mx_stylable_set_style_pseudo_class (MX_STYLABLE (toggle), NULL);
560
g_object_notify (G_OBJECT (toggle), "active");
565
mx_toggle_get_active (MxToggle *toggle)
567
g_return_val_if_fail (MX_IS_TOGGLE (toggle), FALSE);
569
return toggle->priv->active;