~tualatrix/ibentu/mx

« back to all changes in this revision

Viewing changes to mx/mx-toggle.c

  • Committer: Tualatrix Chou
  • Date: 2010-10-09 05:23:41 UTC
  • Revision ID: tualatrix@gmail.com-20101009052341-xpyc3tfjpeh196la
Inital commit

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * mx-toggle: toggle switch actor
 
3
 *
 
4
 * Copyright 2009 Intel Corporation.
 
5
 *
 
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.
 
9
 *
 
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
 
13
 * more details.
 
14
 *
 
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.
 
18
 *
 
19
 * Written by: Thomas Wood <thomas.wood@intel.com>
 
20
 *
 
21
 */
 
22
 
 
23
#include "mx-toggle.h"
 
24
#include "mx-private.h"
 
25
#include "mx-stylable.h"
 
26
 
 
27
 
 
28
/* mx-toggle-handle */
 
29
#define MX_TYPE_TOGGLE_HANDLE mx_toggle_handle_get_type()
 
30
 
 
31
#define MX_TOGGLE_HANDLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MX_TYPE_TOGGLE_HANDLE, MxToggleHandle))
 
32
 
 
33
#define MX_IS_TOGGLE_HANDLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MX_TYPE_TOGGLE_HANDLE))
 
34
 
 
35
typedef struct
 
36
{
 
37
  MxWidget parent;
 
38
} MxToggleHandle;
 
39
 
 
40
typedef struct
 
41
{
 
42
  MxWidgetClass parent_class;
 
43
} MxToggleHandleClass;
 
44
 
 
45
GType mx_toggle_handle_get_type (void) G_GNUC_CONST;
 
46
 
 
47
G_DEFINE_TYPE (MxToggleHandle, mx_toggle_handle, MX_TYPE_WIDGET)
 
48
 
 
49
static void
 
50
mx_toggle_get_preferred_width (ClutterActor *actor,
 
51
                               gfloat        for_height,
 
52
                               gfloat       *min_width_p,
 
53
                               gfloat       *pref_width_p)
 
54
{
 
55
  ClutterActor *background;
 
56
  gfloat pref_w;
 
57
 
 
58
  background = mx_widget_get_border_image (MX_WIDGET (actor));
 
59
  if (!background)
 
60
    background = mx_widget_get_background_image (MX_WIDGET (actor));
 
61
 
 
62
  if (!background)
 
63
    {
 
64
      if (min_width_p)
 
65
        *min_width_p = 0;
 
66
      if (pref_width_p)
 
67
        *pref_width_p = 0;
 
68
 
 
69
      return;
 
70
    }
 
71
 
 
72
  clutter_actor_get_preferred_width (background, -1, NULL, &pref_w);
 
73
 
 
74
  if (min_width_p)
 
75
    *min_width_p = pref_w;
 
76
 
 
77
  if (pref_width_p)
 
78
    *pref_width_p = pref_w;
 
79
}
 
80
 
 
81
static void
 
82
mx_toggle_get_preferred_height (ClutterActor *actor,
 
83
                                gfloat        for_width,
 
84
                                gfloat       *min_height_p,
 
85
                                gfloat       *pref_height_p)
 
86
{
 
87
  ClutterActor *background;
 
88
  gfloat pref_h;
 
89
 
 
90
  background = mx_widget_get_border_image (MX_WIDGET (actor));
 
91
  if (!background)
 
92
    background = mx_widget_get_background_image (MX_WIDGET (actor));
 
93
 
 
94
  if (!background)
 
95
    {
 
96
      if (min_height_p)
 
97
        *min_height_p = 0;
 
98
      if (pref_height_p)
 
99
        *pref_height_p = 0;
 
100
 
 
101
      return;
 
102
    }
 
103
 
 
104
  clutter_actor_get_preferred_height (background, -1, NULL, &pref_h);
 
105
 
 
106
  if (min_height_p)
 
107
    *min_height_p = pref_h;
 
108
 
 
109
  if (pref_height_p)
 
110
    *pref_height_p = pref_h;
 
111
}
 
