summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthias Clasen <mclasen@redhat.com>2013-04-22 09:31:32 -0400
committerMatthias Clasen <mclasen@redhat.com>2013-04-22 09:31:32 -0400
commit9412c695e8a804d7766111828c718d8ebbf8ad80 (patch)
tree064194c838bfd5794bae1f17ca441ecb3abd604b
parent6186429f5f6b2792f286f231d11e37216820c15f (diff)
downloadgtk+-9412c695e8a804d7766111828c718d8ebbf8ad80.tar.gz
Add GtkRevealer
This is a widget that can hide or show (ie reveal) its child in an animated fashion. This widget was initially developed in libgd.
-rw-r--r--gtk/Makefile.am2
-rw-r--r--gtk/gtk.h1
-rw-r--r--gtk/gtkrevealer.c751
-rw-r--r--gtk/gtkrevealer.h74
-rw-r--r--tests/testrevealer.c115
5 files changed, 943 insertions, 0 deletions
diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index 3718d477cc..dc1fd41b80 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -314,6 +314,7 @@ gtk_public_h_sources = \
gtkrecentchooserwidget.h \
gtkrecentfilter.h \
gtkrecentmanager.h \
+ gtkrevealer.h \
gtkscale.h \
gtkscalebutton.h \
gtkscrollable.h \
@@ -815,6 +816,7 @@ gtk_base_c_sources = \
gtkrecentfilter.c \
gtkrecentmanager.c \
gtkresources.c \
+ gtkrevealer.c \
gtkroundedbox.c \
gtkscale.c \
gtkscalebutton.c \
diff --git a/gtk/gtk.h b/gtk/gtk.h
index fd8b7d32f8..e8a7af94cb 100644
--- a/gtk/gtk.h
+++ b/gtk/gtk.h
@@ -163,6 +163,7 @@
#include <gtk/gtkrecentchooserwidget.h>
#include <gtk/gtkrecentfilter.h>
#include <gtk/gtkrecentmanager.h>
+#include <gtk/gtkrevealer.h>
#include <gtk/gtkscale.h>
#include <gtk/gtkscalebutton.h>
#include <gtk/gtkscrollable.h>
diff --git a/gtk/gtkrevealer.c b/gtk/gtkrevealer.c
new file mode 100644
index 0000000000..133793e251
--- /dev/null
+++ b/gtk/gtkrevealer.c
@@ -0,0 +1,751 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright (c) 2013 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>
+ *
+ */
+
+#include "config.h"
+#include "gtkrevealer.h"
+#include <gdk/gdk.h>
+#include "gtktypebuiltins.h"
+#include "gtkprivate.h"
+#include "gtkintl.h"
+#include <math.h>
+
+enum {
+ PROP_0,
+ PROP_TRANSITION_TYPE,
+ PROP_TRANSITION_DURATION,
+ PROP_REVEAL_CHILD,
+ PROP_CHILD_REVEALED
+};
+
+struct _GtkRevealerPrivate {
+ 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;
+};
+
+
+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_style_updated (GtkWidget *widget);
+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 (GtkRevealer, gtk_revealer, GTK_TYPE_BIN);
+
+
+static void
+gtk_revealer_init (GtkRevealer *revealer)
+{
+ GtkRevealerPrivate *priv;
+
+ priv = G_TYPE_INSTANCE_GET_PRIVATE (revealer, GTK_TYPE_REVEALER, GtkRevealerPrivate);
+ revealer->priv = priv;
+
+ 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 = revealer->priv;
+
+ 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->style_updated = gtk_revealer_real_style_updated;
+ 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;
+
+ g_object_class_install_property (object_class,
+ 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_object_class_install_property (object_class,
+ 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_object_class_install_property (object_class,
+ 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_object_class_install_property (object_class,
+ 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_type_class_add_private (klass, sizeof (GtkRevealerPrivate));
+}
+
+GtkWidget *
+gtk_revealer_new (void)
+{
+ return g_object_new (GTK_TYPE_REVEALER, NULL);
+}
+
+static void
+gtk_revealer_get_child_allocation (GtkRevealer *revealer,
+ GtkAllocation *allocation,
+ GtkAllocation *child_allocation)
+{
+ GtkWidget *child;
+ GtkRevealerPrivate *priv;
+
+ g_return_if_fail (revealer != NULL);
+ g_return_if_fail (allocation != NULL);
+
+ priv = revealer->priv;
+
+ child_allocation->x = 0;
+ child_allocation->y = 0;
+ child_allocation->width = allocation->width;
+ child_allocation->height = allocation->height;
+
+ child = gtk_bin_get_child (GTK_BIN (revealer));
+ if (child != NULL && gtk_widget_get_visible (child))
+ {
+ if (priv->transition_type == GTK_REVEALER_TRANSITION_TYPE_SLIDE_LEFT ||
+ priv->transition_type == GTK_REVEALER_TRANSITION_TYPE_SLIDE_RIGHT)
+ gtk_widget_get_preferred_width_for_height (child, child_allocation->height, NULL,
+ &child_allocation->width);
+ else
+ gtk_widget_get_preferred_height_for_width (child, child_allocation->width, NULL,
+ &child_allocation->height);
+ }
+}
+
+static void
+gtk_revealer_real_realize (GtkWidget *widget)
+{
+ GtkRevealer *revealer = GTK_REVEALER (widget);
+ GtkRevealerPrivate *priv = revealer->priv;
+ GtkAllocation allocation;
+ GdkWindowAttr attributes = { 0 };
+ GdkWindowAttributesType attributes_mask;
+ GtkAllocation child_allocation;
+ GtkWidget *child;
+ GtkStyleContext *context;
+
+ 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);
+
+ attributes.x = 0;
+ attributes.y = 0;
+ attributes.width = child_allocation.width;
+ attributes.height = child_allocation.height;
+
+ if (priv->transition_type == GTK_REVEALER_TRANSITION_TYPE_SLIDE_DOWN)
+ attributes.y = allocation.height - child_allocation.height;
+ else if (priv->transition_type == GTK_REVEALER_TRANSITION_TYPE_SLIDE_RIGHT)
+ attributes.x = allocation.width - child_allocation.width;
+
+ 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);
+
+ context = gtk_widget_get_style_context (widget);
+ gtk_style_context_set_background (context, priv->view_window);
+ gtk_style_context_set_background (context, priv->bin_window);
+ gdk_window_show (priv->bin_window);
+}
+
+static void
+gtk_revealer_real_unrealize (GtkWidget *widget)
+{
+ GtkRevealer *revealer = GTK_REVEALER (widget);
+ GtkRevealerPrivate *priv = revealer->priv;
+
+ 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 = revealer->priv;
+
+ 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_style_updated (GtkWidget *widget)
+{
+ GtkRevealer *revealer = GTK_REVEALER (widget);
+ GtkRevealerPrivate *priv = revealer->priv;
+ GtkStyleContext* context;
+
+ GTK_WIDGET_CLASS (gtk_revealer_parent_class)->style_updated (widget);
+
+ if (gtk_widget_get_realized (widget))
+ {
+ context = gtk_widget_get_style_context (widget);
+ gtk_style_context_set_background (context, priv->bin_window);
+ gtk_style_context_set_background (context, priv->view_window);
+ }
+}
+
+static void
+gtk_revealer_real_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation)
+{
+ GtkRevealer *revealer = GTK_REVEALER (widget);
+ GtkRevealerPrivate *priv = revealer->priv;
+ GtkAllocation child_allocation;
+ GtkWidget *child;
+ gboolean window_visible;
+ int bin_x, bin_y;
+
+ 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);
+ }
+
+ gdk_window_move_resize (priv->view_window,
+ allocation->x, allocation->y,
+ allocation->width, allocation->height);
+
+ bin_x = 0;
+ bin_y = 0;
+ if (priv->transition_type == GTK_REVEALER_TRANSITION_TYPE_SLIDE_DOWN)
+ bin_y = allocation->height - child_allocation.height;
+ else if (priv->transition_type == GTK_REVEALER_TRANSITION_TYPE_SLIDE_RIGHT)
+ bin_x = allocation->width - child_allocation.width;
+
+ 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 = revealer->priv;
+ gboolean new_visible;
+ GtkWidget *child;
+
+ 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);
+
+ gtk_widget_queue_resize (GTK_WIDGET (revealer));
+
+ if (priv->current_pos == priv->target_pos)
+ g_object_notify (G_OBJECT (revealer), "child-revealed");
+}
+
+static gdouble
+ease_out_quad (gdouble t, gdouble d)
+{
+ gdouble p = t / d;
+ return ((-1.0) * p) * (p - 2);
+}
+
+static void
+gtk_revealer_animate_step (GtkRevealer *revealer,
+ gint64 now)
+{
+ GtkRevealerPrivate *priv = revealer->priv;
+ gdouble t;
+
+ t = 1.0;
+ if (now < priv->end_time)
+ t = (now - priv->start_time) / (gdouble) (priv->end_time - priv->start_time);
+ t = ease_out_quad (t, 1.0);
+
+ gtk_revealer_set_position (revealer,
+ priv->source_pos + (t * (priv->target_pos - priv->source_pos)));
+}
+
+static gboolean
+gtk_revealer_animate_cb (GtkRevealer *revealer,
+ GdkFrameClock *frame_clock,
+ gpointer user_data)
+{
+ GtkRevealerPrivate *priv = revealer->priv;
+ gint64 now;
+
+ now = gdk_frame_clock_get_frame_time (frame_clock);
+ gtk_revealer_animate_step (revealer, now);
+ if (priv->current_pos == priv->target_pos)
+ {
+ gtk_widget_set_opacity (GTK_WIDGET (revealer), 1.0);
+ priv->tick_id = 0;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+gtk_revealer_start_animation (GtkRevealer *revealer,
+ gdouble target)
+{
+ GtkRevealerPrivate *priv = revealer->priv;
+ GtkWidget *widget = GTK_WIDGET (revealer);
+
+ if (priv->target_pos == target)
+ return;
+
+ priv->target_pos = target;
+ g_object_notify (G_OBJECT (revealer), "reveal-child");
+
+ if (gtk_widget_get_mapped (widget) &&
+ priv->transition_duration != 0 &&
+ priv->transition_type != GTK_REVEALER_TRANSITION_TYPE_NONE)
+ {
+ gtk_widget_set_opacity (widget, 0.999);
+
+ 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, (GtkTickCallback)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 = revealer->priv;
+
+ 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 = revealer->priv;
+ 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 = revealer->priv;
+
+ if (gtk_cairo_should_draw_window (cr, priv->bin_window))
+ GTK_WIDGET_CLASS (gtk_revealer_parent_class)->draw (widget, cr);
+
+ return TRUE;
+}
+
+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);
+}
+
+gboolean
+gtk_revealer_get_reveal_child (GtkRevealer *revealer)
+{
+ g_return_val_if_fail (GTK_IS_REVEALER (revealer), FALSE);
+
+ return revealer->priv->target_pos != 0.0;
+}
+
+gboolean
+gtk_revealer_get_child_revealed (GtkRevealer *revealer)
+{
+ gboolean animation_finished = (revealer->priv->target_pos == revealer->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
+gtk_revealer_real_get_preferred_height (GtkWidget *widget,
+ gint *minimum_height_out,
+ gint *natural_height_out)
+{
+ GtkRevealer *revealer = GTK_REVEALER (widget);
+ GtkRevealerPrivate *priv = revealer->priv;
+ gint minimum_height;
+ gint natural_height;
+
+ GTK_WIDGET_CLASS (gtk_revealer_parent_class)->get_preferred_height (widget, &minimum_height, &natural_height);
+
+ if (priv->transition_type == GTK_REVEALER_TRANSITION_TYPE_SLIDE_UP ||
+ priv->transition_type == GTK_REVEALER_TRANSITION_TYPE_SLIDE_DOWN)
+ natural_height = round (natural_height * priv->current_pos);
+
+ minimum_height = natural_height;
+
+ if (minimum_height_out)
+ *minimum_height_out = minimum_height;
+ if (natural_height_out)
+ *natural_height_out = natural_height;
+}
+
+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);
+ GtkRevealerPrivate *priv = revealer->priv;
+ gint minimum_height;
+ gint natural_height;
+
+ GTK_WIDGET_CLASS (gtk_revealer_parent_class)->get_preferred_height_for_width (widget, width, &minimum_height, &natural_height);
+
+ if (priv->transition_type == GTK_REVEALER_TRANSITION_TYPE_SLIDE_UP ||
+ priv->transition_type == GTK_REVEALER_TRANSITION_TYPE_SLIDE_DOWN)
+ natural_height = round (natural_height * priv->current_pos);
+
+ minimum_height = natural_height;
+
+ if (minimum_height_out)
+ *minimum_height_out = minimum_height;
+ if (natural_height_out)
+ *natural_height_out = natural_height;
+}
+
+static void
+gtk_revealer_real_get_preferred_width (GtkWidget *widget,
+ gint *minimum_width_out,
+ gint *natural_width_out)
+{
+ GtkRevealer *revealer = GTK_REVEALER (widget);
+ GtkRevealerPrivate *priv = revealer->priv;
+ gint minimum_width;
+ gint natural_width;
+
+ GTK_WIDGET_CLASS (gtk_revealer_parent_class)->get_preferred_width (widget, &minimum_width, &natural_width);
+
+ if (priv->transition_type == GTK_REVEALER_TRANSITION_TYPE_SLIDE_LEFT ||
+ priv->transition_type == GTK_REVEALER_TRANSITION_TYPE_SLIDE_RIGHT)
+ natural_width = round (natural_width * priv->current_pos);
+
+ minimum_width = natural_width;
+
+ if (minimum_width_out)
+ *minimum_width_out = minimum_width;
+ if (natural_width_out)
+ *natural_width_out = natural_width;
+}
+
+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);
+ GtkRevealerPrivate *priv = revealer->priv;
+ gint minimum_width;
+ gint natural_width;
+
+ GTK_WIDGET_CLASS (gtk_revealer_parent_class)->get_preferred_width_for_height (widget, height, &minimum_width, &natural_width);
+
+ if (priv->transition_type == GTK_REVEALER_TRANSITION_TYPE_SLIDE_LEFT ||
+ priv->transition_type == GTK_REVEALER_TRANSITION_TYPE_SLIDE_RIGHT)
+ natural_width = round (natural_width * priv->current_pos);
+
+ minimum_width = natural_width;
+
+ if (minimum_width_out)
+ *minimum_width_out = minimum_width;
+ if (natural_width_out)
+ *natural_width_out = natural_width;
+}
+
+guint
+gtk_revealer_get_transition_duration (GtkRevealer *revealer)
+{
+ g_return_val_if_fail (GTK_IS_REVEALER (revealer), 0);
+
+ return revealer->priv->transition_duration;
+}
+
+void
+gtk_revealer_set_transition_duration (GtkRevealer *revealer,
+ guint value)
+{
+ g_return_if_fail (GTK_IS_REVEALER (revealer));
+
+ revealer->priv->transition_duration = value;
+ g_object_notify (G_OBJECT (revealer), "transition-duration");
+}
+
+GtkRevealerTransitionType
+gtk_revealer_get_transition_type (GtkRevealer *revealer)
+{
+ g_return_val_if_fail (GTK_IS_REVEALER (revealer), GTK_REVEALER_TRANSITION_TYPE_NONE);
+
+ return revealer->priv->transition_type;
+}
+
+void
+gtk_revealer_set_transition_type (GtkRevealer *revealer,
+ GtkRevealerTransitionType transition)
+{
+ g_return_if_fail (GTK_IS_REVEALER (revealer));
+
+ revealer->priv->transition_type = transition;
+ gtk_widget_queue_resize (GTK_WIDGET (revealer));
+ g_object_notify (G_OBJECT (revealer), "transition-type");
+}
diff --git a/gtk/gtkrevealer.h b/gtk/gtkrevealer.h
new file mode 100644
index 0000000000..c6da3d794a
--- /dev/null
+++ b/gtk/gtkrevealer.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2013 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>
+ *
+ */
+
+#ifndef __GTK_REVEALER_H__
+#define __GTK_REVEALER_H__
+
+#include <gtk/gtkbin.h>
+
+G_BEGIN_DECLS
+
+
+#define GTK_TYPE_REVEALER (gtk_revealer_get_type ())
+#define GTK_REVEALER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_REVEALER, GtkRevealer))
+#define GTK_REVEALER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_REVEALER, GtkRevealerClass))
+#define GTK_IS_REVEALER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_REVEALER))
+#define GTK_IS_REVEALER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_REVEALER))
+#define GTK_REVEALER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_REVEALER, GtkRevealerClass))
+
+typedef struct _GtkRevealer GtkRevealer;
+typedef struct _GtkRevealerClass GtkRevealerClass;
+typedef struct _GtkRevealerPrivate GtkRevealerPrivate;
+
+typedef enum {
+ GTK_REVEALER_TRANSITION_TYPE_NONE,
+ GTK_REVEALER_TRANSITION_TYPE_SLIDE_RIGHT,
+ GTK_REVEALER_TRANSITION_TYPE_SLIDE_LEFT,
+ GTK_REVEALER_TRANSITION_TYPE_SLIDE_UP,
+ GTK_REVEALER_TRANSITION_TYPE_SLIDE_DOWN
+} GtkRevealerTransitionType;
+
+struct _GtkRevealer {
+ GtkBin parent_instance;
+ GtkRevealerPrivate * priv;
+};
+
+struct _GtkRevealerClass {
+ GtkBinClass parent_class;
+};
+
+GType gtk_revealer_get_type (void) G_GNUC_CONST;
+GtkWidget* gtk_revealer_new (void);
+gboolean gtk_revealer_get_reveal_child (GtkRevealer *revealer);
+void gtk_revealer_set_reveal_child (GtkRevealer *revealer,
+ gboolean reveal_child);
+gboolean gtk_revealer_get_child_revealed (GtkRevealer *revealer);
+guint gtk_revealer_get_transition_duration (GtkRevealer *revealer);
+void gtk_revealer_set_transition_duration (GtkRevealer *revealer,
+ guint duration);
+void gtk_revealer_set_transition_type (GtkRevealer *revealer,
+ GtkRevealerTransitionType transition);
+GtkRevealerTransitionType gtk_revealer_get_transition_type (GtkRevealer *revealer);
+
+
+G_END_DECLS
+
+#endif
diff --git a/tests/testrevealer.c b/tests/testrevealer.c
new file mode 100644
index 0000000000..a71327de2c
--- /dev/null
+++ b/tests/testrevealer.c
@@ -0,0 +1,115 @@
+#include <gtk/gtk.h>
+
+gint
+main (gint argc,
+ gchar ** argv)
+{
+ GtkWidget *window, *revealer, *box, *widget, *entry;
+
+ gtk_init (&argc, &argv);
+
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_widget_set_size_request (window, 300, 300);
+
+ box = gtk_grid_new ();
+ gtk_container_add (GTK_CONTAINER (window), box);
+
+ widget = gtk_label_new ("Some filler text to avoid\nresizing of the window");
+ gtk_widget_set_margin_top (widget, 10);
+ gtk_widget_set_margin_bottom (widget, 10);
+ gtk_widget_set_margin_left (widget, 10);
+ gtk_widget_set_margin_right (widget, 10);
+ gtk_grid_attach (GTK_GRID (box), widget, 1, 1, 1, 1);
+
+ widget = gtk_label_new ("Some filler text to avoid\nresizing of the window");
+ gtk_widget_set_margin_top (widget, 10);
+ gtk_widget_set_margin_bottom (widget, 10);
+ gtk_widget_set_margin_left (widget, 10);
+ gtk_widget_set_margin_right (widget, 10);
+ gtk_grid_attach (GTK_GRID (box), widget, 3, 3, 1, 1);
+
+ widget = gtk_toggle_button_new_with_label ("None");
+ gtk_grid_attach (GTK_GRID (box), widget, 0, 0, 1, 1);
+ revealer = gtk_revealer_new ();
+ gtk_widget_set_halign (revealer, GTK_ALIGN_START);
+ gtk_widget_set_valign (revealer, GTK_ALIGN_START);
+ entry = gtk_entry_new ();
+ gtk_entry_set_text (GTK_ENTRY (entry), "00000");
+ gtk_container_add (GTK_CONTAINER (revealer), entry);
+ g_object_bind_property (widget, "active", revealer, "reveal-child", 0);
+ gtk_revealer_set_transition_type (GTK_REVEALER (revealer), GTK_REVEALER_TRANSITION_TYPE_NONE);
+ gtk_revealer_set_transition_duration (GTK_REVEALER (revealer), 2000);
+ gtk_grid_attach (GTK_GRID (box), revealer, 1, 0, 1, 1);
+
+ widget = gtk_toggle_button_new_with_label ("None");
+ gtk_grid_attach (GTK_GRID (box), widget, 4, 4, 1, 1);
+ revealer = gtk_revealer_new ();
+ gtk_widget_set_halign (revealer, GTK_ALIGN_END);
+ gtk_widget_set_valign (revealer, GTK_ALIGN_END);
+ entry = gtk_entry_new ();
+ gtk_entry_set_text (GTK_ENTRY (entry), "00000");
+ gtk_container_add (GTK_CONTAINER (revealer), entry);
+ g_object_bind_property (widget, "active", revealer, "reveal-child", 0);
+ gtk_revealer_set_transition_type (GTK_REVEALER (revealer), GTK_REVEALER_TRANSITION_TYPE_NONE);
+ gtk_revealer_set_transition_duration (GTK_REVEALER (revealer), 2000);
+ gtk_grid_attach (GTK_GRID (box), revealer, 3, 4, 1, 1);
+
+ widget = gtk_toggle_button_new_with_label ("Right");
+ gtk_grid_attach (GTK_GRID (box), widget, 0, 2, 1, 1);
+ revealer = gtk_revealer_new ();
+ gtk_widget_set_hexpand (revealer, TRUE);
+ gtk_widget_set_halign (revealer, GTK_ALIGN_START);
+ entry = gtk_entry_new ();
+ gtk_entry_set_text (GTK_ENTRY (entry), "12345");
+ gtk_container_add (GTK_CONTAINER (revealer), entry);
+ g_object_bind_property (widget, "active", revealer, "reveal-child", 0);
+ gtk_revealer_set_transition_type (GTK_REVEALER (revealer), GTK_REVEALER_TRANSITION_TYPE_SLIDE_RIGHT);
+ gtk_revealer_set_transition_duration (GTK_REVEALER (revealer), 2000);
+ gtk_grid_attach (GTK_GRID (box), revealer, 1, 2, 1, 1);
+
+ widget = gtk_toggle_button_new_with_label ("Down");
+ gtk_grid_attach (GTK_GRID (box), widget, 2, 0, 1, 1);
+ revealer = gtk_revealer_new ();
+ gtk_widget_set_vexpand (revealer, TRUE);
+ gtk_widget_set_valign (revealer, GTK_ALIGN_START);
+ entry = gtk_entry_new ();
+ gtk_entry_set_text (GTK_ENTRY (entry), "23456");
+ gtk_container_add (GTK_CONTAINER (revealer), entry);
+ g_object_bind_property (widget, "active", revealer, "reveal-child", 0);
+ gtk_revealer_set_transition_type (GTK_REVEALER (revealer), GTK_REVEALER_TRANSITION_TYPE_SLIDE_DOWN);
+ gtk_revealer_set_transition_duration (GTK_REVEALER (revealer), 2000);
+ gtk_grid_attach (GTK_GRID (box), revealer, 2, 1, 1, 1);
+
+ widget = gtk_toggle_button_new_with_label ("Left");
+ gtk_grid_attach (GTK_GRID (box), widget, 4, 2, 1, 1);
+ revealer = gtk_revealer_new ();
+ gtk_widget_set_hexpand (revealer, TRUE);
+ gtk_widget_set_halign (revealer, GTK_ALIGN_END);
+ entry = gtk_entry_new ();
+ gtk_entry_set_text (GTK_ENTRY (entry), "34567");
+ gtk_container_add (GTK_CONTAINER (revealer), entry);
+ g_object_bind_property (widget, "active", revealer, "reveal-child", 0);
+ gtk_revealer_set_transition_type (GTK_REVEALER (revealer), GTK_REVEALER_TRANSITION_TYPE_SLIDE_LEFT);
+ gtk_revealer_set_transition_duration (GTK_REVEALER (revealer), 2000);
+ gtk_grid_attach (GTK_GRID (box), revealer, 3, 2, 1, 1);
+
+ widget = gtk_toggle_button_new_with_label ("Up");
+ gtk_grid_attach (GTK_GRID (box), widget, 2, 4, 1, 1);
+ revealer = gtk_revealer_new ();
+ gtk_widget_set_vexpand (revealer, TRUE);
+ gtk_widget_set_valign (revealer, GTK_ALIGN_END);
+ entry = gtk_entry_new ();
+ gtk_entry_set_text (GTK_ENTRY (entry), "45678");
+ gtk_container_add (GTK_CONTAINER (revealer), entry);
+ g_object_bind_property (widget, "active", revealer, "reveal-child", 0);
+ gtk_revealer_set_transition_type (GTK_REVEALER (revealer), GTK_REVEALER_TRANSITION_TYPE_SLIDE_UP);
+ gtk_revealer_set_transition_duration (GTK_REVEALER (revealer), 2000);
+ gtk_grid_attach (GTK_GRID (box), revealer, 2, 3, 1, 1);
+
+ gtk_widget_show_all (window);
+ gtk_main ();
+
+ gtk_widget_destroy (window);
+
+ return 0;
+}