Compare commits

...

16 Commits

Author SHA1 Message Date
Matt Watson
c9f4c86b1f animatedstyle: don't share styleanimations
Because of our port of css animation and css transition to
progress tracker, we should not think of animated styles as
immutable objects that can map any timestamp to css values.
Rather, timestamps can correspond to different values depending
on the value of GTK_SLOWDOWN over the course of the animation.

To keep animated styles and style animations totally immutable,
we will not share styleanimations between animatedstyles, and
make a new copy of a styleanimation for each timestamp.
2016-04-06 15:40:53 -07:00
Matt Watson
945f4a45d5 animatedstyle: just ref current style if timestamp the same 2016-04-06 15:40:53 -07:00
Matt Watson
e56a1ddce3 animatedstyle: fail to create new style if timestamp goes backwards
With slowdown factor, we will only we be able to handle timestamps
that monotonically increase.
2016-04-06 15:40:53 -07:00
Matt Watson
b1c94375e8 cssanimation: port to progress tracker 2016-04-06 15:40:53 -07:00
Matt Watson
865201f11f csstransition: port to progress tracker 2016-04-06 15:40:53 -07:00
Matt Watson
f5a8201b0f entry: port to progress tracker 2016-04-06 15:40:52 -07:00
Matt Watson
4bf81e6ab5 progressbar: port to progress tracker 2016-04-06 15:40:52 -07:00
Matt Watson
a09586175e scrolledwindow: port indicator fade to progress tracker 2016-04-06 15:40:52 -07:00
Matt Watson
3fed034b0c popover: port to progress tracker 2016-04-06 15:40:52 -07:00
Matt Watson
c2b97c1b22 switch: port to progress tracker 2016-04-06 15:40:52 -07:00
Matt Watson
5d5ee76a8b revealer: port to progress tracker 2016-04-06 15:40:52 -07:00
Matt Watson
abc8b34683 stack: skip first frame for animations
Not the ideal solution for this problem, but in practice leads to
much better performance on lower end hardware.

Stack does a double draw on the first frame of its animation, of
both the old contents (into a cairo surface) and the new contents.
Homogeneous stacks only need to reallocate contents on the first
frame.

On lower powered hardware where our frames will be a good deal
slower than the refresh rate anyway, we can assure a smother
experience by waiting a frame to start tweening where frame duration
will be more consistent.
2016-04-06 15:40:52 -07:00
Matt Watson
d3961dacad stack: port to progress tracker 2016-04-06 15:40:52 -07:00
Matt Watson
e962ebab82 inspector: add slider to control slowdown factor 2016-04-06 15:40:52 -07:00
Matt Watson
ed75fa655b progresstracker: add GTK_SLOWDOWN environment variable
As we consolidate widgets to use progress tracker, this will allow
us to control the speed of all animations in a centralized place
2016-04-06 15:40:52 -07:00
Matt Watson
44a9c0e36d progresstracker: simple struct to track animation progress 2016-04-06 15:40:52 -07:00
21 changed files with 805 additions and 381 deletions

View File

@@ -520,6 +520,7 @@ gtk_private_h_sources = \
gtkprintutils.h \
gtkprivate.h \
gtkpixelcacheprivate.h \
gtkprogresstrackerprivate.h \
gtkquery.h \
gtkrangeprivate.h \
gtkrbtree.h \
@@ -832,6 +833,7 @@ gtk_base_c_sources = \
gtkprivate.c \
gtkprivatetypebuiltins.c \
gtkprogressbar.c \
gtkprogresstracker.c \
gtkpixelcache.c \
gtkpopover.c \
gtkpopovermenu.c \

View File

