mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2024-11-17 06:10:15 +00:00
0f935a0483
A typo which was using minimum_width for natural_width and viceversa.
1006 lines
33 KiB
C
1006 lines
33 KiB
C
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/*
|
|
* Copyright 2013, 2015 Red Hat, Inc.
|
|
*
|
|
* This program 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 of the License, or (at your
|
|
* option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
|
* License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
* Author: Alexander Larsson <alexl@redhat.com>
|
|
* Carlos Soriano <csoriano@gnome.org>
|
|
*/
|
|
|
|
#include "config.h"
|
|
#include "gtkrevealer.h"
|
|
#include <gdk/gdk.h>
|
|
#include "gtktypebuiltins.h"
|
|
#include "gtkprivate.h"
|
|
#include "gtkintl.h"
|
|
|
|
#include "fallback-c89.c"
|
|
|
|
/**
|
|
* SECTION:gtkrevealer
|
|
* @Short_description: Hide and show with animation
|
|
* @Title: GtkRevealer
|
|
* @See_also: #GtkExpander
|
|
*
|
|
* The GtkRevealer widget is a container which animates
|
|
* the transition of its child from invisible to visible.
|
|
*
|
|
* The style of transition can be controlled with
|
|
* gtk_revealer_set_transition_type().
|
|
*
|
|
* These animations respect the #GtkSettings:gtk-enable-animations
|
|
* setting.
|
|
*
|
|
* The GtkRevealer widget was added in GTK+ 3.10.
|
|
*/
|
|
|
|
/**
|
|
* GtkRevealerTransitionType:
|
|
* @GTK_REVEALER_TRANSITION_TYPE_NONE: No transition
|
|
* @GTK_REVEALER_TRANSITION_TYPE_CROSSFADE: Fade in
|
|
* @GTK_REVEALER_TRANSITION_TYPE_SLIDE_RIGHT: Slide in from the left
|
|
* @GTK_REVEALER_TRANSITION_TYPE_SLIDE_LEFT: Slide in from the right
|
|
* @GTK_REVEALER_TRANSITION_TYPE_SLIDE_UP: Slide in from the bottom
|
|
* @GTK_REVEALER_TRANSITION_TYPE_SLIDE_DOWN: Slide in from the top
|
|
*
|
|
* These enumeration values describe the possible transitions
|
|
* when the child of a #GtkRevealer widget is shown or hidden.
|
|
*/
|
|
|
|
enum {
|
|
PROP_0,
|
|
PROP_TRANSITION_TYPE,
|
|
PROP_TRANSITION_DURATION,
|
|
PROP_REVEAL_CHILD,
|
|
PROP_CHILD_REVEALED,
|
|
LAST_PROP
|
|
};
|
|
|
|
typedef struct {
|
|
GtkRevealerTransitionType transition_type;
|
|
guint transition_duration;
|
|
|
|
GdkWindow* bin_window;
|
|
GdkWindow* view_window;
|
|
|
|
gdouble current_pos;
|
|
gdouble source_pos;
|
|
gdouble target_pos;
|
|
|
|
guint tick_id;
|
|
gint64 start_time;
|
|
gint64 end_time;
|
|
} GtkRevealerPrivate;
|
|
|
|
static GParamSpec *props[LAST_PROP] = { NULL, };
|
|
|
|
static void gtk_revealer_real_realize (GtkWidget *widget);
|
|
static void gtk_revealer_real_unrealize (GtkWidget *widget);
|
|
static void gtk_revealer_real_add (GtkContainer *widget,
|
|
GtkWidget *child);
|
|
static void gtk_revealer_real_size_allocate (GtkWidget *widget,
|
|
GtkAllocation *allocation);
|
|
static void gtk_revealer_real_map (GtkWidget *widget);
|
|
static void gtk_revealer_real_unmap (GtkWidget *widget);
|
|
static gboolean gtk_revealer_real_draw (GtkWidget *widget,
|
|
cairo_t *cr);
|
|
static void gtk_revealer_real_get_preferred_height (GtkWidget *widget,
|
|
gint *minimum_height,
|
|
gint *natural_height);
|
|
static void gtk_revealer_real_get_preferred_height_for_width (GtkWidget *widget,
|
|
gint width,
|
|
gint *minimum_height,
|
|
gint *natural_height);
|
|
static void gtk_revealer_real_get_preferred_width (GtkWidget *widget,
|
|
gint *minimum_width,
|
|
gint *natural_width);
|
|
static void gtk_revealer_real_get_preferred_width_for_height (GtkWidget *widget,
|
|
gint height,
|
|
gint *minimum_width,
|
|
gint *natural_width);
|
|
|
|
G_DEFINE_TYPE_WITH_PRIVATE (GtkRevealer, gtk_revealer, GTK_TYPE_BIN)
|
|
|
|
static void
|
|
gtk_revealer_get_padding (GtkRevealer *revealer,
|
|
GtkBorder *padding)
|
|
{
|
|
GtkWidget *widget = GTK_WIDGET (revealer);
|
|
GtkStyleContext *context;
|
|
GtkStateFlags state;
|
|
|
|
context = gtk_widget_get_style_context (widget);
|
|
state = gtk_style_context_get_state (context);
|
|
|
|
gtk_style_context_get_padding (context, state, padding);
|
|
}
|
|
|
|
static void
|
|
gtk_revealer_init (GtkRevealer *revealer)
|
|
{
|
|
GtkRevealerPrivate *priv = gtk_revealer_get_instance_private (revealer);
|
|
|
|
priv->transition_type = GTK_REVEALER_TRANSITION_TYPE_SLIDE_DOWN;
|
|
priv->transition_duration = 250;
|
|
priv->current_pos = 0.0;
|
|
priv->target_pos = 0.0;
|
|
|
|
gtk_widget_set_has_window ((GtkWidget*) revealer, TRUE);
|
|
gtk_widget_set_redraw_on_allocate ((GtkWidget*) revealer, FALSE);
|
|
}
|
|
|
|
static void
|
|
gtk_revealer_finalize (GObject *obj)
|
|
{
|
|
GtkRevealer *revealer = GTK_REVEALER (obj);
|
|
GtkRevealerPrivate *priv = gtk_revealer_get_instance_private (revealer);
|
|
|
|
if (priv->tick_id != 0)
|
|
gtk_widget_remove_tick_callback (GTK_WIDGET (revealer), priv->tick_id);
|
|
priv->tick_id = 0;
|
|
|
|
G_OBJECT_CLASS (gtk_revealer_parent_class)->finalize (obj);
|
|
}
|
|
|
|
static void
|
|
gtk_revealer_get_property (GObject *object,
|
|
guint property_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
GtkRevealer *revealer = GTK_REVEALER (object);
|
|
|
|
switch (property_id)
|
|
{
|
|
case PROP_TRANSITION_TYPE:
|
|
g_value_set_enum (value, gtk_revealer_get_transition_type (revealer));
|
|
break;
|
|
case PROP_TRANSITION_DURATION:
|
|
g_value_set_uint (value, gtk_revealer_get_transition_duration (revealer));
|
|
break;
|
|
case PROP_REVEAL_CHILD:
|
|
g_value_set_boolean (value, gtk_revealer_get_reveal_child (revealer));
|
|
break;
|
|
case PROP_CHILD_REVEALED:
|
|
g_value_set_boolean (value, gtk_revealer_get_child_revealed (revealer));
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gtk_revealer_set_property (GObject *object,
|
|
guint property_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
GtkRevealer *revealer = GTK_REVEALER (object);
|
|
|
|
switch (property_id)
|
|
{
|
|
case PROP_TRANSITION_TYPE:
|
|
gtk_revealer_set_transition_type (revealer, g_value_get_enum (value));
|
|
break;
|
|
case PROP_TRANSITION_DURATION:
|
|
gtk_revealer_set_transition_duration (revealer, g_value_get_uint (value));
|
|
break;
|
|
case PROP_REVEAL_CHILD:
|
|
gtk_revealer_set_reveal_child (revealer, g_value_get_boolean (value));
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gtk_revealer_class_init (GtkRevealerClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
|
|
GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
|
|
|
|
object_class->get_property = gtk_revealer_get_property;
|
|
object_class->set_property = gtk_revealer_set_property;
|
|
object_class->finalize = gtk_revealer_finalize;
|
|
|
|
widget_class->realize = gtk_revealer_real_realize;
|
|
widget_class->unrealize = gtk_revealer_real_unrealize;
|
|
widget_class->size_allocate = gtk_revealer_real_size_allocate;
|
|
widget_class->map = gtk_revealer_real_map;
|
|
widget_class->unmap = gtk_revealer_real_unmap;
|
|
widget_class->draw = gtk_revealer_real_draw;
|
|
widget_class->get_preferred_height = gtk_revealer_real_get_preferred_height;
|
|
widget_class->get_preferred_height_for_width = gtk_revealer_real_get_preferred_height_for_width;
|
|
widget_class->get_preferred_width = gtk_revealer_real_get_preferred_width;
|
|
widget_class->get_preferred_width_for_height = gtk_revealer_real_get_preferred_width_for_height;
|
|
|
|
container_class->add = gtk_revealer_real_add;
|
|
|
|
props[PROP_TRANSITION_TYPE] =
|
|
g_param_spec_enum ("transition-type",
|
|
P_("Transition type"),
|
|
P_("The type of animation used to transition"),
|
|
GTK_TYPE_REVEALER_TRANSITION_TYPE,
|
|
GTK_REVEALER_TRANSITION_TYPE_SLIDE_DOWN,
|
|
GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY);
|
|
|
|
props[PROP_TRANSITION_DURATION] =
|
|
g_param_spec_uint ("transition-duration",
|
|
P_("Transition duration"),
|
|
P_("The animation duration, in milliseconds"),
|
|
0, G_MAXUINT, 250,
|
|
GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY);
|
|
|
|
props[PROP_REVEAL_CHILD] =
|
|
g_param_spec_boolean ("reveal-child",
|
|
P_("Reveal Child"),
|
|
P_("Whether the container should reveal the child"),
|
|
FALSE,
|
|
GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY);
|
|
|
|
props[PROP_CHILD_REVEALED] =
|
|
g_param_spec_boolean ("child-revealed",
|
|
P_("Child Revealed"),
|
|
P_("Whether the child is revealed and the animation target reached"),
|
|
FALSE,
|
|
G_PARAM_READABLE);
|
|
|
|
g_object_class_install_properties (object_class, LAST_PROP, props);
|
|
}
|
|
|
|
/**
|
|
* gtk_revealer_new:
|
|
*
|
|
* Creates a new #GtkRevealer.
|
|
*
|
|
* Returns: a newly created #GtkRevealer
|
|
*
|
|
* Since: 3.10
|
|
*/
|
|
GtkWidget *
|
|
gtk_revealer_new (void)
|
|
{
|
|
return g_object_new (GTK_TYPE_REVEALER, NULL);
|
|
}
|
|
|
|
static GtkRevealerTransitionType
|
|
effective_transition (GtkRevealer *revealer)
|
|
{
|
|
GtkRevealerPrivate *priv = gtk_revealer_get_instance_private (revealer);
|
|
gboolean animations_enabled;
|
|
|
|
g_object_get (gtk_widget_get_settings (GTK_WIDGET (revealer)),
|
|
"gtk-enable-animations", &animations_enabled,
|
|
NULL);
|
|
if (!animations_enabled)
|
|
return GTK_REVEALER_TRANSITION_TYPE_NONE;
|
|
|
|
if (gtk_widget_get_direction (GTK_WIDGET (revealer)) == GTK_TEXT_DIR_RTL)
|
|
{
|
|
if (priv->transition_type == GTK_REVEALER_TRANSITION_TYPE_SLIDE_LEFT)
|
|
return GTK_REVEALER_TRANSITION_TYPE_SLIDE_RIGHT;
|
|
else if (priv->transition_type == GTK_REVEALER_TRANSITION_TYPE_SLIDE_RIGHT)
|
|
return GTK_REVEALER_TRANSITION_TYPE_SLIDE_LEFT;
|
|
}
|
|
|
|
return priv->transition_type;
|
|
}
|
|
|
|
static void
|
|
gtk_revealer_get_child_allocation (GtkRevealer *revealer,
|
|
GtkAllocation *allocation,
|
|
GtkAllocation *child_allocation)
|
|
{
|
|
GtkWidget *child;
|
|
GtkRevealerTransitionType transition;
|
|
GtkBorder padding;
|
|
gint vertical_padding, horizontal_padding;
|
|
|
|
g_return_if_fail (revealer != NULL);
|
|
g_return_if_fail (allocation != NULL);
|
|
|
|
/* See explanation on gtk_revealer_real_size_allocate */
|
|
gtk_revealer_get_padding (revealer, &padding);
|
|
vertical_padding = padding.top + padding.bottom;
|
|
horizontal_padding = padding.left + padding.right;
|
|
|
|
child_allocation->x = 0;
|
|
child_allocation->y = 0;
|
|
child_allocation->width = 0;
|
|
child_allocation->height = 0;
|
|
|
|
child = gtk_bin_get_child (GTK_BIN (revealer));
|
|
if (child != NULL && gtk_widget_get_visible (child))
|
|
{
|
|
transition = effective_transition (revealer);
|
|
if (transition == GTK_REVEALER_TRANSITION_TYPE_SLIDE_LEFT ||
|
|
transition == GTK_REVEALER_TRANSITION_TYPE_SLIDE_RIGHT)
|
|
gtk_widget_get_preferred_width_for_height (child, MAX (0, allocation->height - vertical_padding), NULL,
|
|
&child_allocation->width);
|
|
else
|
|
gtk_widget_get_preferred_height_for_width (child, MAX (0, allocation->width - horizontal_padding), NULL,
|
|
&child_allocation->height);
|
|
}
|
|
|
|
child_allocation->width = MAX (child_allocation->width, allocation->width - horizontal_padding);
|
|
child_allocation->height = MAX (child_allocation->height, allocation->height - vertical_padding);
|
|
}
|
|
|
|
static void
|
|
gtk_revealer_real_realize (GtkWidget *widget)
|
|
{
|
|
GtkRevealer *revealer = GTK_REVEALER (widget);
|
|
GtkRevealerPrivate *priv = gtk_revealer_get_instance_private (revealer);
|
|
GtkAllocation allocation;
|
|
GdkWindowAttr attributes = { 0 };
|
|
GdkWindowAttributesType attributes_mask;
|
|
GtkAllocation child_allocation;
|
|
GtkWidget *child;
|
|
GtkRevealerTransitionType transition;
|
|
GtkBorder padding;
|
|
|
|
gtk_widget_set_realized (widget, TRUE);
|
|
|
|
gtk_widget_get_allocation (widget, &allocation);
|
|
|
|
attributes.x = allocation.x;
|
|
attributes.y = allocation.y;
|
|
attributes.width = allocation.width;
|
|
attributes.height = allocation.height;
|
|
attributes.window_type = GDK_WINDOW_CHILD;
|
|
attributes.wclass = GDK_INPUT_OUTPUT;
|
|
attributes.visual = gtk_widget_get_visual (widget);
|
|
attributes.event_mask =
|
|
gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
|
|
attributes_mask = (GDK_WA_X | GDK_WA_Y) | GDK_WA_VISUAL;
|
|
|
|
priv->view_window =
|
|
gdk_window_new (gtk_widget_get_parent_window ((GtkWidget*) revealer),
|
|
&attributes, attributes_mask);
|
|
gtk_widget_set_window (widget, priv->view_window);
|
|
gtk_widget_register_window (widget, priv->view_window);
|
|
|
|
gtk_revealer_get_child_allocation (revealer, &allocation, &child_allocation);
|
|
|
|
gtk_revealer_get_padding (revealer, &padding);
|
|
attributes.x = 0;
|
|
attributes.y = 0;
|
|
attributes.width = child_allocation.width;
|
|
attributes.height = child_allocation.height;
|
|
|
|
/* See explanation on gtk_revealer_real_size_allocate */
|
|
transition = effective_transition (revealer);
|
|
if (transition == GTK_REVEALER_TRANSITION_TYPE_SLIDE_DOWN)
|
|
{
|
|
attributes.y = allocation.height - child_allocation.height - padding.bottom;
|
|
attributes.x = padding.left;
|
|
}
|
|
else if (transition == GTK_REVEALER_TRANSITION_TYPE_SLIDE_RIGHT)
|
|
{
|
|
attributes.y = padding.top;
|
|
attributes.x = allocation.width - child_allocation.width - padding.right;
|
|
}
|
|
else
|
|
{
|
|
attributes.y = padding.top;
|
|
attributes.x = padding.left;
|
|
}
|
|
|
|
priv->bin_window =
|
|
gdk_window_new (priv->view_window, &attributes, attributes_mask);
|
|
gtk_widget_register_window (widget, priv->bin_window);
|
|
|
|
child = gtk_bin_get_child (GTK_BIN (revealer));
|
|
if (child != NULL)
|
|
gtk_widget_set_parent_window (child, priv->bin_window);
|
|
|
|
gdk_window_show (priv->bin_window);
|
|
}
|
|
|
|
static void
|
|
gtk_revealer_real_unrealize (GtkWidget *widget)
|
|
{
|
|
GtkRevealer *revealer = GTK_REVEALER (widget);
|
|
GtkRevealerPrivate *priv = gtk_revealer_get_instance_private (revealer);
|
|
|
|
gtk_widget_unregister_window (widget, priv->bin_window);
|
|
gdk_window_destroy (priv->bin_window);
|
|
priv->view_window = NULL;
|
|
|
|
GTK_WIDGET_CLASS (gtk_revealer_parent_class)->unrealize (widget);
|
|
}
|
|
|
|
static void
|
|
gtk_revealer_real_add (GtkContainer *container,
|
|
GtkWidget *child)
|
|
{
|
|
GtkRevealer *revealer = GTK_REVEALER (container);
|
|
GtkRevealerPrivate *priv = gtk_revealer_get_instance_private (revealer);
|
|
|
|
g_return_if_fail (child != NULL);
|
|
|
|
gtk_widget_set_parent_window (child, priv->bin_window);
|
|
gtk_widget_set_child_visible (child, priv->current_pos != 0.0);
|
|
|
|
GTK_CONTAINER_CLASS (gtk_revealer_parent_class)->add (container, child);
|
|
}
|
|
|
|
static void
|
|
gtk_revealer_real_size_allocate (GtkWidget *widget,
|
|
GtkAllocation *allocation)
|
|
{
|
|
GtkRevealer *revealer = GTK_REVEALER (widget);
|
|
GtkRevealerPrivate *priv = gtk_revealer_get_instance_private (revealer);
|
|
GtkAllocation child_allocation;
|
|
GtkWidget *child;
|
|
gboolean window_visible;
|
|
int bin_x, bin_y;
|
|
GtkRevealerTransitionType transition;
|
|
GtkBorder padding;
|
|
|
|
g_return_if_fail (allocation != NULL);
|
|
|
|
gtk_widget_set_allocation (widget, allocation);
|
|
gtk_revealer_get_child_allocation (revealer, allocation, &child_allocation);
|
|
|
|
child = gtk_bin_get_child (GTK_BIN (revealer));
|
|
if (child != NULL && gtk_widget_get_visible (child))
|
|
gtk_widget_size_allocate (child, &child_allocation);
|
|
|
|
if (gtk_widget_get_realized (widget))
|
|
{
|
|
if (gtk_widget_get_mapped (widget))
|
|
{
|
|
window_visible = allocation->width > 0 && allocation->height > 0;
|
|
|
|
if (!window_visible && gdk_window_is_visible (priv->view_window))
|
|
gdk_window_hide (priv->view_window);
|
|
|
|
if (window_visible && !gdk_window_is_visible (priv->view_window))
|
|
gdk_window_show (priv->view_window);
|
|
}
|
|
|
|
/* The view window will follow the revealer allocation, which is modified
|
|
* along the animation */
|
|
gdk_window_move_resize (priv->view_window,
|
|
allocation->x, allocation->y,
|
|
allocation->width, allocation->height);
|
|
|
|
gtk_revealer_get_padding (revealer, &padding);
|
|
bin_x = 0;
|
|
bin_y = 0;
|
|
|
|
transition = effective_transition (revealer);
|
|
/* The child allocation is fixed (it is not modified by the animation),
|
|
* and it's origin is relative to the bin_window.
|
|
* The bin_window has the same allocation as the child, and then the bin_window
|
|
* deals with the relative positioning with respect to the revealer taking
|
|
* into account the paddings of the revealer.
|
|
*
|
|
* For most of transitions, the bin_window moves along with the revealer,
|
|
* as its allocation changes.
|
|
* However for GTK_REVEALER_TRANSITION_TYPE_SLIDE_DOWN
|
|
* we need to first move the bin_window upwards and then slide it down in
|
|
* the revealer.
|
|
* Otherwise the child would appear as static and the revealer will allocate
|
|
* following the animation, clipping the child.
|
|
* To calculate the correct y position for this case:
|
|
* allocation->height - child_allocation.height is the relative position
|
|
* towards the revealer taking into account the animation progress with
|
|
* both vertical paddings added, therefore we need to substract the part
|
|
* that we don't want to take into account for the y position, which
|
|
* in this case is the bottom padding.
|
|
*
|
|
* The same special treatment is needed for GTK_REVEALER_TRANSITION_TYPE_SLIDE_RIGHT.
|
|
*/
|
|
if (transition == GTK_REVEALER_TRANSITION_TYPE_SLIDE_DOWN)
|
|
{
|
|
bin_y = allocation->height - child_allocation.height - padding.bottom;
|
|
bin_x = padding.left;
|
|
}
|
|
else if (transition == GTK_REVEALER_TRANSITION_TYPE_SLIDE_RIGHT)
|
|
{
|
|
bin_y = padding.top;
|
|
bin_x = allocation->width - child_allocation.width - padding.right;
|
|
}
|
|
else
|
|
{
|
|
bin_x = padding.left;
|
|
bin_y = padding.top;
|
|
}
|
|
|
|
gdk_window_move_resize (priv->bin_window,
|
|
bin_x, bin_y,
|
|
child_allocation.width, child_allocation.height);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gtk_revealer_set_position (GtkRevealer *revealer,
|
|
gdouble pos)
|
|
{
|
|
GtkRevealerPrivate *priv = gtk_revealer_get_instance_private (revealer);
|
|
gboolean new_visible;
|
|
GtkWidget *child;
|
|
GtkRevealerTransitionType transition;
|
|
|
|
priv->current_pos = pos;
|
|
|
|
/* We check target_pos here too, because we want to ensure we set
|
|
* child_visible immediately when starting a reveal operation
|
|
* otherwise the child widgets will not be properly realized
|
|
* after the reveal returns.
|
|
*/
|
|
new_visible = priv->current_pos != 0.0 || priv->target_pos != 0.0;
|
|
|
|
child = gtk_bin_get_child (GTK_BIN (revealer));
|
|
if (child != NULL &&
|
|
new_visible != gtk_widget_get_child_visible (child))
|
|
gtk_widget_set_child_visible (child, new_visible);
|
|
|
|
transition = effective_transition (revealer);
|
|
if (transition == GTK_REVEALER_TRANSITION_TYPE_CROSSFADE)
|
|
{
|
|
gtk_widget_set_opacity (GTK_WIDGET (revealer), priv->current_pos);
|
|
gtk_widget_queue_draw (GTK_WIDGET (revealer));
|
|
}
|
|
else
|
|
{
|
|
gtk_widget_queue_resize (GTK_WIDGET (revealer));
|
|
}
|
|
|
|
if (priv->current_pos == priv->target_pos)
|
|
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,
|
|
gpointer user_data)
|
|
{
|
|
GtkRevealer *revealer = GTK_REVEALER (widget);
|
|
GtkRevealerPrivate *priv = gtk_revealer_get_instance_private (revealer);
|
|
gint64 now;
|
|
|
|
now = gdk_frame_clock_get_frame_time (frame_clock);
|
|
gtk_revealer_animate_step (revealer, now);
|
|
if (priv->current_pos == priv->target_pos)
|
|
{
|
|
priv->tick_id = 0;
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
gtk_revealer_start_animation (GtkRevealer *revealer,
|
|
gdouble target)
|
|
{
|
|
GtkRevealerPrivate *priv = gtk_revealer_get_instance_private (revealer);
|
|
GtkWidget *widget = GTK_WIDGET (revealer);
|
|
GtkRevealerTransitionType transition;
|
|
|
|
if (priv->target_pos == target)
|
|
return;
|
|
|
|
priv->target_pos = target;
|
|
g_object_notify_by_pspec (G_OBJECT (revealer), props[PROP_REVEAL_CHILD]);
|
|
|
|
transition = effective_transition (revealer);
|
|
if (gtk_widget_get_mapped (widget) &&
|
|
priv->transition_duration != 0 &&
|
|
transition != GTK_REVEALER_TRANSITION_TYPE_NONE)
|
|
{
|
|
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);
|
|
}
|
|
else
|
|
{
|
|
gtk_revealer_set_position (revealer, target);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gtk_revealer_stop_animation (GtkRevealer *revealer)
|
|
{
|
|
GtkRevealerPrivate *priv = gtk_revealer_get_instance_private (revealer);
|
|
|
|
priv->current_pos = priv->target_pos;
|
|
if (priv->tick_id != 0)
|
|
{
|
|
gtk_widget_remove_tick_callback (GTK_WIDGET (revealer), priv->tick_id);
|
|
priv->tick_id = 0;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gtk_revealer_real_map (GtkWidget *widget)
|
|
{
|
|
GtkRevealer *revealer = GTK_REVEALER (widget);
|
|
GtkRevealerPrivate *priv = gtk_revealer_get_instance_private (revealer);
|
|
GtkAllocation allocation;
|
|
|
|
if (!gtk_widget_get_mapped (widget))
|
|
{
|
|
gtk_widget_get_allocation (widget, &allocation);
|
|
|
|
if (allocation.width > 0 && allocation.height > 0)
|
|
gdk_window_show (priv->view_window);
|
|
|
|
gtk_revealer_start_animation (revealer, priv->target_pos);
|
|
}
|
|
|
|
GTK_WIDGET_CLASS (gtk_revealer_parent_class)->map (widget);
|
|
}
|
|
|
|
static void
|
|
gtk_revealer_real_unmap (GtkWidget *widget)
|
|
{
|
|
GtkRevealer *revealer = GTK_REVEALER (widget);
|
|
|
|
GTK_WIDGET_CLASS (gtk_revealer_parent_class)->unmap (widget);
|
|
|
|
gtk_revealer_stop_animation (revealer);
|
|
}
|
|
|
|
static gboolean
|
|
gtk_revealer_real_draw (GtkWidget *widget,
|
|
cairo_t *cr)
|
|
{
|
|
GtkRevealer *revealer = GTK_REVEALER (widget);
|
|
GtkRevealerPrivate *priv = gtk_revealer_get_instance_private (revealer);
|
|
|
|
if (gtk_cairo_should_draw_window (cr, priv->bin_window))
|
|
GTK_WIDGET_CLASS (gtk_revealer_parent_class)->draw (widget, cr);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* gtk_revealer_set_reveal_child:
|
|
* @revealer: a #GtkRevealer
|
|
* @reveal_child: %TRUE to reveal the child
|
|
*
|
|
* Tells the #GtkRevealer to reveal or conceal its child.
|
|
*
|
|
* The transition will be animated with the current
|
|
* transition type of @revealer.
|
|
*
|
|
* Since: 3.10
|
|
*/
|
|
void
|
|
gtk_revealer_set_reveal_child (GtkRevealer *revealer,
|
|
gboolean reveal_child)
|
|
{
|
|
g_return_if_fail (GTK_IS_REVEALER (revealer));
|
|
|
|
if (reveal_child)
|
|
gtk_revealer_start_animation (revealer, 1.0);
|
|
else
|
|
gtk_revealer_start_animation (revealer, 0.0);
|
|
}
|
|
|
|
/**
|
|
* gtk_revealer_get_reveal_child:
|
|
* @revealer: a #GtkRevealer
|
|
*
|
|
* Returns whether the child is currently
|
|
* revealed. See gtk_revealer_set_reveal_child().
|
|
*
|
|
* This function returns %TRUE as soon as the transition
|
|
* is to the revealed state is started. To learn whether
|
|
* the child is fully revealed (ie the transition is completed),
|
|
* use gtk_revealer_get_child_revealed().
|
|
*
|
|
* Returns: %TRUE if the child is revealed.
|
|
*
|
|
* Since: 3.10
|
|
*/
|
|
gboolean
|
|
gtk_revealer_get_reveal_child (GtkRevealer *revealer)
|
|
{
|
|
GtkRevealerPrivate *priv = gtk_revealer_get_instance_private (revealer);
|
|
|
|
g_return_val_if_fail (GTK_IS_REVEALER (revealer), FALSE);
|
|
|
|
return priv->target_pos != 0.0;
|
|
}
|
|
|
|
/**
|
|
* gtk_revealer_get_child_revealed:
|
|
* @revealer: a #GtkRevealer
|
|
*
|
|
* Returns whether the child is fully revealed, ie wether
|
|
* the transition to the revealed state is completed.
|
|
*
|
|
* Returns: %TRUE if the child is fully revealed
|
|
*
|
|
* Since: 3.10
|
|
*/
|
|
gboolean
|
|
gtk_revealer_get_child_revealed (GtkRevealer *revealer)
|
|
{
|
|
GtkRevealerPrivate *priv = gtk_revealer_get_instance_private (revealer);
|
|
gboolean animation_finished = (priv->target_pos == priv->current_pos);
|
|
gboolean reveal_child = gtk_revealer_get_reveal_child (revealer);
|
|
|
|
if (animation_finished)
|
|
return reveal_child;
|
|
else
|
|
return !reveal_child;
|
|
}
|
|
|
|
/* These all report only the natural size, ignoring the minimal size,
|
|
* because its not really possible to allocate the right size during
|
|
* animation if the child size can change (without the child
|
|
* re-arranging itself during the animation).
|
|
*/
|
|
|
|
static void
|
|
set_height_with_paddings (GtkRevealer *revealer,
|
|
gint preferred_minimum_height,
|
|
gint preferred_natural_height,
|
|
gint *minimum_height_out,
|
|
gint *natural_height_out)
|
|
{
|
|
GtkRevealerPrivate *priv = gtk_revealer_get_instance_private (revealer);
|
|
gint minimum_height;
|
|
gint natural_height;
|
|
GtkRevealerTransitionType transition;
|
|
GtkBorder padding;
|
|
gint vertical_padding;
|
|
|
|
gtk_revealer_get_padding (revealer, &padding);
|
|
vertical_padding = padding.top + padding.bottom;
|
|
minimum_height = preferred_minimum_height + vertical_padding;
|
|
natural_height = preferred_natural_height + vertical_padding;
|
|
|
|
transition = effective_transition (revealer);
|
|
if (transition == GTK_REVEALER_TRANSITION_TYPE_NONE ||
|
|
transition == GTK_REVEALER_TRANSITION_TYPE_SLIDE_UP ||
|
|
transition == GTK_REVEALER_TRANSITION_TYPE_SLIDE_DOWN)
|
|
{
|
|
/* Padding are included in the animation */
|
|
minimum_height = round (minimum_height * priv->current_pos);
|
|
natural_height = round (natural_height * priv->current_pos);
|
|
}
|
|
|
|
*minimum_height_out = MIN (minimum_height, natural_height);
|
|
*natural_height_out = natural_height;
|
|
}
|
|
|
|
static void
|
|
gtk_revealer_real_get_preferred_height (GtkWidget *widget,
|
|
gint *minimum_height_out,
|
|
gint *natural_height_out)
|
|
{
|
|
GtkRevealer *revealer = GTK_REVEALER (widget);
|
|
gint minimum_height;
|
|
gint natural_height;
|
|
|
|
GTK_WIDGET_CLASS (gtk_revealer_parent_class)->get_preferred_height (widget, &minimum_height, &natural_height);
|
|
|
|
set_height_with_paddings (revealer, minimum_height, natural_height,
|
|
minimum_height_out, natural_height_out);
|
|
}
|
|
|
|
static void
|
|
gtk_revealer_real_get_preferred_height_for_width (GtkWidget *widget,
|
|
gint width,
|
|
gint *minimum_height_out,
|
|
gint *natural_height_out)
|
|
{
|
|
GtkRevealer *revealer = GTK_REVEALER (widget);
|
|
gint minimum_height;
|
|
gint natural_height;
|
|
|
|
GTK_WIDGET_CLASS (gtk_revealer_parent_class)->get_preferred_height_for_width (widget, width, &minimum_height, &natural_height);
|
|
|
|
set_height_with_paddings (revealer, minimum_height, natural_height,
|
|
minimum_height_out, natural_height_out);
|
|
}
|
|
|
|
static void
|
|
set_width_with_paddings (GtkRevealer *revealer,
|
|
gint preferred_minimum_width,
|
|
gint preferred_natural_width,
|
|
gint *minimum_width_out,
|
|
gint *natural_width_out)
|
|
{
|
|
GtkRevealerPrivate *priv = gtk_revealer_get_instance_private (revealer);
|
|
gint minimum_width;
|
|
gint natural_width;
|
|
GtkRevealerTransitionType transition;
|
|
GtkBorder padding;
|
|
gint horizontal_padding;
|
|
|
|
gtk_revealer_get_padding (revealer, &padding);
|
|
horizontal_padding = padding.left + padding.right;
|
|
minimum_width = preferred_minimum_width + horizontal_padding;
|
|
natural_width = preferred_natural_width + horizontal_padding;
|
|
|
|
transition = effective_transition (revealer);
|
|
if (transition == GTK_REVEALER_TRANSITION_TYPE_SLIDE_LEFT ||
|
|
transition == GTK_REVEALER_TRANSITION_TYPE_SLIDE_RIGHT)
|
|
{
|
|
/* Paddings are included in the animation */
|
|
minimum_width = round (minimum_width * priv->current_pos);
|
|
natural_width = round (natural_width * priv->current_pos);
|
|
}
|
|
|
|
*minimum_width_out = MIN (minimum_width, natural_width);
|
|
*natural_width_out = natural_width;
|
|
}
|
|
|
|
static void
|
|
gtk_revealer_real_get_preferred_width (GtkWidget *widget,
|
|
gint *minimum_width_out,
|
|
gint *natural_width_out)
|
|
{
|
|
GtkRevealer *revealer = GTK_REVEALER (widget);
|
|
gint minimum_width;
|
|
gint natural_width;
|
|
|
|
GTK_WIDGET_CLASS (gtk_revealer_parent_class)->get_preferred_width (widget, &minimum_width, &natural_width);
|
|
set_width_with_paddings (revealer, minimum_width, natural_width,
|
|
minimum_width_out, natural_width_out);
|
|
}
|
|
|
|
static void
|
|
gtk_revealer_real_get_preferred_width_for_height (GtkWidget *widget,
|
|
gint height,
|
|
gint *minimum_width_out,
|
|
gint *natural_width_out)
|
|
{
|
|
GtkRevealer *revealer = GTK_REVEALER (widget);
|
|
gint minimum_width;
|
|
gint natural_width;
|
|
|
|
GTK_WIDGET_CLASS (gtk_revealer_parent_class)->get_preferred_width_for_height (widget, height, &minimum_width, &natural_width);
|
|
|
|
set_width_with_paddings (revealer, minimum_width, natural_width,
|
|
minimum_width_out, natural_width_out);
|
|
}
|
|
|
|
/**
|
|
* gtk_revealer_get_transition_duration:
|
|
* @revealer: a #GtkRevealer
|
|
*
|
|
* Returns the amount of time (in milliseconds) that
|
|
* transitions will take.
|
|
*
|
|
* Returns: the transition duration
|
|
*
|
|
* Since: 3.10
|
|
*/
|
|
guint
|
|
gtk_revealer_get_transition_duration (GtkRevealer *revealer)
|
|
{
|
|
GtkRevealerPrivate *priv = gtk_revealer_get_instance_private (revealer);
|
|
|
|
g_return_val_if_fail (GTK_IS_REVEALER (revealer), 0);
|
|
|
|
return priv->transition_duration;
|
|
}
|
|
|
|
/**
|
|
* gtk_revealer_set_transition_duration:
|
|
* @revealer: a #GtkRevealer
|
|
* @duration: the new duration, in milliseconds
|
|
*
|
|
* Sets the duration that transitions will take.
|
|
*
|
|
* Since: 3.10
|
|
*/
|
|
void
|
|
gtk_revealer_set_transition_duration (GtkRevealer *revealer,
|
|
guint value)
|
|
{
|
|
GtkRevealerPrivate *priv = gtk_revealer_get_instance_private (revealer);
|
|
|
|
g_return_if_fail (GTK_IS_REVEALER (revealer));
|
|
|
|
if (priv->transition_duration == value)
|
|
return;
|
|
|
|
priv->transition_duration = value;
|
|
g_object_notify_by_pspec (G_OBJECT (revealer), props[PROP_TRANSITION_DURATION]);
|
|
}
|
|
|
|
/**
|
|
* gtk_revealer_get_transition_type:
|
|
* @revealer: a #GtkRevealer
|
|
*
|
|
* Gets the type of animation that will be used
|
|
* for transitions in @revealer.
|
|
*
|
|
* Returns: the current transition type of @revealer
|
|
*
|
|
* Since: 3.10
|
|
*/
|
|
GtkRevealerTransitionType
|
|
gtk_revealer_get_transition_type (GtkRevealer *revealer)
|
|
{
|
|
GtkRevealerPrivate *priv = gtk_revealer_get_instance_private (revealer);
|
|
|
|
g_return_val_if_fail (GTK_IS_REVEALER (revealer), GTK_REVEALER_TRANSITION_TYPE_NONE);
|
|
|
|
return priv->transition_type;
|
|
}
|
|
|
|
/**
|
|
* gtk_revealer_set_transition_type:
|
|
* @revealer: a #GtkRevealer
|
|
* @transition: the new transition type
|
|
*
|
|
* Sets the type of animation that will be used for
|
|
* transitions in @revealer. Available types include
|
|
* various kinds of fades and slides.
|
|
*
|
|
* Since: 3.10
|
|
*/
|
|
void
|
|
gtk_revealer_set_transition_type (GtkRevealer *revealer,
|
|
GtkRevealerTransitionType transition)
|
|
{
|
|
GtkRevealerPrivate *priv = gtk_revealer_get_instance_private (revealer);
|
|
|
|
g_return_if_fail (GTK_IS_REVEALER (revealer));
|
|
|
|
if (priv->transition_type == transition)
|
|
return;
|
|
|
|
priv->transition_type = transition;
|
|
gtk_widget_queue_resize (GTK_WIDGET (revealer));
|
|
g_object_notify_by_pspec (G_OBJECT (revealer), props[PROP_TRANSITION_TYPE]);
|
|
}
|