X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gtk%2Fgtkwidget.c;h=37460e3419680aab8b42d52e0f51003bb474fc0a;hb=d84f5ae17e5b891b30dd54ddc10fd22c2a609e0e;hp=c7406fc882701a3e6de16de9f3bfd9cd01c6308e;hpb=6c257040a54eb91cc9323c8441b6025667dace69;p=~andy%2Fgtk diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c index c7406fc88..37460e341 100644 --- a/gtk/gtkwidget.c +++ b/gtk/gtkwidget.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -35,12 +36,15 @@ #include "gtkcontainer.h" #include "gtkaccelmapprivate.h" #include "gtkclipboard.h" +#include "gtkcssstylepropertyprivate.h" +#include "gtkcssnumbervalueprivate.h" #include "gtkiconfactory.h" #include "gtkintl.h" #include "gtkmarshalers.h" #include "gtkselectionprivate.h" #include "gtksettingsprivate.h" #include "gtksizegroup-private.h" +#include "gtksizerequestcacheprivate.h" #include "gtkwidget.h" #include "gtkwidgetprivate.h" #include "gtkwindowprivate.h" @@ -54,9 +58,7 @@ #include "gtkbuilderprivate.h" #include "gtksizerequest.h" #include "gtkstylecontextprivate.h" -#include "gtksymboliccolor.h" #include "gtkcssprovider.h" -#include "gtkanimationdescription.h" #include "gtkmodifierstyle.h" #include "gtkversion.h" #include "gtkdebug.h" @@ -272,7 +274,7 @@ * * * - * Clickable Button + * Clickable Button * * * @@ -300,12 +302,12 @@ * all flags will be set on children (think prelight or active), but we * might want to not do this for some. */ -#define GTK_STATE_FLAGS_DONT_PROPAGATE (GTK_STATE_FLAG_FOCUSED) +#define GTK_STATE_FLAGS_DONT_PROPAGATE (GTK_STATE_FLAG_FOCUSED | GTK_STATE_FLAG_DIR_LTR | GTK_STATE_FLAG_DIR_RTL) #define GTK_STATE_FLAGS_DO_PROPAGATE (~GTK_STATE_FLAGS_DONT_PROPAGATE) #define WIDGET_CLASS(w) GTK_WIDGET_GET_CLASS (w) -#define GTK_STATE_FLAGS_BITS 7 +#define GTK_STATE_FLAGS_BITS 9 struct _GtkWidgetPrivate { @@ -343,10 +345,7 @@ struct _GtkWidgetPrivate guint in_reparent : 1; /* Queue-resize related flags */ - guint resize_pending : 1; guint alloc_needed : 1; - guint width_request_needed : 1; - guint height_request_needed : 1; /* Expand-related flags */ guint need_compute_expand : 1; /* Need to recompute computed_[hv]_expand */ @@ -358,10 +357,15 @@ struct _GtkWidgetPrivate guint vexpand_set : 1; /* instead of computing from children */ /* SizeGroup related flags */ - guint sizegroup_visited : 1; - guint sizegroup_bumping : 1; guint have_size_groups : 1; + guint opacity_group : 1; + guint norender_children : 1; + guint norender : 1; /* Don't expose windows, instead recurse via draw */ + + guint8 alpha; + guint8 user_alpha; + /* The widget's name. If the widget does not have a name * (the name is NULL), then its name (as returned by * "gtk_widget_get_name") is its class's name. @@ -392,15 +396,22 @@ struct _GtkWidgetPrivate /* The widget's requested sizes */ SizeRequestCache requests; + /* actions attached to this or any parent widget */ + GActionMuxer *muxer; + /* The widget's window or its parent window if it does * not have a window. (Which will be indicated by the * GTK_NO_WINDOW flag being set). */ GdkWindow *window; + GList *registered_windows; /* The widget's parent */ GtkWidget *parent; + /* Animations and other things to update on clock ticks */ + GList *tick_callbacks; + #ifdef G_ENABLE_DEBUG /* Number of gtk_widget_push_verify_invariants () */ guint verifying_invariants_count; @@ -509,6 +520,7 @@ enum { PROP_TOOLTIP_MARKUP, PROP_TOOLTIP_TEXT, PROP_WINDOW, + PROP_OPACITY, PROP_DOUBLE_BUFFERED, PROP_HALIGN, PROP_VALIGN, @@ -526,16 +538,10 @@ enum { typedef struct _GtkStateData GtkStateData; -enum { - STATE_CHANGE_REPLACE, - STATE_CHANGE_SET, - STATE_CHANGE_UNSET -}; - struct _GtkStateData { - guint flags : GTK_STATE_FLAGS_BITS; - guint operation : 2; + guint flags_to_set; + guint flags_to_unset; }; /* --- prototypes --- */ @@ -551,6 +557,7 @@ static void gtk_widget_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); +static void gtk_widget_constructed (GObject *object); static void gtk_widget_dispose (GObject *object); static void gtk_widget_real_destroy (GtkWidget *object); static void gtk_widget_finalize (GObject *object); @@ -609,7 +616,8 @@ static PangoContext* gtk_widget_peek_pango_context (GtkWidget *widget); static void gtk_widget_update_pango_context (GtkWidget *widget); static void gtk_widget_propagate_state (GtkWidget *widget, GtkStateData *data); -; +static void gtk_widget_update_alpha (GtkWidget *widget); + static gint gtk_widget_event_internal (GtkWidget *widget, GdkEvent *event); static gboolean gtk_widget_real_mnemonic_activate (GtkWidget *widget, @@ -640,7 +648,6 @@ static AtkObject* gtk_widget_ref_accessible (AtkImplementor *implementor); static void gtk_widget_invalidate_widget_windows (GtkWidget *widget, cairo_region_t *region); static GdkScreen * gtk_widget_get_screen_unchecked (GtkWidget *widget); -static void gtk_widget_queue_shallow_draw (GtkWidget *widget); static gboolean gtk_widget_real_can_activate_accel (GtkWidget *widget, guint signal_id); @@ -707,6 +714,14 @@ static void gtk_widget_set_device_enabled_internal (GtkWidget *widget, gboolean recurse, gboolean enabled); +static void gtk_widget_on_frame_clock_update (GdkFrameClock *frame_clock, + GtkWidget *widget); + +static gboolean event_window_is_still_viewable (GdkEvent *event); +static void gtk_cairo_set_event (cairo_t *cr, + GdkEventExpose *event); +static void gtk_widget_propagate_alpha (GtkWidget *widget); + /* --- variables --- */ static gpointer gtk_widget_parent_class = NULL; static guint widget_signals[LAST_SIGNAL] = { 0 }; @@ -815,9 +830,24 @@ gtk_widget_draw_marshaller (GClosure *closure, gpointer invocation_hint, gpointer marshal_data) { + GtkWidget *widget = g_value_get_object (¶m_values[0]); + GdkEventExpose *tmp_event; + gboolean push_group; cairo_t *cr = g_value_get_boxed (¶m_values[1]); cairo_save (cr); + tmp_event = _gtk_cairo_get_event (cr); + + push_group = + widget->priv->opacity_group || + (widget->priv->alpha != 255 && + (!gtk_widget_get_has_window (widget) || tmp_event == NULL)); + + if (push_group) + { + cairo_push_group (cr); + gtk_cairo_set_event (cr, NULL); + } _gtk_marshal_BOOLEAN__BOXED (closure, return_value, @@ -826,7 +856,70 @@ gtk_widget_draw_marshaller (GClosure *closure, invocation_hint, marshal_data); + + if (push_group) + { + cairo_pop_group_to_source (cr); + cairo_set_operator (cr, CAIRO_OPERATOR_OVER); + cairo_paint_with_alpha (cr, widget->priv->alpha / 255.0); + } + + gtk_cairo_set_event (cr, tmp_event); + cairo_restore (cr); +} + +static void +gtk_widget_draw_marshallerv (GClosure *closure, + GValue *return_value, + gpointer instance, + va_list args, + gpointer marshal_data, + int n_params, + GType *param_types) +{ + GtkWidget *widget = GTK_WIDGET (instance); + GdkEventExpose *tmp_event; + gboolean push_group; + cairo_t *cr; + va_list args_copy; + + G_VA_COPY (args_copy, args); + cr = va_arg (args_copy, gpointer); + + cairo_save (cr); + tmp_event = _gtk_cairo_get_event (cr); + + push_group = + widget->priv->opacity_group || + (widget->priv->alpha != 255 && + (!gtk_widget_get_has_window (widget) || tmp_event == NULL)); + + if (push_group) + { + cairo_push_group (cr); + gtk_cairo_set_event (cr, NULL); + } + + _gtk_marshal_BOOLEAN__BOXEDv (closure, + return_value, + instance, + args, + marshal_data, + n_params, + param_types); + + + if (push_group) + { + cairo_pop_group_to_source (cr); + cairo_set_operator (cr, CAIRO_OPERATOR_OVER); + cairo_paint_with_alpha (cr, widget->priv->alpha / 255.0); + } + + gtk_cairo_set_event (cr, tmp_event); cairo_restore (cr); + + va_end (args_copy); } static void @@ -865,6 +958,7 @@ gtk_widget_class_init (GtkWidgetClass *klass) cpn_context.dispatcher = child_property_notify_dispatcher; _gtk_widget_child_property_notify_context = &cpn_context; + gobject_class->constructed = gtk_widget_constructed; gobject_class->dispose = gtk_widget_dispose; gobject_class->finalize = gtk_widget_finalize; gobject_class->set_property = gtk_widget_set_property; @@ -1155,7 +1249,7 @@ gtk_widget_class_init (GtkWidgetClass *klass) GTK_PARAM_READABLE)); /** - * GtkWidget:double-buffered + * GtkWidget:double-buffered: * * Whether the widget is double buffered. * @@ -1202,7 +1296,7 @@ gtk_widget_class_init (GtkWidgetClass *klass) GTK_PARAM_READWRITE)); /** - * GtkWidget:margin-left + * GtkWidget:margin-left: * * Margin on left side of widget. * @@ -1223,7 +1317,7 @@ gtk_widget_class_init (GtkWidgetClass *klass) GTK_PARAM_READWRITE)); /** - * GtkWidget:margin-right + * GtkWidget:margin-right: * * Margin on right side of widget. * @@ -1244,7 +1338,7 @@ gtk_widget_class_init (GtkWidgetClass *klass) GTK_PARAM_READWRITE)); /** - * GtkWidget:margin-top + * GtkWidget:margin-top: * * Margin on top side of widget. * @@ -1265,7 +1359,7 @@ gtk_widget_class_init (GtkWidgetClass *klass) GTK_PARAM_READWRITE)); /** - * GtkWidget:margin-bottom + * GtkWidget:margin-bottom: * * Margin on bottom side of widget. * @@ -1286,7 +1380,7 @@ gtk_widget_class_init (GtkWidgetClass *klass) GTK_PARAM_READWRITE)); /** - * GtkWidget:margin + * GtkWidget:margin: * * Sets all four sides' margin at once. If read, returns max * margin on any side. @@ -1321,7 +1415,7 @@ gtk_widget_class_init (GtkWidgetClass *klass) G_TYPE_NONE, 0); /** - * GtkWidget:hexpand + * GtkWidget:hexpand: * * Whether to expand horizontally. See gtk_widget_set_hexpand(). * @@ -1336,7 +1430,7 @@ gtk_widget_class_init (GtkWidgetClass *klass) GTK_PARAM_READWRITE)); /** - * GtkWidget:hexpand-set + * GtkWidget:hexpand-set: * * Whether to use the #GtkWidget:hexpand property. See gtk_widget_get_hexpand_set(). * @@ -1351,7 +1445,7 @@ gtk_widget_class_init (GtkWidgetClass *klass) GTK_PARAM_READWRITE)); /** - * GtkWidget:vexpand + * GtkWidget:vexpand: * * Whether to expand vertically. See gtk_widget_set_vexpand(). * @@ -1366,7 +1460,7 @@ gtk_widget_class_init (GtkWidgetClass *klass) GTK_PARAM_READWRITE)); /** - * GtkWidget:vexpand-set + * GtkWidget:vexpand-set: * * Whether to use the #GtkWidget:vexpand property. See gtk_widget_get_vexpand_set(). * @@ -1381,7 +1475,7 @@ gtk_widget_class_init (GtkWidgetClass *klass) GTK_PARAM_READWRITE)); /** - * GtkWidget:expand + * GtkWidget:expand: * * Whether to expand in both directions. Setting this sets both #GtkWidget:hexpand and #GtkWidget:vexpand * @@ -1395,6 +1489,25 @@ gtk_widget_class_init (GtkWidgetClass *klass) FALSE, GTK_PARAM_READWRITE)); + /** + * GtkWidget:opacity: + * + * The requested opacity of the widget. See gtk_widget_set_opacity() for + * more details about window opacity. + * + * Before 3.8 this was only availible in GtkWindow + * + * Since: 3.8 + */ + g_object_class_install_property (gobject_class, + PROP_OPACITY, + g_param_spec_double ("opacity", + P_("Opacity for Widget"), + P_("The opacity of the widget, from 0 to 1"), + 0.0, + 1.0, + 1.0, + GTK_PARAM_READWRITE)); /** * GtkWidget::show: * @widget: the object which received the signal. @@ -1660,7 +1773,7 @@ gtk_widget_class_init (GtkWidgetClass *klass) /** * GtkWidget::child-notify: * @widget: the object which received the signal - * @pspec: the #GParamSpec of the changed child property + * @child_property: the #GParamSpec of the changed child property * * The ::child-notify signal is emitted for each * child property that has @@ -1692,6 +1805,16 @@ gtk_widget_class_init (GtkWidgetClass *klass) * restore it. The signal emission takes care of calling cairo_save() * before and cairo_restore() after invoking the handler. * + * The signal handler will get a @cr with a clip region already set to the + * widget's dirty region, i.e. to the area that needs repainting. Complicated + * widgets that want to avoid redrawing themselves completely can get the full + * extents of the clip region with gdk_cairo_get_clip_rectangle(), or they can + * get a finer-grained representation of the dirty region with + * cairo_copy_clip_rectangle_list(). + * + * Returns: %TRUE to stop other handlers from being invoked for the event. + % %FALSE to propagate the event further. + * * Since: 3.0 */ widget_signals[DRAW] = @@ -1703,11 +1826,16 @@ gtk_widget_class_init (GtkWidgetClass *klass) gtk_widget_draw_marshaller, G_TYPE_BOOLEAN, 1, CAIRO_GOBJECT_TYPE_CONTEXT); + g_signal_set_va_marshaller (widget_signals[DRAW], G_TYPE_FROM_CLASS (klass), + gtk_widget_draw_marshallerv); /** * GtkWidget::mnemonic-activate: * @widget: the object which received the signal. * @arg1: + * + * Returns: %TRUE to stop other handlers from being invoked for the event. + * %FALSE to propagate the event further. */ widget_signals[MNEMONIC_ACTIVATE] = g_signal_new (I_("mnemonic-activate"), @@ -1815,6 +1943,8 @@ gtk_widget_class_init (GtkWidgetClass *klass) _gtk_marshal_BOOLEAN__BOXED, G_TYPE_BOOLEAN, 1, GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE); + g_signal_set_va_marshaller (widget_signals[EVENT], G_TYPE_FROM_CLASS (klass), + _gtk_marshal_BOOLEAN__BOXEDv); /** * GtkWidget::event-after: @@ -1835,6 +1965,8 @@ gtk_widget_class_init (GtkWidgetClass *klass) _gtk_marshal_VOID__BOXED, G_TYPE_NONE, 1, GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE); + g_signal_set_va_marshaller (widget_signals[EVENT_AFTER], G_TYPE_FROM_CLASS (klass), + _gtk_marshal_BOOLEAN__BOXEDv); /** * GtkWidget::button-press-event: @@ -1862,6 +1994,8 @@ gtk_widget_class_init (GtkWidgetClass *klass) _gtk_marshal_BOOLEAN__BOXED, G_TYPE_BOOLEAN, 1, GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE); + g_signal_set_va_marshaller (widget_signals[BUTTON_PRESS_EVENT], G_TYPE_FROM_CLASS (klass), + _gtk_marshal_BOOLEAN__BOXEDv); /** * GtkWidget::button-release-event: @@ -1889,6 +2023,8 @@ gtk_widget_class_init (GtkWidgetClass *klass) _gtk_marshal_BOOLEAN__BOXED, G_TYPE_BOOLEAN, 1, GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE); + g_signal_set_va_marshaller (widget_signals[BUTTON_RELEASE_EVENT], G_TYPE_FROM_CLASS (klass), + _gtk_marshal_BOOLEAN__BOXEDv); widget_signals[TOUCH_EVENT] = g_signal_new (I_("touch-event"), @@ -1899,6 +2035,9 @@ gtk_widget_class_init (GtkWidgetClass *klass) _gtk_marshal_BOOLEAN__BOXED, G_TYPE_BOOLEAN, 1, GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE); + g_signal_set_va_marshaller (widget_signals[TOUCH_EVENT], G_TYPE_FROM_CLASS (klass), + _gtk_marshal_BOOLEAN__BOXEDv); + /** * GtkWidget::scroll-event: * @widget: the object which received the signal. @@ -1910,7 +2049,7 @@ gtk_widget_class_init (GtkWidgetClass *klass) * button press events for buttons 4 and 5 when the wheel is turned. * * To receive this signal, the #GdkWindow associated to the widget needs - * to enable the #GDK_BUTTON_PRESS_MASK mask. + * to enable the #GDK_SCROLL_MASK mask. * * This signal will be sent to the grab widget if there is one. * @@ -1926,6 +2065,8 @@ gtk_widget_class_init (GtkWidgetClass *klass) _gtk_marshal_BOOLEAN__BOXED, G_TYPE_BOOLEAN, 1, GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE); + g_signal_set_va_marshaller (widget_signals[SCROLL_EVENT], G_TYPE_FROM_CLASS (klass), + _gtk_marshal_BOOLEAN__BOXEDv); /** * GtkWidget::motion-notify-event: @@ -1953,6 +2094,8 @@ gtk_widget_class_init (GtkWidgetClass *klass) _gtk_marshal_BOOLEAN__BOXED, G_TYPE_BOOLEAN, 1, GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE); + g_signal_set_va_marshaller (widget_signals[MOTION_NOTIFY_EVENT], G_TYPE_FROM_CLASS (klass), + _gtk_marshal_BOOLEAN__BOXEDv); /** * GtkWidget::composited-changed: @@ -1994,6 +2137,8 @@ gtk_widget_class_init (GtkWidgetClass *klass) _gtk_marshal_BOOLEAN__BOXED, G_TYPE_BOOLEAN, 1, GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE); + g_signal_set_va_marshaller (widget_signals[DELETE_EVENT], G_TYPE_FROM_CLASS (klass), + _gtk_marshal_BOOLEAN__BOXEDv); /** * GtkWidget::destroy-event: @@ -2021,6 +2166,8 @@ gtk_widget_class_init (GtkWidgetClass *klass) _gtk_marshal_BOOLEAN__BOXED, G_TYPE_BOOLEAN, 1, GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE); + g_signal_set_va_marshaller (widget_signals[DESTROY_EVENT], G_TYPE_FROM_CLASS (klass), + _gtk_marshal_BOOLEAN__BOXEDv); /** * GtkWidget::key-press-event: @@ -2047,6 +2194,8 @@ gtk_widget_class_init (GtkWidgetClass *klass) _gtk_marshal_BOOLEAN__BOXED, G_TYPE_BOOLEAN, 1, GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE); + g_signal_set_va_marshaller (widget_signals[KEY_PRESS_EVENT], G_TYPE_FROM_CLASS (klass), + _gtk_marshal_BOOLEAN__BOXEDv); /** * GtkWidget::key-release-event: @@ -2072,6 +2221,8 @@ gtk_widget_class_init (GtkWidgetClass *klass) _gtk_marshal_BOOLEAN__BOXED, G_TYPE_BOOLEAN, 1, GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE); + g_signal_set_va_marshaller (widget_signals[KEY_RELEASE_EVENT], G_TYPE_FROM_CLASS (klass), + _gtk_marshal_BOOLEAN__BOXEDv); /** * GtkWidget::enter-notify-event: @@ -2099,6 +2250,8 @@ gtk_widget_class_init (GtkWidgetClass *klass) _gtk_marshal_BOOLEAN__BOXED, G_TYPE_BOOLEAN, 1, GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE); + g_signal_set_va_marshaller (widget_signals[ENTER_NOTIFY_EVENT], G_TYPE_FROM_CLASS (klass), + _gtk_marshal_BOOLEAN__BOXEDv); /** * GtkWidget::leave-notify-event: @@ -2126,6 +2279,8 @@ gtk_widget_class_init (GtkWidgetClass *klass) _gtk_marshal_BOOLEAN__BOXED, G_TYPE_BOOLEAN, 1, GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE); + g_signal_set_va_marshaller (widget_signals[LEAVE_NOTIFY_EVENT], G_TYPE_FROM_CLASS (klass), + _gtk_marshal_BOOLEAN__BOXEDv); /** * GtkWidget::configure-event: @@ -2152,6 +2307,8 @@ gtk_widget_class_init (GtkWidgetClass *klass) _gtk_marshal_BOOLEAN__BOXED, G_TYPE_BOOLEAN, 1, GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE); + g_signal_set_va_marshaller (widget_signals[CONFIGURE_EVENT], G_TYPE_FROM_CLASS (klass), + _gtk_marshal_BOOLEAN__BOXEDv); /** * GtkWidget::focus-in-event: @@ -2177,6 +2334,8 @@ gtk_widget_class_init (GtkWidgetClass *klass) _gtk_marshal_BOOLEAN__BOXED, G_TYPE_BOOLEAN, 1, GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE); + g_signal_set_va_marshaller (widget_signals[FOCUS_IN_EVENT], G_TYPE_FROM_CLASS (klass), + _gtk_marshal_BOOLEAN__BOXEDv); /** * GtkWidget::focus-out-event: @@ -2202,6 +2361,8 @@ gtk_widget_class_init (GtkWidgetClass *klass) _gtk_marshal_BOOLEAN__BOXED, G_TYPE_BOOLEAN, 1, GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE); + g_signal_set_va_marshaller (widget_signals[FOCUS_OUT_EVENT], G_TYPE_FROM_CLASS (klass), + _gtk_marshal_BOOLEAN__BOXEDv); /** * GtkWidget::map-event: @@ -2227,6 +2388,8 @@ gtk_widget_class_init (GtkWidgetClass *klass) _gtk_marshal_BOOLEAN__BOXED, G_TYPE_BOOLEAN, 1, GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE); + g_signal_set_va_marshaller (widget_signals[MAP_EVENT], G_TYPE_FROM_CLASS (klass), + _gtk_marshal_BOOLEAN__BOXEDv); /** * GtkWidget::unmap-event: @@ -2252,6 +2415,8 @@ gtk_widget_class_init (GtkWidgetClass *klass) _gtk_marshal_BOOLEAN__BOXED, G_TYPE_BOOLEAN, 1, GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE); + g_signal_set_va_marshaller (widget_signals[UNMAP_EVENT], G_TYPE_FROM_CLASS (klass), + _gtk_marshal_BOOLEAN__BOXEDv); /** * GtkWidget::property-notify-event: @@ -2277,6 +2442,8 @@ gtk_widget_class_init (GtkWidgetClass *klass) _gtk_marshal_BOOLEAN__BOXED, G_TYPE_BOOLEAN, 1, GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE); + g_signal_set_va_marshaller (widget_signals[PROPERTY_NOTIFY_EVENT], G_TYPE_FROM_CLASS (klass), + _gtk_marshal_BOOLEAN__BOXEDv); /** * GtkWidget::selection-clear-event: @@ -2299,6 +2466,8 @@ gtk_widget_class_init (GtkWidgetClass *klass) _gtk_marshal_BOOLEAN__BOXED, G_TYPE_BOOLEAN, 1, GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE); + g_signal_set_va_marshaller (widget_signals[SELECTION_CLEAR_EVENT], G_TYPE_FROM_CLASS (klass), + _gtk_marshal_BOOLEAN__BOXEDv); /** * GtkWidget::selection-request-event: @@ -2322,6 +2491,8 @@ gtk_widget_class_init (GtkWidgetClass *klass) _gtk_marshal_BOOLEAN__BOXED, G_TYPE_BOOLEAN, 1, GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE); + g_signal_set_va_marshaller (widget_signals[SELECTION_REQUEST_EVENT], G_TYPE_FROM_CLASS (klass), + _gtk_marshal_BOOLEAN__BOXEDv); /** * GtkWidget::selection-notify-event: @@ -2339,6 +2510,8 @@ gtk_widget_class_init (GtkWidgetClass *klass) _gtk_marshal_BOOLEAN__BOXED, G_TYPE_BOOLEAN, 1, GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE); + g_signal_set_va_marshaller (widget_signals[SELECTION_NOTIFY_EVENT], G_TYPE_FROM_CLASS (klass), + _gtk_marshal_BOOLEAN__BOXEDv); /** * GtkWidget::selection-received: @@ -2399,6 +2572,8 @@ gtk_widget_class_init (GtkWidgetClass *klass) _gtk_marshal_BOOLEAN__BOXED, G_TYPE_BOOLEAN, 1, GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE); + g_signal_set_va_marshaller (widget_signals[PROXIMITY_IN_EVENT], G_TYPE_FROM_CLASS (klass), + _gtk_marshal_BOOLEAN__BOXEDv); /** * GtkWidget::proximity-out-event: @@ -2423,11 +2598,13 @@ gtk_widget_class_init (GtkWidgetClass *klass) _gtk_marshal_BOOLEAN__BOXED, G_TYPE_BOOLEAN, 1, GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE); + g_signal_set_va_marshaller (widget_signals[PROXIMITY_OUT_EVENT], G_TYPE_FROM_CLASS (klass), + _gtk_marshal_BOOLEAN__BOXEDv); /** * GtkWidget::drag-leave: * @widget: the object which received the signal. - * @drag_context: the drag context + * @context: the drag context * @time: the timestamp of the motion event * * The ::drag-leave signal is emitted on the drop site when the cursor @@ -2449,11 +2626,11 @@ gtk_widget_class_init (GtkWidgetClass *klass) /** * GtkWidget::drag-begin: * @widget: the object which received the signal - * @drag_context: the drag context + * @context: the drag context * * The ::drag-begin signal is emitted on the drag source when a drag is * started. A typical reason to connect to this signal is to set up a - * custom drag icon with gtk_drag_source_set_icon(). + * custom drag icon with e.g. gtk_drag_source_set_icon_pixbuf(). * * Note that some widgets set up a drag icon in the default handler of * this signal, so you may have to use g_signal_connect_after() to @@ -2472,7 +2649,7 @@ gtk_widget_class_init (GtkWidgetClass *klass) /** * GtkWidget::drag-end: * @widget: the object which received the signal - * @drag_context: the drag context + * @context: the drag context * * The ::drag-end signal is emitted on the drag source when a drag is * finished. A typical reason to connect to this signal is to undo @@ -2491,7 +2668,7 @@ gtk_widget_class_init (GtkWidgetClass *klass) /** * GtkWidget::drag-data-delete: * @widget: the object which received the signal - * @drag_context: the drag context + * @context: the drag context * * The ::drag-data-delete signal is emitted on the drag source when a drag * with the action %GDK_ACTION_MOVE is successfully completed. The signal @@ -2511,7 +2688,7 @@ gtk_widget_class_init (GtkWidgetClass *klass) /** * GtkWidget::drag-failed: * @widget: the object which received the signal - * @drag_context: the drag context + * @context: the drag context * @result: the result of the drag operation * * The ::drag-failed signal is emitted on the drag source when a drag has @@ -2538,12 +2715,12 @@ gtk_widget_class_init (GtkWidgetClass *klass) /** * GtkWidget::drag-motion: * @widget: the object which received the signal - * @drag_context: the drag context + * @context: the drag context * @x: the x coordinate of the current cursor position * @y: the y coordinate of the current cursor position * @time: the timestamp of the motion event * - * The drag-motion signal is emitted on the drop site when the user + * The ::drag-motion signal is emitted on the drop site when the user * moves the cursor over the widget during a drag. The signal handler * must determine whether the cursor position is in a drop zone or not. * If it is not in a drop zone, it returns %FALSE and no further processing @@ -2566,11 +2743,11 @@ gtk_widget_class_init (GtkWidgetClass *klass) * the drop site with gtk_drag_highlight(). * |[ * static void - * drag_motion (GtkWidget *widget, + * drag_motion (GtkWidget *widget, * GdkDragContext *context, - * gint x, - * gint y, - * guint time) + * gint x, + * gint y, + * guint time) * { * GdkAtom target; * @@ -2587,7 +2764,7 @@ gtk_widget_class_init (GtkWidgetClass *klass) * gdk_drag_status (context, 0, time); * else * { - * private_data->pending_status = context->suggested_action; + * private_data->pending_status = gdk_drag_context_get_suggested_action (context); * gtk_drag_get_data (widget, context, target, time); * } * @@ -2609,11 +2786,11 @@ gtk_widget_class_init (GtkWidgetClass *klass) * { * private_data->suggested_action = 0; * - * /* We are getting this data due to a request in drag_motion, - * * rather than due to a request in drag_drop, so we are just - * * supposed to call gdk_drag_status (), not actually paste in - * * the data. - * */ + * /* We are getting this data due to a request in drag_motion, + * * rather than due to a request in drag_drop, so we are just + * * supposed to call gdk_drag_status(), not actually paste in + * * the data. + * */ * str = gtk_selection_data_get_text (selection_data); * if (!data_is_acceptable (str)) * gdk_drag_status (context, 0, time); @@ -2645,7 +2822,7 @@ gtk_widget_class_init (GtkWidgetClass *klass) /** * GtkWidget::drag-drop: * @widget: the object which received the signal - * @drag_context: the drag context + * @context: the drag context * @x: the x coordinate of the current cursor position * @y: the y coordinate of the current cursor position * @time: the timestamp of the motion event @@ -2679,7 +2856,7 @@ gtk_widget_class_init (GtkWidgetClass *klass) /** * GtkWidget::drag-data-get: * @widget: the object which received the signal - * @drag_context: the drag context + * @context: the drag context * @data: the #GtkSelectionData to be filled with the dragged data * @info: the info that has been registered with the target in the * #GtkTargetList @@ -2707,7 +2884,7 @@ gtk_widget_class_init (GtkWidgetClass *klass) /** * GtkWidget::drag-data-received: * @widget: the object which received the signal - * @drag_context: the drag context + * @context: the drag context * @x: where the drop happened * @y: where the drop happened * @data: the received data @@ -2722,16 +2899,17 @@ gtk_widget_class_init (GtkWidgetClass *klass) * If the data was received in response to a #GtkWidget::drag-drop signal * (and this is the last target to be received), the handler for this * signal is expected to process the received data and then call - * gtk_drag_finish(), setting the @success parameter depending on whether - * the data was processed successfully. + * gtk_drag_finish(), setting the @success parameter depending on + * whether the data was processed successfully. * - * The handler may inspect and modify @drag_context->action before calling - * gtk_drag_finish(), e.g. to implement %GDK_ACTION_ASK as shown in the - * following example: + * The handler may inspect the selected action with + * gdk_drag_context_get_selected_action() before calling + * gtk_drag_finish(), e.g. to implement %GDK_ACTION_ASK as + * shown in the following example: * |[ * void * drag_data_received (GtkWidget *widget, - * GdkDragContext *drag_context, + * GdkDragContext *context, * gint x, * gint y, * GtkSelectionData *data, @@ -2740,7 +2918,12 @@ gtk_widget_class_init (GtkWidgetClass *klass) * { * if ((data->length >= 0) && (data->format == 8)) * { - * if (drag_context->action == GDK_ACTION_ASK) + * GdkDragAction action; + * + * /* handle data here */ + * + * action = gdk_drag_context_get_selected_action (context); + * if (action == GDK_ACTION_ASK) * { * GtkWidget *dialog; * gint response; @@ -2755,16 +2938,15 @@ gtk_widget_class_init (GtkWidgetClass *klass) * gtk_widget_destroy (dialog); * * if (response == GTK_RESPONSE_YES) - * drag_context->action = GDK_ACTION_MOVE; + * action = GDK_ACTION_MOVE; * else - * drag_context->action = GDK_ACTION_COPY; + * action = GDK_ACTION_COPY; * } * - * gtk_drag_finish (drag_context, TRUE, FALSE, time); - * return; + * gtk_drag_finish (context, TRUE, action == GDK_ACTION_MOVE, time); * } - * - * gtk_drag_finish (drag_context, FALSE, FALSE, time); + * else + * gtk_drag_finish (context, FALSE, FALSE, time); * } * ]| */ @@ -2833,6 +3015,8 @@ gtk_widget_class_init (GtkWidgetClass *klass) _gtk_marshal_BOOLEAN__BOXED, G_TYPE_BOOLEAN, 1, GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE); + g_signal_set_va_marshaller (widget_signals[WINDOW_STATE_EVENT], G_TYPE_FROM_CLASS (klass), + _gtk_marshal_BOOLEAN__BOXEDv); /** * GtkWidget::damage-event: @@ -2857,6 +3041,8 @@ gtk_widget_class_init (GtkWidgetClass *klass) _gtk_marshal_BOOLEAN__BOXED, G_TYPE_BOOLEAN, 1, GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE); + g_signal_set_va_marshaller (widget_signals[DAMAGE_EVENT], G_TYPE_FROM_CLASS (klass), + _gtk_marshal_BOOLEAN__BOXEDv); /** * GtkWidget::grab-broken-event: @@ -2884,6 +3070,8 @@ gtk_widget_class_init (GtkWidgetClass *klass) _gtk_marshal_BOOLEAN__BOXED, G_TYPE_BOOLEAN, 1, GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE); + g_signal_set_va_marshaller (widget_signals[GRAB_BROKEN_EVENT], G_TYPE_FROM_CLASS (klass), + _gtk_marshal_BOOLEAN__BOXEDv); /** * GtkWidget::query-tooltip: @@ -2926,7 +3114,7 @@ gtk_widget_class_init (GtkWidgetClass *klass) GTK_TYPE_TOOLTIP); /** - * GtkWidget::popup-menu + * GtkWidget::popup-menu: * @widget: the object which received the signal * * This signal gets emitted whenever a widget should pop up a context @@ -2951,6 +3139,9 @@ gtk_widget_class_init (GtkWidgetClass *klass) * GtkWidget::show-help: * @widget: the object which received the signal. * @help_type: + * + * Returns: %TRUE to stop other handlers from being invoked for the event. + * %FALSE to propagate the event further. */ widget_signals[SHOW_HELP] = g_signal_new (I_("show-help"), @@ -3194,6 +3385,19 @@ gtk_widget_class_init (GtkWidgetClass *klass) 1, G_MAXINT, 16, GTK_PARAM_READABLE)); + gtk_widget_class_install_style_property (klass, + g_param_spec_int ("text-handle-width", + P_("Width of text selection handles"), + P_("Width of text selection handles"), + 1, G_MAXINT, 16, + GTK_PARAM_READABLE)); + gtk_widget_class_install_style_property (klass, + g_param_spec_int ("text-handle-height", + P_("Height of text selection handles"), + P_("Height of text selection handles"), + 1, G_MAXINT, 20, + GTK_PARAM_READABLE)); + g_type_class_add_private (klass, sizeof (GtkWidgetPrivate)); gtk_widget_class_set_accessible_type (klass, GTK_TYPE_WIDGET_ACCESSIBLE); @@ -3375,6 +3579,9 @@ gtk_widget_set_property (GObject *object, gtk_widget_set_vexpand (widget, g_value_get_boolean (value)); g_object_thaw_notify (G_OBJECT (widget)); break; + case PROP_OPACITY: + gtk_widget_set_opacity (widget, g_value_get_double (value)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -3531,6 +3738,9 @@ gtk_widget_get_property (GObject *object, gtk_widget_get_hexpand (widget) && gtk_widget_get_vexpand (widget)); break; + case PROP_OPACITY: + g_value_set_double (value, gtk_widget_get_opacity (widget)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -3553,6 +3763,8 @@ gtk_widget_init (GtkWidget *widget) priv->allocation.y = -1; priv->allocation.width = 1; priv->allocation.height = 1; + priv->user_alpha = 255; + priv->alpha = 255; priv->window = NULL; priv->parent = NULL; @@ -3560,9 +3772,24 @@ gtk_widget_init (GtkWidget *widget) priv->composite_child = composite_child_stack != 0; priv->double_buffered = TRUE; priv->redraw_on_alloc = TRUE; - priv->width_request_needed = TRUE; - priv->height_request_needed = TRUE; priv->alloc_needed = TRUE; + + switch (gtk_widget_get_direction (widget)) + { + case GTK_TEXT_DIR_LTR: + priv->state_flags = GTK_STATE_FLAG_DIR_LTR; + break; + + case GTK_TEXT_DIR_RTL: + priv->state_flags = GTK_STATE_FLAG_DIR_RTL; + break; + + case GTK_TEXT_DIR_NONE: + default: + g_assert_not_reached (); + break; + } + /* this will be set to TRUE if the widget gets a child or if the * expand flag is set on the widget, but until one of those happen @@ -3573,6 +3800,8 @@ gtk_widget_init (GtkWidget *widget) */ priv->need_compute_expand = FALSE; + _gtk_size_request_cache_init (&priv->requests); + priv->style = gtk_widget_get_default_style (); g_object_ref (priv->style); } @@ -3804,6 +4033,8 @@ gtk_widget_unparent (GtkWidget *widget) if (priv->context) gtk_style_context_set_parent (priv->context, NULL); + _gtk_widget_update_parent_muxer (widget); + g_signal_emit (widget, widget_signals[PARENT_SET], 0, old_parent); if (toplevel) { @@ -3824,6 +4055,8 @@ gtk_widget_unparent (GtkWidget *widget) g_object_notify_queue_clear (G_OBJECT (widget), nqueue); g_object_notify_queue_thaw (G_OBJECT (widget), nqueue); + gtk_widget_propagate_alpha (widget); + gtk_widget_pop_verify_invariants (widget); g_object_unref (widget); } @@ -4089,82 +4322,6 @@ gtk_widget_show_all (GtkWidget *widget) class->show_all (widget); } -static void -_gtk_widget_notify_state_change (GtkWidget *widget, - GtkStateFlags flag, - gboolean target) -{ - GtkStateType state; - - switch (flag) - { - case GTK_STATE_FLAG_ACTIVE: - state = GTK_STATE_ACTIVE; - break; - case GTK_STATE_FLAG_PRELIGHT: - state = GTK_STATE_PRELIGHT; - break; - case GTK_STATE_FLAG_SELECTED: - state = GTK_STATE_SELECTED; - break; - case GTK_STATE_FLAG_INSENSITIVE: - state = GTK_STATE_INSENSITIVE; - break; - case GTK_STATE_FLAG_INCONSISTENT: - state = GTK_STATE_INCONSISTENT; - break; - case GTK_STATE_FLAG_FOCUSED: - state = GTK_STATE_FOCUSED; - break; - default: - return; - } - - gtk_style_context_notify_state_change (widget->priv->context, - gtk_widget_get_window (widget), - NULL, state, target); -} - -/* Initializes state transitions for those states that - * were enabled before mapping and have a looping animation. - */ -static void -_gtk_widget_start_state_transitions (GtkWidget *widget) -{ - GtkStateFlags state, flag; - - if (!widget->priv->context) - return; - - state = gtk_widget_get_state_flags (widget); - flag = GTK_STATE_FLAG_FOCUSED; - - while (flag) - { - GtkAnimationDescription *animation_desc; - - if ((state & flag) == 0) - { - flag >>= 1; - continue; - } - - gtk_style_context_get (widget->priv->context, state, - "transition", &animation_desc, - NULL); - - if (animation_desc) - { - if (_gtk_animation_description_get_loop (animation_desc)) - _gtk_widget_notify_state_change (widget, flag, TRUE); - - _gtk_animation_description_unref (animation_desc); - } - - flag >>= 1; - } -} - /** * gtk_widget_map: * @widget: a #GtkWidget @@ -4195,9 +4352,10 @@ gtk_widget_map (GtkWidget *widget) if (!gtk_widget_get_has_window (widget)) gdk_window_invalidate_rect (priv->window, &priv->allocation, FALSE); - gtk_widget_pop_verify_invariants (widget); + if (widget->priv->context) + _gtk_style_context_update_animating (widget->priv->context); - _gtk_widget_start_state_transitions (widget); + gtk_widget_pop_verify_invariants (widget); } } @@ -4224,13 +4382,14 @@ gtk_widget_unmap (GtkWidget *widget) if (!gtk_widget_get_has_window (widget)) gdk_window_invalidate_rect (priv->window, &priv->allocation, FALSE); _gtk_tooltip_hide (widget); + + if (widget->priv->context) + _gtk_style_context_update_animating (widget->priv->context); + g_signal_emit (widget, widget_signals[UNMAP], 0); gtk_widget_pop_verify_invariants (widget); - if (priv->context) - gtk_style_context_cancel_animations (priv->context, NULL); - /* Unset pointer/window info */ g_object_set_qdata (G_OBJECT (widget), quark_pointer_window, NULL); } @@ -4360,6 +4519,239 @@ gtk_widget_update_devices_mask (GtkWidget *widget, gtk_widget_set_device_enabled_internal (widget, GDK_DEVICE (l->data), recurse, TRUE); } +typedef struct _GtkTickCallbackInfo GtkTickCallbackInfo; + +struct _GtkTickCallbackInfo +{ + guint refcount; + + guint id; + GtkTickCallback callback; + gpointer user_data; + GDestroyNotify notify; + + guint destroyed : 1; +}; + +static void +ref_tick_callback_info (GtkTickCallbackInfo *info) +{ + info->refcount++; +} + +static void +unref_tick_callback_info (GtkWidget *widget, + GtkTickCallbackInfo *info, + GList *link) +{ + GtkWidgetPrivate *priv = widget->priv; + + info->refcount--; + if (info->refcount == 0) + { + priv->tick_callbacks = g_list_delete_link (priv->tick_callbacks, link); + if (info->notify) + info->notify (info->user_data); + g_slice_free (GtkTickCallbackInfo, info); + } + + if (priv->tick_callbacks == NULL && priv->realized) + { + GdkFrameClock *frame_clock = gtk_widget_get_frame_clock (widget); + g_signal_handlers_disconnect_by_func (frame_clock, + (gpointer) gtk_widget_on_frame_clock_update, + widget); + gdk_frame_clock_end_updating (frame_clock); + } +} + +static void +destroy_tick_callback_info (GtkWidget *widget, + GtkTickCallbackInfo *info, + GList *link) +{ + if (!info->destroyed) + { + info->destroyed = TRUE; + unref_tick_callback_info (widget, info, link); + } +} + +static void +gtk_widget_on_frame_clock_update (GdkFrameClock *frame_clock, + GtkWidget *widget) +{ + GtkWidgetPrivate *priv = widget->priv; + GList *l; + + g_object_ref (widget); + + for (l = priv->tick_callbacks; l;) + { + GtkTickCallbackInfo *info = l->data; + GList *next; + + ref_tick_callback_info (info); + if (!info->destroyed) + { + if (info->callback (widget, + frame_clock, + info->user_data) == G_SOURCE_REMOVE) + { + destroy_tick_callback_info (widget, info, l); + } + } + + next = l->next; + unref_tick_callback_info (widget, info, l); + l = next; + } + + g_object_unref (widget); +} + +static guint tick_callback_id; + +/** + * gtk_widget_add_tick_callback: + * @widget: a #GtkWidget + * @callback: function to call for updating animations + * @user_data: data to pass to @callback + * @notify: function to call to free @user_data when the callback is removed. + * + * Queues a animation frame update and adds a callback to be called + * before each frame. Until the tick callback is removed, it will be + * called frequently (usually at the frame rate of the output device + * or as quickly as the application an be repainted, whichever is + * slower). For this reason, is most suitable for handling graphics + * that change every frame or every few frames. The tick callback does + * not automatically imply a relayout or repaint. If you want a + * repaint or relayout, and aren't changing widget properties that + * would trigger that (for example, changing the text of a #GtkLabel), + * then you will have to call gtk_widget_queue_resize() or + * gtk_widget_queue_draw_area() yourself. + * + * gdk_frame_clock_get_frame_time() should generally be used for timing + * continuous animations and + * gdk_frame_timings_get_predicted_presentation_time() if you are + * trying to display isolated frames at particular times. + * + * This is a more convenient alternative to connecting directly to the + * #GdkFrameClock::update signal of #GdkFrameClock, since you don't + * have to worry about when a #GdkFrameClock is assigned to a widget. + * + * Returns: an id for the connection of this callback. Remove the callback + * by passing it to gtk_widget_remove_tick_callback() + * + * Since: 3.8 + */ +guint +gtk_widget_add_tick_callback (GtkWidget *widget, + GtkTickCallback callback, + gpointer user_data, + GDestroyNotify notify) +{ + GtkWidgetPrivate *priv; + GtkTickCallbackInfo *info; + + g_return_val_if_fail (GTK_IS_WIDGET (widget), 0); + + priv = widget->priv; + + if (priv->tick_callbacks == NULL && priv->realized) + { + GdkFrameClock *frame_clock = gtk_widget_get_frame_clock (widget); + g_signal_connect (frame_clock, "update", + G_CALLBACK (gtk_widget_on_frame_clock_update), + widget); + gdk_frame_clock_begin_updating (frame_clock); + } + + info = g_slice_new0 (GtkTickCallbackInfo); + + info->refcount = 1; + info->id = ++tick_callback_id; + info->callback = callback; + info->user_data = user_data; + info->notify = notify; + + priv->tick_callbacks = g_list_prepend (priv->tick_callbacks, + info); + + return info->id; +} + +/** + * gtk_widget_remove_tick_callback: + * @widget: a #GtkWidget + * @id: an id returned by gtk_widget_add_tick_callback() + * + * Removes a tick callback previously registered with + * gtk_widget_add_tick_callback(). + * + * Since: 3.8 + */ +void +gtk_widget_remove_tick_callback (GtkWidget *widget, + guint id) +{ + GtkWidgetPrivate *priv; + GList *l; + + g_return_if_fail (GTK_IS_WIDGET (widget)); + + priv = widget->priv; + + for (l = priv->tick_callbacks; l; l = l->next) + { + GtkTickCallbackInfo *info = l->data; + if (info->id == id) + destroy_tick_callback_info (widget, info, l); + } +} + +static void +gtk_widget_connect_frame_clock (GtkWidget *widget, + GdkFrameClock *frame_clock) +{ + GtkWidgetPrivate *priv = widget->priv; + + if (GTK_IS_CONTAINER (widget)) + _gtk_container_maybe_start_idle_sizer (GTK_CONTAINER (widget)); + + if (priv->tick_callbacks != NULL) + { + g_signal_connect (frame_clock, "update", + G_CALLBACK (gtk_widget_on_frame_clock_update), + widget); + gdk_frame_clock_begin_updating (frame_clock); + } + + if (priv->context) + gtk_style_context_set_frame_clock (priv->context, frame_clock); +} + +static void +gtk_widget_disconnect_frame_clock (GtkWidget *widget, + GdkFrameClock *frame_clock) +{ + GtkWidgetPrivate *priv = widget->priv; + + if (GTK_IS_CONTAINER (widget)) + _gtk_container_stop_idle_sizer (GTK_CONTAINER (widget)); + + if (priv->tick_callbacks) + { + g_signal_handlers_disconnect_by_func (frame_clock, + (gpointer) gtk_widget_on_frame_clock_update, + widget); + gdk_frame_clock_end_updating (frame_clock); + } + + if (priv->context) + gtk_style_context_set_frame_clock (priv->context, NULL); +} + /** * gtk_widget_realize: * @widget: a #GtkWidget @@ -4440,6 +4832,9 @@ gtk_widget_realize (GtkWidget *widget) _gtk_widget_enable_device_events (widget); gtk_widget_update_devices_mask (widget, TRUE); + gtk_widget_connect_frame_clock (widget, + gtk_widget_get_frame_clock (widget)); + gtk_widget_pop_verify_invariants (widget); } } @@ -4472,6 +4867,9 @@ gtk_widget_unrealize (GtkWidget *widget) if (widget->priv->mapped) gtk_widget_unmap (widget); + gtk_widget_disconnect_frame_clock (widget, + gtk_widget_get_frame_clock (widget)); + g_signal_emit (widget, widget_signals[UNREALIZE], 0); g_assert (!widget->priv->mapped); gtk_widget_set_realized (widget, FALSE); @@ -4610,7 +5008,7 @@ gtk_widget_queue_resize (GtkWidget *widget) g_return_if_fail (GTK_IS_WIDGET (widget)); if (gtk_widget_get_realized (widget)) - gtk_widget_queue_shallow_draw (widget); + gtk_widget_queue_draw (widget); _gtk_size_group_queue_resize (widget, 0); } @@ -4633,26 +5031,81 @@ gtk_widget_queue_resize_no_redraw (GtkWidget *widget) } /** - * gtk_widget_size_request: + * gtk_widget_get_frame_clock: * @widget: a #GtkWidget - * @requisition: (out): a #GtkRequisition to be filled in * - * This function is typically used when implementing a #GtkContainer - * subclass. Obtains the preferred size of a widget. The container - * uses this information to arrange its child widgets and decide what - * size allocations to give them with gtk_widget_size_allocate(). + * Obtains the frame clock for a widget. The frame clock is a global + * "ticker" that can be used to drive animations and repaints. The + * most common reason to get the frame clock is to call + * gdk_frame_clock_get_frame_time(), in order to get a time to use for + * animating. For example you might record the start of the animation + * with an initial value from gdk_frame_clock_get_frame_time(), and + * then update the animation by calling + * gdk_frame_clock_get_frame_time() again during each repaint. * - * You can also call this function from an application, with some - * caveats. Most notably, getting a size request requires the widget - * to be associated with a screen, because font information may be - * needed. Multihead-aware applications should keep this in mind. + * gdk_frame_clock_request_phase() will result in a new frame on the + * clock, but won't necessarily repaint any widgets. To repaint a + * widget, you have to use gtk_widget_queue_draw() which invalidates + * the widget (thus scheduling it to receive a draw on the next + * frame). gtk_widget_queue_draw() will also end up requesting a frame + * on the appropriate frame clock. * - * Also remember that the size request is not necessarily the size - * a widget will actually be allocated. + * A widget's frame clock will not change while the widget is + * mapped. Reparenting a widget (which implies a temporary unmap) can + * change the widget's frame clock. * - * Deprecated: 3.0: Use gtk_widget_get_preferred_size() instead. - **/ -void + * Unrealized widgets do not have a frame clock. + * + * Return value: (transfer none): a #GdkFrameClock (or #NULL if widget is unrealized) + * + * Since: 3.0 + */ +GdkFrameClock* +gtk_widget_get_frame_clock (GtkWidget *widget) +{ + g_return_val_if_fail (GTK_IS_WIDGET (widget), 0); + + if (widget->priv->realized) + { + /* We use gtk_widget_get_toplevel() here to make it explicit that + * the frame clock is a property of the toplevel that a widget + * is anchored to; gdk_window_get_toplevel() will go up the + * hierarchy anyways, but should squash any funny business with + * reparenting windows and widgets. + */ + GtkWidget *toplevel = gtk_widget_get_toplevel (widget); + GdkWindow *window = gtk_widget_get_window (toplevel); + g_assert (window != NULL); + + return gdk_window_get_frame_clock (window); + } + else + { + return NULL; + } +} + +/** + * gtk_widget_size_request: + * @widget: a #GtkWidget + * @requisition: (out): a #GtkRequisition to be filled in + * + * This function is typically used when implementing a #GtkContainer + * subclass. Obtains the preferred size of a widget. The container + * uses this information to arrange its child widgets and decide what + * size allocations to give them with gtk_widget_size_allocate(). + * + * You can also call this function from an application, with some + * caveats. Most notably, getting a size request requires the widget + * to be associated with a screen, because font information may be + * needed. Multihead-aware applications should keep this in mind. + * + * Also remember that the size request is not necessarily the size + * a widget will actually be allocated. + * + * Deprecated: 3.0: Use gtk_widget_get_preferred_size() instead. + **/ +void gtk_widget_size_request (GtkWidget *widget, GtkRequisition *requisition) { @@ -4732,29 +5185,6 @@ gtk_widget_invalidate_widget_windows (GtkWidget *widget, invalidate_predicate, widget); } -/** - * gtk_widget_queue_shallow_draw: - * @widget: a #GtkWidget - * - * Like gtk_widget_queue_draw(), but only windows owned - * by @widget are invalidated. - **/ -static void -gtk_widget_queue_shallow_draw (GtkWidget *widget) -{ - GdkRectangle rect; - cairo_region_t *region; - - if (!gtk_widget_get_realized (widget)) - return; - - gtk_widget_get_allocation (widget, &rect); - - region = cairo_region_create_rectangle (&rect); - gtk_widget_invalidate_widget_windows (widget, region); - cairo_region_destroy (region); -} - /** * gtk_widget_size_allocate: * @widget: a #GtkWidget @@ -4787,6 +5217,9 @@ gtk_widget_size_allocate (GtkWidget *widget, g_return_if_fail (GTK_IS_WIDGET (widget)); + if (!priv->visible && !gtk_widget_is_toplevel (widget)) + return; + gtk_widget_push_verify_invariants (widget); #ifdef G_ENABLE_DEBUG @@ -4812,9 +5245,8 @@ gtk_widget_size_allocate (GtkWidget *widget, #endif /* G_ENABLE_DEBUG */ alloc_needed = priv->alloc_needed; - if (!priv->width_request_needed && !priv->height_request_needed) - /* Preserve request/allocate ordering */ - priv->alloc_needed = FALSE; + /* Preserve request/allocate ordering */ + priv->alloc_needed = FALSE; old_allocation = priv->allocation; real_allocation = *allocation; @@ -4906,9 +5338,7 @@ gtk_widget_size_allocate (GtkWidget *widget, g_signal_emit (widget, widget_signals[SIZE_ALLOCATE], 0, &real_allocation); /* Size allocation is god... after consulting god, no further requests or allocations are needed */ - priv->width_request_needed = FALSE; - priv->height_request_needed = FALSE; - priv->alloc_needed = FALSE; + priv->alloc_needed = FALSE; if (gtk_widget_get_mapped (widget)) { @@ -4936,14 +5366,6 @@ gtk_widget_size_allocate (GtkWidget *widget, cairo_region_destroy (invalidate); } } - - if (size_changed || position_changed) - { - GtkStyleContext *context; - - context = gtk_widget_get_style_context (widget); - _gtk_style_context_invalidate_animation_areas (context); - } } if ((size_changed || position_changed) && priv->parent && @@ -5358,7 +5780,7 @@ widget_new_accel_closure (GtkWidget *widget, } /** - * gtk_widget_add_accelerator + * gtk_widget_add_accelerator: * @widget: widget to install an accelerator on * @accel_signal: widget signal to emit on accelerator activation * @accel_group: accel group for this widget, added to its toplevel @@ -5369,7 +5791,7 @@ widget_new_accel_closure (GtkWidget *widget, * Installs an accelerator for this @widget in @accel_group that causes * @accel_signal to be emitted if the accelerator is activated. * The @accel_group needs to be added to the widget's toplevel via - * gtk_window_add_accel_group(), and the signal must be of type %G_RUN_ACTION. + * gtk_window_add_accel_group(), and the signal must be of type %G_SIGNAL_ACTION. * Accelerators added through this function are not user changeable during * runtime. If you want to support accelerators that can be changed by the * user, use gtk_accel_map_add_entry() and gtk_widget_set_accel_path() or @@ -5719,8 +6141,6 @@ _gtk_widget_draw_internal (GtkWidget *widget, cairo_t *cr, gboolean clip_to_size) { - GtkStyleContext *context; - if (!gtk_widget_is_drawable (widget)) return; @@ -5756,9 +6176,6 @@ _gtk_widget_draw_internal (GtkWidget *widget, cairo_status_to_string (cairo_status (cr))); } } - - context = gtk_widget_get_style_context (widget); - _gtk_style_context_coalesce_animation_areas (context, widget); } /** @@ -5829,7 +6246,7 @@ static gboolean gtk_widget_real_focus_in_event (GtkWidget *widget, GdkEventFocus *event) { - gtk_widget_queue_shallow_draw (widget); + gtk_widget_queue_draw (widget); return FALSE; } @@ -5838,7 +6255,7 @@ static gboolean gtk_widget_real_focus_out_event (GtkWidget *widget, GdkEventFocus *event) { - gtk_widget_queue_shallow_draw (widget); + gtk_widget_queue_draw (widget); return FALSE; } @@ -5951,6 +6368,59 @@ gtk_widget_event (GtkWidget *widget, return gtk_widget_event_internal (widget, event); } +void +_gtk_widget_set_captured_event_handler (GtkWidget *widget, + GtkCapturedEventHandler callback) +{ + g_object_set_data (G_OBJECT (widget), "captured-event-handler", callback); +} + +gboolean +_gtk_widget_captured_event (GtkWidget *widget, + GdkEvent *event) +{ + gboolean return_val = FALSE; + GtkCapturedEventHandler handler; + + g_return_val_if_fail (GTK_IS_WIDGET (widget), TRUE); + g_return_val_if_fail (WIDGET_REALIZED_FOR_EVENT (widget, event), TRUE); + + if (event->type == GDK_EXPOSE) + { + g_warning ("Events of type GDK_EXPOSE cannot be synthesized. To get " + "the same effect, call gdk_window_invalidate_rect/region(), " + "followed by gdk_window_process_updates()."); + return TRUE; + } + + if (!event_window_is_still_viewable (event)) + return TRUE; + + handler = g_object_get_data (G_OBJECT (widget), "captured-event-handler"); + if (!handler) + return FALSE; + + g_object_ref (widget); + + return_val = handler (widget, event); + return_val |= !WIDGET_REALIZED_FOR_EVENT (widget, event); + + /* The widget that was originally to receive the event + * handles motion hints, but the capturing widget might + * not, so ensure we get further motion events. + */ + if (return_val && + event->type == GDK_MOTION_NOTIFY && + event->motion.is_hint && + (gdk_window_get_events (event->any.window) & + GDK_POINTER_MOTION_HINT_MASK) != 0) + gdk_event_request_motions (&event->motion); + + g_object_unref (widget); + + return return_val; +} + /* Returns TRUE if a translation should be done */ gboolean _gtk_widget_get_translation_to_window (GtkWidget *widget, @@ -6646,6 +7116,7 @@ gtk_widget_real_style_updated (GtkWidget *widget) GtkWidgetPrivate *priv = widget->priv; gtk_widget_update_pango_context (widget); + gtk_widget_update_alpha (widget); if (priv->style != NULL && priv->style != gtk_widget_get_default_style ()) @@ -6661,14 +7132,27 @@ gtk_widget_real_style_updated (GtkWidget *widget) if (widget->priv->context) { + const GtkBitmask *changes = _gtk_style_context_get_changes (widget->priv->context); + if (gtk_widget_get_realized (widget) && - gtk_widget_get_has_window (widget)) + gtk_widget_get_has_window (widget) && + !gtk_widget_get_app_paintable (widget)) gtk_style_context_set_background (widget->priv->context, widget->priv->window); - } - if (widget->priv->anchored) - gtk_widget_queue_resize (widget); + if (widget->priv->anchored) + { + if (changes && _gtk_css_style_property_changes_affect_size (changes)) + gtk_widget_queue_resize (widget); + else + gtk_widget_queue_draw (widget); + } + } + else + { + if (widget->priv->anchored) + gtk_widget_queue_resize (widget); + } } static gboolean @@ -6945,17 +7429,26 @@ void _gtk_widget_set_has_default (GtkWidget *widget, gboolean has_default) { + GtkStyleContext *context; + widget->priv->has_default = has_default; + + context = gtk_widget_get_style_context (widget); + + if (has_default) + gtk_style_context_add_class (context, GTK_STYLE_CLASS_DEFAULT); + else + gtk_style_context_remove_class (context, GTK_STYLE_CLASS_DEFAULT); } /** * gtk_widget_grab_default: * @widget: a #GtkWidget * - * Causes @widget to become the default widget. @widget must have the - * %GTK_CAN_DEFAULT flag set; typically you have to set this flag - * yourself by calling gtk_widget_set_can_default (@widget, - * %TRUE). The default widget is activated when + * Causes @widget to become the default widget. @widget must be able to be + * a default widget; typically you would ensure this yourself + * by calling gtk_widget_set_can_default() with a %TRUE value. + * The default widget is activated when * the user presses Enter in a window. Default widgets must be * activatable, that is, gtk_widget_activate() should affect them. Note * that #GtkEntry widgets require the "activates-default" property @@ -7010,13 +7503,13 @@ gtk_widget_set_receives_default (GtkWidget *widget, * gtk_widget_get_receives_default: * @widget: a #GtkWidget * - * Determines whether @widget is alyways treated as default widget - * withing its toplevel when it has the focus, even if another widget + * Determines whether @widget is always treated as the default widget + * within its toplevel when it has the focus, even if another widget * is the default. * * See gtk_widget_set_receives_default(). * - * Return value: %TRUE if @widget acts as default widget when focussed, + * Return value: %TRUE if @widget acts as the default widget when focussed, * %FALSE otherwise * * Since: 2.18 @@ -7140,7 +7633,7 @@ gtk_widget_set_name (GtkWidget *widget, g_free (priv->name); priv->name = new_name; - gtk_widget_reset_style (widget); + _gtk_widget_invalidate_style_context (widget, GTK_CSS_CHANGE_NAME); g_object_notify (G_OBJECT (widget), "name"); } @@ -7170,9 +7663,9 @@ gtk_widget_get_name (GtkWidget *widget) } static void -_gtk_widget_update_state_flags (GtkWidget *widget, - GtkStateFlags flags, - guint operation) +gtk_widget_update_state_flags (GtkWidget *widget, + GtkStateFlags flags_to_set, + GtkStateFlags flags_to_unset) { GtkWidgetPrivate *priv; @@ -7181,27 +7674,22 @@ _gtk_widget_update_state_flags (GtkWidget *widget, /* Handle insensitive first, since it is propagated * differently throughout the widget hierarchy. */ - if ((priv->state_flags & GTK_STATE_FLAG_INSENSITIVE) && (flags & GTK_STATE_FLAG_INSENSITIVE) && (operation == STATE_CHANGE_UNSET)) + if ((priv->state_flags & GTK_STATE_FLAG_INSENSITIVE) && (flags_to_unset & GTK_STATE_FLAG_INSENSITIVE)) gtk_widget_set_sensitive (widget, TRUE); - else if (!(priv->state_flags & GTK_STATE_FLAG_INSENSITIVE) && (flags & GTK_STATE_FLAG_INSENSITIVE) && (operation != STATE_CHANGE_UNSET)) + else if (!(priv->state_flags & GTK_STATE_FLAG_INSENSITIVE) && (flags_to_set & GTK_STATE_FLAG_INSENSITIVE)) gtk_widget_set_sensitive (widget, FALSE); - else if ((priv->state_flags & GTK_STATE_FLAG_INSENSITIVE) && !(flags & GTK_STATE_FLAG_INSENSITIVE) && (operation == STATE_CHANGE_REPLACE)) - gtk_widget_set_sensitive (widget, TRUE); - if (operation != STATE_CHANGE_REPLACE) - flags &= ~(GTK_STATE_FLAG_INSENSITIVE); + flags_to_set &= ~(GTK_STATE_FLAG_INSENSITIVE); + flags_to_unset &= ~(GTK_STATE_FLAG_INSENSITIVE); - if (flags != 0 || - operation == STATE_CHANGE_REPLACE) + if (flags_to_set != 0 || flags_to_unset != 0) { GtkStateData data; - data.flags = flags; - data.operation = operation; + data.flags_to_set = flags_to_set; + data.flags_to_unset = flags_to_unset; gtk_widget_propagate_state (widget, &data); - - gtk_widget_queue_resize (widget); } } @@ -7235,9 +7723,9 @@ gtk_widget_set_state_flags (GtkWidget *widget, return; if (clear) - _gtk_widget_update_state_flags (widget, flags, STATE_CHANGE_REPLACE); + gtk_widget_update_state_flags (widget, flags, ~(flags ^ (GTK_STATE_FLAG_DIR_LTR | GTK_STATE_FLAG_DIR_RTL))); else - _gtk_widget_update_state_flags (widget, flags, STATE_CHANGE_SET); + gtk_widget_update_state_flags (widget, flags, 0); } /** @@ -7260,7 +7748,7 @@ gtk_widget_unset_state_flags (GtkWidget *widget, if ((widget->priv->state_flags & flags) == 0) return; - _gtk_widget_update_state_flags (widget, flags, STATE_CHANGE_UNSET); + gtk_widget_update_state_flags (widget, 0, flags); } /** @@ -7337,7 +7825,10 @@ gtk_widget_set_state (GtkWidget *widget, break; } - _gtk_widget_update_state_flags (widget, flags, STATE_CHANGE_REPLACE); + gtk_widget_update_state_flags (widget, + flags, + (GTK_STATE_FLAG_ACTIVE | GTK_STATE_FLAG_PRELIGHT | GTK_STATE_FLAG_SELECTED + | GTK_STATE_FLAG_INSENSITIVE | GTK_STATE_FLAG_INCONSISTENT | GTK_STATE_FLAG_FOCUSED) ^ flags); } /** @@ -7407,16 +7898,28 @@ void _gtk_widget_set_visible_flag (GtkWidget *widget, gboolean visible) { - widget->priv->visible = visible; + GtkWidgetPrivate *priv = widget->priv; + + priv->visible = visible; + + if (!visible) + { + priv->allocation.x = -1; + priv->allocation.y = -1; + priv->allocation.width = 1; + priv->allocation.height = 1; + } } /** * gtk_widget_get_visible: * @widget: a #GtkWidget * - * Determines whether the widget is visible. Note that this doesn't - * take into account whether the widget's parent is also visible - * or the widget is obscured in any way. + * Determines whether the widget is visible. If you want to + * take into account whether the widget's parent is also marked as + * visible, use gtk_widget_is_visible() instead. + * + * This function does not check if the widget is obscured in any way. * * See gtk_widget_set_visible(). * @@ -7432,6 +7935,39 @@ gtk_widget_get_visible (GtkWidget *widget) return widget->priv->visible; } +/** + * gtk_widget_is_visible: + * @widget: a #GtkWidget + * + * Determines whether the widget and all its parents are marked as + * visible. + * + * This function does not check if the widget is obscured in any way. + * + * See also gtk_widget_get_visible() and gtk_widget_set_visible() + * + * Return value: %TRUE if the widget and all its parents are visible + * + * Since: 3.8 + **/ +gboolean +gtk_widget_is_visible (GtkWidget *widget) +{ + g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); + + while (widget) + { + GtkWidgetPrivate *priv = widget->priv; + + if (!priv->visible) + return FALSE; + + widget = priv->parent; + } + + return TRUE; +} + /** * gtk_widget_set_has_window: * @widget: a #GtkWidget @@ -7671,7 +8207,7 @@ gtk_widget_get_app_paintable (GtkWidget *widget) * to turn off the buffering. "Double buffered" simply means that * gdk_window_begin_paint_region() and gdk_window_end_paint() are called * automatically around expose events sent to the - * widget. gdk_window_begin_paint() diverts all drawing to a widget's + * widget. gdk_window_begin_paint_region() diverts all drawing to a widget's * window to an offscreen buffer, and gdk_window_end_paint() draws the * buffer to the screen. The result is that users see the window * update in one smooth step, and don't see individual graphics @@ -7684,7 +8220,7 @@ gtk_widget_get_app_paintable (GtkWidget *widget) * Note: if you turn off double-buffering, you have to handle * expose events, since even the clearing to the background color or * pixmap will not happen automatically (as it is done in - * gdk_window_begin_paint()). + * gdk_window_begin_paint_region()). **/ void gtk_widget_set_double_buffered (GtkWidget *widget, @@ -7785,12 +8321,16 @@ gtk_widget_set_sensitive (GtkWidget *widget, { GtkStateData data; - data.flags = GTK_STATE_FLAG_INSENSITIVE; - if (sensitive) - data.operation = STATE_CHANGE_UNSET; + { + data.flags_to_set = 0; + data.flags_to_unset = GTK_STATE_FLAG_INSENSITIVE; + } else - data.operation = STATE_CHANGE_SET; + { + data.flags_to_set = GTK_STATE_FLAG_INSENSITIVE; + data.flags_to_unset = 0; + } gtk_widget_propagate_state (widget, &data); @@ -7841,18 +8381,6 @@ gtk_widget_is_sensitive (GtkWidget *widget) return !(widget->priv->state_flags & GTK_STATE_FLAG_INSENSITIVE); } -static void -_gtk_widget_update_path (GtkWidget *widget) -{ - if (widget->priv->path) - { - gtk_widget_path_free (widget->priv->path); - widget->priv->path = NULL; - } - - gtk_widget_get_path (widget); -} - /** * gtk_widget_set_parent: * @widget: a #GtkWidget @@ -7903,16 +8431,15 @@ gtk_widget_set_parent (GtkWidget *widget, /* Merge both old state and current parent state, * making sure to only propagate the right states */ - data.flags = parent_flags & GTK_STATE_FLAGS_DO_PROPAGATE; - data.flags |= priv->state_flags; - - data.operation = STATE_CHANGE_REPLACE; + data.flags_to_set = parent_flags & GTK_STATE_FLAGS_DO_PROPAGATE; + data.flags_to_unset = 0; gtk_widget_propagate_state (widget, &data); if (priv->context) gtk_style_context_set_parent (priv->context, gtk_widget_get_style_context (parent)); - gtk_widget_reset_style (widget); + + _gtk_widget_update_parent_muxer (widget); g_signal_emit (widget, widget_signals[PARENT_SET], 0, NULL); if (priv->parent->priv->anchored) @@ -7952,6 +8479,8 @@ gtk_widget_set_parent (GtkWidget *widget, gtk_widget_queue_compute_expand (parent); } + gtk_widget_propagate_alpha (widget); + gtk_widget_pop_verify_invariants (widget); } @@ -7975,10 +8504,7 @@ static void modifier_style_changed (GtkModifierStyle *style, GtkWidget *widget) { - GtkStyleContext *context; - - context = gtk_widget_get_style_context (widget); - gtk_style_context_invalidate (context); + _gtk_widget_invalidate_style_context (widget, GTK_CSS_CHANGE_ANY); } static GtkModifierStyle * @@ -8235,6 +8761,17 @@ gtk_widget_propagate_hierarchy_changed_recurse (GtkWidget *widget, priv->anchored = new_anchored; + /* This can only happen with gtk_widget_reparent() */ + if (priv->realized) + { + if (new_anchored) + gtk_widget_connect_frame_clock (widget, + gtk_widget_get_frame_clock (widget)); + else + gtk_widget_disconnect_frame_clock (widget, + gtk_widget_get_frame_clock (info->previous_toplevel)); + } + g_signal_emit (widget, widget_signals[HIERARCHY_CHANGED], 0, info->previous_toplevel); do_screen_change (widget, info->previous_screen, info->new_screen); @@ -8381,7 +8918,7 @@ _gtk_widget_propagate_screen_changed (GtkWidget *widget, static void reset_style_recurse (GtkWidget *widget, gpointer data) { - _gtk_widget_update_path (widget); + _gtk_widget_invalidate_style_context (widget, GTK_CSS_CHANGE_ANY); if (GTK_IS_CONTAINER (widget)) gtk_container_forall (GTK_CONTAINER (widget), @@ -8531,10 +9068,6 @@ gtk_widget_verify_invariants (GtkWidget *widget) #if 0 /* widget_system.txt says these hold, but they don't. */ - if (widget->priv->resize_pending) - g_warning ("%s %p resize pending but not realized", - G_OBJECT_TYPE_NAME (widget), widget); - if (widget->priv->alloc_needed) g_warning ("%s %p alloc needed but not realized", G_OBJECT_TYPE_NAME (widget), widget); @@ -8612,12 +9145,8 @@ gtk_widget_peek_pango_context (GtkWidget *widget) * by gtk_widget_create_pango_context(), this context is owned by * the widget (it can be used until the screen for the widget changes * or the widget is removed from its toplevel), and will be updated to - * match any changes to the widget's attributes. - * - * If you create and keep a #PangoLayout using this context, you must - * deal with changes to the context by calling pango_layout_context_changed() - * on the layout in response to the #GtkWidget::style-updated and - * #GtkWidget::direction-changed signals for the widget. + * match any changes to the widget's attributes. This can be tracked + * by using the #GtkWidget::screen-changed signal on the widget. * * Return value: (transfer none): the #PangoContext for the widget. **/ @@ -8645,18 +9174,21 @@ static void update_pango_context (GtkWidget *widget, PangoContext *context) { - const PangoFontDescription *font_desc; + PangoFontDescription *font_desc; GtkStyleContext *style_context; style_context = gtk_widget_get_style_context (widget); - - font_desc = gtk_style_context_get_font (style_context, - gtk_widget_get_state_flags (widget)); + gtk_style_context_get (style_context, + gtk_widget_get_state_flags (widget), + "font", &font_desc, + NULL); pango_context_set_font_description (context, font_desc); pango_context_set_base_dir (context, gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR ? PANGO_DIRECTION_LTR : PANGO_DIRECTION_RTL); + + pango_font_description_free (font_desc); } static void @@ -8725,11 +9257,10 @@ gtk_widget_create_pango_context (GtkWidget *widget) * font description, and base direction for drawing text for * this widget. * - * If you keep a #PangoLayout created in this way around, in order to - * notify the layout of changes to the base direction or font of this - * widget, you must call pango_layout_context_changed() in response to - * the #GtkWidget::style-updated and #GtkWidget::direction-changed signals - * for the widget. + * If you keep a #PangoLayout created in this way around, you need + * to re-create it when the widget #PangoContext is replaced. + * This can be tracked by using the #GtkWidget::screen-changed signal + * on the widget. * * Return value: (transfer full): the new #PangoLayout **/ @@ -9282,9 +9813,8 @@ gtk_widget_set_usize_internal (GtkWidget *widget, * @height: height @widget should request, or -1 to unset * * Sets the minimum size of a widget; that is, the widget's size - * request will be @width by @height. You can use this function to - * force a widget to be either larger or smaller than it normally - * would be. + * request will be at least @width by @height. You can use this + * function to force a widget to be larger than it normally would be. * * In most cases, gtk_window_set_default_size() is a better choice for * toplevel windows than this function; setting the default size will @@ -9308,9 +9838,6 @@ gtk_widget_set_usize_internal (GtkWidget *widget, * If the size request in a given direction is -1 (unset), then * the "natural" size request of the widget will be used instead. * - * Widgets can't actually be allocated a size less than 1 by 1, but - * you can pass 0,0 to this function to mean "as small as possible." - * * The size request set here does not include any margin from the * #GtkWidget properties margin-left, margin-right, margin-top, and * margin-bottom, but it does include pretty much all other padding @@ -9372,7 +9899,7 @@ gtk_widget_get_size_request (GtkWidget *widget, * @width: new forced minimum width * @height: new forced minimum height * @old_width: location to store previous forced minimum width - * @old_width: location to store previous forced minumum height + * @old_height: location to store previous forced minumum height * * Temporarily establishes a forced minimum size for a widget; this * is used by GtkWindow when calculating the size to add to the @@ -9675,7 +10202,7 @@ gtk_widget_add_device_events (GtkWidget *widget, * gtk_widget_get_ancestor (widget, GTK_TYPE_WINDOW) * would return * %NULL if @widget wasn't inside a toplevel window, and if the - * window was inside a #GtkWindow-derived widget which was in turn + * window was inside a #GtkWindow-derived widget which was in turn * inside the toplevel #GtkWindow. While the second case may * seem unlikely, it actually happens when a #GtkPlug is embedded * inside a #GtkSocket within the same application. @@ -9744,7 +10271,7 @@ gtk_widget_get_ancestor (GtkWidget *widget, * * Sets the visual that should be used for by widget and its children for * creating #GdkWindows. The visual must be on the same #GdkScreen as - * returned by gdk_widget_get_screen(), so handling the + * returned by gtk_widget_get_screen(), so handling the * #GtkWidget::screen-changed signal is necessary. * * Setting a new @visual will not cause @widget to recreate its windows, @@ -10047,11 +10574,32 @@ static void gtk_widget_emit_direction_changed (GtkWidget *widget, GtkTextDirection old_dir) { + GtkTextDirection direction; + GtkStateFlags state; + gtk_widget_update_pango_context (widget); - if (widget->priv->context) - gtk_style_context_set_direction (widget->priv->context, - gtk_widget_get_direction (widget)); + direction = gtk_widget_get_direction (widget); + state = widget->priv->state_flags; + state &= GTK_STATE_FLAG_DIR_LTR | GTK_STATE_FLAG_DIR_RTL; + + switch (direction) + { + case GTK_TEXT_DIR_LTR: + state |= GTK_STATE_FLAG_DIR_LTR; + break; + + case GTK_TEXT_DIR_RTL: + state |= GTK_STATE_FLAG_DIR_RTL; + break; + + case GTK_TEXT_DIR_NONE: + default: + g_assert_not_reached (); + break; + } + + gtk_widget_set_state_flags (widget, state, TRUE); g_signal_emit (widget, widget_signals[DIRECTION_CHANGED], 0, old_dir); } @@ -10178,6 +10726,27 @@ gtk_widget_get_default_direction (void) return gtk_default_direction; } +static void +gtk_widget_constructed (GObject *object) +{ + GtkWidget *widget = GTK_WIDGET (object); + GtkWidgetPrivate *priv = widget->priv; + + /* As strange as it may seem, this may happen on object construction. + * init() implementations of parent types may eventually call this function, + * each with its corresponding GType, which could leave a child + * implementation with a wrong widget type in the widget path + */ + if (priv->path && + G_OBJECT_TYPE (widget) != gtk_widget_path_get_object_type (priv->path)) + { + gtk_widget_path_free (priv->path); + priv->path = NULL; + } + + G_OBJECT_CLASS (gtk_widget_parent_class)->constructed (object); +} + static void gtk_widget_dispose (GObject *object) { @@ -10200,6 +10769,8 @@ gtk_widget_dispose (GObject *object) priv->in_destruction = FALSE; } + g_clear_object (&priv->muxer); + G_OBJECT_CLASS (gtk_widget_parent_class)->dispose (object); } @@ -10209,6 +10780,7 @@ gtk_widget_real_destroy (GtkWidget *object) /* gtk_object_destroy() will already hold a refcount on object */ GtkWidget *widget = GTK_WIDGET (object); GtkWidgetPrivate *priv = widget->priv; + GList *l; if (GTK_WIDGET_GET_CLASS (widget)->priv->accessible_type != GTK_TYPE_ACCESSIBLE) { @@ -10230,6 +10802,13 @@ gtk_widget_real_destroy (GtkWidget *object) gtk_grab_remove (widget); + for (l = priv->tick_callbacks; l;) + { + GList *next = l->next; + destroy_tick_callback_info (widget, l->data, l); + l = next; + } + if (priv->style) g_object_unref (priv->style); priv->style = gtk_widget_get_default_style (); @@ -10263,9 +10842,12 @@ gtk_widget_finalize (GObject *object) gtk_widget_path_free (priv->path); if (priv->context) - g_object_unref (priv->context); + { + _gtk_style_context_set_widget (priv->context, NULL); + g_object_unref (priv->context); + } - _gtk_widget_free_cached_sizes (widget); + _gtk_size_request_cache_free (&priv->requests); if (g_object_is_floating (object)) g_warning ("A floating object was finalized. This means that someone\n" @@ -10376,7 +10958,7 @@ gtk_widget_real_unrealize (GtkWidget *widget) if (gtk_widget_get_has_window (widget)) { - gdk_window_set_user_data (priv->window, NULL); + gtk_widget_unregister_window (widget, priv->window); gdk_window_destroy (priv->window); priv->window = NULL; } @@ -10785,18 +11367,8 @@ gtk_widget_propagate_state (GtkWidget *widget, old_state = gtk_widget_get_state (widget); - switch (data->operation) - { - case STATE_CHANGE_REPLACE: - priv->state_flags = data->flags; - break; - case STATE_CHANGE_SET: - priv->state_flags |= data->flags; - break; - case STATE_CHANGE_UNSET: - priv->state_flags &= ~(data->flags); - break; - } + priv->state_flags |= data->flags_to_set; + priv->state_flags &= ~(data->flags_to_unset); /* make insensitivity unoverridable */ if (!priv->sensitive) @@ -10865,40 +11437,17 @@ gtk_widget_propagate_state (GtkWidget *widget, if (GTK_IS_CONTAINER (widget)) { - GtkStateData child_data = *data; + GtkStateData child_data; /* Make sure to only propate the right states further */ - child_data.flags &= GTK_STATE_FLAGS_DO_PROPAGATE; + child_data.flags_to_set = data->flags_to_set & GTK_STATE_FLAGS_DO_PROPAGATE; + child_data.flags_to_unset = data->flags_to_unset & GTK_STATE_FLAGS_DO_PROPAGATE; gtk_container_forall (GTK_CONTAINER (widget), (GtkCallback) gtk_widget_propagate_state, &child_data); } - /* Trigger state change transitions for the widget */ - if (priv->context && - gtk_widget_get_mapped (widget)) - { - gint diff, flag = 1; - - diff = old_flags ^ new_flags; - - while (diff != 0) - { - if ((diff & flag) != 0) - { - gboolean target; - - target = ((new_flags & flag) != 0); - _gtk_widget_notify_state_change (widget, flag, target); - - diff &= ~flag; - } - - flag <<= 1; - } - } - g_object_unref (widget); } } @@ -11460,7 +12009,7 @@ _gtk_widget_peek_accessible (GtkWidget *widget) * class for which such an implementation is defined. * * The documentation of the - * ATK + * ATK * library contains more information about accessible objects and their uses. * * Returns: (transfer none): the #AtkObject associated with @widget @@ -13205,7 +13754,7 @@ gtk_widget_queue_tooltip_query (GtkWidget *widget) /** * gtk_widget_set_tooltip_text: * @widget: a #GtkWidget - * @text: the contents of the tooltip for @widget + * @text: (allow-none): the contents of the tooltip for @widget * * Sets @text as the contents of the tooltip. This function will take * care of setting #GtkWidget:has-tooltip to %TRUE and of the default @@ -13399,6 +13948,7 @@ gtk_widget_set_allocation (GtkWidget *widget, GtkWidgetPrivate *priv; g_return_if_fail (GTK_IS_WIDGET (widget)); + g_return_if_fail (gtk_widget_get_visible (widget) || gtk_widget_is_toplevel (widget)); g_return_if_fail (allocation != NULL); priv = widget->priv; @@ -13505,10 +14055,83 @@ gtk_widget_set_window (GtkWidget *widget, if (priv->window != window) { priv->window = window; + + if (gtk_widget_get_has_window (widget) && window != NULL && !gdk_window_has_native (window)) + gdk_window_set_opacity (window, + priv->norender ? 0 : priv->alpha / 255.0); + g_object_notify (G_OBJECT (widget), "window"); } } +/** + * gtk_widget_register_window: + * @widget: a #GtkWidget + * @window: a #GdkWindow + * + * Registers a #GdkWindow with the widget and sets it up so that + * the widget recieves events for it. Call gtk_widget_unregister_window() + * when destroying the window. + * + * Before 3.8 you needed to call gdk_window_set_user_data() directly to set + * this up. This is now deprecated and you should use gtk_widget_register_window() + * instead. Old code will keep working as is, although some new features like + * transparency might not work perfectly. + * + * Since: 3.8 + */ +void +gtk_widget_register_window (GtkWidget *widget, + GdkWindow *window) +{ + GtkWidgetPrivate *priv; + gpointer user_data; + + g_return_if_fail (GTK_IS_WIDGET (widget)); + g_return_if_fail (GDK_IS_WINDOW (window)); + + gdk_window_get_user_data (window, &user_data); + g_assert (user_data == NULL); + + priv = widget->priv; + + gdk_window_set_user_data (window, widget); + priv->registered_windows = g_list_prepend (priv->registered_windows, window); + + if (priv->window != window && !gdk_window_has_native (window)) + gdk_window_set_opacity (window, + priv->norender_children ? 0.0 : 1.0); +} + +/** + * gtk_widget_unregister_window: + * @widget: a #GtkWidget + * @window: a #GdkWindow + * + * Unregisters a #GdkWindow from the widget that was previously set up with + * gtk_widget_register_window(). You need to call this when the window is + * no longer used by the widget, such as when you destroy it. + * + * Since: 3.8 + */ +void +gtk_widget_unregister_window (GtkWidget *widget, + GdkWindow *window) +{ + GtkWidgetPrivate *priv; + gpointer user_data; + + g_return_if_fail (GTK_IS_WIDGET (widget)); + g_return_if_fail (GDK_IS_WINDOW (window)); + + priv = widget->priv; + + gdk_window_get_user_data (window, &user_data); + g_assert (user_data == widget); + gdk_window_set_user_data (window, NULL); + priv->registered_windows = g_list_remove (priv->registered_windows, window); +} + /** * gtk_widget_get_window: * @widget: a #GtkWidget @@ -13571,6 +14194,211 @@ gtk_widget_set_support_multidevice (GtkWidget *widget, gdk_window_set_support_multidevice (priv->window, support_multidevice); } +/* There are multiple alpha related sources. First of all the user can specify alpha + * in gtk_widget_set_opacity, secondly we can get it from the css opacity. These two + * are multiplied together to form the total alpha. Secondly, the user can specify + * an opacity group for a widget, which means we must essentially handle it as having alpha. + * + * We handle opacity in two ways. For a windowed widget, with opacity set but no opacity + * group we directly set the opacity of widget->window. This will cause gdk to properly + * redirect drawing inside the window to a buffer and do OVER paint_with_alpha. + * + * However, if the widget is not windowed, or the user specified an opacity group for the + * widget we do the opacity handling in the ::draw marshaller for the widget. A naive + * implementation of this would break for windowed widgets or descendant widgets with + * windows, as these would not be handled by the ::draw signal. To handle this we set + * all such gdkwindows as fully transparent and then override gtk_cairo_should_draw_window() + * to make the draw signal propagate to *all* child widgets/windows. + * + * Note: We don't make all child windows fully transparent, we stop at the first one + * in each branch when propagating down the hierarchy. + */ + + +/* This is called when priv->alpha or priv->opacity_group group changes, and should + * update priv->norender and GdkWindow opacity for this widget and any children that + * needs changing. It is also called whenver the parent changes, the parents + * norender_children state changes, or the has_window state of the widget changes. + */ +static void +gtk_widget_propagate_alpha (GtkWidget *widget) +{ + GtkWidgetPrivate *priv = widget->priv; + GtkWidget *parent; + gboolean norender, norender_children; + GList *l; + + parent = priv->parent; + + /* Norender affects only windowed widget and means don't render widget->window in the + normal fashion. + We only set this if the parent has norender_children, because: + a) For an opacity group (that does not have a norender_children parent) we still + need to render the window or we will never get an expose event. + b) For alpha we set the opacity of window->widget directly, so no other + work is needed. + */ + norender = (parent != NULL && parent->priv->norender_children); + + /* windows under this widget should not render if: + a) This widget has an opacity group + b) This widget has alpha and is no-windowed (otherwise we'd set alpha on widget->window) + c) This widget has norender but is no-windowed (a windowed widget would "swallow" the norender) + */ + norender_children = + priv->opacity_group || + (!gtk_widget_get_has_window (widget) && + ( norender || priv->alpha != 255)); + + if (gtk_widget_get_has_window (widget)) + { + if (priv->window != NULL && !gdk_window_has_native (priv->window)) + gdk_window_set_opacity (priv->window, + norender ? 0 : priv->alpha / 255.0); + } + + for (l = priv->registered_windows; l != NULL; l = l->next) + { + GdkWindow *w = l->data; + if (w != priv->window && !gdk_window_has_native (w)) + gdk_window_set_opacity (w, norender_children ? 0.0 : 1.0); + } + + priv->norender = norender; + if (priv->norender_children != norender_children) + { + priv->norender_children = norender_children; + + if (GTK_IS_CONTAINER (widget)) + gtk_container_forall (GTK_CONTAINER (widget), (GtkCallback)gtk_widget_propagate_alpha, NULL); + } + + if (gtk_widget_get_realized (widget)) + gtk_widget_queue_draw (widget); +} + +static void +gtk_widget_update_alpha (GtkWidget *widget) +{ + GtkWidgetPrivate *priv; + double opacity; + guint8 alpha; + + priv = widget->priv; + + alpha = priv->user_alpha; + + if (priv->context) + { + opacity = + _gtk_css_number_value_get (_gtk_style_context_peek_property (priv->context, + GTK_CSS_PROPERTY_OPACITY), + 100); + opacity = CLAMP (opacity, 0.0, 1.0); + alpha = round (priv->user_alpha * opacity); + } + + if (alpha == priv->alpha) + return; + + priv->alpha = alpha; + + gtk_widget_propagate_alpha (widget); + +} + +static void +gtk_widget_set_has_opacity_group (GtkWidget *widget, + gboolean has_opacity_group) +{ + GtkWidgetPrivate *priv; + + g_return_if_fail (GTK_IS_WIDGET (widget)); + + priv = widget->priv; + + has_opacity_group = !!has_opacity_group; + + if (priv->opacity_group == has_opacity_group) + return; + + priv->opacity_group = has_opacity_group; + + gtk_widget_propagate_alpha (widget); +} + +/** + * gtk_widget_set_opacity: + * @widget: a #GtkWidget + * @opacity: desired opacity, between 0 and 1 + * + * Request the @widget to be rendered partially transparent, + * with opacity 0 being fully transparent and 1 fully opaque. (Opacity values + * are clamped to the [0,1] range.). + * This works on both toplevel widget, and child widgets, although there + * are some limitations: + * + * For toplevel widgets this depends on the capabilities of the windowing + * system. On X11 this has any effect only on X screens with a compositing manager + * running. See gtk_widget_is_composited(). On Windows it should work + * always, although setting a window's opacity after the window has been + * shown causes it to flicker once on Windows. + * + * For child widgets it doesn't work if any affected widget has a native window, or + * disables double buffering. + * + * Since: 3.8 + **/ +void +gtk_widget_set_opacity (GtkWidget *widget, + gdouble opacity) +{ + GtkWidgetPrivate *priv; + guint8 alpha; + + g_return_if_fail (GTK_IS_WIDGET (widget)); + + priv = widget->priv; + + opacity = CLAMP (opacity, 0.0, 1.0); + + alpha = round (opacity * 255); + + /* As a kind of hack for internal use we treat an alpha very + close to 1.0 (rounds to 255) but not 1.0 as specifying that + we want the opacity group behaviour wrt draw handling, but + not actually an alpha value. See bug #687842 for discussions. */ + gtk_widget_set_has_opacity_group (widget, + alpha == 255 && opacity != 1.0); + + if (alpha == priv->user_alpha) + return; + + priv->user_alpha = alpha; + + gtk_widget_update_alpha (widget); + +} + +/** + * gtk_widget_get_opacity: + * @widget: a #GtkWidget + * + * Fetches the requested opacity for this widget. See + * gtk_widget_set_opacity(). + * + * Return value: the requested opacity for this widget. + * + * Since: 3.8 + **/ +gdouble +gtk_widget_get_opacity (GtkWidget *widget) +{ + g_return_val_if_fail (GTK_IS_WIDGET (widget), 0.0); + + return widget->priv->alpha / 255.0; +} + static void _gtk_widget_set_has_focus (GtkWidget *widget, gboolean has_focus) @@ -13655,19 +14483,6 @@ gtk_widget_in_destruction (GtkWidget *widget) return widget->priv->in_destruction; } -gboolean -_gtk_widget_get_resize_pending (GtkWidget *widget) -{ - return widget->priv->resize_pending; -} - -void -_gtk_widget_set_resize_pending (GtkWidget *widget, - gboolean resize_pending) -{ - widget->priv->resize_pending = resize_pending; -} - gboolean _gtk_widget_get_in_reparent (GtkWidget *widget) { @@ -13720,58 +14535,6 @@ _gtk_widget_set_alloc_needed (GtkWidget *widget, widget->priv->alloc_needed = alloc_needed; } -gboolean -_gtk_widget_get_width_request_needed (GtkWidget *widget) -{ - return widget->priv->width_request_needed; -} - -void -_gtk_widget_set_width_request_needed (GtkWidget *widget, - gboolean width_request_needed) -{ - widget->priv->width_request_needed = width_request_needed; -} - -gboolean -_gtk_widget_get_height_request_needed (GtkWidget *widget) -{ - return widget->priv->height_request_needed; -} - -void -_gtk_widget_set_height_request_needed (GtkWidget *widget, - gboolean height_request_needed) -{ - widget->priv->height_request_needed = height_request_needed; -} - -gboolean -_gtk_widget_get_sizegroup_visited (GtkWidget *widget) -{ - return widget->priv->sizegroup_visited; -} - -void -_gtk_widget_set_sizegroup_visited (GtkWidget *widget, - gboolean visited) -{ - widget->priv->sizegroup_visited = visited; -} - -gboolean -_gtk_widget_get_sizegroup_bumping (GtkWidget *widget) -{ - return widget->priv->sizegroup_bumping; -} - -void -_gtk_widget_set_sizegroup_bumping (GtkWidget *widget, - gboolean bumping) -{ - widget->priv->sizegroup_bumping = bumping; -} - void _gtk_widget_add_sizegroup (GtkWidget *widget, gpointer group) @@ -13867,6 +14630,39 @@ gtk_widget_path_append_for_widget (GtkWidgetPath *path, return pos; } +GtkWidgetPath * +_gtk_widget_create_path (GtkWidget *widget) +{ + GtkWidget *parent; + + parent = widget->priv->parent; + + if (parent) + return gtk_container_get_path_for_child (GTK_CONTAINER (parent), widget); + else + { + /* Widget is either toplevel or unparented, treat both + * as toplevels style wise, since there are situations + * where style properties might be retrieved on that + * situation. + */ + GtkWidget *attach_widget = NULL; + GtkWidgetPath *result; + + if (GTK_IS_WINDOW (widget)) + attach_widget = gtk_window_get_attached_to (GTK_WINDOW (widget)); + + if (attach_widget != NULL) + result = gtk_widget_path_copy (gtk_widget_get_path (attach_widget)); + else + result = gtk_widget_path_new (); + + gtk_widget_path_append_for_widget (result, widget); + + return result; + } +} + /** * gtk_widget_get_path: * @widget: a #GtkWidget @@ -13882,59 +14678,20 @@ gtk_widget_get_path (GtkWidget *widget) { g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL); - /* As strange as it may seem, this may happen on object construction. - * init() implementations of parent types may eventually call this function, - * each with its corresponding GType, which could leave a child - * implementation with a wrong widget type in the widget path - */ - if (widget->priv->path && - G_OBJECT_TYPE (widget) != gtk_widget_path_get_object_type (widget->priv->path)) - { - gtk_widget_path_free (widget->priv->path); - widget->priv->path = NULL; - } - if (!widget->priv->path) - { - GtkWidget *parent; - - parent = widget->priv->parent; - - if (parent) - widget->priv->path = gtk_container_get_path_for_child (GTK_CONTAINER (parent), widget); - else - { - /* Widget is either toplevel or unparented, treat both - * as toplevels style wise, since there are situations - * where style properties might be retrieved on that - * situation. - */ - GtkWidget *attach_widget = NULL; - - if (GTK_IS_WINDOW (widget)) - attach_widget = gtk_window_get_attached_to (GTK_WINDOW (widget)); - - if (attach_widget != NULL) - widget->priv->path = gtk_widget_path_copy (gtk_widget_get_path (attach_widget)); - else - widget->priv->path = gtk_widget_path_new (); - - gtk_widget_path_append_for_widget (widget->priv->path, widget); - } - - if (widget->priv->context) - gtk_style_context_set_path (widget->priv->context, - widget->priv->path); - } + widget->priv->path = _gtk_widget_create_path (widget); return widget->priv->path; } -static void -style_context_changed (GtkStyleContext *context, - gpointer user_data) +void +_gtk_widget_style_context_invalidated (GtkWidget *widget) { - GtkWidget *widget = user_data; + if (widget->priv->path) + { + gtk_widget_path_free (widget->priv->path); + widget->priv->path = NULL; + } if (gtk_widget_get_realized (widget)) g_signal_emit (widget, widget_signals[STYLE_UPDATED], 0); @@ -13945,9 +14702,6 @@ style_context_changed (GtkStyleContext *context, */ widget->priv->style_update_pending = TRUE; } - - if (widget->priv->anchored) - gtk_widget_queue_resize (widget); } /** @@ -13963,40 +14717,52 @@ GtkStyleContext * gtk_widget_get_style_context (GtkWidget *widget) { GtkWidgetPrivate *priv; - GtkWidgetPath *path; g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL); priv = widget->priv; - /* updates style context if it exists already */ - path = gtk_widget_get_path (widget); - if (G_UNLIKELY (priv->context == NULL)) { GdkScreen *screen; + GdkFrameClock *frame_clock; - priv->context = g_object_new (GTK_TYPE_STYLE_CONTEXT, - "direction", gtk_widget_get_direction (widget), - NULL); + priv->context = gtk_style_context_new (); - g_signal_connect (widget->priv->context, "changed", - G_CALLBACK (style_context_changed), widget); + gtk_style_context_set_state (priv->context, priv->state_flags); screen = gtk_widget_get_screen (widget); - if (screen) gtk_style_context_set_screen (priv->context, screen); - gtk_style_context_set_path (priv->context, path); + frame_clock = gtk_widget_get_frame_clock (widget); + if (frame_clock) + gtk_style_context_set_frame_clock (priv->context, frame_clock); + if (priv->parent) gtk_style_context_set_parent (priv->context, gtk_widget_get_style_context (priv->parent)); + + _gtk_style_context_set_widget (priv->context, widget); } return widget->priv->context; } +void +_gtk_widget_invalidate_style_context (GtkWidget *widget, + GtkCssChange change) +{ + GtkWidgetPrivate *priv; + + priv = widget->priv; + + if (priv->context == NULL) + return; + + _gtk_style_context_queue_invalidate (priv->context, change); +} + /** * gtk_widget_get_modifier_mask: * @widget: a #GtkWidget @@ -14037,3 +14803,65 @@ _gtk_widget_set_style (GtkWidget *widget, { widget->priv->style = style; } + +void +_gtk_widget_update_parent_muxer (GtkWidget *widget) +{ + GtkWidget *parent; + GActionMuxer *parent_muxer; + + if (widget->priv->muxer == NULL) + return; + + if (GTK_IS_MENU (widget)) + parent = gtk_menu_get_attach_widget (GTK_MENU (widget)); + else + parent = gtk_widget_get_parent (widget); + + parent_muxer = parent ? _gtk_widget_get_action_muxer (parent) : NULL; + + g_action_muxer_set_parent (widget->priv->muxer, parent_muxer); +} + +GActionMuxer * +_gtk_widget_get_action_muxer (GtkWidget *widget) +{ + if (widget->priv->muxer == NULL) + { + widget->priv->muxer = g_action_muxer_new (); + _gtk_widget_update_parent_muxer (widget); + } + + return widget->priv->muxer; +} + +/** + * gtk_widget_insert_action_group: + * @widget: a #GtkWidget + * @name: the prefix for actions in @group + * @group: a #GActionGroup + * + * Inserts @group into @widget. Children of @widget that implement + * #GtkActionable can then be associated with actions in @group by + * setting their 'action-name' to + * @prefix.action-name. + * + * Since: 3.6 + */ +void +gtk_widget_insert_action_group (GtkWidget *widget, + const gchar *name, + GActionGroup *group) +{ + GActionMuxer *muxer; + + g_return_if_fail (GTK_IS_WIDGET (widget)); + g_return_if_fail (name != NULL); + + muxer = _gtk_widget_get_action_muxer (widget); + + if (group) + g_action_muxer_insert (muxer, name, group); + else + g_action_muxer_remove (muxer, name); +}