forked from AuroraMiddleware/gtk
popover: Add show/hide transitions
These have the same visual effect and timing than the gnome-shell ones. During the hide animation, the popover has been made to take focus elsewhere, and refuse to take any pointer/keyboard input until the popover is shown again. This has been based on work from Timm Bäder. https://bugzilla.gnome.org/show_bug.cgi?id=741405
This commit is contained in:
parent
3a671994d4
commit
cff1694c99
@ -7965,6 +7965,8 @@ gtk_popover_set_position
|
||||
gtk_popover_get_position
|
||||
gtk_popover_set_modal
|
||||
gtk_popover_get_modal
|
||||
gtk_popover_set_transitions_enabled
|
||||
gtk_popover_get_transitions_enabled
|
||||
<SUBSECTION Private>
|
||||
gtk_popover_get_type
|
||||
</SECTION>
|
||||
|
307
gtk/gtkpopover.c
307
gtk/gtkpopover.c
@ -103,8 +103,10 @@
|
||||
#include "wayland/gdkwayland.h"
|
||||
#endif
|
||||
|
||||
#define TAIL_GAP_WIDTH 24
|
||||
#define TAIL_HEIGHT 12
|
||||
#define TAIL_GAP_WIDTH 24
|
||||
#define TAIL_HEIGHT 12
|
||||
#define TRANSITION_DIFF 20
|
||||
#define TRANSITION_DURATION 330 * 1000
|
||||
|
||||
#define POS_IS_VERTICAL(p) ((p) == GTK_POS_TOP || (p) == GTK_POS_BOTTOM)
|
||||
|
||||
@ -112,7 +114,8 @@ enum {
|
||||
PROP_RELATIVE_TO = 1,
|
||||
PROP_POINTING_TO,
|
||||
PROP_POSITION,
|
||||
PROP_MODAL
|
||||
PROP_MODAL,
|
||||
PROP_TRANSITIONS_ENABLED
|
||||
};
|
||||
|
||||
enum {
|
||||
@ -120,6 +123,13 @@ enum {
|
||||
N_SIGNALS
|
||||
};
|
||||
|
||||
enum {
|
||||
STATE_SHOWING,
|
||||
STATE_SHOWN,
|
||||
STATE_HIDING,
|
||||
STATE_HIDDEN
|
||||
};
|
||||
|
||||
struct _GtkPopoverPrivate
|
||||
{
|
||||
GtkWidget *widget;
|
||||
@ -144,6 +154,12 @@ struct _GtkPopoverPrivate
|
||||
guint button_pressed : 1;
|
||||
guint apply_shape : 1;
|
||||
guint grab_notify_blocked : 1;
|
||||
guint transitions_enabled : 1;
|
||||
guint state : 2;
|
||||
guint visible : 1;
|
||||
gint64 start_time;
|
||||
gint transition_diff;
|
||||
guint tick_id;
|
||||
};
|
||||
|
||||
static GQuark quark_widget_popovers = 0;
|
||||
@ -151,6 +167,8 @@ static guint signals[N_SIGNALS] = { 0 };
|
||||
|
||||
static void gtk_popover_update_relative_to (GtkPopover *popover,
|
||||
GtkWidget *relative_to);
|
||||
static void gtk_popover_set_state (GtkPopover *popover,
|
||||
guint state);
|
||||
|
||||
G_DEFINE_TYPE_WITH_PRIVATE (GtkPopover, gtk_popover, GTK_TYPE_BIN)
|
||||
|
||||
@ -165,6 +183,8 @@ gtk_popover_init (GtkPopover *popover)
|
||||
popover->priv = gtk_popover_get_instance_private (popover);
|
||||
popover->priv->modal = TRUE;
|
||||
popover->priv->apply_shape = TRUE;
|
||||
popover->priv->tick_id = 0;
|
||||
popover->priv->transitions_enabled = TRUE;
|
||||
|
||||
context = gtk_widget_get_style_context (widget);
|
||||
gtk_style_context_add_class (context, GTK_STYLE_CLASS_BACKGROUND);
|
||||
@ -195,6 +215,10 @@ gtk_popover_set_property (GObject *object,
|
||||
gtk_popover_set_modal (GTK_POPOVER (object),
|
||||
g_value_get_boolean (value));
|
||||
break;
|
||||
case PROP_TRANSITIONS_ENABLED:
|
||||
gtk_popover_set_transitions_enabled (GTK_POPOVER (object),
|
||||
g_value_get_boolean (value));
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
}
|
||||
@ -222,11 +246,27 @@ gtk_popover_get_property (GObject *object,
|
||||
case PROP_MODAL:
|
||||
g_value_set_boolean (value, priv->modal);
|
||||
break;
|
||||
case PROP_TRANSITIONS_ENABLED:
|
||||
g_value_set_boolean (value, priv->transitions_enabled);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
transitions_enabled (GtkPopover *popover)
|
||||
{
|
||||
GtkPopoverPrivate *priv = popover->priv;
|
||||
gboolean animations_enabled;
|
||||
|
||||
g_object_get (gtk_widget_get_settings (GTK_WIDGET (popover)),
|
||||
"gtk-enable-animations", &animations_enabled,
|
||||
NULL);
|
||||
|
||||
return animations_enabled && priv->transitions_enabled;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_popover_finalize (GObject *object)
|
||||
{
|
||||
@ -263,8 +303,6 @@ gtk_popover_dispose (GObject *object)
|
||||
GtkPopover *popover = GTK_POPOVER (object);
|
||||
GtkPopoverPrivate *priv = popover->priv;
|
||||
|
||||
gtk_widget_set_visible (GTK_WIDGET (object), FALSE);
|
||||
|
||||
if (priv->window)
|
||||
_gtk_window_remove_popover (priv->window, GTK_WIDGET (object));
|
||||
|
||||
@ -421,25 +459,126 @@ gtk_popover_apply_modality (GtkPopover *popover,
|
||||
if (priv->prev_focus_widget &&
|
||||
gtk_widget_is_drawable (priv->prev_focus_widget))
|
||||
gtk_widget_grab_focus (priv->prev_focus_widget);
|
||||
else
|
||||
else if (priv->window)
|
||||
gtk_widget_grab_focus (GTK_WIDGET (priv->window));
|
||||
|
||||
popover_unset_prev_focus (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,
|
||||
gpointer user_data)
|
||||
{
|
||||
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);
|
||||
|
||||
if (priv->state == STATE_SHOWING)
|
||||
{
|
||||
priv->transition_diff = TRANSITION_DIFF - (TRANSITION_DIFF * t);
|
||||
gtk_widget_set_opacity (widget, t);
|
||||
}
|
||||
else if (priv->state == STATE_HIDING)
|
||||
{
|
||||
priv->transition_diff = -TRANSITION_DIFF * t;
|
||||
gtk_widget_set_opacity (widget, 1.0 - t);
|
||||
}
|
||||
|
||||
gtk_widget_queue_resize (GTK_WIDGET (popover));
|
||||
|
||||
if (t >= 1.0)
|
||||
{
|
||||
if (priv->state == STATE_SHOWING)
|
||||
{
|
||||
gtk_popover_set_state (popover, STATE_SHOWN);
|
||||
|
||||
if (!priv->visible)
|
||||
gtk_popover_set_state (popover, STATE_HIDING);
|
||||
}
|
||||
else
|
||||
gtk_popover_set_state (popover, STATE_HIDDEN);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
else
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
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);
|
||||
priv->tick_id = gtk_widget_add_tick_callback (GTK_WIDGET (popover),
|
||||
show_animate_cb,
|
||||
popover, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_popover_set_state (GtkPopover *popover,
|
||||
guint state)
|
||||
{
|
||||
GtkPopoverPrivate *priv = popover->priv;
|
||||
|
||||
if (!transitions_enabled (popover) ||
|
||||
!gtk_widget_get_realized (GTK_WIDGET (popover)))
|
||||
{
|
||||
if (state == STATE_SHOWING)
|
||||
state = STATE_SHOWN;
|
||||
else if (state == STATE_HIDING)
|
||||
state = STATE_HIDDEN;
|
||||
}
|
||||
|
||||
priv->state = state;
|
||||
|
||||
if (state == STATE_SHOWING || state == STATE_HIDING)
|
||||
gtk_popover_start_transition (popover);
|
||||
else
|
||||
{
|
||||
if (priv->tick_id)
|
||||
{
|
||||
gtk_widget_remove_tick_callback (GTK_WIDGET (popover), priv->tick_id);
|
||||
priv->tick_id = 0;
|
||||
}
|
||||
|
||||
gtk_widget_set_visible (GTK_WIDGET (popover), state == STATE_SHOWN);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_popover_map (GtkWidget *widget)
|
||||
{
|
||||
GtkPopoverPrivate *priv = GTK_POPOVER (widget)->priv;
|
||||
|
||||
GTK_WIDGET_CLASS (gtk_popover_parent_class)->map (widget);
|
||||
|
||||
gdk_window_show (gtk_widget_get_window (widget));
|
||||
gtk_popover_update_position (GTK_POPOVER (widget));
|
||||
|
||||
if (priv->modal)
|
||||
gtk_popover_apply_modality (GTK_POPOVER (widget), TRUE);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -449,13 +588,8 @@ gtk_popover_unmap (GtkWidget *widget)
|
||||
|
||||
priv->button_pressed = FALSE;
|
||||
|
||||
if (priv->modal)
|
||||
gtk_popover_apply_modality (GTK_POPOVER (widget), FALSE);
|
||||
|
||||
gdk_window_hide (gtk_widget_get_window (widget));
|
||||
GTK_WIDGET_CLASS (gtk_popover_parent_class)->unmap (widget);
|
||||
|
||||
g_signal_emit (widget, signals[CLOSED], 0);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -853,6 +987,22 @@ gtk_popover_update_position (GtkPopover *popover)
|
||||
priv->final_position = pos;
|
||||
}
|
||||
|
||||
switch (priv->final_position)
|
||||
{
|
||||
case GTK_POS_TOP:
|
||||
rect.y += priv->transition_diff;
|
||||
break;
|
||||
case GTK_POS_BOTTOM:
|
||||
rect.y -= priv->transition_diff;
|
||||
break;
|
||||
case GTK_POS_LEFT:
|
||||
rect.x += priv->transition_diff;
|
||||
break;
|
||||
case GTK_POS_RIGHT:
|
||||
rect.x -= priv->transition_diff;
|
||||
break;
|
||||
}
|
||||
|
||||
_gtk_window_set_popover_position (priv->window, widget,
|
||||
priv->final_position, &rect);
|
||||
|
||||
@ -1281,8 +1431,12 @@ gtk_popover_key_press (GtkWidget *widget,
|
||||
static void
|
||||
gtk_popover_grab_focus (GtkWidget *widget)
|
||||
{
|
||||
GtkPopoverPrivate *priv = GTK_POPOVER (widget)->priv;
|
||||
GtkWidget *child;
|
||||
|
||||
if (!priv->visible)
|
||||
return;
|
||||
|
||||
/* Focus the first natural child */
|
||||
child = gtk_bin_get_child (GTK_BIN (widget));
|
||||
|
||||
@ -1295,6 +1449,10 @@ gtk_popover_focus (GtkWidget *widget,
|
||||
GtkDirectionType direction)
|
||||
{
|
||||
GtkPopover *popover = GTK_POPOVER (widget);
|
||||
GtkPopoverPrivate *priv = popover->priv;
|
||||
|
||||
if (!priv->visible)
|
||||
return FALSE;
|
||||
|
||||
if (!GTK_WIDGET_CLASS (gtk_popover_parent_class)->focus (widget, direction))
|
||||
{
|
||||
@ -1319,6 +1477,53 @@ gtk_popover_focus (GtkWidget *widget,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_popover_show (GtkWidget *widget)
|
||||
{
|
||||
GtkPopoverPrivate *priv = GTK_POPOVER (widget)->priv;
|
||||
|
||||
priv->visible = TRUE;
|
||||
|
||||
GTK_WIDGET_CLASS (gtk_popover_parent_class)->show (widget);
|
||||
|
||||
if (priv->modal)
|
||||
gtk_popover_apply_modality (GTK_POPOVER (widget), TRUE);
|
||||
|
||||
gtk_popover_set_state (GTK_POPOVER (widget), STATE_SHOWING);
|
||||
|
||||
if (gtk_widget_get_realized (widget))
|
||||
gdk_window_input_shape_combine_region (gtk_widget_get_parent_window (widget),
|
||||
NULL, 0, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_popover_hide (GtkWidget *widget)
|
||||
{
|
||||
GtkPopoverPrivate *priv = GTK_POPOVER (widget)->priv;
|
||||
cairo_region_t *region;
|
||||
|
||||
if (priv->visible)
|
||||
{
|
||||
priv->visible = FALSE;
|
||||
g_signal_emit (widget, signals[CLOSED], 0);
|
||||
|
||||
if (priv->modal)
|
||||
gtk_popover_apply_modality (GTK_POPOVER (widget), FALSE);
|
||||
}
|
||||
|
||||
if (gtk_widget_get_realized (widget))
|
||||
{
|
||||
region = cairo_region_create ();
|
||||
gdk_window_input_shape_combine_region (gtk_widget_get_parent_window (widget),
|
||||
region, 0, 0);
|
||||
cairo_region_destroy (region);
|
||||
}
|
||||
|
||||
if (!priv->window || priv->state == STATE_HIDDEN)
|
||||
GTK_WIDGET_CLASS (gtk_popover_parent_class)->hide (widget);
|
||||
else if (priv->state != STATE_SHOWING)
|
||||
gtk_popover_set_state (GTK_POPOVER (widget), STATE_HIDING);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_popover_class_init (GtkPopoverClass *klass)
|
||||
@ -1345,6 +1550,8 @@ gtk_popover_class_init (GtkPopoverClass *klass)
|
||||
widget_class->key_press_event = gtk_popover_key_press;
|
||||
widget_class->grab_focus = gtk_popover_grab_focus;
|
||||
widget_class->focus = gtk_popover_focus;
|
||||
widget_class->show = gtk_popover_show;
|
||||
widget_class->hide = gtk_popover_hide;
|
||||
|
||||
/**
|
||||
* GtkPopover:relative-to:
|
||||
@ -1405,6 +1612,21 @@ gtk_popover_class_init (GtkPopoverClass *klass)
|
||||
TRUE,
|
||||
GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
|
||||
|
||||
/**
|
||||
* GtkPopover:transitions-enabled
|
||||
*
|
||||
* Whether show/hide transitions are enabled for this popover.
|
||||
*
|
||||
* Since: 3.16
|
||||
*/
|
||||
g_object_class_install_property (object_class,
|
||||
PROP_TRANSITIONS_ENABLED,
|
||||
g_param_spec_boolean ("transitions-enabled",
|
||||
P_("Transitions enabled"),
|
||||
P_("Whether show/hide transitions are enabled or not"),
|
||||
TRUE,
|
||||
GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
|
||||
|
||||
signals[CLOSED] =
|
||||
g_signal_new (I_("closed"),
|
||||
G_TYPE_FROM_CLASS (object_class),
|
||||
@ -1514,7 +1736,12 @@ static void
|
||||
_gtk_popover_parent_unmap (GtkWidget *widget,
|
||||
GtkPopover *popover)
|
||||
{
|
||||
gtk_widget_unmap (GTK_WIDGET (popover));
|
||||
GtkPopoverPrivate *priv = popover->priv;
|
||||
|
||||
if (priv->state == STATE_SHOWING)
|
||||
priv->visible = FALSE;
|
||||
else if (priv->state == STATE_SHOWN)
|
||||
gtk_popover_set_state (popover, STATE_HIDING);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -1974,6 +2201,52 @@ gtk_popover_get_modal (GtkPopover *popover)
|
||||
return popover->priv->modal;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_popover_set_transitions_enabled:
|
||||
* @popover: a #GtkPopover
|
||||
* @transitions_enabled: Whether transitions are enabled
|
||||
*
|
||||
* Sets whether show/hide transitions are enabled on this popover
|
||||
*
|
||||
* Since: 3.16
|
||||
*/
|
||||
void
|
||||
gtk_popover_set_transitions_enabled (GtkPopover *popover,
|
||||
gboolean transitions_enabled)
|
||||
{
|
||||
GtkPopoverPrivate *priv = popover->priv;
|
||||
|
||||
g_return_if_fail (GTK_IS_POPOVER (popover));
|
||||
|
||||
transitions_enabled = !!transitions_enabled;
|
||||
|
||||
if (priv->transitions_enabled == transitions_enabled)
|
||||
return;
|
||||
|
||||
priv->transitions_enabled = transitions_enabled;
|
||||
g_object_notify (G_OBJECT (popover), "transitions-enabled");
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_popover_get_transitions_enabled:
|
||||
* @popover: a #GtkPopover
|
||||
*
|
||||
* Returns whether show/hide transitions are enabled on this popover.
|
||||
*
|
||||
* Returns: #TRUE if the show and hide transitions of the given
|
||||
* popover are enabled, #FALSE otherwise.
|
||||
*
|
||||
* Since: 3.16
|
||||
*/
|
||||
gboolean
|
||||
gtk_popover_get_transitions_enabled (GtkPopover *popover)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_POPOVER (popover), FALSE);
|
||||
|
||||
return popover->priv->transitions_enabled;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
back_to_main (GtkWidget *popover)
|
||||
{
|
||||
|
@ -97,6 +97,12 @@ void gtk_popover_bind_model (GtkPopover *popover,
|
||||
GMenuModel *model,
|
||||
const gchar *action_namespace);
|
||||
|
||||
GDK_AVAILABLE_IN_3_16
|
||||
void gtk_popover_set_transitions_enabled (GtkPopover *popover,
|
||||
gboolean transitions_enabled);
|
||||
GDK_AVAILABLE_IN_3_16
|
||||
gboolean gtk_popover_get_transitions_enabled (GtkPopover *popover);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_POPOVER_H__ */
|
||||
|
Loading…
Reference in New Issue
Block a user