112
 
 
113
 
 
114
 
 
115
static void
 
116
mx_toggle_handle_class_init (MxToggleHandleClass *klass)
 
117
{
 
118
}
 
119
 
 
120
static void
 
121
mx_toggle_handle_init (MxToggleHandle *self)
 
122
{
 
123
}
 
124
 
 
125
/* mx-toggle */
 
126
 
 
127
static void mx_toggle_stylable_iface_init (MxStylableIface *iface);
 
128
 
 
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))
 
132
 
 
133
#define TOGGLE_PRIVATE(o) \
 
134
  (G_TYPE_INSTANCE_GET_PRIVATE ((o), MX_TYPE_TOGGLE, MxTogglePrivate))
 
135
 
 
136
struct _MxTogglePrivate
 
137
{
 
138
  gboolean active;
 
139
 
 
140
  ClutterActor *handle;
 
141
  gchar        *handle_filename;
 
142
 
 
143
 
 
144
  ClutterAlpha *alpha;
 
145
  gfloat        position;
 
146
 
 
147
  gfloat        drag_offset;
 
148
  gfloat        slide_length;
 
149
  gfloat        last_move;
 
150
};
 
151
 
 
152
enum
 
153
{
 
154
  PROP_ACTIVE = 1
 
155
};
 
156
 
 
157
static void
 
158
mx_toggle_stylable_iface_init (MxStylableIface *iface)
 
159
{
 
160
  GParamSpec *pspec;
 
161
 
 
162
  pspec = g_param_spec_string ("handle-image",
 
163
                               "Handle Image",
 
164
                               "Image used for the handle of the toggle",
 
165
                               "",
 
166
                               MX_PARAM_READWRITE);
 
167
  mx_stylable_iface_install_property (iface, MX_TYPE_TOGGLE, pspec);
 
168
}
 
169
 
 
170
static void
 
171
mx_toggle_get_property (GObject    *object,
 
172
                        guint       property_id,
 
173
                        GValue     *value,
 
174
                        GParamSpec *pspec)
 
175
{
 
176
  MxTogglePrivate *priv = MX_TOGGLE (object)->priv;
 
177
 
 
178
  switch (property_id)
 
179
    {
 
180
    case PROP_ACTIVE:
 
181
      g_value_set_boolean (value, priv->active);
 
182
      break;
 
183
 
 
184
    default:
 
185
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 
186
    }
 
187
}
 
188
 
 
189
static void
 
190
mx_toggle_set_property (GObject      *object,
 
191
                        guint         property_id,
 
192
                        const GValue *value,
 
193
                        GParamSpec   *pspec)
 
194
{
 
195
  MxToggle *toggle = MX_TOGGLE (object);
 
196
 
 
197
  switch (property_id)
 
198
    {
 
199
    case PROP_ACTIVE:
 
200
      mx_toggle_set_active (toggle, g_value_get_boolean (value));
 
201
      break;
 
202
 
 
203
    default:
 
204
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 
205
    }
 
206
}
 
207
 
 
208
static void
 
209
mx_toggle_dispose (GObject *object)
 
210
{
 
211
  MxTogglePrivate *priv = MX_TOGGLE (object)->priv;
 
212
 
 
213
  if (priv->handle)
 
214
    {
 
215
      clutter_actor_destroy (priv->handle);
 
216
      priv->handle = NULL;
 
217
    }
 
218
 
 
219
  G_OBJECT_CLASS (mx_toggle_parent_class)->dispose (object);
 
220
}
 
221
 
 
222
static void
 
223
mx_toggle_finalize (GObject *object)
 
224
{
 
225
  G_OBJECT_CLASS (mx_toggle_parent_class)->finalize (object);
 
226
}
 
227
 
 
228
static void
 
