diff options
author | Matthias Clasen <mclasen@redhat.com> | 2019-03-27 23:47:32 -0400 |
---|---|---|
committer | Matthias Clasen <mclasen@redhat.com> | 2019-03-28 16:36:59 -0400 |
commit | 866640c0c2091b015f8a8edd19ebc474324ec03b (patch) | |
tree | 3a0b39ec1f60941cac5d8de32f5cf76c6556f452 | |
parent | 0f7ca7e550eb8fed9d39460c9911be80ad09b9df (diff) | |
download | gtk+-866640c0c2091b015f8a8edd19ebc474324ec03b.tar.gz |
overlay: Use a layout manager
This lets us get rid of the child properties,
by converting them to layout properties.
-rw-r--r-- | gtk/gtkoverlay.c | 373 | ||||
-rw-r--r-- | gtk/gtkoverlay.h | 1 | ||||
-rw-r--r-- | gtk/gtkoverlaylayout.c | 448 | ||||
-rw-r--r-- | gtk/gtkoverlaylayoutprivate.h | 57 | ||||
-rw-r--r-- | gtk/meson.build | 1 |
5 files changed, 537 insertions, 343 deletions
diff --git a/gtk/gtkoverlay.c b/gtk/gtkoverlay.c index 0b3c8932c3..5195f9ac0a 100644 --- a/gtk/gtkoverlay.c +++ b/gtk/gtkoverlay.c @@ -22,6 +22,7 @@ #include "gtkoverlay.h" +#include "gtkoverlaylayoutprivate.h" #include "gtkbuildable.h" #include "gtkintl.h" #include "gtkmarshalers.h" @@ -61,104 +62,25 @@ * whose alignments cause them to be positioned at an edge get the style classes * “.left”, “.right”, “.top”, and/or “.bottom” according to their position. */ -typedef struct _GtkOverlayChild GtkOverlayChild; - -struct _GtkOverlayChild -{ - guint measure : 1; - guint clip_overlay : 1; -}; enum { GET_CHILD_POSITION, LAST_SIGNAL }; -enum -{ - CHILD_PROP_0, - CHILD_PROP_PASS_THROUGH, - CHILD_PROP_MEASURE, - CHILD_PROP_CLIP_OVERLAY -}; - static guint signals[LAST_SIGNAL] = { 0 }; -static GQuark child_data_quark = 0; static void gtk_overlay_buildable_init (GtkBuildableIface *iface); +typedef struct { + GtkLayoutManager *layout; +} GtkOverlayPrivate; + G_DEFINE_TYPE_WITH_CODE (GtkOverlay, gtk_overlay, GTK_TYPE_BIN, + G_ADD_PRIVATE (GtkOverlay) G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, gtk_overlay_buildable_init)) -static void -gtk_overlay_set_overlay_child (GtkWidget *widget, - GtkOverlayChild *child_data) -{ - g_object_set_qdata_full (G_OBJECT (widget), child_data_quark, child_data, g_free); -} - -static GtkOverlayChild * -gtk_overlay_get_overlay_child (GtkWidget *widget) -{ - return (GtkOverlayChild *) g_object_get_qdata (G_OBJECT (widget), child_data_quark); -} - -static void -gtk_overlay_measure (GtkWidget *widget, - GtkOrientation orientation, - int for_size, - int *minimum, - int *natural, - int *minimum_baseline, - int *natural_baseline) -{ - GtkWidget *child; - - for (child = gtk_widget_get_first_child (widget); - child != NULL; - child = gtk_widget_get_next_sibling (child)) - { - GtkOverlayChild *child_data = gtk_overlay_get_overlay_child (child); - - if (child_data == NULL || child_data->measure) - { - int child_min, child_nat, child_min_baseline, child_nat_baseline; - - gtk_widget_measure (child, - orientation, - for_size, - &child_min, &child_nat, - &child_min_baseline, &child_nat_baseline); - - *minimum = MAX (*minimum, child_min); - *natural = MAX (*natural, child_nat); - if (child_min_baseline > -1) - *minimum_baseline = MAX (*minimum_baseline, child_min_baseline); - if (child_nat_baseline > -1) - *natural_baseline = MAX (*natural_baseline, child_nat_baseline); - } - } -} - -static void -gtk_overlay_compute_child_allocation (GtkOverlay *overlay, - GtkWidget *widget, - GtkOverlayChild *child, - GtkAllocation *widget_allocation) -{ - GtkAllocation allocation; - gboolean result; - - g_signal_emit (overlay, signals[GET_CHILD_POSITION], - 0, widget, &allocation, &result); - - widget_allocation->x = allocation.x; - widget_allocation->y = allocation.y; - widget_allocation->width = allocation.width; - widget_allocation->height = allocation.height; -} - static GtkAlign effective_align (GtkAlign align, GtkTextDirection direction) @@ -177,110 +99,6 @@ effective_align (GtkAlign align, } } -static void -gtk_overlay_child_update_style_classes (GtkOverlay *overlay, - GtkWidget *child, - GtkAllocation *child_allocation) -{ - int width, height; - GtkAlign valign, halign; - gboolean is_left, is_right, is_top, is_bottom; - gboolean has_left, has_right, has_top, has_bottom; - GtkStyleContext *context; - - context = gtk_widget_get_style_context (child); - has_left = gtk_style_context_has_class (context, GTK_STYLE_CLASS_LEFT); - has_right = gtk_style_context_has_class (context, GTK_STYLE_CLASS_RIGHT); - has_top = gtk_style_context_has_class (context, GTK_STYLE_CLASS_TOP); - has_bottom = gtk_style_context_has_class (context, GTK_STYLE_CLASS_BOTTOM); - - is_left = is_right = is_top = is_bottom = FALSE; - - width = gtk_widget_get_width (GTK_WIDGET (overlay)); - height = gtk_widget_get_height (GTK_WIDGET (overlay)); - - halign = effective_align (gtk_widget_get_halign (child), - gtk_widget_get_direction (child)); - - if (halign == GTK_ALIGN_START) - is_left = (child_allocation->x == 0); - else if (halign == GTK_ALIGN_END) - is_right = (child_allocation->x + child_allocation->width == width); - - valign = gtk_widget_get_valign (child); - - if (valign == GTK_ALIGN_START) - is_top = (child_allocation->y == 0); - else if (valign == GTK_ALIGN_END) - is_bottom = (child_allocation->y + child_allocation->height == height); - - if (has_left && !is_left) - gtk_style_context_remove_class (context, GTK_STYLE_CLASS_LEFT); - else if (!has_left && is_left) - gtk_style_context_add_class (context, GTK_STYLE_CLASS_LEFT); - - if (has_right && !is_right) - gtk_style_context_remove_class (context, GTK_STYLE_CLASS_RIGHT); - else if (!has_right && is_right) - gtk_style_context_add_class (context, GTK_STYLE_CLASS_RIGHT); - - if (has_top && !is_top) - gtk_style_context_remove_class (context, GTK_STYLE_CLASS_TOP); - else if (!has_top && is_top) - gtk_style_context_add_class (context, GTK_STYLE_CLASS_TOP); - - if (has_bottom && !is_bottom) - gtk_style_context_remove_class (context, GTK_STYLE_CLASS_BOTTOM); - else if (!has_bottom && is_bottom) - gtk_style_context_add_class (context, GTK_STYLE_CLASS_BOTTOM); -} - -static void -gtk_overlay_child_allocate (GtkOverlay *overlay, - GtkWidget *widget, - GtkOverlayChild *child) -{ - GtkAllocation child_allocation; - - if (!gtk_widget_get_visible (widget)) - return; - - gtk_overlay_compute_child_allocation (overlay, widget, child, &child_allocation); - - gtk_overlay_child_update_style_classes (overlay, widget, &child_allocation); - gtk_widget_size_allocate (widget, &child_allocation, -1); -} - -static void -gtk_overlay_size_allocate (GtkWidget *widget, - int width, - int height, - int baseline) -{ - GtkOverlay *overlay = GTK_OVERLAY (widget); - GtkWidget *child; - GtkWidget *main_widget; - - main_widget = gtk_bin_get_child (GTK_BIN (overlay)); - if (main_widget && gtk_widget_get_visible (main_widget)) - gtk_widget_size_allocate (main_widget, - &(GtkAllocation) { - 0, 0, - width, height - }, -1); - - for (child = gtk_widget_get_first_child (widget); - child != NULL; - child = gtk_widget_get_next_sibling (child)) - { - if (child != main_widget) - { - GtkOverlayChild *child_data = gtk_overlay_get_overlay_child (child); - gtk_overlay_child_allocate (overlay, child, child_data); - } - } -} - static gboolean gtk_overlay_get_child_position (GtkOverlay *overlay, GtkWidget *widget, @@ -355,7 +173,8 @@ gtk_overlay_add (GtkContainer *container, /* We only get into this path if we have no child * (since GtkOverlay is a GtkBin) and the only child * we can add through gtk_container_add is the main child, - * which we want to keep the lowest in the hierarchy. */ + * which we want to keep the lowest in the hierarchy. + */ gtk_widget_insert_after (widget, GTK_WIDGET (container), NULL); _gtk_bin_set_child (GTK_BIN (container), widget); } @@ -394,106 +213,6 @@ gtk_overlay_forall (GtkContainer *overlay, } } - -static void -gtk_overlay_set_child_property (GtkContainer *container, - GtkWidget *child, - guint property_id, - const GValue *value, - GParamSpec *pspec) -{ - GtkOverlay *overlay = GTK_OVERLAY (container); - GtkOverlayChild *child_info; - GtkWidget *main_widget; - - main_widget = gtk_bin_get_child (GTK_BIN (overlay)); - if (child == main_widget) - child_info = NULL; - else - { - child_info = gtk_overlay_get_overlay_child (child); - if (child_info == NULL) - { - GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec); - return; - } - } - - switch (property_id) - { - case CHILD_PROP_MEASURE: - if (child_info) - { - if (g_value_get_boolean (value) != child_info->measure) - { - child_info->measure = g_value_get_boolean (value); - gtk_container_child_notify (container, child, "measure"); - gtk_widget_queue_resize (GTK_WIDGET (overlay)); - } - } - break; - case CHILD_PROP_CLIP_OVERLAY: - if (child_info) - { - if (g_value_get_boolean (value) != child_info->clip_overlay) - { - child_info->clip_overlay = g_value_get_boolean (value); - gtk_container_child_notify (container, child, "clip-overlay"); - gtk_widget_queue_resize (GTK_WIDGET (overlay)); - } - } - break; - - default: - GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec); - break; - } -} - -static void -gtk_overlay_get_child_property (GtkContainer *container, - GtkWidget *child, - guint property_id, - GValue *value, - GParamSpec *pspec) -{ - GtkOverlay *overlay = GTK_OVERLAY (container); - GtkOverlayChild *child_info; - GtkWidget *main_widget; - - main_widget = gtk_bin_get_child (GTK_BIN (overlay)); - if (child == main_widget) - child_info = NULL; - else - { - child_info = gtk_overlay_get_overlay_child (child); - if (child_info == NULL) - { - GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec); - return; - } - } - - switch (property_id) - { - case CHILD_PROP_MEASURE: - if (child_info) - g_value_set_boolean (value, child_info->measure); - else - g_value_set_boolean (value, TRUE); - break; - case CHILD_PROP_CLIP_OVERLAY: - if (child_info) - g_value_set_boolean (value, child_info->clip_overlay); - else - g_value_set_boolean (value, FALSE); - break; - default: - GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec); - break; - } -} - static void gtk_overlay_snapshot_child (GtkWidget *overlay, GtkWidget *child, @@ -540,44 +259,15 @@ gtk_overlay_class_init (GtkOverlayClass *klass) GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass); - widget_class->measure = gtk_overlay_measure; - widget_class->size_allocate = gtk_overlay_size_allocate; widget_class->snapshot = gtk_overlay_snapshot; container_class->add = gtk_overlay_add; container_class->remove = gtk_overlay_remove; container_class->forall = gtk_overlay_forall; - container_class->set_child_property = gtk_overlay_set_child_property; - container_class->get_child_property = gtk_overlay_get_child_property; klass->get_child_position = gtk_overlay_get_child_position; /** - * GtkOverlay:measure: - * - * Include this child in determining the child request. - * - * The main child will always be measured. - */ - gtk_container_class_install_child_property (container_class, CHILD_PROP_MEASURE, - g_param_spec_boolean ("measure", P_("Measure"), P_("Include in size measurement"), - FALSE, - GTK_PARAM_READWRITE)); - - /** - * GtkOverlay:clip-overlay: - * - * Clip the overlay child widget so as to fit the parent - */ - gtk_container_class_install_child_property (container_class, CHILD_PROP_CLIP_OVERLAY, - g_param_spec_boolean ("clip-overlay", - P_("Clip Overlay"), - P_("Clip the overlay child widget so as to fit the parent"), - FALSE, - GTK_PARAM_READWRITE)); - - - /** * GtkOverlay::get-child-position: * @overlay: the #GtkOverlay * @widget: the child widget to position @@ -611,15 +301,18 @@ gtk_overlay_class_init (GtkOverlayClass *klass) GTK_TYPE_WIDGET, GDK_TYPE_RECTANGLE | G_SIGNAL_TYPE_STATIC_SCOPE); - child_data_quark = g_quark_from_static_string ("gtk-overlay-child-data"); - gtk_widget_class_set_css_name (widget_class, I_("overlay")); } static void gtk_overlay_init (GtkOverlay *overlay) { + GtkOverlayPrivate *priv = gtk_overlay_get_instance_private (overlay); + gtk_widget_set_has_surface (GTK_WIDGET (overlay), FALSE); + + priv->layout = gtk_overlay_layout_new (); + gtk_widget_set_layout_manager (GTK_WIDGET (overlay), priv->layout); } static GtkBuildableIface *parent_buildable_iface; @@ -687,15 +380,10 @@ void gtk_overlay_add_overlay (GtkOverlay *overlay, GtkWidget *widget) { - GtkOverlayChild *child; - g_return_if_fail (GTK_IS_OVERLAY (overlay)); g_return_if_fail (GTK_IS_WIDGET (widget)); - child = g_new0 (GtkOverlayChild, 1); - gtk_widget_insert_before (widget, GTK_WIDGET (overlay), NULL); - gtk_overlay_set_overlay_child (widget, child); } /** @@ -715,12 +403,14 @@ gtk_overlay_set_measure_overlay (GtkOverlay *overlay, GtkWidget *widget, gboolean measure) { + GtkOverlayPrivate *priv = gtk_overlay_get_instance_private (overlay); + GtkOverlayLayoutChild *child; + g_return_if_fail (GTK_IS_OVERLAY (overlay)); g_return_if_fail (GTK_IS_WIDGET (widget)); - gtk_container_child_set (GTK_CONTAINER (overlay), widget, - "measure", measure, - NULL); + child = GTK_OVERLAY_LAYOUT_CHILD (gtk_layout_manager_get_layout_child (priv->layout, widget)); + gtk_overlay_layout_child_set_measure (child, measure); } /** @@ -737,16 +427,14 @@ gboolean gtk_overlay_get_measure_overlay (GtkOverlay *overlay, GtkWidget *widget) { - gboolean measure; + GtkOverlayPrivate *priv = gtk_overlay_get_instance_private (overlay); + GtkOverlayLayoutChild *child; g_return_val_if_fail (GTK_IS_OVERLAY (overlay), FALSE); g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); - gtk_container_child_get (GTK_CONTAINER (overlay), widget, - "measure", &measure, - NULL); - - return measure; + child = GTK_OVERLAY_LAYOUT_CHILD (gtk_layout_manager_get_layout_child (priv->layout, widget)); + return gtk_overlay_layout_child_get_measure (child); } /** @@ -763,12 +451,14 @@ gtk_overlay_set_clip_overlay (GtkOverlay *overlay, GtkWidget *widget, gboolean clip_overlay) { + GtkOverlayPrivate *priv = gtk_overlay_get_instance_private (overlay); + GtkOverlayLayoutChild *child; + g_return_if_fail (GTK_IS_OVERLAY (overlay)); g_return_if_fail (GTK_IS_WIDGET (widget)); - gtk_container_child_set (GTK_CONTAINER (overlay), widget, - "clip-overlay", clip_overlay, - NULL); + child = GTK_OVERLAY_LAYOUT_CHILD (gtk_layout_manager_get_layout_child (priv->layout, widget)); + gtk_overlay_layout_child_set_clip_overlay (child, clip_overlay); } /** @@ -785,14 +475,13 @@ gboolean gtk_overlay_get_clip_overlay (GtkOverlay *overlay, GtkWidget *widget) { - gboolean clip_overlay; + GtkOverlayPrivate *priv = gtk_overlay_get_instance_private (overlay); + GtkOverlayLayoutChild *child; g_return_val_if_fail (GTK_IS_OVERLAY (overlay), FALSE); g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); - gtk_container_child_get (GTK_CONTAINER (overlay), widget, - "clip-overlay", &clip_overlay, - NULL); + child = GTK_OVERLAY_LAYOUT_CHILD (gtk_layout_manager_get_layout_child (priv->layout, widget)); - return clip_overlay; + return gtk_overlay_layout_child_get_clip_overlay (child); } diff --git a/gtk/gtkoverlay.h b/gtk/gtkoverlay.h index b0ff60d4ad..a4ac9480dd 100644 --- a/gtk/gtkoverlay.h +++ b/gtk/gtkoverlay.h @@ -38,7 +38,6 @@ G_BEGIN_DECLS typedef struct _GtkOverlay GtkOverlay; typedef struct _GtkOverlayClass GtkOverlayClass; -typedef struct _GtkOverlayPrivate GtkOverlayPrivate; struct _GtkOverlay { diff --git a/gtk/gtkoverlaylayout.c b/gtk/gtkoverlaylayout.c new file mode 100644 index 0000000000..4fa4f12dd8 --- /dev/null +++ b/gtk/gtkoverlaylayout.c @@ -0,0 +1,448 @@ +/* gtkoverlaylayout.c: Overlay layout manager + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * Copyright 2019 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "config.h" + +#include "gtkoverlaylayoutprivate.h" + +#include "gtkintl.h" +#include "gtklayoutchild.h" +#include "gtkoverlay.h" +#include "gtkprivate.h" +#include "gtkstylecontext.h" +#include "gtkwidgetprivate.h" + +#include <graphene-gobject.h> + +struct _GtkOverlayLayout +{ + GtkLayoutManager parent_instance; +}; + +struct _GtkOverlayLayoutChild +{ + GtkLayoutChild parent_instance; + + guint measure : 1; + guint clip_overlay : 1; +}; + +enum +{ + PROP_MEASURE = 1, + PROP_CLIP_OVERLAY, + + N_CHILD_PROPERTIES +}; + +static GParamSpec *child_props[N_CHILD_PROPERTIES]; + +G_DEFINE_TYPE (GtkOverlayLayoutChild, gtk_overlay_layout_child, GTK_TYPE_LAYOUT_CHILD) + +static void +gtk_overlay_layout_child_set_property (GObject *gobject, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GtkOverlayLayoutChild *self = GTK_OVERLAY_LAYOUT_CHILD (gobject); + + switch (prop_id) + { + case PROP_MEASURE: + gtk_overlay_layout_child_set_measure (self, g_value_get_boolean (value)); + break; + + case PROP_CLIP_OVERLAY: + gtk_overlay_layout_child_set_clip_overlay (self, g_value_get_boolean (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + break; + } +} + +static void +gtk_overlay_layout_child_get_property (GObject *gobject, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GtkOverlayLayoutChild *self = GTK_OVERLAY_LAYOUT_CHILD (gobject); + + switch (prop_id) + { + case PROP_MEASURE: + g_value_set_boolean (value, self->measure); + break; + + case PROP_CLIP_OVERLAY: + g_value_set_boolean (value, self->clip_overlay); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + break; + } +} + +static void +gtk_overlay_layout_child_finalize (GObject *gobject) +{ + //GtkOverlayLayoutChild *self = GTK_OVERLAY_LAYOUT_CHILD (gobject); + + G_OBJECT_CLASS (gtk_overlay_layout_child_parent_class)->finalize (gobject); +} + +static void +gtk_overlay_layout_child_class_init (GtkOverlayLayoutChildClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->set_property = gtk_overlay_layout_child_set_property; + gobject_class->get_property = gtk_overlay_layout_child_get_property; + gobject_class->finalize = gtk_overlay_layout_child_finalize; + + child_props[PROP_MEASURE] = + g_param_spec_boolean ("measure", + P_("Measure"), + P_("Include in size measurement"), + FALSE, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); + child_props[PROP_CLIP_OVERLAY] = + g_param_spec_boolean ("clip-overlay", + P_("Clip Overlay"), + P_("Clip the overlay child widget so as to fit the parent"), + FALSE, + GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); + + g_object_class_install_properties (gobject_class, N_CHILD_PROPERTIES, child_props); +} + +static void +gtk_overlay_layout_child_init (GtkOverlayLayoutChild *self) +{ +} + +/** + * gtk_overlay_layout_child_set_measure: + * @child: a #GtkOverlayLayoutChild + * @measure: whether to measure this child + * + * Sets whether to measure this child. + */ +void +gtk_overlay_layout_child_set_measure (GtkOverlayLayoutChild *child, + gboolean measure) +{ + GtkLayoutManager *layout; + + g_return_if_fail (GTK_IS_OVERLAY_LAYOUT_CHILD (child)); + + if (child->measure == measure) + return; + + child->measure = measure; + + layout = gtk_layout_child_get_layout_manager (GTK_LAYOUT_CHILD (child)); + gtk_layout_manager_layout_changed (layout); + + g_object_notify_by_pspec (G_OBJECT (child), child_props[PROP_MEASURE]); +} + +/** + * gtk_overlay_layout_child_get_measure: + * @child: a #GtkOverlayLayoutChild + * + * Retrieves whether the child is measured. + * + * Returns: (transfer none) (nullable): whether the child is measured + */ +gboolean +gtk_overlay_layout_child_get_measure (GtkOverlayLayoutChild *child) +{ + g_return_val_if_fail (GTK_IS_OVERLAY_LAYOUT_CHILD (child), FALSE); + + return child->measure; +} + +/** + * gtk_overlay_layout_child_set_clip_overlay: + * @child: a #GtkOverlayLayoutChild + * @clip_overlay: whether to clip this child + * + * Sets whether to clip this child. + */ +void +gtk_overlay_layout_child_set_clip_overlay (GtkOverlayLayoutChild *child, + gboolean clip_overlay) +{ + GtkLayoutManager *layout; + + g_return_if_fail (GTK_IS_OVERLAY_LAYOUT_CHILD (child)); + + if (child->clip_overlay == clip_overlay) + return; + + child->clip_overlay = clip_overlay; + + layout = gtk_layout_child_get_layout_manager (GTK_LAYOUT_CHILD (child)); + gtk_layout_manager_layout_changed (layout); + + g_object_notify_by_pspec (G_OBJECT (child), child_props[PROP_CLIP_OVERLAY]); +} + +/** + * gtk_overlay_layout_child_get_clip_overlay: + * @child: a #GtkOverlayLayoutChild + * + * Retrieves whether the child is clipped. + * + * Returns: (transfer none) (nullable): whether the child is clipped + */ +gboolean +gtk_overlay_layout_child_get_clip_overlay (GtkOverlayLayoutChild *child) +{ + g_return_val_if_fail (GTK_IS_OVERLAY_LAYOUT_CHILD (child), FALSE); + + return child->clip_overlay; +} + +G_DEFINE_TYPE (GtkOverlayLayout, gtk_overlay_layout, GTK_TYPE_LAYOUT_MANAGER) + +static void +gtk_overlay_layout_measure (GtkLayoutManager *layout_manager, + GtkWidget *widget, + GtkOrientation orientation, + int for_size, + int *minimum, + int *natural, + int *minimum_baseline, + int *natural_baseline) +{ + GtkOverlayLayoutChild *child_info; + GtkWidget *child; + int min, nat; + GtkWidget *main_widget; + + main_widget = gtk_bin_get_child (GTK_BIN (widget)); + + min = 0; + nat = 0; + + for (child = _gtk_widget_get_first_child (widget); + child != NULL; + child = _gtk_widget_get_next_sibling (child)) + { + child_info = GTK_OVERLAY_LAYOUT_CHILD (gtk_layout_manager_get_layout_child (layout_manager, child)); + + if (child == main_widget || child_info->measure) + { + int child_min, child_nat, child_min_baseline, child_nat_baseline; + + gtk_widget_measure (child, + orientation, + for_size, + &child_min, &child_nat, + &child_min_baseline, &child_nat_baseline); + + min = MAX (min, child_min); + nat = MAX (nat, child_nat); + } + } + + if (minimum != NULL) + *minimum = min; + if (natural != NULL) + *natural = nat; +} + +static void +gtk_overlay_compute_child_allocation (GtkOverlay *overlay, + GtkWidget *widget, + GtkOverlayLayoutChild *child, + GtkAllocation *widget_allocation) +{ + GtkAllocation allocation; + gboolean result; + + g_signal_emit_by_name (overlay, "get-child-position", + widget, &allocation, &result); + + widget_allocation->x = allocation.x; + widget_allocation->y = allocation.y; + widget_allocation->width = allocation.width; + widget_allocation->height = allocation.height; +} + +static GtkAlign +effective_align (GtkAlign align, + GtkTextDirection direction) +{ + switch (align) + { + case GTK_ALIGN_START: + return direction == GTK_TEXT_DIR_RTL ? GTK_ALIGN_END : GTK_ALIGN_START; + case GTK_ALIGN_END: + return direction == GTK_TEXT_DIR_RTL ? GTK_ALIGN_START : GTK_ALIGN_END; + case GTK_ALIGN_FILL: + case GTK_ALIGN_CENTER: + case GTK_ALIGN_BASELINE: + default: + return align; + } +} + +static void +gtk_overlay_child_update_style_classes (GtkOverlay *overlay, + GtkWidget *child, + GtkAllocation *child_allocation) +{ + int width, height; + GtkAlign valign, halign; + gboolean is_left, is_right, is_top, is_bottom; + gboolean has_left, has_right, has_top, has_bottom; + GtkStyleContext *context; + + context = gtk_widget_get_style_context (child); + has_left = gtk_style_context_has_class (context, GTK_STYLE_CLASS_LEFT); + has_right = gtk_style_context_has_class (context, GTK_STYLE_CLASS_RIGHT); + has_top = gtk_style_context_has_class (context, GTK_STYLE_CLASS_TOP); + has_bottom = gtk_style_context_has_class (context, GTK_STYLE_CLASS_BOTTOM); + + is_left = is_right = is_top = is_bottom = FALSE; + + width = gtk_widget_get_width (GTK_WIDGET (overlay)); + height = gtk_widget_get_height (GTK_WIDGET (overlay)); + + halign = effective_align (gtk_widget_get_halign (child), + gtk_widget_get_direction (child)); + + if (halign == GTK_ALIGN_START) + is_left = (child_allocation->x == 0); + else if (halign == GTK_ALIGN_END) + is_right = (child_allocation->x + child_allocation->width == width); + + valign = gtk_widget_get_valign (child); + + if (valign == GTK_ALIGN_START) + is_top = (child_allocation->y == 0); + else if (valign == GTK_ALIGN_END) + is_bottom = (child_allocation->y + child_allocation->height == height); + + if (has_left && !is_left) + gtk_style_context_remove_class (context, GTK_STYLE_CLASS_LEFT); + else if (!has_left && is_left) + gtk_style_context_add_class (context, GTK_STYLE_CLASS_LEFT); + + if (has_right && !is_right) + gtk_style_context_remove_class (context, GTK_STYLE_CLASS_RIGHT); + else if (!has_right && is_right) + gtk_style_context_add_class (context, GTK_STYLE_CLASS_RIGHT); + + if (has_top && !is_top) + gtk_style_context_remove_class (context, GTK_STYLE_CLASS_TOP); + else if (!has_top && is_top) + gtk_style_context_add_class (context, GTK_STYLE_CLASS_TOP); + + if (has_bottom && !is_bottom) + gtk_style_context_remove_class (context, GTK_STYLE_CLASS_BOTTOM); + else if (!has_bottom && is_bottom) + gtk_style_context_add_class (context, GTK_STYLE_CLASS_BOTTOM); +} + +static void +gtk_overlay_child_allocate (GtkOverlay *overlay, + GtkWidget *widget, + GtkOverlayLayoutChild *child) +{ + GtkAllocation child_allocation; + + if (!gtk_widget_get_visible (widget)) + return; + + gtk_overlay_compute_child_allocation (overlay, widget, child, &child_allocation); + + gtk_overlay_child_update_style_classes (overlay, widget, &child_allocation); + gtk_widget_size_allocate (widget, &child_allocation, -1); +} + +static void +gtk_overlay_layout_allocate (GtkLayoutManager *layout_manager, + GtkWidget *widget, + int width, + int height, + int baseline) +{ + GtkWidget *child; + GtkWidget *main_widget; + + main_widget = gtk_bin_get_child (GTK_BIN (widget)); + if (main_widget && gtk_widget_get_visible (main_widget)) + gtk_widget_size_allocate (main_widget, + &(GtkAllocation) { 0, 0, width, height }, + -1); + + for (child = _gtk_widget_get_first_child (widget); + child != NULL; + child = _gtk_widget_get_next_sibling (child)) + { + if (child != main_widget) + { + GtkOverlayLayoutChild *child_data; + child_data = GTK_OVERLAY_LAYOUT_CHILD (gtk_layout_manager_get_layout_child (layout_manager, child)); + + gtk_overlay_child_allocate (GTK_OVERLAY (widget), child, child_data); + } + } +} + +static GtkLayoutChild * +gtk_overlay_layout_create_layout_child (GtkLayoutManager *manager, + GtkWidget *widget, + GtkWidget *for_child) +{ + return g_object_new (GTK_TYPE_OVERLAY_LAYOUT_CHILD, + "layout-manager", manager, + "child-widget", for_child, + NULL); +} + +static void +gtk_overlay_layout_class_init (GtkOverlayLayoutClass *klass) +{ + GtkLayoutManagerClass *layout_class = GTK_LAYOUT_MANAGER_CLASS (klass); + + layout_class->measure = gtk_overlay_layout_measure; + layout_class->allocate = gtk_overlay_layout_allocate; + layout_class->create_layout_child = gtk_overlay_layout_create_layout_child; +} + +static void +gtk_overlay_layout_init (GtkOverlayLayout *self) +{ +} + +GtkLayoutManager * +gtk_overlay_layout_new (void) +{ + return g_object_new (GTK_TYPE_OVERLAY_LAYOUT, NULL); +} diff --git a/gtk/gtkoverlaylayoutprivate.h b/gtk/gtkoverlaylayoutprivate.h new file mode 100644 index 0000000000..1e5406015a --- /dev/null +++ b/gtk/gtkoverlaylayoutprivate.h @@ -0,0 +1,57 @@ +/* gtkoverlaylayout.h: Overlay layout manager + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * Copyright 2019 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include <gtk/gtklayoutmanager.h> + +G_BEGIN_DECLS + +#define GTK_TYPE_OVERLAY_LAYOUT (gtk_overlay_layout_get_type ()) +#define GTK_TYPE_OVERLAY_LAYOUT_CHILD (gtk_overlay_layout_child_get_type ()) + +/* GtkOverlayLayout */ + +GDK_AVAILABLE_IN_ALL +G_DECLARE_FINAL_TYPE (GtkOverlayLayout, gtk_overlay_layout, GTK, OVERLAY_LAYOUT, GtkLayoutManager) + +GDK_AVAILABLE_IN_ALL +GtkLayoutManager * gtk_overlay_layout_new (void); + +/* GtkOverlayLayoutChild */ + +GDK_AVAILABLE_IN_ALL +G_DECLARE_FINAL_TYPE (GtkOverlayLayoutChild, gtk_overlay_layout_child, GTK, OVERLAY_LAYOUT_CHILD, GtkLayoutChild) + +GDK_AVAILABLE_IN_ALL +void gtk_overlay_layout_child_set_measure (GtkOverlayLayoutChild *child, + gboolean measure); + +GDK_AVAILABLE_IN_ALL +gboolean gtk_overlay_layout_child_get_measure (GtkOverlayLayoutChild *child); + +GDK_AVAILABLE_IN_ALL +void gtk_overlay_layout_child_set_clip_overlay (GtkOverlayLayoutChild *child, + gboolean clip_overlay); + +GDK_AVAILABLE_IN_ALL +gboolean gtk_overlay_layout_child_get_clip_overlay (GtkOverlayLayoutChild *child); + +G_END_DECLS diff --git a/gtk/meson.build b/gtk/meson.build index dbdbbb213a..0acc012217 100644 --- a/gtk/meson.build +++ b/gtk/meson.build @@ -289,6 +289,7 @@ gtk_public_sources = files([ 'gtknotebook.c', 'gtkorientable.c', 'gtkoverlay.c', + 'gtkoverlaylayout.c', 'gtkpadcontroller.c', 'gtkpagesetup.c', 'gtkpaned.c', |