1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 2007 Carlos Garnacho <carlos@imendio.com>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
18 #include <gtk/gtktimeline.h>
19 #include <gtk/gtktypebuiltins.h>
20 #include <gtk/gtksettings.h>
23 typedef struct GtkTimelinePriv GtkTimelinePriv;
25 struct GtkTimelinePriv
33 gdouble last_progress;
36 GdkFrameClock *frame_clock;
41 GtkTimelineProgressType progress_type;
43 guint animations_enabled : 1;
68 static guint signals [LAST_SIGNAL] = { 0, };
71 static void gtk_timeline_start_running (GtkTimeline *timeline);
72 static void gtk_timeline_stop_running (GtkTimeline *timeline);
74 static void frame_clock_target_iface_init (GdkFrameClockTargetInterface *target);
76 static void gtk_timeline_set_property (GObject *object,
80 static void gtk_timeline_get_property (GObject *object,
84 static void gtk_timeline_finalize (GObject *object);
86 static void gtk_timeline_set_clock (GdkFrameClockTarget *target,
87 GdkFrameClock *frame_clock);
89 G_DEFINE_TYPE_WITH_CODE (GtkTimeline, gtk_timeline, G_TYPE_OBJECT,
90 G_IMPLEMENT_INTERFACE (GDK_TYPE_FRAME_CLOCK_TARGET, frame_clock_target_iface_init))
93 gtk_timeline_class_init (GtkTimelineClass *klass)
95 GObjectClass *object_class = G_OBJECT_CLASS (klass);
97 object_class->set_property = gtk_timeline_set_property;
98 object_class->get_property = gtk_timeline_get_property;
99 object_class->finalize = gtk_timeline_finalize;
101 g_object_class_install_property (object_class,
103 g_param_spec_uint ("duration",
104 "Animation Duration",
105 "Animation Duration",
109 g_object_class_install_property (object_class,
111 g_param_spec_boolean ("loop",
113 "Whether the timeline loops or not",
116 g_object_class_install_property (object_class,
118 g_param_spec_object ("paint-clock",
120 "clock used for timing the animation (not needed if :widget is set)",
121 GDK_TYPE_FRAME_CLOCK,
123 g_object_class_install_property (object_class,
125 g_param_spec_enum ("progress-type",
127 "Easing function for animation progress",
128 GTK_TYPE_TIMELINE_PROGRESS_TYPE,
129 GTK_TIMELINE_PROGRESS_EASE_OUT,
131 g_object_class_install_property (object_class,
133 g_param_spec_object ("screen",
135 "Screen to get the settings from (not needed if :widget is set)",
138 g_object_class_install_property (object_class,
140 g_param_spec_object ("widget",
142 "Widget the timeline will be used with",
147 g_signal_new ("started",
148 G_TYPE_FROM_CLASS (object_class),
150 G_STRUCT_OFFSET (GtkTimelineClass, started),
152 g_cclosure_marshal_VOID__VOID,
156 g_signal_new ("paused",
157 G_TYPE_FROM_CLASS (object_class),
159 G_STRUCT_OFFSET (GtkTimelineClass, paused),
161 g_cclosure_marshal_VOID__VOID,
165 g_signal_new ("finished",
166 G_TYPE_FROM_CLASS (object_class),
168 G_STRUCT_OFFSET (GtkTimelineClass, finished),
170 g_cclosure_marshal_VOID__VOID,
174 g_signal_new ("frame",
175 G_TYPE_FROM_CLASS (object_class),
177 G_STRUCT_OFFSET (GtkTimelineClass, frame),
179 g_cclosure_marshal_VOID__DOUBLE,
183 g_type_class_add_private (klass, sizeof (GtkTimelinePriv));
187 frame_clock_target_iface_init (GdkFrameClockTargetInterface *iface)
189 iface->set_clock = gtk_timeline_set_clock;
193 gtk_timeline_init (GtkTimeline *timeline)
195 GtkTimelinePriv *priv;
197 priv = timeline->priv = G_TYPE_INSTANCE_GET_PRIVATE (timeline,
201 priv->duration = 0.0;
202 priv->direction = GTK_TIMELINE_DIRECTION_FORWARD;
203 priv->progress_type = GTK_TIMELINE_PROGRESS_EASE_OUT;
204 priv->screen = gdk_screen_get_default ();
206 priv->last_progress = 0;
210 gtk_timeline_set_property (GObject *object,
215 GtkTimeline *timeline;
217 timeline = GTK_TIMELINE (object);
222 gtk_timeline_set_duration (timeline, g_value_get_uint (value));
225 gtk_timeline_set_loop (timeline, g_value_get_boolean (value));
228 gtk_timeline_set_direction (timeline, g_value_get_enum (value));
230 case PROP_FRAME_CLOCK:
231 gtk_timeline_set_frame_clock (timeline,
232 GDK_FRAME_CLOCK (g_value_get_object (value)));
234 case PROP_PROGRESS_TYPE:
235 gtk_timeline_set_progress_type (timeline,
236 g_value_get_enum (value));
239 gtk_timeline_set_screen (timeline,
240 GDK_SCREEN (g_value_get_object (value)));
243 gtk_timeline_set_widget (timeline,
244 GTK_WIDGET (g_value_get_object (value)));
247 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
252 gtk_timeline_get_property (GObject *object,
257 GtkTimeline *timeline;
258 GtkTimelinePriv *priv;
260 timeline = GTK_TIMELINE (object);
261 priv = timeline->priv;
266 g_value_set_uint (value, priv->duration);
269 g_value_set_boolean (value, priv->loop);
272 g_value_set_enum (value, priv->direction);
274 case PROP_FRAME_CLOCK:
275 g_value_set_object (value, priv->frame_clock);
277 case PROP_PROGRESS_TYPE:
278 g_value_set_enum (value, priv->progress_type);
281 g_value_set_object (value, priv->screen);
284 g_value_set_object (value, priv->widget);
287 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
292 gtk_timeline_finalize (GObject *object)
294 GtkTimelinePriv *priv;
295 GtkTimeline *timeline;
297 timeline = (GtkTimeline *) object;
298 priv = timeline->priv;
302 gtk_timeline_stop_running (timeline);
303 priv->running = FALSE;
306 G_OBJECT_CLASS (gtk_timeline_parent_class)->finalize (object);
309 /* Implementation of GdkFrameClockTarget method */
311 gtk_timeline_set_clock (GdkFrameClockTarget *target,
312 GdkFrameClock *frame_clock)
314 gtk_timeline_set_frame_clock (GTK_TIMELINE (target),
319 calculate_progress (gdouble linear_progress,
320 GtkTimelineProgressType progress_type)
324 progress = linear_progress;
326 switch (progress_type)
328 case GTK_TIMELINE_PROGRESS_LINEAR:
330 case GTK_TIMELINE_PROGRESS_EASE_IN_OUT:
334 progress = pow (progress, 3) / 2;
336 progress = (pow (progress - 2, 3) + 2) / 2;
339 case GTK_TIMELINE_PROGRESS_EASE:
340 progress = (sin ((progress - 0.5) * G_PI) + 1) / 2;
342 case GTK_TIMELINE_PROGRESS_EASE_IN:
343 progress = pow (progress, 3);
345 case GTK_TIMELINE_PROGRESS_EASE_OUT:
346 progress = pow (progress - 1, 3) + 1;
349 g_warning ("Timeline progress type not implemented");
356 gtk_timeline_on_update (GdkFrameClock *clock,
357 GtkTimeline *timeline)
359 GtkTimelinePriv *priv;
360 gdouble delta_progress, progress, adjust;
363 /* the user may unref us during the signals, so save ourselves */
364 g_object_ref (timeline);
366 priv = timeline->priv;
368 now = gdk_frame_clock_get_frame_time (clock);
369 priv->elapsed_time = (now - priv->last_time) / 1000;
370 priv->last_time = now;
372 if (priv->animations_enabled)
374 delta_progress = (gdouble) priv->elapsed_time / priv->duration;
375 progress = priv->last_progress;
377 if (priv->direction == GTK_TIMELINE_DIRECTION_BACKWARD)
378 progress -= delta_progress;
380 progress += delta_progress;
382 priv->last_progress = progress;
384 /* When looping, if we go past the end, start that much into the
388 adjust = progress - ceil(progress);
391 else if (progress > 1.0)
393 adjust = progress - floor(progress);
399 progress = CLAMP (progress, 0., 1.);
402 progress = (priv->direction == GTK_TIMELINE_DIRECTION_FORWARD) ? 1.0 : 0.0;
404 priv->progress = progress;
405 g_signal_emit (timeline, signals [FRAME], 0,
406 calculate_progress (progress, priv->progress_type));
408 if ((priv->direction == GTK_TIMELINE_DIRECTION_FORWARD && progress == 1.0) ||
409 (priv->direction == GTK_TIMELINE_DIRECTION_BACKWARD && progress == 0.0))
413 loop = priv->loop && priv->animations_enabled;
417 gtk_timeline_rewind (timeline);
418 priv->progress += adjust;
422 gtk_timeline_stop_running (timeline);
423 priv->running = FALSE;
425 g_signal_emit (timeline, signals [FINISHED], 0);
426 g_object_unref (timeline);
431 g_object_unref (timeline);
432 gdk_frame_clock_request_phase (clock, GDK_FRAME_CLOCK_PHASE_UPDATE);
436 gtk_timeline_start_updating (GtkTimeline *timeline)
438 GtkTimelinePriv *priv = timeline->priv;
440 g_assert (priv->running && priv->frame_clock && priv->update_id == 0);
442 priv->update_id = g_signal_connect (priv->frame_clock,
444 G_CALLBACK (gtk_timeline_on_update),
447 gdk_frame_clock_request_phase (priv->frame_clock, GDK_FRAME_CLOCK_PHASE_UPDATE);
448 priv->last_time = gdk_frame_clock_get_frame_time (priv->frame_clock);
452 gtk_timeline_stop_updating (GtkTimeline *timeline)
454 GtkTimelinePriv *priv = timeline->priv;
456 g_assert (priv->running && priv->frame_clock && priv->update_id != 0);
458 g_signal_handler_disconnect (priv->frame_clock,
464 gtk_timeline_start_running (GtkTimeline *timeline)
466 GtkTimelinePriv *priv = timeline->priv;
468 g_assert (priv->running);
471 gtk_widget_add_frame_clock_target (priv->widget,
472 GDK_FRAME_CLOCK_TARGET (timeline));
473 else if (priv->frame_clock)
474 gtk_timeline_start_updating (timeline);
478 gtk_timeline_stop_running (GtkTimeline *timeline)
480 GtkTimelinePriv *priv = timeline->priv;
482 g_assert (priv->running);
485 gtk_widget_remove_frame_clock_target (priv->widget,
486 GDK_FRAME_CLOCK_TARGET (timeline));
487 else if (priv->frame_clock)
488 gtk_timeline_stop_updating (timeline);
493 * @widget: a widget the timeline will be used with
494 * @duration: duration in milliseconds for the timeline
496 * Creates a new #GtkTimeline with the specified duration
498 * Return Value: the newly created #GtkTimeline
501 gtk_timeline_new (GtkWidget *widget,
504 return g_object_new (GTK_TYPE_TIMELINE,
506 "duration", duration,
511 * gtk_timeline_start:
512 * @timeline: A #GtkTimeline
514 * Runs the timeline from the current frame.
517 gtk_timeline_start (GtkTimeline *timeline)
519 GtkTimelinePriv *priv;
520 GtkSettings *settings;
521 gboolean enable_animations = FALSE;
523 g_return_if_fail (GTK_IS_TIMELINE (timeline));
525 priv = timeline->priv;
531 settings = gtk_settings_get_for_screen (priv->screen);
532 g_object_get (settings, "gtk-enable-animations", &enable_animations, NULL);
535 priv->animations_enabled = enable_animations;
537 priv->running = TRUE;
538 gtk_timeline_start_running (timeline);
540 g_signal_emit (timeline, signals [STARTED], 0);
545 * gtk_timeline_pause:
546 * @timeline: A #GtkTimeline
548 * Pauses the timeline.
551 gtk_timeline_pause (GtkTimeline *timeline)
553 GtkTimelinePriv *priv;
555 g_return_if_fail (GTK_IS_TIMELINE (timeline));
557 priv = timeline->priv;
561 gtk_timeline_stop_running (timeline);
562 priv->running = FALSE;
564 g_signal_emit (timeline, signals [PAUSED], 0);
569 * gtk_timeline_rewind:
570 * @timeline: A #GtkTimeline
572 * Rewinds the timeline.
575 gtk_timeline_rewind (GtkTimeline *timeline)
577 GtkTimelinePriv *priv;
579 g_return_if_fail (GTK_IS_TIMELINE (timeline));
581 priv = timeline->priv;
583 if (gtk_timeline_get_direction (timeline) != GTK_TIMELINE_DIRECTION_FORWARD)
584 priv->progress = priv->last_progress = 1.;
586 priv->progress = priv->last_progress = 0.;
588 if (priv->running && priv->frame_clock)
589 priv->last_time = gdk_frame_clock_get_frame_time (priv->frame_clock);
593 * gtk_timeline_is_running:
594 * @timeline: A #GtkTimeline
596 * Returns whether the timeline is running or not.
598 * Return Value: %TRUE if the timeline is running
601 gtk_timeline_is_running (GtkTimeline *timeline)
603 GtkTimelinePriv *priv;
605 g_return_val_if_fail (GTK_IS_TIMELINE (timeline), FALSE);
607 priv = timeline->priv;
609 return priv->running;
613 * gtk_timeline_get_elapsed_time:
614 * @timeline: A #GtkTimeline
616 * Returns the elapsed time since the last GtkTimeline::frame signal
618 * Return Value: elapsed time in milliseconds since the last frame
621 gtk_timeline_get_elapsed_time (GtkTimeline *timeline)
623 GtkTimelinePriv *priv;
625 g_return_val_if_fail (GTK_IS_TIMELINE (timeline), 0);
627 priv = timeline->priv;
628 return priv->elapsed_time;
632 * gtk_timeline_get_loop:
633 * @timeline: A #GtkTimeline
635 * Returns whether the timeline loops to the
636 * beginning when it has reached the end.
638 * Return Value: %TRUE if the timeline loops
641 gtk_timeline_get_loop (GtkTimeline *timeline)
643 GtkTimelinePriv *priv;
645 g_return_val_if_fail (GTK_IS_TIMELINE (timeline), FALSE);
647 priv = timeline->priv;
652 * gtk_timeline_set_loop:
653 * @timeline: A #GtkTimeline
654 * @loop: %TRUE to make the timeline loop
656 * Sets whether the timeline loops to the beginning
657 * when it has reached the end.
660 gtk_timeline_set_loop (GtkTimeline *timeline,
663 GtkTimelinePriv *priv;
665 g_return_if_fail (GTK_IS_TIMELINE (timeline));
667 priv = timeline->priv;
669 if (loop != priv->loop)
672 g_object_notify (G_OBJECT (timeline), "loop");
677 gtk_timeline_set_duration (GtkTimeline *timeline,
680 GtkTimelinePriv *priv;
682 g_return_if_fail (GTK_IS_TIMELINE (timeline));
684 priv = timeline->priv;
686 if (duration != priv->duration)
688 priv->duration = duration;
689 g_object_notify (G_OBJECT (timeline), "duration");
694 gtk_timeline_get_duration (GtkTimeline *timeline)
696 GtkTimelinePriv *priv;
698 g_return_val_if_fail (GTK_IS_TIMELINE (timeline), 0);
700 priv = timeline->priv;
702 return priv->duration;
706 * gtk_timeline_set_direction:
707 * @timeline: A #GtkTimeline
708 * @direction: direction
710 * Sets the direction of the timeline.
713 gtk_timeline_set_direction (GtkTimeline *timeline,
714 GtkTimelineDirection direction)
716 GtkTimelinePriv *priv;
718 g_return_if_fail (GTK_IS_TIMELINE (timeline));
720 priv = timeline->priv;
721 priv->direction = direction;
725 * gtk_timeline_get_direction:
726 * @timeline: A #GtkTimeline
728 * Returns the direction of the timeline.
730 * Return Value: direction
733 gtk_timeline_get_direction (GtkTimeline *timeline)
735 GtkTimelinePriv *priv;
737 g_return_val_if_fail (GTK_IS_TIMELINE (timeline), GTK_TIMELINE_DIRECTION_FORWARD);
739 priv = timeline->priv;
740 return priv->direction;
744 gtk_timeline_set_frame_clock (GtkTimeline *timeline,
745 GdkFrameClock *frame_clock)
747 GtkTimelinePriv *priv;
749 g_return_if_fail (GTK_IS_TIMELINE (timeline));
750 g_return_if_fail (frame_clock == NULL || GDK_IS_FRAME_CLOCK (frame_clock));
752 priv = timeline->priv;
754 if (frame_clock == priv->frame_clock)
757 if (priv->running && priv->frame_clock)
758 gtk_timeline_stop_updating (timeline);
760 if (priv->frame_clock)
761 g_object_unref (priv->frame_clock);
763 priv->frame_clock = frame_clock;
765 if (priv->frame_clock)
766 g_object_ref (priv->frame_clock);
768 if (priv->running && priv->frame_clock)
769 gtk_timeline_start_updating (timeline);
771 g_object_notify (G_OBJECT (timeline), "paint-clock");
775 * gtk_timeline_get_frame_clock:
777 * Returns: (transfer none):
780 gtk_timeline_get_frame_clock (GtkTimeline *timeline)
782 GtkTimelinePriv *priv;
784 g_return_val_if_fail (GTK_IS_TIMELINE (timeline), NULL);
786 priv = timeline->priv;
787 return priv->frame_clock;
791 gtk_timeline_set_screen (GtkTimeline *timeline,
794 GtkTimelinePriv *priv;
796 g_return_if_fail (GTK_IS_TIMELINE (timeline));
797 g_return_if_fail (screen == NULL || GDK_IS_SCREEN (screen));
799 priv = timeline->priv;
801 if (screen == priv->screen)
805 g_object_unref (priv->screen);
807 priv->screen = screen;
810 g_object_ref (priv->screen);
812 g_object_notify (G_OBJECT (timeline), "screen");
816 * gtk_timeline_get_screen:
818 * Returns: (transfer none):
821 gtk_timeline_get_screen (GtkTimeline *timeline)
823 GtkTimelinePriv *priv;
825 g_return_val_if_fail (GTK_IS_TIMELINE (timeline), NULL);
827 priv = timeline->priv;
831 gtk_timeline_set_widget (GtkTimeline *timeline,
834 GtkTimelinePriv *priv;
836 g_return_if_fail (GTK_IS_TIMELINE (timeline));
837 g_return_if_fail (widget == NULL || GTK_IS_WIDGET (widget));
839 priv = timeline->priv;
841 if (widget == priv->widget)
845 gtk_timeline_stop_running (timeline);
848 g_object_unref (priv->widget);
850 priv->widget = widget;
853 g_object_ref (widget);
856 gtk_timeline_start_running (timeline);
858 g_object_notify (G_OBJECT (timeline), "widget");
862 * gtk_timeline_get_widget:
864 * Returns: (transfer none):
867 gtk_timeline_get_widget (GtkTimeline *timeline)
869 GtkTimelinePriv *priv;
871 g_return_val_if_fail (GTK_IS_TIMELINE (timeline), NULL);
873 priv = timeline->priv;
878 gtk_timeline_get_progress (GtkTimeline *timeline)
880 GtkTimelinePriv *priv;
882 g_return_val_if_fail (GTK_IS_TIMELINE (timeline), 0.);
884 priv = timeline->priv;
885 return calculate_progress (priv->progress, priv->progress_type);
888 GtkTimelineProgressType
889 gtk_timeline_get_progress_type (GtkTimeline *timeline)
891 GtkTimelinePriv *priv;
893 g_return_val_if_fail (GTK_IS_TIMELINE (timeline), GTK_TIMELINE_PROGRESS_LINEAR);
895 priv = timeline->priv;
896 return priv->progress_type;
900 gtk_timeline_set_progress_type (GtkTimeline *timeline,
901 GtkTimelineProgressType progress_type)
903 GtkTimelinePriv *priv;
905 g_return_if_fail (GTK_IS_TIMELINE (timeline));
907 priv = timeline->priv;
908 priv->progress_type = progress_type;