229
mx_toggle_pick (ClutterActor       *actor,
 
230
                const ClutterColor *color)
 
231
{
 
232
  CLUTTER_ACTOR_CLASS (mx_toggle_parent_class)->pick (actor, color);
 
233
 
 
234
  clutter_actor_paint (MX_TOGGLE (actor)->priv->handle);
 
235
}
 
236
 
 
237
static void
 
238
mx_toggle_paint (ClutterActor *actor)
 
239
{
 
240
  CLUTTER_ACTOR_CLASS (mx_toggle_parent_class)->paint (actor);
 
241
 
 
242
  clutter_actor_paint (MX_TOGGLE (actor)->priv->handle);
 
243
}
 
244
 
 
245
static void
 
246
mx_toggle_map (ClutterActor *actor)
 
247
{
 
248
  MxTogglePrivate *priv = MX_TOGGLE (actor)->priv;
 
249
 
 
250
  CLUTTER_ACTOR_CLASS (mx_toggle_parent_class)->map (actor);
 
251
 
 
252
  clutter_actor_map (priv->handle);
 
253
}
 
254
 
 
255
static void
 
256
mx_toggle_unmap (ClutterActor *actor)
 
257
{
 
258
  MxTogglePrivate *priv = MX_TOGGLE (actor)->priv;
 
259
 
 
260
  clutter_actor_unmap (priv->handle);
 
261
 
 
262
  CLUTTER_ACTOR_CLASS (mx_toggle_parent_class)->unmap (actor);
 
263
}
 
264
 
 
265
static void
 
266
mx_toggle_allocate (ClutterActor          *actor,
 
267
                    const ClutterActorBox *box,
 
268
                    ClutterAllocationFlags flags)
 
269
{
 
270
  MxTogglePrivate *priv = MX_TOGGLE (actor)->priv;
 
271
  ClutterActorBox handle_box, child_box;
 
272
  ClutterActor *background;
 
273
  gfloat handle_w;
 
274
  gfloat toggle_pos;
 
275
 
 
276
  CLUTTER_ACTOR_CLASS (mx_toggle_parent_class)->allocate (actor, box, flags);
 
277
 
 
278
  mx_widget_get_available_area (MX_WIDGET (actor), box, &child_box);
 
279
 
 
280
  /* background-image don't get stretched, so adjust the child box so that the
 
281
   * handle appears in the correct place.
 
282
   */
 
283
  background = mx_widget_get_background_image (MX_WIDGET (actor));
 
284
  if (background)
 
285
    {
 
286
      gfloat width;
 
287
      MxPadding padding;
 
288
 
 
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;
 
292
 
 
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;
 
296
    }
 
297
 
 
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;
 
301
 
 
302
  toggle_pos = toggle_pos * priv->position;
 
303
 
 
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;
 
308
 
 
309
  clutter_actor_allocate (priv->handle, &handle_box, flags);
 
310
}
 
311
 
 
312
static gboolean
 
313
mx_toggle_button_release_event (ClutterActor       *actor,
 
314
                                ClutterButtonEvent *event)
 
315
{
 
316
  MxToggle *toggle = MX_TOGGLE (actor);
 
317
 
 
318
  if (mx_widget_get_disabled (MX_WIDGET (actor)))
 
319
    return FALSE;
 
320
 
 
321
  mx_toggle_set_active (toggle, !toggle->priv->active);
 
322
 
 
323
  return FALSE;
 
324
}
 
325
 
 
326
static gboolean
 
327
mx_toggle_handle_button_press_event (ClutterActor       *actor,
 
328
                                     ClutterButtonEvent *event,
 
329
                                     MxToggle           *toggle)
 
330
{
 
331
  if (mx_widget_get_disabled (MX_WIDGET (actor)))
 
332
    return FALSE;
 
333
 
 
334
  clutter_grab_pointer (actor);
 
335
 
 
336
  toggle->priv->drag_offset = event->x;
 
337
 
 
338
  return FALSE;
 
339
}
 