@@ -71,7 +71,7 @@ gtk_css_animated_style_is_static (GtkCssStyle *style)
for (list = animated->animations; list; list = list->next)
{
if (!_gtk_style_animation_is_static (list->data, animated->current_time))
if (!_gtk_style_animation_is_static (list->data))
return FALSE;
}
@@ -288,7 +288,10 @@ gtk_css_animated_style_create_css_transitions (GSList *animations,
{
animation = gtk_css_animated_style_find_transition (GTK_CSS_ANIMATED_STYLE (source), i);
if (animation)
animations = g_slist_prepend (animations, g_object_ref (animation));
{
animation = _gtk_style_animation_advance (animation, timestamp);
animations = g_slist_prepend (animations, animation);
}
continue;
}
@@ -301,8 +304,9 @@ gtk_css_animated_style_create_css_transitions (GSList *animations,
animation = _gtk_css_transition_new (i,
gtk_css_style_get_value (source, i),
_gtk_css_array_value_get_nth (timing_functions, i),
timestamp + delay * G_USEC_PER_SEC,
timestamp + (delay + duration) * G_USEC_PER_SEC);
timestamp,
duration * G_USEC_PER_SEC,
delay * G_USEC_PER_SEC);
animations = g_slist_prepend (animations, animation);
}
@@ -367,9 +371,9 @@ gtk_css_animated_style_create_css_animations (GSList *animation
if (animation)
{
animation = _gtk_css_animation_copy (GTK_CSS_ANIMATION (animation),
timestamp,
_gtk_css_play_state_value_get (_gtk_css_array_value_get_nth (play_states, i)));
animation = _gtk_css_animation_advance_with_play_state (GTK_CSS_ANIMATION (animation),
timestamp,
_gtk_css_play_state_value_get (_gtk_css_array_value_get_nth (play_states, i)));
}
else
{
@@ -400,8 +404,7 @@ gtk_css_animated_style_create_css_animations (GSList *animation
/* PUBLIC API */
static void
gtk_css_animated_style_apply_animations (GtkCssAnimatedStyle *style,
gint64 timestamp)
gtk_css_animated_style_apply_animations (GtkCssAnimatedStyle *style)
{
GSList *l;
@@ -409,9 +412,8 @@ gtk_css_animated_style_apply_animations (GtkCssAnimatedStyle *style,
{
GtkStyleAnimation *animation = l->data;
_gtk_style_animation_set_values (animation,
timestamp,
GTK_CSS_ANIMATED_STYLE (style));
_gtk_style_animation_apply_values (animation,
GTK_CSS_ANIMATED_STYLE (style));
}
}
@@ -448,7 +450,7 @@ gtk_css_animated_style_new (GtkCssStyle *base_style,
result->current_time = timestamp;
result->animations = animations;
gtk_css_animated_style_apply_animations (result, timestamp);
gtk_css_animated_style_apply_animations (result);
return GTK_CSS_STYLE (result);
}
@@ -464,18 +466,21 @@ gtk_css_animated_style_new_advance (GtkCssAnimatedStyle *source,
gtk_internal_return_val_if_fail (GTK_IS_CSS_ANIMATED_STYLE (source), NULL);
gtk_internal_return_val_if_fail (GTK_IS_CSS_STYLE (base), NULL);
if (timestamp == 0)
if (timestamp == 0 || timestamp == source->current_time)
return g_object_ref (source->style);
gtk_internal_return_val_if_fail (timestamp > source->current_time, NULL);
animations = NULL;
for (l = source->animations; l; l = l->next)
{
GtkStyleAnimation *animation = l->data;
if (_gtk_style_animation_is_finished (animation, timestamp))
if (_gtk_style_animation_is_finished (animation))
continue;
animations = g_slist_prepend (animations, g_object_ref (animation));
animation = _gtk_style_animation_advance (animation, timestamp);
animations = g_slist_prepend (animations, animation);
}
animations = g_slist_reverse (animations);
@@ -488,7 +493,7 @@ gtk_css_animated_style_new_advance (GtkCssAnimatedStyle *source,
result->current_time = timestamp;
result->animations = animations;
gtk_css_animated_style_apply_animations (result, timestamp);
gtk_css_animated_style_apply_animations (result);
return GTK_CSS_STYLE (result);
}

View File

@@ -22,41 +22,25 @@
#include "gtkcssanimationprivate.h"
#include "gtkcsseasevalueprivate.h"
#include "gtkprogresstrackerprivate.h"
#include <math.h>
G_DEFINE_TYPE (GtkCssAnimation, _gtk_css_animation, GTK_TYPE_STYLE_ANIMATION)
/* NB: Return value can be negative (if animation hasn't started yet) */
static gint64
gtk_css_animation_get_elapsed (GtkCssAnimation *animation,
gint64 for_time_us)
{
if (animation->play_state == GTK_CSS_PLAY_STATE_PAUSED)
return animation->timestamp;
else
return for_time_us - animation->timestamp;
}
/* NB: Return value can be negative and +-Inf */
static double
gtk_css_animation_get_iteration (GtkCssAnimation *animation,
gint64 for_time_us)
{
return (double) gtk_css_animation_get_elapsed (animation, for_time_us) / animation->duration;
}
static gboolean
gtk_css_animation_is_executing_at_iteration (GtkCssAnimation *animation,
double iteration)
gtk_css_animation_is_executing (GtkCssAnimation *animation)
{
GtkProgressState state = gtk_progress_tracker_get_state (&animation->tracker);
switch (animation->fill_mode)
{
case GTK_CSS_FILL_NONE:
return iteration >= 0 && iteration <= animation->iteration_count;
return state == GTK_PROGRESS_STATE_DURING;
case GTK_CSS_FILL_FORWARDS:
return iteration >= 0;
return state != GTK_PROGRESS_STATE_BEFORE;
case GTK_CSS_FILL_BACKWARDS:
return iteration <= animation->iteration_count;
return state != GTK_PROGRESS_STATE_AFTER;
case GTK_CSS_FILL_BOTH:
return TRUE;
default:
@@ -65,57 +49,58 @@ gtk_css_animation_is_executing_at_iteration (GtkCssAnimation *animation,
}
static double
gtk_css_animation_get_progress_from_iteration (GtkCssAnimation *animation,
double iteration)
gtk_css_animation_get_progress (GtkCssAnimation *animation)
{
gboolean reverse;
double completed;
iteration = CLAMP (iteration, 0.0, animation->iteration_count);
completed = floor (iteration);
gboolean reverse, odd_iteration;
gint cycle = gtk_progress_tracker_get_iteration_cycle (&animation->tracker);
odd_iteration = cycle % 2 > 0;
switch (animation->direction)
{
case GTK_CSS_DIRECTION_NORMAL:
reverse = completed == iteration && iteration > 0;
reverse = FALSE;
break;
case GTK_CSS_DIRECTION_REVERSE:
reverse = !(completed == iteration && iteration > 0);
reverse = TRUE;
break;
case GTK_CSS_DIRECTION_ALTERNATE:
reverse = fmod (iteration, 2) >= 1.0;
reverse = odd_iteration;
break;
case GTK_CSS_DIRECTION_ALTERNATE_REVERSE:
reverse = !(fmod (iteration, 2) >= 1.0);
reverse = !odd_iteration;
break;
default:
g_return_val_if_reached (0.0);
}
iteration -= completed;
if (reverse)
iteration = 1.0 - iteration;
return gtk_progress_tracker_get_progress (&animation->tracker, reverse);
}
return iteration;
GtkStyleAnimation *
gtk_css_animation_advance (GtkStyleAnimation *style_animation,
gint64 timestamp)
{
GtkCssAnimation *animation = GTK_CSS_ANIMATION (style_animation);
return _gtk_css_animation_advance_with_play_state (animation,
timestamp,
animation->play_state);
}
static void
gtk_css_animation_set_values (GtkStyleAnimation *style_animation,
gint64 for_time_us,
GtkCssAnimatedStyle *style)
gtk_css_animation_apply_values (GtkStyleAnimation *style_animation,
GtkCssAnimatedStyle *style)
{
GtkCssAnimation *animation = GTK_CSS_ANIMATION (style_animation);
double iteration, progress;
double progress;
guint i;
iteration = gtk_css_animation_get_iteration (animation, for_time_us);
if (!gtk_css_animation_is_executing_at_iteration (animation, iteration))
if (!gtk_css_animation_is_executing (animation))
return;
progress = gtk_css_animation_get_progress_from_iteration (animation, iteration);
progress = gtk_css_animation_get_progress (animation);
progress = _gtk_css_ease_value_transform (animation->ease, progress);
for (i = 0; i < _gtk_css_keyframes_get_n_properties (animation->keyframes); i++)
{
GtkCssValue *value;
@@ -133,25 +118,20 @@ gtk_css_animation_set_values (GtkStyleAnimation *style_animation,
}
static gboolean
gtk_css_animation_is_finished (GtkStyleAnimation *style_animation,
gint64 at_time_us)
gtk_css_animation_is_finished (GtkStyleAnimation *style_animation)
{
return FALSE;
}
static gboolean
gtk_css_animation_is_static (GtkStyleAnimation *style_animation,
gint64 at_time_us)
gtk_css_animation_is_static (GtkStyleAnimation *style_animation)
{
GtkCssAnimation *animation = GTK_CSS_ANIMATION (style_animation);
double iteration;
if (animation->play_state == GTK_CSS_PLAY_STATE_PAUSED)
return TRUE;
iteration = gtk_css_animation_get_iteration (animation, at_time_us);
return iteration >= animation->iteration_count;
return gtk_progress_tracker_get_state (&animation->tracker) == GTK_PROGRESS_STATE_AFTER;
}
static void
@@ -174,7 +154,8 @@ _gtk_css_animation_class_init (GtkCssAnimationClass *klass)
object_class->finalize = gtk_css_animation_finalize;
animation_class->set_values = gtk_css_animation_set_values;
animation_class->advance = gtk_css_animation_advance;
animation_class->apply_values = gtk_css_animation_apply_values;
animation_class->is_finished = gtk_css_animation_is_finished;
animation_class->is_static = gtk_css_animation_is_static;
}
@@ -207,17 +188,16 @@ _gtk_css_animation_new (const char *name,
animation->name = g_strdup (name);
animation->keyframes = _gtk_css_keyframes_ref (keyframes);
if (play_state == GTK_CSS_PLAY_STATE_PAUSED)
animation->timestamp = - delay_us;
else
animation->timestamp = timestamp + delay_us;
animation->duration = duration_us;
animation->ease = _gtk_css_value_ref (ease);
animation->direction = direction;
animation->play_state = play_state;
animation->fill_mode = fill_mode;
animation->iteration_count = iteration_count;
gtk_progress_tracker_start (&animation->tracker, duration_us, delay_us, iteration_count);
if (animation->play_state == GTK_CSS_PLAY_STATE_PAUSED)
gtk_progress_tracker_skip_frame (&animation->tracker, timestamp);
else
gtk_progress_tracker_advance_frame (&animation->tracker, timestamp);
return GTK_STYLE_ANIMATION (animation);
}
@@ -231,24 +211,28 @@ _gtk_css_animation_get_name (GtkCssAnimation *animation)
}
GtkStyleAnimation *
_gtk_css_animation_copy (GtkCssAnimation *animation,
gint64 at_time_us,
GtkCssPlayState play_state)
_gtk_css_animation_advance_with_play_state (GtkCssAnimation *source,
gint64 timestamp,
GtkCssPlayState play_state)
{
g_return_val_if_fail (GTK_IS_CSS_ANIMATION (animation), NULL);
GtkCssAnimation *animation;
if (animation->play_state == play_state)
return g_object_ref (animation);
g_return_val_if_fail (GTK_IS_CSS_ANIMATION (source), NULL);
return _gtk_css_animation_new (animation->name,
animation->keyframes,
at_time_us,
- gtk_css_animation_get_elapsed (animation, at_time_us),
animation->duration,
animation->ease,
animation->direction,
play_state,
animation->fill_mode,
animation->iteration_count);
animation = g_object_new (GTK_TYPE_CSS_ANIMATION, NULL);
animation->name = g_strdup (source->name);
animation->keyframes = _gtk_css_keyframes_ref (source->keyframes);
animation->ease = _gtk_css_value_ref (source->ease);
animation->direction = source->direction;
animation->play_state = play_state;
animation->fill_mode = source->fill_mode;
gtk_progress_tracker_init_copy (&source->tracker, &animation->tracker);
if (animation->play_state == GTK_CSS_PLAY_STATE_PAUSED)
gtk_progress_tracker_skip_frame (&animation->tracker, timestamp);
else
gtk_progress_tracker_advance_frame (&animation->tracker, timestamp);
return GTK_STYLE_ANIMATION (animation);
}

View File

@@ -23,6 +23,7 @@
#include "gtkstyleanimationprivate.h"
#include "gtkcsskeyframesprivate.h"
#include "gtkprogresstrackerprivate.h"
G_BEGIN_DECLS
@@ -43,12 +44,10 @@ struct _GtkCssAnimation
char *name;
GtkCssKeyframes *keyframes;
GtkCssValue *ease;
gint64 timestamp; /* elapsed time when paused, start time when playing (can be negative) */
gint64 duration; /* duration of 1 cycle */
double iteration_count;
GtkCssDirection direction;
GtkCssPlayState play_state;
GtkCssFillMode fill_mode;
GtkProgressTracker tracker;
};
struct _GtkCssAnimationClass
@@ -69,9 +68,9 @@ GtkStyleAnimation * _gtk_css_animation_new (const char *
GtkCssFillMode fill_mode,
double iteration_count);
GtkStyleAnimation * _gtk_css_animation_copy (GtkCssAnimation *animation,
gint64 at_time_us,
GtkCssPlayState play_state);
GtkStyleAnimation * _gtk_css_animation_advance_with_play_state (GtkCssAnimation *animation,
gint64 timestamp,
GtkCssPlayState play_state);
const char * _gtk_css_animation_get_name (GtkCssAnimation *animation);

View File

@@ -22,60 +22,79 @@
#include "gtkcsstransitionprivate.h"
#include "gtkcsseasevalueprivate.h"
#include "gtkprogresstrackerprivate.h"
G_DEFINE_TYPE (GtkCssTransition, _gtk_css_transition, GTK_TYPE_STYLE_ANIMATION)
static void
gtk_css_transition_set_values (GtkStyleAnimation *animation,
gint64 for_time_us,
GtkCssAnimatedStyle *style)
static GtkStyleAnimation *
gtk_css_transition_advance (GtkStyleAnimation *style_animation,
gint64 timestamp)
{
GtkCssTransition *transition = GTK_CSS_TRANSITION (animation);
GtkCssTransition *source = GTK_CSS_TRANSITION (style_animation);
GtkCssTransition *transition;
transition = g_object_new (GTK_TYPE_CSS_TRANSITION, NULL);
transition->property = source->property;
transition->start = _gtk_css_value_ref (source->start);
transition->ease = _gtk_css_value_ref (source->ease);
gtk_progress_tracker_init_copy (&source->tracker, &transition->tracker);
gtk_progress_tracker_advance_frame (&transition->tracker, timestamp);
return GTK_STYLE_ANIMATION (transition);
}
static void
gtk_css_transition_apply_values (GtkStyleAnimation *style_animation,
GtkCssAnimatedStyle *style)
{
GtkCssTransition *transition = GTK_CSS_TRANSITION (style_animation);
GtkCssValue *value, *end;
double progress;
GtkProgressState state;
end = gtk_css_animated_style_get_intrinsic_value (style, transition->property);
if (transition->start_time >= for_time_us)
state = gtk_progress_tracker_get_state (&transition->tracker);
if (state == GTK_PROGRESS_STATE_BEFORE)
value = _gtk_css_value_ref (transition->start);
else if (transition->end_time > for_time_us)
else if (state == GTK_PROGRESS_STATE_DURING)
{
progress = (double) (for_time_us - transition->start_time) / (transition->end_time - transition->start_time);
progress = gtk_progress_tracker_get_progress (&transition->tracker, FALSE);
progress = _gtk_css_ease_value_transform (transition->ease, progress);
value = _gtk_css_value_transition (transition->start,
end,
transition->property,
progress);
if (value == NULL)
value = _gtk_css_value_ref (end);
}
else
value = NULL;
return;
if (value)
{
gtk_css_animated_style_set_animated_value (style, transition->property, value);
_gtk_css_value_unref (value);
}
if (value == NULL)
value = _gtk_css_value_ref (end);
gtk_css_animated_style_set_animated_value (style, transition->property, value);
_gtk_css_value_unref (value);
}
static gboolean
gtk_css_transition_is_finished (GtkStyleAnimation *animation,
gint64 at_time_us)
gtk_css_transition_is_finished (GtkStyleAnimation *animation)
{
GtkCssTransition *transition = GTK_CSS_TRANSITION (animation);
return at_time_us >= transition->end_time;
return gtk_progress_tracker_get_state (&transition->tracker) == GTK_PROGRESS_STATE_AFTER;
}
static gboolean
gtk_css_transition_is_static (GtkStyleAnimation *animation,
gint64 at_time_us)
gtk_css_transition_is_static (GtkStyleAnimation *animation)
{
GtkCssTransition *transition = GTK_CSS_TRANSITION (animation);
return at_time_us >= transition->end_time;
return gtk_progress_tracker_get_state (&transition->tracker) == GTK_PROGRESS_STATE_AFTER;
}
static void
@@ -97,7 +116,8 @@ _gtk_css_transition_class_init (GtkCssTransitionClass *klass)
object_class->finalize = gtk_css_transition_finalize;
animation_class->set_values = gtk_css_transition_set_values;
animation_class->advance = gtk_css_transition_advance;
animation_class->apply_values = gtk_css_transition_apply_values;
animation_class->is_finished = gtk_css_transition_is_finished;
animation_class->is_static = gtk_css_transition_is_static;
}
@@ -111,22 +131,22 @@ GtkStyleAnimation *
_gtk_css_transition_new (guint property,
GtkCssValue *start,
GtkCssValue *ease,
gint64 start_time_us,
gint64 end_time_us)
gint64 timestamp,
gint64 duration_us,
gint64 delay_us)
{
GtkCssTransition *transition;
g_return_val_if_fail (start != NULL, NULL);
g_return_val_if_fail (ease != NULL, NULL);
g_return_val_if_fail (start_time_us <= end_time_us, NULL);
transition = g_object_new (GTK_TYPE_CSS_TRANSITION, NULL);
transition->property = property;
transition->start = _gtk_css_value_ref (start);
transition->ease = _gtk_css_value_ref (ease);
transition->start_time = start_time_us;
transition->end_time = end_time_us;
gtk_progress_tracker_start (&transition->tracker, duration_us, delay_us, 1.0);
gtk_progress_tracker_advance_frame (&transition->tracker, timestamp);
return GTK_STYLE_ANIMATION (transition);
}

View File

@@ -21,6 +21,7 @@
#define __GTK_CSS_TRANSITION_PRIVATE_H__
#include "gtkstyleanimationprivate.h"
#include "gtkprogresstrackerprivate.h"
G_BEGIN_DECLS
@@ -38,11 +39,10 @@ struct _GtkCssTransition
{
GtkStyleAnimation parent;
guint property;
GtkCssValue *start;
GtkCssValue *ease;
gint64 start_time;
gint64 end_time;
guint property;
GtkCssValue *start;
GtkCssValue *ease;
GtkProgressTracker tracker;
};
struct _GtkCssTransitionClass
@@ -55,8 +55,9 @@ GType _gtk_css_transition_get_type (void) G_GNUC_CONST;
GtkStyleAnimation * _gtk_css_transition_new (guint property,
GtkCssValue *start,
GtkCssValue *ease,
gint64 start_time_us,
gint64 end_time_us);
gint64 timestamp,
gint64 duration_us,
gint64 delay_us);
guint _gtk_css_transition_get_property (GtkCssTransition *transition);

View File

@@ -70,6 +70,7 @@
#include "gtkmagnifierprivate.h"
#include "gtkcssnodeprivate.h"
#include "gtkcsscustomgadgetprivate.h"
#include "gtkprogresstrackerprivate.h"
#include "a11y/gtkentryaccessible.h"
@@ -196,10 +197,11 @@ struct _GtkEntryPrivate
gdouble progress_pulse_fraction;
gdouble progress_pulse_current;
guint tick_id;
gint64 pulse1;
gint64 pulse2;
gint64 frame1;
guint tick_id;
GtkProgressTracker tracker;
gint64 pulse1;
gint64 pulse2;
gdouble last_iteration;
gchar *placeholder_text;
@@ -10394,31 +10396,30 @@ tick_cb (GtkWidget *widget,
{
GtkEntry *entry = GTK_ENTRY (widget);
GtkEntryPrivate *priv = entry->priv;
gint64 frame2;
gdouble fraction;
gint64 frame_time;
gdouble iteration, pulse_iterations, current_iterations, fraction;
frame2 = gdk_frame_clock_get_frame_time (frame_clock);
if (priv->frame1 == 0)
priv->frame1 = frame2 - 16667;
if (priv->pulse1 == 0)
priv->pulse1 = priv->pulse2 - 250 * 1000000;
if (priv->pulse2 == 0 && priv->pulse1 == 0)
return G_SOURCE_CONTINUE;
frame_time = gdk_frame_clock_get_frame_time (frame_clock);
gtk_progress_tracker_advance_frame (&priv->tracker, frame_time);
g_assert (priv->pulse2 > priv->pulse1);
g_assert (frame2 > priv->frame1);
if (frame2 - priv->pulse2 > 3 * (priv->pulse2 - priv->pulse1))
{
priv->pulse1 = 0;
return G_SOURCE_CONTINUE;
}
pulse_iterations = (priv->pulse2 - priv->pulse1) / (gdouble) G_USEC_PER_SEC;
current_iterations = (frame_time - priv->pulse1) / (gdouble) G_USEC_PER_SEC;
iteration = gtk_progress_tracker_get_iteration (&priv->tracker);
/* Determine the fraction to move the block from one frame
* to the next when pulse_fraction is how far the block should
* move between two calls to gtk_entry_progress_pulse().
*/
fraction = priv->progress_pulse_fraction * (frame2 - priv->frame1) / MAX (frame2 - priv->pulse2, priv->pulse2 - priv->pulse1);
fraction = priv->progress_pulse_fraction * (iteration - priv->last_iteration) / MAX (pulse_iterations, current_iterations);
priv->last_iteration = iteration;
priv->frame1 = frame2;
if (current_iterations > 3 * pulse_iterations)
return G_SOURCE_CONTINUE;
/* advance the block */
if (priv->progress_pulse_way_back)
@@ -10483,6 +10484,9 @@ gtk_entry_start_pulse_mode (GtkEntry *entry)
gtk_css_gadget_add_class (priv->progress_gadget, GTK_STYLE_CLASS_PULSE);
priv->progress_pulse_mode = TRUE;
/* How long each pulse should last depends on calls to gtk_entry_progress_pulse.
* Just start the tracker to repeat forever with iterations every second. */
gtk_progress_tracker_start (&priv->tracker, G_USEC_PER_SEC, 0, INFINITY);
priv->tick_id = gtk_widget_add_tick_callback (GTK_WIDGET (entry), tick_cb, NULL, NULL);
priv->progress_fraction = 0.0;
@@ -10491,7 +10495,7 @@ gtk_entry_start_pulse_mode (GtkEntry *entry)
priv->pulse2 = 0;
priv->pulse1 = 0;
priv->frame1 = 0;
priv->last_iteration = 0;
}
static void
@@ -10514,9 +10518,13 @@ static void
gtk_entry_update_pulse (GtkEntry *entry)
{
GtkEntryPrivate *priv = entry->priv;
gint64 pulse_time = g_get_monotonic_time ();
if (priv->pulse2 == pulse_time)
return;
priv->pulse1 = priv->pulse2;
priv->pulse2 = g_get_monotonic_time ();
priv->pulse2 = pulse_time;
}
/**

View File

@@ -636,6 +636,7 @@ do_pre_parse_initialization (int *argc,
char ***argv)
{
const gchar *env_string;
double slowdown;
if (pre_initialized)
return;
@@ -673,6 +674,13 @@ do_pre_parse_initialization (int *argc,
g_string_append (gtk_modules_string, env_string);
}
env_string = g_getenv ("GTK_SLOWDOWN");
if (env_string)
{
slowdown = g_ascii_strtod (env_string, NULL);
_gtk_set_slowdown (slowdown);
}
}
static void

View File

@@ -110,6 +110,7 @@
#include "gtkmenusectionbox.h"
#include "gtkroundedboxprivate.h"
#include "gtkstylecontextprivate.h"
#include "gtkprogresstrackerprivate.h"
#ifdef GDK_WINDOWING_WAYLAND
#include "wayland/gdkwayland.h"
@@ -156,6 +157,7 @@ struct _GtkPopoverPrivate
GtkAdjustment *hadj;
GdkRectangle pointing_to;
GtkPopoverConstraint constraint;
GtkProgressTracker tracker;
guint prev_focus_unmap_id;
guint hierarchy_changed_id;
guint size_allocate_id;
@@ -173,7 +175,6 @@ struct _GtkPopoverPrivate
guint transitions_enabled : 1;
guint state : 2;
guint visible : 1;
gint64 start_time;
gint transition_diff;
guint tick_id;
@@ -510,17 +511,6 @@ gtk_popover_apply_modality (GtkPopover *popover,
}
}
/* From clutter-easing.c, based on Robert Penner's
* infamous easing equations, MIT license.
*/
static double
ease_out_cubic (double t)
{
double p = t - 1;
return p * p * p + 1;
}
static gboolean
show_animate_cb (GtkWidget *widget,
GdkFrameClock *frame_clock,
@@ -528,15 +518,11 @@ show_animate_cb (GtkWidget *widget,
{
GtkPopover *popover = GTK_POPOVER (widget);
GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover);
gint64 now = gdk_frame_clock_get_frame_time (frame_clock);
gdouble t;
if (now < (priv->start_time + TRANSITION_DURATION))
t = (now - priv->start_time) / (gdouble) (TRANSITION_DURATION);
else
t = 1.0;
t = ease_out_cubic (t);
gtk_progress_tracker_advance_frame (&priv->tracker,
gdk_frame_clock_get_frame_time (frame_clock));
t = gtk_progress_tracker_get_ease_out_cubic (&priv->tracker, FALSE);
if (priv->state == STATE_SHOWING)
{
@@ -551,7 +537,7 @@ show_animate_cb (GtkWidget *widget,
gtk_popover_update_position (popover);
if (t >= 1.0)
if (gtk_progress_tracker_get_state (&priv->tracker) == GTK_PROGRESS_STATE_AFTER)
{
if (priv->state == STATE_SHOWING)
{
@@ -573,13 +559,11 @@ static void
gtk_popover_start_transition (GtkPopover *popover)
{
GtkPopoverPrivate *priv = popover->priv;
GdkFrameClock *clock;
if (priv->tick_id != 0)
return;
clock = gtk_widget_get_frame_clock (GTK_WIDGET (popover));
priv->start_time = gdk_frame_clock_get_frame_time (clock);
gtk_progress_tracker_start (&priv->tracker, TRANSITION_DURATION, 0, 1.0);
priv->tick_id = gtk_widget_add_tick_callback (GTK_WIDGET (popover),
show_animate_cb,
popover, NULL);

View File

@@ -91,6 +91,10 @@ gboolean _gtk_propagate_captured_event (GtkWidget *widget,
GdkEvent *event,
GtkWidget *topmost);
gdouble _gtk_get_slowdown ();
void _gtk_set_slowdown (gdouble slowdown_factor);
#ifdef G_OS_WIN32
void _gtk_load_dll_with_libgtk3_manifest (const char *dllname);
#endif

View File

@@ -38,6 +38,7 @@
#include "gtkcssstylepropertyprivate.h"
#include "gtkcsscustomgadgetprivate.h"
#include "gtkcssnumbervalueprivate.h"
#include "gtkprogresstrackerprivate.h"
#include "a11y/gtkprogressbaraccessible.h"
@@ -92,6 +93,7 @@
#define MIN_VERTICAL_BAR_WIDTH 7
#define MIN_VERTICAL_BAR_HEIGHT 80
#define DEFAULT_PULSE_DURATION 250000000
struct _GtkProgressBarPrivate
{
@@ -110,10 +112,11 @@ struct _GtkProgressBarPrivate
GtkOrientation orientation;
guint tick_id;
gint64 pulse1;
gint64 pulse2;
gint64 frame1;
guint tick_id;
GtkProgressTracker tracker;
gint64 pulse1;
gint64 pulse2;
gdouble last_iteration;
guint activity_dir : 1;
guint activity_mode : 1;
@@ -1142,31 +1145,30 @@ tick_cb (GtkWidget *widget,
{
GtkProgressBar *pbar = GTK_PROGRESS_BAR (widget);
GtkProgressBarPrivate *priv = pbar->priv;
gint64 frame2;
gdouble fraction;
gint64 frame_time;
gdouble iteration, pulse_iterations, current_iterations, fraction;
frame2 = gdk_frame_clock_get_frame_time (frame_clock);
if (priv->frame1 == 0)
priv->frame1 = frame2 - 16667;
if (priv->pulse1 == 0)
priv->pulse1 = priv->pulse2 - 250 * 1000000;
if (priv->pulse2 == 0 && priv->pulse1 == 0)
return G_SOURCE_CONTINUE;
frame_time = gdk_frame_clock_get_frame_time (frame_clock);
gtk_progress_tracker_advance_frame (&priv->tracker, frame_time);
g_assert (priv->pulse2 > priv->pulse1);
g_assert (frame2 > priv->frame1);
if (frame2 - priv->pulse2 > 3 * (priv->pulse2 - priv->pulse1))
{
priv->pulse1 = 0;
return G_SOURCE_CONTINUE;
}
pulse_iterations = (priv->pulse2 - priv->pulse1) / (gdouble) G_USEC_PER_SEC;
current_iterations = (frame_time - priv->pulse1) / (gdouble) G_USEC_PER_SEC;
iteration = gtk_progress_tracker_get_iteration (&priv->tracker);
/* Determine the fraction to move the block from one frame
* to the next when pulse_fraction is how far the block should
* move between two calls to gtk_progress_bar_pulse().
*/
fraction = priv->pulse_fraction * (frame2 - priv->frame1) / MAX (frame2 - priv->pulse2, priv->pulse2 - priv->pulse1);
fraction = priv->pulse_fraction * (iteration - priv->last_iteration) / MAX (pulse_iterations, current_iterations);
priv->last_iteration = iteration;
priv->frame1 = frame2;
if (current_iterations > 3 * pulse_iterations)
return G_SOURCE_CONTINUE;
/* advance the block */
if (priv->activity_dir == 0)
@@ -1243,10 +1245,13 @@ gtk_progress_bar_act_mode_enter (GtkProgressBar *pbar)
}
update_node_classes (pbar);
/* No fixed schedule for pulses, will adapt after calls to update_pulse. Just
* start the tracker to repeat forever with iterations every second.*/
gtk_progress_tracker_start (&priv->tracker, G_USEC_PER_SEC, 0, INFINITY);
priv->tick_id = gtk_widget_add_tick_callback (widget, tick_cb, NULL, NULL);
priv->pulse2 = 0;
priv->pulse1 = 0;
priv->frame1 = 0;
priv->last_iteration = 0;
}
static void
@@ -1406,9 +1411,13 @@ static void
gtk_progress_bar_update_pulse (GtkProgressBar *pbar)
{
GtkProgressBarPrivate *priv = pbar->priv;
gint64 pulse_time = g_get_monotonic_time ();
if (priv->pulse2 == pulse_time)
return;
priv->pulse1 = priv->pulse2;
priv->pulse2 = g_get_monotonic_time ();
priv->pulse2 = pulse_time;
}
/**

268
gtk/gtkprogresstracker.c Normal file
View File

@@ -0,0 +1,268 @@
/*
* Copyright © 2016 Endless Mobile Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Matthew Watson <mattdangerw@gmail.com>
*/
#include "gtkprogresstrackerprivate.h"
#include "gtkprivate.h"
#include "gtkcsseasevalueprivate.h"
#include <math.h>
#include <string.h>
/*
* Progress tracker is small helper for tracking progress through gtk
* animations. It's a simple zero-initable struct, meant to be thrown in a
* widget's private data without the need for setup or teardown.
*
* Progress tracker will handle translating frame clock timestamps to a
* fractional progress value for interpolating between animation targets.
*
* Progress tracker will use the GTK_SLOWDOWN environment variable to control
* the speed of animations. This can be useful for debugging.
*/
static gdouble gtk_slowdown = 1.0;
void
_gtk_set_slowdown (gdouble factor)
{
gtk_slowdown = factor;
}
gdouble
_gtk_get_slowdown (gdouble factor)
{
return gtk_slowdown;
}
/**
* gtk_progress_tracker_init_copy:
* @source: The source progress tracker
* @dest: The destination progress tracker
*
* Copy all progress tracker state from the source tracker to dest tracker.
**/
void
gtk_progress_tracker_init_copy (GtkProgressTracker *source,
GtkProgressTracker *dest)
{
memcpy (dest, source, sizeof (GtkProgressTracker));
}
/**
* gtk_progress_tracker_start:
* @tracker: The progress tracker
* @duration: Animation duration in us
* @delay: Animation delay in us
* @iteration_count: Number of iterations to run the animation, must be >= 0
*
* Begins tracking progress for a new animation. Clears all previous state.
**/
void
gtk_progress_tracker_start (GtkProgressTracker *tracker,
guint64 duration,
gint64 delay,
gdouble iteration_count)
{
tracker->is_running = TRUE;
tracker->last_frame_time = 0;
tracker->duration = duration;
tracker->iteration = - delay / (gdouble) duration;
tracker->iteration_count = iteration_count;
}
/**
* gtk_progress_tracker_finish:
* @tracker: The progress tracker
*
* Stops running the current animation.
**/
void
gtk_progress_tracker_finish (GtkProgressTracker *tracker)
{
tracker->is_running = FALSE;
}
/**
* gtk_progress_tracker_advance_frame:
* @tracker: The progress tracker
* @frame_time: The current frame time, usually from the frame clock.
*
* Increments the progress of the animation forward a frame. If no animation has
* been started, does nothing.
**/
void
gtk_progress_tracker_advance_frame (GtkProgressTracker *tracker,
guint64 frame_time)
{
gdouble delta;
if (!tracker->is_running)
return;
if (tracker->last_frame_time == 0)
{
tracker->last_frame_time = frame_time;
return;
}
if (frame_time < tracker->last_frame_time)
{
g_warning ("Progress tracker frame set backwards, ignoring.");
return;
}
delta = (frame_time - tracker->last_frame_time) / gtk_slowdown / tracker->duration;
tracker->last_frame_time = frame_time;
tracker->iteration += delta;
}
/**
* gtk_progress_tracker_skip_frame:
* @tracker: The progress tracker
* @frame_time: The current frame time, usually from the frame clock.
*
* Does not update the progress of the animation forward, but records the frame
* to calculate future deltas. Calling this each frame will effectively pause
* the animation.
**/
void
gtk_progress_tracker_skip_frame (GtkProgressTracker *tracker,
guint64 frame_time)
{
if (!tracker->is_running)
return;
tracker->last_frame_time = frame_time;
}
/**
* gtk_progress_tracker_get_state:
* @tracker: The progress tracker
*
* Returns whether the tracker is before, during or after the currently started
* animation. The tracker will only ever be in the before state if the animation
* was started with a delay. If no animation has been started, returns
* %GTK_PROGRESS_STATE_AFTER.
*
* Returns: A GtkProgressState
**/
GtkProgressState
gtk_progress_tracker_get_state (GtkProgressTracker *tracker)
{
if (!tracker->is_running || tracker->iteration > tracker->iteration_count)
return GTK_PROGRESS_STATE_AFTER;
if (tracker->iteration < 0)
return GTK_PROGRESS_STATE_BEFORE;
return GTK_PROGRESS_STATE_DURING;
}
/**
* gtk_progress_tracker_get_iteration:
* @tracker: The progress tracker
*
* Returns the fractional number of cycles the animation has completed. For
* example, it you started an animation with iteration-count of 2 and are half
* way through the second animation, this returns 1.5.
*
* Returns: The current iteration.
**/
gdouble
gtk_progress_tracker_get_iteration (GtkProgressTracker *tracker)
{
return tracker->is_running ? CLAMP (tracker->iteration, 0.0, tracker->iteration_count) : 1.0;
}
/**
* gtk_progress_tracker_get_iteration_cycle:
* @tracker: The progress tracker
*
* Returns an integer index of the current iteration cycle tracker is
* progressing through. Handles edge cases, such as an iteration value of 2.0
* which could be considered the end of the second iteration of the beginning of
* the third, in the same way as gtk_progress_tracker_get_progress().
*
* Returns: The integer count of the current animation cycle.
**/
guint64
gtk_progress_tracker_get_iteration_cycle (GtkProgressTracker *tracker)
{
gdouble iteration = gtk_progress_tracker_get_iteration (tracker);
/* Some complexity here. We want an iteration of 0.0 to always map to 0 (start
* of the first iteration), but an iteration of 1.0 to also map to 0 (end of
* first iteration) and 2.0 to 1 (end of the second iteration).
*/
if (iteration == 0.0)
return 0;
return (guint64) ceil (iteration) - 1;
}
/**
* gtk_progress_tracker_get_progress:
* @tracker: The progress tracker
* @reversed: If progress should be reversed.
*
* Gets the progress through the current animation iteration, from [0, 1]. Use
* to interpolate between animation targets. If reverse is true each iteration
* will begin at 1 and end at 0.
*
* Returns: The progress value.
**/
gdouble
gtk_progress_tracker_get_progress (GtkProgressTracker *tracker,
gboolean reversed)
{
gdouble progress, iteration;
guint64 iteration_cycle;
iteration = gtk_progress_tracker_get_iteration (tracker);
iteration_cycle = gtk_progress_tracker_get_iteration_cycle (tracker);
progress = iteration - iteration_cycle;
return reversed ? 1.0 - progress : progress;
}
/* From clutter-easing.c, based on Robert Penner's
* infamous easing equations, MIT license.
*/
static gdouble
ease_out_cubic (gdouble t)
{
gdouble p = t - 1;
return p * p * p + 1;
}
/**
* gtk_progress_tracker_get_ease_out_cubic:
* @tracker: The progress tracker
* @reversed: If progress should be reversed before applying the ease function.
*
* Applies a simple ease out cubic function to the result of
* gtk_progress_tracker_get_progress().
*
* Returns: The eased progress value.
**/
gdouble
gtk_progress_tracker_get_ease_out_cubic (GtkProgressTracker *tracker,
gboolean reversed)
{
gdouble progress = gtk_progress_tracker_get_progress (tracker, reversed);
return ease_out_cubic (progress);
}

View File

@@ -0,0 +1,75 @@
/*
* Copyright © 2016 Endless Mobile Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Matthew Watson <mattdangerw@gmail.com>
*/
#ifndef __GTK_PROGRESS_TRACKER_PRIVATE_H__
#define __GTK_PROGRESS_TRACKER_PRIVATE_H__
#include <glib-object.h>
#include "gtkcsseasevalueprivate.h"
G_BEGIN_DECLS
typedef enum {
GTK_PROGRESS_STATE_BEFORE,
GTK_PROGRESS_STATE_DURING,
GTK_PROGRESS_STATE_AFTER,
} GtkProgressState;
typedef struct _GtkProgressTracker GtkProgressTracker;
struct _GtkProgressTracker
{
gboolean is_running;
guint64 last_frame_time;
guint64 duration;
gdouble iteration;
gdouble iteration_count;
};
void gtk_progress_tracker_init_copy (GtkProgressTracker *source,
GtkProgressTracker *dest);
void gtk_progress_tracker_start (GtkProgressTracker *tracker,
guint64 duration,
gint64 delay,
gdouble iteration_count);
void gtk_progress_tracker_finish (GtkProgressTracker *tracker);
void gtk_progress_tracker_advance_frame (GtkProgressTracker *tracker,
guint64 frame_time);
void gtk_progress_tracker_skip_frame (GtkProgressTracker *tracker,
guint64 frame_time);
GtkProgressState gtk_progress_tracker_get_state (GtkProgressTracker *tracker);
gdouble gtk_progress_tracker_get_iteration (GtkProgressTracker *tracker);
guint64 gtk_progress_tracker_get_iteration_cycle (GtkProgressTracker *tracker);
gdouble gtk_progress_tracker_get_progress (GtkProgressTracker *tracker,
gboolean reverse);
gdouble gtk_progress_tracker_get_ease_out_cubic (GtkProgressTracker *tracker,
gboolean reverse);
G_END_DECLS
#endif /* __GTK_PROGRESS_TRACKER_PRIVATE_H__ */

View File

@@ -25,6 +25,7 @@
#include <gdk/gdk.h>
#include "gtktypebuiltins.h"
#include "gtkprivate.h"
#include "gtkprogresstrackerprivate.h"
#include "gtkintl.h"
#include "fallback-c89.c"
@@ -85,8 +86,7 @@ typedef struct {
gdouble target_pos;
guint tick_id;
gint64 start_time;
gint64 end_time;
GtkProgressTracker tracker;
} GtkRevealerPrivate;
static GParamSpec *props[LAST_PROP] = { NULL, };
@@ -568,33 +568,6 @@ gtk_revealer_set_position (GtkRevealer *revealer,
g_object_notify_by_pspec (G_OBJECT (revealer), props[PROP_CHILD_REVEALED]);
}
/* From clutter-easing.c, based on Robert Penner's
* infamous easing equations, MIT license.
*/
static double
ease_out_cubic (double t)
{
double p = t - 1;
return p * p * p + 1;
}
static void
gtk_revealer_animate_step (GtkRevealer *revealer,
gint64 now)
{
GtkRevealerPrivate *priv = gtk_revealer_get_instance_private (revealer);
gdouble t;
if (now < priv->end_time)
t = (now - priv->start_time) / (gdouble) (priv->end_time - priv->start_time);
else
t = 1.0;
t = ease_out_cubic (t);
gtk_revealer_set_position (revealer,
priv->source_pos + (t * (priv->target_pos - priv->source_pos)));
}
static gboolean
gtk_revealer_animate_cb (GtkWidget *widget,
GdkFrameClock *frame_clock,
@@ -602,11 +575,15 @@ gtk_revealer_animate_cb (GtkWidget *widget,
{
GtkRevealer *revealer = GTK_REVEALER (widget);
GtkRevealerPrivate *priv = gtk_revealer_get_instance_private (revealer);
gint64 now;
gdouble ease;
now = gdk_frame_clock_get_frame_time (frame_clock);
gtk_revealer_animate_step (revealer, now);
if (priv->current_pos == priv->target_pos)
gtk_progress_tracker_advance_frame (&priv->tracker,
gdk_frame_clock_get_frame_time (frame_clock));
ease = gtk_progress_tracker_get_ease_out_cubic (&priv->tracker, FALSE);
gtk_revealer_set_position (revealer,
priv->source_pos + (ease * (priv->target_pos - priv->source_pos)));
if (gtk_progress_tracker_get_state (&priv->tracker) == GTK_PROGRESS_STATE_AFTER)
{
priv->tick_id = 0;
return FALSE;
@@ -641,12 +618,13 @@ gtk_revealer_start_animation (GtkRevealer *revealer,
animations_enabled)
{
priv->source_pos = priv->current_pos;
priv->start_time = gdk_frame_clock_get_frame_time (gtk_widget_get_frame_clock (widget));
priv->end_time = priv->start_time + (priv->transition_duration * 1000);
if (priv->tick_id == 0)
priv->tick_id =
gtk_widget_add_tick_callback (widget, gtk_revealer_animate_cb, revealer, NULL);
gtk_revealer_animate_step (revealer, priv->start_time);
gtk_progress_tracker_start (&priv->tracker,
priv->transition_duration * 1000,
0,
1.0);
}
else
{

View File

@@ -45,6 +45,7 @@
#include "gtkkineticscrolling.h"
#include "a11y/gtkscrolledwindowaccessible.h"
#include "gtkstylecontextprivate.h"
#include "gtkprogresstrackerprivate.h"
#include <math.h>
@@ -193,8 +194,7 @@ typedef struct
gdouble current_pos;
gdouble source_pos;
gdouble target_pos;
gint64 start_time;
gint64 end_time;
GtkProgressTracker tracker;
guint tick_id;
guint over_timeout_id;
} Indicator;
@@ -4002,40 +4002,22 @@ indicator_set_fade (Indicator *indicator,
}
}
static double
ease_out_cubic (double t)
{
double p = t - 1;
return p * p * p + 1;
}
static void
indicator_fade_step (Indicator *indicator,
gint64 now)
{
gdouble t;
if (now < indicator->end_time)
t = (now - indicator->start_time) / (gdouble) (indicator->end_time - indicator->start_time);
else
t = 1.0;
t = ease_out_cubic (t);
indicator_set_fade (indicator,
indicator->source_pos + (t * (indicator->target_pos - indicator->source_pos)));
}
static gboolean
indicator_fade_cb (GtkWidget *widget,
GdkFrameClock *frame_clock,
gpointer user_data)
{
Indicator *indicator = user_data;
gint64 now;
gdouble t;
now = gdk_frame_clock_get_frame_time (frame_clock);
indicator_fade_step (indicator, now);
if (indicator->current_pos == indicator->target_pos)
gtk_progress_tracker_advance_frame (&indicator->tracker,
gdk_frame_clock_get_frame_time (frame_clock));
t = gtk_progress_tracker_get_ease_out_cubic (&indicator->tracker, FALSE);
indicator_set_fade (indicator,
indicator->source_pos + (t * (indicator->target_pos - indicator->source_pos)));
if (gtk_progress_tracker_get_state (&indicator->tracker) == GTK_PROGRESS_STATE_AFTER)
{
indicator->tick_id = 0;
return FALSE;
@@ -4065,12 +4047,9 @@ indicator_start_fade (Indicator *indicator,
if (gtk_widget_get_mapped (indicator->scrollbar) && animations_enabled)
{
indicator->source_pos = indicator->current_pos;
indicator->start_time = gdk_frame_clock_get_frame_time (gtk_widget_get_frame_clock (indicator->scrollbar));
indicator->end_time = indicator->start_time + INDICATOR_FADE_OUT_DURATION * 1000;
gtk_progress_tracker_start (&indicator->tracker, INDICATOR_FADE_OUT_DURATION * 1000, 0, 1.0);
if (indicator->tick_id == 0)
indicator->tick_id = gtk_widget_add_tick_callback (indicator->scrollbar, indicator_fade_cb, indicator, NULL);
indicator_fade_step (indicator, indicator->start_time);
}
else
indicator_set_fade (indicator, target);
@@ -4093,8 +4072,9 @@ indicator_stop_fade (Indicator *indicator)
}
gdk_window_hide (indicator->window);
gtk_progress_tracker_finish (&indicator->tracker);
indicator->current_pos = indicator->source_pos = indicator->target_pos = 0;
indicator->start_time = indicator->end_time = indicator->last_scroll_time = 0;
indicator->last_scroll_time = 0;
}
static gboolean
@@ -4297,8 +4277,9 @@ indicator_reset (Indicator *indicator)
indicator->scrollbar = NULL;
indicator->over = FALSE;
gtk_progress_tracker_finish (&indicator->tracker);
indicator->current_pos = indicator->source_pos = indicator->target_pos = 0;
indicator->start_time = indicator->end_time = indicator->last_scroll_time = 0;
indicator->last_scroll_time = 0;
}
static void

View File

@@ -27,6 +27,7 @@
#include "gtkintl.h"
#include "gtkcsscustomgadgetprivate.h"
#include "gtkcontainerprivate.h"
#include "gtkprogresstrackerprivate.h"
#include "gtkwidgetprivate.h"
#include <math.h>
#include <string.h>
@@ -143,10 +144,9 @@ typedef struct {
GtkStackChildInfo *last_visible_child;
cairo_surface_t *last_visible_surface;
GtkAllocation last_visible_surface_allocation;
gdouble transition_pos;
guint tick_id;
gint64 start_time;
gint64 end_time;
GtkProgressTracker tracker;
gboolean first_frame_skipped;
gint last_visible_widget_width;
gint last_visible_widget_height;
@@ -765,16 +765,6 @@ gtk_stack_set_child_property (GtkContainer *container,
}
}
/* From clutter-easing.c, based on Robert Penner's
* infamous easing equations, MIT license.
*/
static double
ease_out_cubic (double t)
{
double p = t - 1;
return p * p * p + 1;
}
static inline gboolean
is_left_transition (GtkStackTransitionType transition_type)
{
@@ -863,12 +853,12 @@ get_bin_window_x (GtkStack *stack,
GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
int x = 0;
if (priv->transition_pos < 1.0)
if (gtk_progress_tracker_get_state (&priv->tracker) != GTK_PROGRESS_STATE_AFTER)
{
if (is_left_transition (priv->active_transition_type))
x = allocation->width * (1 - ease_out_cubic (priv->transition_pos));
x = allocation->width * (1 - gtk_progress_tracker_get_ease_out_cubic (&priv->tracker, FALSE));
if (is_right_transition (priv->active_transition_type))
x = -allocation->width * (1 - ease_out_cubic (priv->transition_pos));
x = -allocation->width * (1 - gtk_progress_tracker_get_ease_out_cubic (&priv->tracker, FALSE));
}
return x;
@@ -881,25 +871,22 @@ get_bin_window_y (GtkStack *stack,
GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
int y = 0;
if (priv->transition_pos < 1.0)
if (gtk_progress_tracker_get_state (&priv->tracker) != GTK_PROGRESS_STATE_AFTER)
{
if (is_up_transition (priv->active_transition_type))
y = allocation->height * (1 - ease_out_cubic (priv->transition_pos));
y = allocation->height * (1 - gtk_progress_tracker_get_ease_out_cubic (&priv->tracker, FALSE));
if (is_down_transition(priv->active_transition_type))
y = -allocation->height * (1 - ease_out_cubic (priv->transition_pos));
y = -allocation->height * (1 - gtk_progress_tracker_get_ease_out_cubic (&priv->tracker, FALSE));
}
return y;
}
static gboolean
gtk_stack_set_transition_position (GtkStack *stack,
gdouble pos)
static void
gtk_stack_progress_updated (GtkStack *stack)
{
GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
gboolean done;
priv->transition_pos = pos;
gtk_widget_queue_draw (GTK_WIDGET (stack));
if (!priv->vhomogeneous || !priv->hhomogeneous)
@@ -914,9 +901,7 @@ gtk_stack_set_transition_position (GtkStack *stack,
get_bin_window_x (stack, &allocation), get_bin_window_y (stack, &allocation));
}
done = pos >= 1.0;
if (done)
if (gtk_progress_tracker_get_state (&priv->tracker) == GTK_PROGRESS_STATE_AFTER)
{
if (priv->last_visible_surface != NULL)
{
@@ -930,8 +915,6 @@ gtk_stack_set_transition_position (GtkStack *stack,
priv->last_visible_child = NULL;
}
}
return done;
}
static gboolean
@@ -941,20 +924,20 @@ gtk_stack_transition_cb (GtkWidget *widget,
{
GtkStack *stack = GTK_STACK (widget);
GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
gint64 now;
gdouble t;
now = gdk_frame_clock_get_frame_time (frame_clock);
t = 1.0;
if (now < priv->end_time)
t = (now - priv->start_time) / (double) (priv->end_time - priv->start_time);
if (priv->first_frame_skipped)
gtk_progress_tracker_advance_frame (&priv->tracker,
gdk_frame_clock_get_frame_time (frame_clock));
else
priv->first_frame_skipped = TRUE;
/* Finish animation early if not mapped anymore */
if (!gtk_widget_get_mapped (widget))
t = 1.0;
gtk_progress_tracker_finish (&priv->tracker);
if (gtk_stack_set_transition_position (stack, t))
gtk_stack_progress_updated (GTK_STACK (widget));
if (gtk_progress_tracker_get_state (&priv->tracker) == GTK_PROGRESS_STATE_AFTER)
{
priv->tick_id = 0;
g_object_notify_by_pspec (G_OBJECT (stack), stack_props[PROP_TRANSITION_RUNNING]);
@@ -1037,17 +1020,20 @@ gtk_stack_start_transition (GtkStack *stack,
transition_duration != 0 &&
priv->last_visible_child != NULL)
{
priv->transition_pos = 0.0;
priv->start_time = gdk_frame_clock_get_frame_time (gtk_widget_get_frame_clock (widget));
priv->end_time = priv->start_time + (transition_duration * 1000);
priv->active_transition_type = effective_transition_type (stack, transition_type);
priv->first_frame_skipped = FALSE;
gtk_stack_schedule_ticks (stack);
gtk_progress_tracker_start (&priv->tracker,
priv->transition_duration * 1000,
0,
1.0);
}
else
{
gtk_stack_unschedule_ticks (stack);
priv->active_transition_type = GTK_STACK_TRANSITION_TYPE_NONE;
gtk_stack_set_transition_position (stack, 1.0);
gtk_progress_tracker_finish (&priv->tracker);
gtk_stack_progress_updated (GTK_STACK (widget));
}
}
@@ -1962,6 +1948,7 @@ gtk_stack_draw_crossfade (GtkWidget *widget,
{
GtkStack *stack = GTK_STACK (widget);
GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
gdouble progress = gtk_progress_tracker_get_progress (&priv->tracker, FALSE);
cairo_push_group (cr);
gtk_container_propagate_draw (GTK_CONTAINER (stack),
@@ -1969,8 +1956,8 @@ gtk_stack_draw_crossfade (GtkWidget *widget,
cr);
cairo_save (cr);
/* Multiply alpha by transition pos */
cairo_set_source_rgba (cr, 1, 1, 1, priv->transition_pos);
/* Multiply alpha by progress */
cairo_set_source_rgba (cr, 1, 1, 1, progress);
cairo_set_operator (cr, CAIRO_OPERATOR_DEST_IN);
cairo_paint (cr);
@@ -1980,7 +1967,7 @@ gtk_stack_draw_crossfade (GtkWidget *widget,
priv->last_visible_surface_allocation.x,
priv->last_visible_surface_allocation.y);
cairo_set_operator (cr, CAIRO_OPERATOR_ADD);
cairo_paint_with_alpha (cr, MAX (1.0 - priv->transition_pos, 0));
cairo_paint_with_alpha (cr, MAX (1.0 - progress, 0));
}
cairo_restore (cr);
@@ -2009,22 +1996,22 @@ gtk_stack_draw_under (GtkWidget *widget,
{
case GTK_STACK_TRANSITION_TYPE_UNDER_DOWN:
y = 0;
height = allocation.height * (ease_out_cubic (priv->transition_pos));
height = allocation.height * (gtk_progress_tracker_get_ease_out_cubic (&priv->tracker, FALSE));
pos_y = height;
break;
case GTK_STACK_TRANSITION_TYPE_UNDER_UP:
y = allocation.height * (1 - ease_out_cubic (priv->transition_pos));
y = allocation.height * (1 - gtk_progress_tracker_get_ease_out_cubic (&priv->tracker, FALSE));
height = allocation.height - y;
pos_y = y - allocation.height;
break;
case GTK_STACK_TRANSITION_TYPE_UNDER_LEFT:
x = allocation.width * (1 - ease_out_cubic (priv->transition_pos));
x = allocation.width * (1 - gtk_progress_tracker_get_ease_out_cubic (&priv->tracker, FALSE));
width = allocation.width - x;
pos_x = x - allocation.width;
break;
case GTK_STACK_TRANSITION_TYPE_UNDER_RIGHT:
x = 0;
width = allocation.width * (ease_out_cubic (priv->transition_pos));
width = allocation.width * (gtk_progress_tracker_get_ease_out_cubic (&priv->tracker, FALSE));
pos_x = width;
break;
default:
@@ -2155,7 +2142,7 @@ gtk_stack_render (GtkCssGadget *gadget,
if (priv->visible_child)
{
if (priv->transition_pos < 1.0)
if (gtk_progress_tracker_get_state (&priv->tracker) != GTK_PROGRESS_STATE_AFTER)
{
if (priv->last_visible_surface == NULL &&
priv->last_visible_child != NULL)
@@ -2428,13 +2415,13 @@ gtk_stack_measure (GtkCssGadget *gadget,
{
if (orientation == GTK_ORIENTATION_VERTICAL && !priv->vhomogeneous)
{
gdouble t = priv->interpolate_size ? ease_out_cubic (priv->transition_pos) : 1.0;
gdouble t = priv->interpolate_size ? gtk_progress_tracker_get_ease_out_cubic (&priv->tracker, FALSE) : 1.0;
*minimum = LERP (*minimum, priv->last_visible_widget_height, t);
*natural = LERP (*natural, priv->last_visible_widget_height, t);
}
if (orientation == GTK_ORIENTATION_HORIZONTAL && !priv->hhomogeneous)
{
gdouble t = priv->interpolate_size ? ease_out_cubic (priv->transition_pos) : 1.0;
gdouble t = priv->interpolate_size ? gtk_progress_tracker_get_ease_out_cubic (&priv->tracker, FALSE) : 1.0;
*minimum = LERP (*minimum, priv->last_visible_widget_width, t);
*natural = LERP (*natural, priv->last_visible_widget_width, t);
}

View File

@@ -23,23 +23,27 @@
G_DEFINE_ABSTRACT_TYPE (GtkStyleAnimation, _gtk_style_animation, G_TYPE_OBJECT)
static GtkStyleAnimation *
gtk_style_animation_real_advance (GtkStyleAnimation *animation,
gint64 timestamp)
{
return NULL;
}
static void
gtk_style_animation_real_set_values (GtkStyleAnimation *animation,
gint64 for_time_us,
GtkCssAnimatedStyle *style)
gtk_style_animation_real_apply_values (GtkStyleAnimation *animation,
GtkCssAnimatedStyle *style)
{
}
static gboolean
gtk_style_animation_real_is_finished (GtkStyleAnimation *animation,
gint64 at_time_us)
gtk_style_animation_real_is_finished (GtkStyleAnimation *animation)
{
return TRUE;
}
static gboolean
gtk_style_animation_real_is_static (GtkStyleAnimation *animation,
gint64 at_time_us)
gtk_style_animation_real_is_static (GtkStyleAnimation *animation)
{
return FALSE;
}
@@ -47,7 +51,8 @@ gtk_style_animation_real_is_static (GtkStyleAnimation *animation,
static void
_gtk_style_animation_class_init (GtkStyleAnimationClass *klass)
{
klass->set_values = gtk_style_animation_real_set_values;
klass->advance = gtk_style_animation_real_advance;
klass->apply_values = gtk_style_animation_real_apply_values;
klass->is_finished = gtk_style_animation_real_is_finished;
klass->is_static = gtk_style_animation_real_is_static;
}
@@ -57,10 +62,22 @@ _gtk_style_animation_init (GtkStyleAnimation *animation)
{
}
GtkStyleAnimation *
_gtk_style_animation_advance (GtkStyleAnimation *animation,
gint64 timestamp)
{
GtkStyleAnimationClass *klass;
g_return_val_if_fail (GTK_IS_STYLE_ANIMATION (animation), NULL);
klass = GTK_STYLE_ANIMATION_GET_CLASS (animation);
return klass->advance (animation, timestamp);
}
void
_gtk_style_animation_set_values (GtkStyleAnimation *animation,
gint64 for_time_us,
GtkCssAnimatedStyle *style)
_gtk_style_animation_apply_values (GtkStyleAnimation *animation,
GtkCssAnimatedStyle *style)
{
GtkStyleAnimationClass *klass;
@@ -69,12 +86,11 @@ _gtk_style_animation_set_values (GtkStyleAnimation *animation,
klass = GTK_STYLE_ANIMATION_GET_CLASS (animation);
klass->set_values (animation, for_time_us, style);
klass->apply_values (animation, style);
}
gboolean
_gtk_style_animation_is_finished (GtkStyleAnimation *animation,
gint64 at_time_us)
_gtk_style_animation_is_finished (GtkStyleAnimation *animation)
{
GtkStyleAnimationClass *klass;
@@ -82,7 +98,7 @@ _gtk_style_animation_is_finished (GtkStyleAnimation *animation,
klass = GTK_STYLE_ANIMATION_GET_CLASS (animation);
return klass->is_finished (animation, at_time_us);
return klass->is_finished (animation);
}
/**
@@ -97,8 +113,7 @@ _gtk_style_animation_is_finished (GtkStyleAnimation *animation,
* Returns: %TRUE if @animation will not change anymore after @at_time_us
**/
gboolean
_gtk_style_animation_is_static (GtkStyleAnimation *animation,
gint64 at_time_us)
_gtk_style_animation_is_static (GtkStyleAnimation *animation)
{
GtkStyleAnimationClass *klass;
@@ -106,5 +121,5 @@ _gtk_style_animation_is_static (GtkStyleAnimation *animation,
klass = GTK_STYLE_ANIMATION_GET_CLASS (animation);
return klass->is_static (animation, at_time_us);
return klass->is_static (animation);
}

View File

@@ -43,24 +43,22 @@ struct _GtkStyleAnimationClass
{
GObjectClass parent_class;
gboolean (* is_finished) (GtkStyleAnimation *animation,
gint64 at_time_us);
gboolean (* is_static) (GtkStyleAnimation *animation,
gint64 at_time_us);
void (* set_values) (GtkStyleAnimation *animation,
gint64 for_time_us,
gboolean (* is_finished) (GtkStyleAnimation *animation);
gboolean (* is_static) (GtkStyleAnimation *animation);
void (* apply_values) (GtkStyleAnimation *animation,
GtkCssAnimatedStyle *style);
GtkStyleAnimation * (* advance) (GtkStyleAnimation *animation,
gint64 timestamp);
};
GType _gtk_style_animation_get_type (void) G_GNUC_CONST;
void _gtk_style_animation_set_values (GtkStyleAnimation *animation,
gint64 for_time_us,
GtkStyleAnimation * _gtk_style_animation_advance (GtkStyleAnimation *animation,
gint64 timestamp);
void _gtk_style_animation_apply_values (GtkStyleAnimation *animation,
GtkCssAnimatedStyle *style);
gboolean _gtk_style_animation_is_finished (GtkStyleAnimation *animation,
gint64 at_time_us);
gboolean _gtk_style_animation_is_static (GtkStyleAnimation *animation,
gint64 at_time_us);
gboolean _gtk_style_animation_is_finished (GtkStyleAnimation *animation);
gboolean _gtk_style_animation_is_static (GtkStyleAnimation *animation);
G_END_DECLS

View File

@@ -67,6 +67,7 @@
#include "gtkwidgetprivate.h"
#include "gtkcssshadowsvalueprivate.h"
#include "gtkcssnumbervalueprivate.h"
#include "gtkprogresstrackerprivate.h"
#include "fallback-c89.c"
@@ -86,9 +87,8 @@ struct _GtkSwitchPrivate
GtkCssGadget *slider_gadget;
double handle_pos;
gint64 start_time;
gint64 end_time;
guint tick_id;
GtkProgressTracker tracker;
guint state : 1;
guint is_active : 1;
@@ -131,17 +131,6 @@ G_DEFINE_TYPE_WITH_CODE (GtkSwitch, gtk_switch, GTK_TYPE_WIDGET,
gtk_switch_activatable_interface_init));
G_GNUC_END_IGNORE_DEPRECATIONS;
/* From clutter-easing.c, based on Robert Penner's
* infamous easing equations, MIT license.
*/
static gdouble
ease_out_cubic (gdouble t)
{
gdouble p = t - 1;
return p * p * p + 1;
}
static void
gtk_switch_end_toggle_animation (GtkSwitch *sw)
{
@@ -161,20 +150,16 @@ gtk_switch_on_frame_clock_update (GtkWidget *widget,
{
GtkSwitch *sw = GTK_SWITCH (widget);
GtkSwitchPrivate *priv = sw->priv;
gint64 now;
now = gdk_frame_clock_get_frame_time (clock);
gtk_progress_tracker_advance_frame (&priv->tracker,
gdk_frame_clock_get_frame_time (clock));
if (now < priv->end_time)
if (gtk_progress_tracker_get_state (&priv->tracker) != GTK_PROGRESS_STATE_AFTER)
{
gdouble t;
t = (now - priv->start_time) / (gdouble) (priv->end_time - priv->start_time);
t = ease_out_cubic (t);
if (priv->is_active)
priv->handle_pos = 1.0 - t;
priv->handle_pos = 1.0 - gtk_progress_tracker_get_ease_out_cubic (&priv->tracker, FALSE);
else
priv->handle_pos = t;
priv->handle_pos = gtk_progress_tracker_get_ease_out_cubic (&priv->tracker, FALSE);
}
else
{
@@ -200,9 +185,7 @@ gtk_switch_begin_toggle_animation (GtkSwitch *sw)
if (animate)
{
GdkFrameClock *clock = gtk_widget_get_frame_clock (GTK_WIDGET (sw));
priv->start_time = gdk_frame_clock_get_frame_time (clock);
priv->end_time = priv->start_time + 1000 * ANIMATION_DURATION;
gtk_progress_tracker_start (&priv->tracker, 1000 * ANIMATION_DURATION, 0, 1.0);
if (priv->tick_id == 0)
priv->tick_id = gtk_widget_add_tick_callback (GTK_WIDGET (sw),
gtk_switch_on_frame_clock_update,

View File

@@ -31,12 +31,16 @@
#include "gtkwindow.h"
#include "gtkcssproviderprivate.h"
#include <math.h>
#ifdef GDK_WINDOWING_X11
#include "x11/gdkx.h"
#endif
#include "gdk/gdk-private.h"
#define EPSILON 1e-10
struct _GtkInspectorVisualPrivate
{
GtkWidget *visual_box;
@@ -51,6 +55,8 @@ struct _GtkInspectorVisualPrivate
GtkWidget *animation_switch;
GtkWidget *font_scale_scale;
GtkAdjustment *scale_adjustment;
GtkAdjustment *slowdown_adjustment;
GtkWidget *slowdown_entry;
GtkAdjustment *cursor_size_adjustment;
GtkAdjustment *font_scale_adjustment;
@@ -561,6 +567,62 @@ init_animation (GtkInspectorVisual *vis)
G_BINDING_BIDIRECTIONAL|G_BINDING_SYNC_CREATE);
}
static void
update_slowdown (GtkInspectorVisual *vis,
gdouble slowdown,
gboolean update_adjustment,
gboolean update_entry)
{
_gtk_set_slowdown (slowdown);
if (update_adjustment)
gtk_adjustment_set_value (vis->priv->slowdown_adjustment,
log2 (slowdown));
if (update_entry)
{
gchar *str = g_strdup_printf ("%0.*f", 2, slowdown);
gtk_entry_set_text (GTK_ENTRY (vis->priv->slowdown_entry), str);
g_free (str);
}
}
static void
slowdown_adjustment_changed (GtkAdjustment *adjustment,
GtkInspectorVisual *vis)
{
gdouble value = gtk_adjustment_get_value (adjustment);
gdouble previous = CLAMP (log2 (_gtk_get_slowdown ()),
gtk_adjustment_get_lower (adjustment),
gtk_adjustment_get_upper (adjustment));
if (fabs (value - previous) > EPSILON)
update_slowdown (vis, exp2 (value), FALSE, TRUE);
}
static void
slowdown_entry_activated (GtkEntry *entry,
GtkInspectorVisual *vis)
{
gdouble slowdown;
gchar *err = NULL;
slowdown = g_strtod (gtk_entry_get_text (entry), &err);
if (err != NULL)
update_slowdown (vis, slowdown, TRUE, FALSE);
}
static void
init_slowdown (GtkInspectorVisual *vis)
{
update_slowdown (vis, _gtk_get_slowdown (), TRUE, TRUE);
g_signal_connect (vis->priv->slowdown_adjustment, "value-changed",
G_CALLBACK (slowdown_adjustment_changed), vis);
g_signal_connect (vis->priv->slowdown_entry, "activate",
G_CALLBACK (slowdown_entry_activated), vis);
}
static void
update_touchscreen (GtkSwitch *sw)
{
@@ -743,6 +805,7 @@ gtk_inspector_visual_init (GtkInspectorVisual *vis)
init_rendering_mode (vis);
init_updates (vis);
init_animation (vis);
init_slowdown (vis);
init_touchscreen (vis);
init_gl (vis);
}
@@ -786,6 +849,8 @@ gtk_inspector_visual_class_init (GtkInspectorVisualClass *klass)
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorVisual, hidpi_spin);
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorVisual, scale_adjustment);
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorVisual, animation_switch);
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorVisual, slowdown_adjustment);
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorVisual, slowdown_entry);
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorVisual, touchscreen_switch);
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorVisual, visual_box);
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorVisual, debug_box);

View File

@@ -12,6 +12,12 @@
<property name="step-increment">0.01</property>
<property name="page-increment">0.01</property>
</object>
<object class="GtkAdjustment" id="slowdown_adjustment">
<property name="lower">-3</property>
<property name="upper">3</property>
<property name="step-increment">1</property>
<property name="page-increment">1</property>
</object>
<object class="GtkAdjustment" id="cursor_size_adjustment">
<property name="lower">24</property>
<property name="upper">64</property>
@@ -382,6 +388,50 @@
</child>
</object>
</child>
<child>
<object class="GtkListBoxRow">
<property name="visible">True</property>
<property name="activatable">False</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="orientation">horizontal</property>
<property name="margin">10</property>
<property name="spacing">20</property>
<child>
<object class="GtkLabel" id="slowdown_label">
<property name="visible">True</property>
<property name="label" translatable="yes">Slowdown</property>
<property name="halign">start</property>
<property name="valign">baseline</property>
<property name="xalign">0.0</property>
</object>
</child>
<child>
<object class="GtkScale" id="slowdown_scale">
<property name="visible">1</property>
<property name="can_focus">1</property>
<property name="adjustment">slowdown_adjustment</property>
<property name="valign">baseline</property>
<property name="draw_value">0</property>
</object>
<packing>
<property name="expand">True</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="slowdown_entry">
<property name="visible">True</property>
<property name="halign">end</property>
<property name="valign">baseline</property>
<property name="width-chars">4</property>
<property name="input-purpose">number</property>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
</object>