340
 
 
341
static gboolean
 
342
mx_toggle_handle_button_release_event (ClutterActor       *actor,
 
343
                                       ClutterButtonEvent *event,
 
344
                                       MxToggle           *toggle)
 
345
{
 
346
  if (mx_widget_get_disabled (MX_WIDGET (actor)))
 
347
    return FALSE;
 
348
 
 
349
  if (toggle->priv->last_move == 0)
 
350
    mx_toggle_set_active (toggle, !toggle->priv->active);
 
351
  else
 
352
    mx_toggle_set_active (toggle, (toggle->priv->last_move > 0.0));
 
353
 
 
354
  toggle->priv->drag_offset = -1;
 
355
 
 
356
  toggle->priv->last_move = 0;
 
357
 
 
358
  clutter_ungrab_pointer ();
 
359
 
 
360
  return TRUE;
 
361
}
 
362
 
 
363
static gboolean
 
364
mx_toggle_handle_motion_event (ClutterActor       *actor,
 
365
                               ClutterMotionEvent *event,
 
366
                               MxToggle           *toggle)
 
367
{
 
368
  MxTogglePrivate *priv = toggle->priv;
 
369
 
 
370
  if (mx_widget_get_disabled (MX_WIDGET (actor)))
 
371
    return FALSE;
 
372
 
 
373
 
 
374
  if (priv->drag_offset > -1)
 
375
    {
 
376
      if (priv->slide_length)
 
377
        {
 
378
          gfloat pos;
 
379
 
 
380
          if (priv->active)
 
381
            pos = 1 - ((priv->drag_offset - event->x) / priv->slide_length);
 
382
          else
 
383
            pos = (event->x - priv->drag_offset) / priv->slide_length;
 
384
 
 
385
          if (pos - priv->position)
 
386
            priv->last_move = (pos - priv->position);
 
387
 
 
388
          priv->position = CLAMP (pos, 0, 1);
 
389
        }
 
390
      else
 
391
        priv->position = 0;
 
392
 
 
393
      clutter_actor_queue_relayout (actor);
 
394
    }
 
395
 
 
396
  return TRUE;
 
397
}
 
398
 
 
399
static gboolean
 
400
mx_toggle_button_press_event (ClutterActor       *actor,
 
401
                              ClutterButtonEvent *event)
 
402
{
 
403
  return TRUE;
 
404
}
 
405
 
 
406
static gboolean
 
407
mx_toggle_leave_event (ClutterActor         *actor,
 
408
                       ClutterCrossingEvent *event)
 
409
{
 
410
  return FALSE;
 
411
}
 
412
 
 
413
static gboolean
 
414
mx_toggle_enter_event (ClutterActor         *actor,
 
415
                       ClutterCrossingEvent *event)
 
416
{
 
417
  return FALSE;
 
418
}
 
419
 
 
420
static void
 
421
mx_toggle_class_init (MxToggleClass *klass)
 
422
{
 
423
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
424
  ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
 
425
  GParamSpec *pspec;
 
426
 
 
427
  g_type_class_add_private (klass, sizeof (MxTogglePrivate));
 
428
 
 
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;
 
433
 
 
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;
 
442
 
 
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;
 
446
 
 
447
  pspec = g_param_spec_boolean ("active",
 
448
                                "Active",
 
449
                                "Whether the toggle switch is activated",
 
450
                                FALSE,
 
451
                                MX_PARAM_READWRITE);
 
452
  g_object_class_install_property (object_class, PROP_ACTIVE, pspec);
 
453
}
 
454
 
 
455
static void
 
456
mx_toggle_update_position (ClutterTimeline *timeline,
 
457
                           gint             msecs,
 
458
                           MxToggle        *toggle)
 
459
{
 
460
  MxTogglePrivate *priv = toggle->priv;
 
461
 
 
462
  priv->position = clutter_alpha_get_alpha (priv->alpha);
 
463
 
 
464
  clutter_actor_queue_relayout (CLUTTER_ACTOR (toggle));
 
465
}
 
466
 
 
467
static void
 
468
mx_toggle_style_changed (MxToggle *toggle)
 
469
{
 
470
  mx_stylable_style_changed (MX_STYLABLE (toggle->priv->handle), 0);
 
471
}
 
472
 
 
473
static void
 
474
mx_toggle_init (MxToggle *self)
 
475
{
 
476
  ClutterTimeline *timeline;
 
477
 
 
478
  self->priv = TOGGLE_PRIVATE (self);
 
479
 
 
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));
 
483
 
 
484
  timeline = clutter_timeline_new (300);
 
485
  g_signal_connect (timeline, "new-frame",
 
486
                    G_CALLBACK (mx_toggle_update_position), self);
 
487
 
 
488
  self->priv->alpha = clutter_alpha_new_full (timeline,
 
489
                                              CLUTTER_EASE_IN_OUT_CUBIC);
 
490
 
 
491
  clutter_actor_set_reactive (CLUTTER_ACTOR (self), TRUE);
 
492
  clutter_actor_set_reactive (CLUTTER_ACTOR (self->priv->handle), TRUE);
 
493
 
 
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);
 
501
 
 
502
  g_signal_connect (self, "style-changed", G_CALLBACK (mx_toggle_style_changed),
 
503
                    NULL);
 
504
 
 
505
}
 
506
 
 
507
ClutterActor *
 
508
mx_toggle_new (void)
 
509
{
 
510
  return g_object_new (MX_TYPE_TOGGLE, NULL);
 
511
}
 
512
 
 
513
void
 
514
mx_toggle_set_active (MxToggle *toggle,
 
515
                      gboolean  active)
 
516
{
 
517
  MxTogglePrivate *priv;
 
518
 
 
519
  g_return_if_fail (MX_IS_TOGGLE (toggle));
 
520
 
 
521
  priv = toggle->priv;
 
522
 
 
523
  if (priv->active != active
 
524
      || (priv->position > 0 && priv->position < 1))
 
525
    {
 
526
      ClutterTimeline *timeline;
 
527
 
 
528
      timeline = clutter_alpha_get_timeline (priv->alpha);
 
529
 
 
530
      if (clutter_timeline_is_playing (timeline))
 
531
        return;
 
532
 
 
533
      if (active)
 
534
        clutter_timeline_set_direction (timeline, CLUTTER_TIMELINE_FORWARD);
 
535
      else
 
536
        clutter_timeline_set_direction (timeline, CLUTTER_TIMELINE_BACKWARD);
 
537
 
 
538
      clutter_timeline_rewind (timeline);
 
539
 
 
540
      if (priv->drag_offset > -1)
 
541
        {
 
542
          clutter_alpha_set_mode (priv->alpha, CLUTTER_LINEAR);
 
543
          clutter_timeline_advance (timeline, priv->position * 300);
 
544
        }
 
545
      else
 
546
        {
 
547
          clutter_alpha_set_mode (priv->alpha, CLUTTER_EASE_IN_OUT_CUBIC);
 
548
        }
 
549
 
 
550
      clutter_timeline_start (timeline);
 
551
 
 
552
      priv->active = active;
 
553
 
 
554
      if (active)
 
555
        mx_stylable_set_style_pseudo_class (MX_STYLABLE (toggle), "checked");
 
556
      else
 
557
        mx_stylable_set_style_pseudo_class (MX_STYLABLE (toggle), NULL);
 
558
 
 
559
 
 
560
      g_object_notify (G_OBJECT (toggle), "active");
 
561
    }
 
562
}
 
563
 
 
564
gboolean
 
565
mx_toggle_get_active (MxToggle *toggle)
 
566
{
 
567
  g_return_val_if_fail (MX_IS_TOGGLE (toggle), FALSE);
 
568
 
 
569
  return toggle->priv->active;
 
570
}