From ea19f7c360c82091c00b900d6bc00508a02f90e8 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sat, 13 Feb 2021 20:47:57 -0500 Subject: [PATCH 01/11] popuplayout: Add shadow width Add shadow width to the GdkPopupLayout struct. This information is needed by the compositor to make correct positioning decisions about popups. --- docs/reference/gdk/gdk4-docs.xml | 4 ++ docs/reference/gdk/gdk4-sections.txt | 2 + gdk/gdkpopuplayout.c | 70 +++++++++++++++++++++++++++- gdk/gdkpopuplayout.h | 14 ++++++ 4 files changed, 89 insertions(+), 1 deletion(-) diff --git a/docs/reference/gdk/gdk4-docs.xml b/docs/reference/gdk/gdk4-docs.xml index 50b018be9d..d63c7e346c 100644 --- a/docs/reference/gdk/gdk4-docs.xml +++ b/docs/reference/gdk/gdk4-docs.xml @@ -75,6 +75,10 @@ Index of all symbols + + Index of new symbols in 4.2 + + Index of deprecated symbols diff --git a/docs/reference/gdk/gdk4-sections.txt b/docs/reference/gdk/gdk4-sections.txt index 2c82ab8d92..5d0cd8a11a 100644 --- a/docs/reference/gdk/gdk4-sections.txt +++ b/docs/reference/gdk/gdk4-sections.txt @@ -627,6 +627,8 @@ gdk_popup_layout_set_anchor_hints gdk_popup_layout_get_anchor_hints gdk_popup_layout_set_offset gdk_popup_layout_get_offset +gdk_popup_layout_set_shadow_width +gdk_popup_layout_get_shadow_width GDK_TYPE_POPUP_LAYOUT gdk_popup_layout_get_type diff --git a/gdk/gdkpopuplayout.c b/gdk/gdkpopuplayout.c index 7192ef259d..686a2cbafe 100644 --- a/gdk/gdkpopuplayout.c +++ b/gdk/gdkpopuplayout.c @@ -74,6 +74,10 @@ struct _GdkPopupLayout GdkAnchorHints anchor_hints; int dx; int dy; + int shadow_left; + int shadow_right; + int shadow_top; + int shadow_bottom; }; G_DEFINE_BOXED_TYPE (GdkPopupLayout, gdk_popup_layout, @@ -165,6 +169,10 @@ gdk_popup_layout_copy (GdkPopupLayout *layout) new_layout->anchor_hints = layout->anchor_hints; new_layout->dx = layout->dx; new_layout->dy = layout->dy; + new_layout->shadow_left = layout->shadow_left; + new_layout->shadow_right = layout->shadow_right; + new_layout->shadow_top = layout->shadow_top; + new_layout->shadow_bottom = layout->shadow_bottom; return new_layout; } @@ -191,7 +199,11 @@ gdk_popup_layout_equal (GdkPopupLayout *layout, layout->surface_anchor == other->surface_anchor && layout->anchor_hints == other->anchor_hints && layout->dx == other->dx && - layout->dy == other->dy); + layout->dy == other->dy && + layout->shadow_left == other->shadow_left && + layout->shadow_right == other->shadow_right && + layout->shadow_top == other->shadow_top && + layout->shadow_bottom == other->shadow_bottom); } /** @@ -346,3 +358,59 @@ gdk_popup_layout_get_offset (GdkPopupLayout *layout, if (dy) *dy = layout->dy; } + +/** + * gdk_popup_layout_set_shadow_width: + * @layout: a #GdkPopupLayout + * @left: width of the left part of the shadow + * @right: width of the right part of the shadow + * @top: height of the top part of the shadow + * @bottom: height of the bottom part of the shadow + * + * The shadow width corresponds to the part of the computed surface size + * that would consist of the shadow margin surrounding the window, would + * there be any. + * + * Since: 4.2 + */ +void +gdk_popup_layout_set_shadow_width (GdkPopupLayout *layout, + int left, + int right, + int top, + int bottom) +{ + layout->shadow_left = left; + layout->shadow_right = right; + layout->shadow_top = top; + layout->shadow_bottom = bottom; +} + +/** + * gdk_popup_layout_get_shadow_width: + * @layout: a #GdkPopupLayout + * @left: (out): return location for the left shadow width + * @right: (out): return location for the right shadow width + * @top: (out): return location for the top shadow width + * @bottom: (out): return location for the bottom shadow width + * + * Obtains the shadow widths of this layout. + * + * Since: 4.2 + */ +void +gdk_popup_layout_get_shadow_width (GdkPopupLayout *layout, + int *left, + int *right, + int *top, + int *bottom) +{ + if (left) + *left = layout->shadow_left; + if (right) + *right = layout->shadow_right; + if (top) + *top = layout->shadow_top; + if (bottom) + *bottom = layout->shadow_bottom; +} diff --git a/gdk/gdkpopuplayout.h b/gdk/gdkpopuplayout.h index f55c748895..254704ead5 100644 --- a/gdk/gdkpopuplayout.h +++ b/gdk/gdkpopuplayout.h @@ -137,6 +137,20 @@ void gdk_popup_layout_get_offset (GdkPopupLayout int *dx, int *dy); +GDK_AVAILABLE_IN_4_2 +void gdk_popup_layout_set_shadow_width (GdkPopupLayout *layout, + int left, + int right, + int top, + int bottom); +GDK_AVAILABLE_IN_4_2 +void gdk_popup_layout_get_shadow_width (GdkPopupLayout *layout, + int *left, + int *right, + int *top, + int *bottom); + + G_END_DECLS #endif /* __GDK_POPUP_LAYOUT_H__ */ From aec738745a014925899611e333a35aac796ab895 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sat, 13 Feb 2021 20:48:15 -0500 Subject: [PATCH 02/11] wayland: Set shadow width on popup surfaces Take the shadow width from the popup layout into account. --- gdk/wayland/gdksurface-wayland.c | 54 +++++++++++++++++++++++--------- 1 file changed, 40 insertions(+), 14 deletions(-) diff --git a/gdk/wayland/gdksurface-wayland.c b/gdk/wayland/gdksurface-wayland.c index d31e4ae6ee..2f95339fb0 100644 --- a/gdk/wayland/gdksurface-wayland.c +++ b/gdk/wayland/gdksurface-wayland.c @@ -630,12 +630,19 @@ static void configure_popup_geometry (GdkSurface *surface) { GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface); + int x, y; + int width, height; - gdk_wayland_surface_move_resize (surface, - impl->next_layout.popup.x, - impl->next_layout.popup.y, - impl->next_layout.configured_width, - impl->next_layout.configured_height); + x = impl->next_layout.popup.x - impl->shadow_left; + y = impl->next_layout.popup.y - impl->shadow_top; + width = + impl->next_layout.configured_width + + (impl->shadow_left + impl->shadow_right); + height = + impl->next_layout.configured_height + + (impl->shadow_top + impl->shadow_bottom); + + gdk_wayland_surface_move_resize (surface, x, y, width, height); } static void @@ -2289,12 +2296,17 @@ calculate_popup_rect (GdkSurface *surface, int width, height; GdkRectangle anchor_rect; int dx, dy; + int shadow_left, shadow_right, shadow_top, shadow_bottom; int x = 0, y = 0; - width = (impl->popup.unconstrained_width - - (impl->shadow_left + impl->shadow_right)); - height = (impl->popup.unconstrained_height - - (impl->shadow_top + impl->shadow_bottom)); + gdk_popup_layout_get_shadow_width (layout, + &shadow_left, + &shadow_right, + &shadow_top, + &shadow_bottom); + + width = (impl->popup.unconstrained_width - (shadow_left + shadow_right)); + height = (impl->popup.unconstrained_height - (shadow_top + shadow_bottom)); anchor_rect = *gdk_popup_layout_get_anchor_rect (layout); gdk_popup_layout_get_offset (layout, &dx, &dy); @@ -2478,7 +2490,6 @@ create_dynamic_positioner (GdkSurface *surface, GdkPopupLayout *layout, gboolean ack_parent_configure) { - GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface); GdkSurface *parent = surface->parent; GdkWaylandSurface *parent_impl = GDK_WAYLAND_SURFACE (parent); GdkWaylandDisplay *display = @@ -2493,12 +2504,21 @@ create_dynamic_positioner (GdkSurface *surface, GdkGravity rect_anchor; GdkGravity surface_anchor; GdkAnchorHints anchor_hints; + int shadow_left; + int shadow_right; + int shadow_top; + int shadow_bottom; + gdk_popup_layout_get_shadow_width (layout, + &shadow_left, + &shadow_right, + &shadow_top, + &shadow_bottom); geometry = (GdkRectangle) { - .x = impl->shadow_left, - .y = impl->shadow_top, - .width = width - (impl->shadow_left + impl->shadow_right), - .height = height - (impl->shadow_top + impl->shadow_bottom), + .x = shadow_left, + .y = shadow_top, + .width = width - (shadow_left + shadow_right), + .height = height - (shadow_top + shadow_bottom), }; anchor_rect = gdk_popup_layout_get_anchor_rect (layout); @@ -2724,6 +2744,12 @@ gdk_wayland_surface_create_xdg_popup (GdkSurface *surface, g_assert_not_reached (); } + gdk_popup_layout_get_shadow_width (layout, + &impl->shadow_left, + &impl->shadow_right, + &impl->shadow_top, + &impl->shadow_bottom); + if (grab_input_seat) { struct wl_seat *seat; From b948799778541709007f848be220d814ed7a7c5a Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 31 Jan 2021 19:26:33 -0500 Subject: [PATCH 03/11] popover: Support shadows Use gdk_popup_layout_set_shadow_width to take shadows into account when positioning popovers, and set the input region to exclude the shadow, since we aren't doing interactive resizing and the like. When the popover has a beak, we make the surface size be content size + shadow + tail, and then position the content according to the final position inside this slightly too large surface. The surface being too large doesn't matter, since we set up an input region. --- gtk/gtkpopover.c | 128 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 103 insertions(+), 25 deletions(-) diff --git a/gtk/gtkpopover.c b/gtk/gtkpopover.c index 6d10169073..217c2f30a2 100644 --- a/gtk/gtkpopover.c +++ b/gtk/gtkpopover.c @@ -126,6 +126,7 @@ #include "gtkstylecontextprivate.h" #include "gtkroundedboxprivate.h" #include "gsk/gskroundedrectprivate.h" +#include "gtkcssshadowvalueprivate.h" #define MNEMONICS_DELAY 300 /* ms */ @@ -436,6 +437,8 @@ create_popup_layout (GtkPopover *popover) GdkAnchorHints anchor_hints; GdkPopupLayout *layout; GtkWidget *parent; + GtkCssStyle *style; + GtkBorder shadow_width; parent = gtk_widget_get_parent (GTK_WIDGET (popover)); gtk_widget_get_surface_allocation (parent, &rect); @@ -447,6 +450,9 @@ create_popup_layout (GtkPopover *popover) rect.height = priv->pointing_to.height; } + style = gtk_css_node_get_style (gtk_widget_get_css_node (GTK_WIDGET (priv->contents_widget))); + gtk_css_shadow_value_get_extents (style->background->box_shadow, &shadow_width); + switch (priv->position) { case GTK_POS_LEFT: @@ -553,6 +559,11 @@ create_popup_layout (GtkPopover *popover) layout = gdk_popup_layout_new (&rect, parent_anchor, surface_anchor); gdk_popup_layout_set_anchor_hints (layout, anchor_hints); + gdk_popup_layout_set_shadow_width (layout, + shadow_width.left, + shadow_width.right, + shadow_width.top, + shadow_width.bottom); if (priv->x_offset || priv->y_offset) gdk_popup_layout_set_offset (layout, priv->x_offset, priv->y_offset); @@ -570,9 +581,7 @@ present_popup (GtkPopover *popover) layout = create_popup_layout (popover); gtk_widget_get_preferred_size (GTK_WIDGET (popover), NULL, &nat); - if (gdk_popup_present (GDK_POPUP (priv->surface), - nat.width, nat.height, - layout)) + if (gdk_popup_present (GDK_POPUP (priv->surface), nat.width, nat.height, layout)) { update_popover_layout (popover, layout, nat.width, nat.height); return TRUE; @@ -1107,6 +1116,7 @@ gtk_popover_get_gap_coords (GtkPopover *popover, int popover_width, popover_height; GtkCssStyle *style; GtkWidget *parent; + GtkBorder shadow_width; popover_width = gtk_widget_get_allocated_width (widget); popover_height = gtk_widget_get_allocated_height (widget); @@ -1132,20 +1142,27 @@ gtk_popover_get_gap_coords (GtkPopover *popover, border_right = _gtk_css_number_value_get (style->border->border_right_width, 100); border_bottom = _gtk_css_number_value_get (style->border->border_bottom_width, 100); - if (pos == GTK_POS_BOTTOM || pos == GTK_POS_RIGHT) + gtk_css_shadow_value_get_extents (style->background->box_shadow, &shadow_width); + + if (pos == GTK_POS_BOTTOM) { - tip = 0; - base = TAIL_HEIGHT + border_top; + tip = shadow_width.top; + base = tip + TAIL_HEIGHT + border_top; + } + else if (pos == GTK_POS_RIGHT) + { + tip = shadow_width.left; + base = tip + TAIL_HEIGHT + border_top; } else if (pos == GTK_POS_TOP) { - base = popover_height - border_bottom - TAIL_HEIGHT; - tip = popover_height; + tip = popover_height - shadow_width.bottom; + base = tip - border_bottom - TAIL_HEIGHT; } else if (pos == GTK_POS_LEFT) { - base = popover_width - border_right - TAIL_HEIGHT; - tip = popover_width; + tip = popover_width - shadow_width.right; + base = tip - border_right - TAIL_HEIGHT; } else g_assert_not_reached (); @@ -1286,7 +1303,31 @@ gtk_popover_update_shape (GtkPopover *popover) cairo_region_destroy (region); } else - gdk_surface_set_input_region (priv->surface, NULL); + { + GtkCssNode *content_css_node; + GtkCssStyle *style; + GtkBorder shadow_width; + cairo_rectangle_int_t input_rect; + cairo_region_t *region; + + content_css_node = + gtk_widget_get_css_node (GTK_WIDGET (priv->contents_widget)); + style = gtk_css_node_get_style (content_css_node); + gtk_css_shadow_value_get_extents (style->background->box_shadow, &shadow_width); + + input_rect.x = shadow_width.left; + input_rect.y = shadow_width.top; + input_rect.width = + gdk_surface_get_width (priv->surface) - + (shadow_width.left + shadow_width.right); + input_rect.height = + gdk_surface_get_height (priv->surface) - + (shadow_width.top + shadow_width.bottom); + + region = cairo_region_create_rectangle (&input_rect); + gdk_surface_set_input_region (priv->surface, region); + cairo_region_destroy (region); + } } static int @@ -1306,14 +1347,22 @@ get_minimal_size (GtkPopover *popover, GtkPositionType pos; int minimal_size; int tail_gap_width = priv->has_arrow ? TAIL_GAP_WIDTH : 0; + int min_width, min_height; - minimal_size = 2 * get_border_radius (GTK_WIDGET (popover)); + minimal_size = 2 * get_border_radius (GTK_WIDGET (priv->contents_widget)); pos = priv->position; if ((orientation == GTK_ORIENTATION_HORIZONTAL && POS_IS_VERTICAL (pos)) || (orientation == GTK_ORIENTATION_VERTICAL && !POS_IS_VERTICAL (pos))) minimal_size += tail_gap_width; + gtk_widget_get_size_request (GTK_WIDGET (popover), &min_width, &min_height); + + if (orientation == GTK_ORIENTATION_HORIZONTAL) + minimal_size = MAX (minimal_size, min_width); + else + minimal_size = MAX (minimal_size, min_height); + return minimal_size; } @@ -1330,10 +1379,15 @@ gtk_popover_measure (GtkWidget *widget, GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); int minimal_size; int tail_height = priv->has_arrow ? TAIL_HEIGHT : 0; + GtkCssStyle *style; + GtkBorder shadow_width; if (for_size >= 0) for_size -= tail_height; + style = gtk_css_node_get_style (gtk_widget_get_css_node (GTK_WIDGET (priv->contents_widget))); + gtk_css_shadow_value_get_extents (style->background->box_shadow, &shadow_width); + gtk_widget_measure (priv->contents_widget, orientation, for_size, minimum, natural, @@ -1343,8 +1397,22 @@ gtk_popover_measure (GtkWidget *widget, *minimum = MAX (*minimum, minimal_size); *natural = MAX (*natural, minimal_size); - *minimum += tail_height; - *natural += tail_height; + if (orientation == GTK_ORIENTATION_HORIZONTAL) + { + *minimum += shadow_width.left + shadow_width.right; + *natural += shadow_width.left + shadow_width.right; + } + else + { + *minimum += shadow_width.top + shadow_width.bottom; + *natural += shadow_width.top + shadow_width.bottom; + } + + if (POS_IS_VERTICAL (priv->position) == (orientation == GTK_ORIENTATION_VERTICAL)) + { + *minimum += tail_height; + *natural += tail_height; + } } static void @@ -1357,32 +1425,42 @@ gtk_popover_size_allocate (GtkWidget *widget, GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); GtkAllocation child_alloc; int tail_height = priv->has_arrow ? TAIL_HEIGHT : 0; + GtkCssStyle *style; + GtkBorder shadow_width; + + style = gtk_css_node_get_style (gtk_widget_get_css_node (GTK_WIDGET (priv->contents_widget))); + gtk_css_shadow_value_get_extents (style->background->box_shadow, &shadow_width); switch (priv->final_position) { case GTK_POS_TOP: - child_alloc.x = tail_height / 2; - child_alloc.y = 0; + child_alloc.x = shadow_width.left; + child_alloc.y = shadow_width.top; + child_alloc.width = width - (shadow_width.left + shadow_width.right); + child_alloc.height = height - (shadow_width.top + shadow_width.bottom + tail_height); break; case GTK_POS_BOTTOM: - child_alloc.x = tail_height / 2; - child_alloc.y = tail_height; + child_alloc.x = shadow_width.left; + child_alloc.y = shadow_width.top + tail_height; + child_alloc.width = width - (shadow_width.left + shadow_width.right); + child_alloc.height = height - (shadow_width.top + shadow_width.bottom + tail_height); break; case GTK_POS_LEFT: - child_alloc.x = 0; - child_alloc.y = tail_height / 2; + child_alloc.x = shadow_width.left; + child_alloc.y = shadow_width.top; + child_alloc.width = width - (shadow_width.left + shadow_width.right + tail_height); + child_alloc.height = height - (shadow_width.top + shadow_width.bottom); break; case GTK_POS_RIGHT: - child_alloc.x = tail_height; - child_alloc.y = tail_height / 2; + child_alloc.x = shadow_width.left + tail_height; + child_alloc.y = shadow_width.top; + child_alloc.width = width - (shadow_width.left + shadow_width.right + tail_height); + child_alloc.height = height - (shadow_width.top + shadow_width.bottom); break; default: break; } - child_alloc.width = width - tail_height; - child_alloc.height = height - tail_height; - gtk_widget_size_allocate (priv->contents_widget, &child_alloc, baseline); if (priv->surface) From 7332b4f1d92880e14b2bc11eedda67bcc4f39ca3 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sat, 13 Feb 2021 15:46:00 -0500 Subject: [PATCH 04/11] Add a test for popover positioning This is useful for testing the interaction of arrows, shadows or offsets with popover positioning. --- tests/meson.build | 1 + tests/testpopup.c | 131 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 132 insertions(+) create mode 100644 tests/testpopup.c diff --git a/tests/meson.build b/tests/meson.build index 783dd61914..c0e5f836b0 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -1,5 +1,6 @@ gtk_tests = [ # testname, optional extra sources + ['testpopup'], ['testupload'], ['testtransform'], ['testdropdown'], diff --git a/tests/testpopup.c b/tests/testpopup.c new file mode 100644 index 0000000000..265ba10e9b --- /dev/null +++ b/tests/testpopup.c @@ -0,0 +1,131 @@ +#include + +static void +update_offset (GObject *object, + GParamSpec *pspec, + GtkWidget *widget) +{ + if (gtk_check_button_get_active (GTK_CHECK_BUTTON (object))) + gtk_popover_set_offset (GTK_POPOVER (widget), 12, 12); + else + gtk_popover_set_offset (GTK_POPOVER (widget), 0, 0); +} + +static void +update_shadow (GObject *object, + GParamSpec *pspec, + GtkWidget *widget) +{ + const char *classes[] = { + "no-shadow", + "shadow1", + "shadow2", + "shadow3", + "shadow4", + }; + guint selected; + + selected = gtk_drop_down_get_selected (GTK_DROP_DOWN (object)); + g_assert (selected < G_N_ELEMENTS (classes)); + + for (int i = 0; i < G_N_ELEMENTS (classes); i++) + gtk_widget_remove_css_class (widget, classes[i]); + + gtk_widget_add_css_class (widget, classes[selected]); +} + +static const char css[] = + "popover.no-shadow > contents { box-shadow: none; }\n" + "popover.shadow1 > contents { box-shadow: 6px 6px rgba(128,0,255,0.5); }\n" + "popover.shadow2 > contents { box-shadow: -6px -6px rgba(255,0,0,0.5), 6px 6px rgba(128,0,255,0.5); }\n" + "popover.shadow3 > contents { box-shadow: -6px -6px rgba(255,0,0,0.5), 18px 18px rgba(128,0,255,0.5); }\n" + "popover.shadow4 > contents { box-shadow: -18px -18px rgba(255,0,0,0.5), 18px 18px rgba(128,0,255,0.5); }\n"; + +int +main (int argc, char *argv[]) +{ + GtkWidget *window; + GtkWidget *box; + GtkWidget *button; + GtkWidget *popover; + GtkWidget *box2; + GtkWidget *box3; + GtkWidget *checkbox; + GtkWidget *checkbox2; + GtkWidget *dropdown; + GtkWidget *dropdown2; + GtkCssProvider *provider; + + gtk_init (); + + provider = gtk_css_provider_new (); + gtk_css_provider_load_from_data (provider, css, -1); + + gtk_style_context_add_provider_for_display (gdk_display_get_default (), + GTK_STYLE_PROVIDER (provider), + 800); + + window = gtk_window_new (); + box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 10); + g_object_set (box, + "margin-top", 20, + "margin-bottom", 20, + "margin-start", 20, + "margin-end", 20, + NULL); + + button = gtk_menu_button_new (); + + gtk_widget_set_halign (button, GTK_ALIGN_CENTER); + gtk_widget_set_valign (button, GTK_ALIGN_CENTER); + + gtk_box_append (GTK_BOX (box), button); + + popover = gtk_popover_new (); + box2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 10); + gtk_popover_set_child (GTK_POPOVER (popover), box2); + + gtk_box_append (GTK_BOX (box2), gtk_label_new ("First item")); + gtk_box_append (GTK_BOX (box2), gtk_label_new ("Second item")); + gtk_box_append (GTK_BOX (box2), gtk_label_new ("Third item")); + + gtk_menu_button_set_popover (GTK_MENU_BUTTON (button), popover); + + box3 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 10); + dropdown = gtk_drop_down_new_from_strings ((const char*[]){ "Left", "Right", "Top", "Bottom", NULL }); + gtk_drop_down_set_selected (GTK_DROP_DOWN (dropdown), 3); + + checkbox = gtk_check_button_new_with_label ("Arrow"); + + checkbox2 = gtk_check_button_new_with_label ("Offset"); + + dropdown2 = gtk_drop_down_new_from_strings ((const char*[]){ "No Shadow", "Shadow 1", "Shadow 2", "Shadow 3", "Shadow 4", NULL }); + + gtk_box_append (GTK_BOX (box3), checkbox); + gtk_box_append (GTK_BOX (box3), checkbox2); + gtk_box_append (GTK_BOX (box3), dropdown); + gtk_box_append (GTK_BOX (box3), dropdown2); + + gtk_box_append (GTK_BOX (box), box3); + + g_object_bind_property (checkbox, "active", + popover, "has-arrow", + G_BINDING_SYNC_CREATE); + g_signal_connect (checkbox2, "notify::active", + G_CALLBACK (update_offset), popover); + g_object_bind_property (dropdown, "selected", + popover, "position", + G_BINDING_SYNC_CREATE); + g_signal_connect (dropdown2, "notify::selected", + G_CALLBACK (update_shadow), popover); + update_shadow (G_OBJECT (dropdown2), NULL, popover); + + gtk_window_set_child (GTK_WINDOW (window), box); + + gtk_window_present (GTK_WINDOW (window)); + + while (g_list_model_get_n_items (gtk_window_get_toplevels ()) > 0) + g_main_context_iteration (NULL, TRUE); + + return 0; +} From 39f72b38341d2ff2d67c2043ce04915797d24fb8 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sat, 13 Feb 2021 19:04:10 -0500 Subject: [PATCH 05/11] gizmo: Add a css_changed function In a break from the current gizmo api, add a separate setter, since the calls with long argument lists full of NULL are getting out of hand. --- gtk/gtkgizmo.c | 20 ++++++++++++++++++++ gtk/gtkgizmoprivate.h | 7 ++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/gtk/gtkgizmo.c b/gtk/gtkgizmo.c index 00162645b3..45b23b20eb 100644 --- a/gtk/gtkgizmo.c +++ b/gtk/gtkgizmo.c @@ -82,6 +82,16 @@ gtk_gizmo_grab_focus (GtkWidget *widget) return FALSE; } +static void +gtk_gizmo_css_changed (GtkWidget *widget, + GtkCssStyleChange *change) +{ + GtkGizmo *self = GTK_GIZMO (widget); + + if (self->css_changed_func) + self->css_changed_func (self, change); +} + static void gtk_gizmo_finalize (GObject *object) { @@ -115,6 +125,7 @@ gtk_gizmo_class_init (GtkGizmoClass *klass) widget_class->contains = gtk_gizmo_contains; widget_class->grab_focus = gtk_gizmo_grab_focus; widget_class->focus = gtk_gizmo_focus; + widget_class->css_changed = gtk_gizmo_css_changed; } static void @@ -165,3 +176,12 @@ gtk_gizmo_new_with_role (const char *css_name, return GTK_WIDGET (gizmo); } + +void +gtk_gizmo_set_css_changed_func (GtkGizmo *gizmo, + GtkGizmoCssChangedFunc css_changed_func) +{ + g_return_if_fail (!gtk_widget_get_realized (GTK_WIDGET (gizmo))); + + gizmo->css_changed_func = css_changed_func; +} diff --git a/gtk/gtkgizmoprivate.h b/gtk/gtkgizmoprivate.h index 9257201a59..a830e3550d 100644 --- a/gtk/gtkgizmoprivate.h +++ b/gtk/gtkgizmoprivate.h @@ -33,7 +33,9 @@ typedef gboolean (* GtkGizmoContainsFunc) (GtkGizmo *gizmo, double y); typedef gboolean (* GtkGizmoFocusFunc) (GtkGizmo *gizmo, GtkDirectionType direction); -typedef gboolean (* GtkGizmoGrabFocusFunc)(GtkGizmo *gizmo); +typedef gboolean (* GtkGizmoGrabFocusFunc) (GtkGizmo *gizmo); +typedef void (* GtkGizmoCssChangedFunc) (GtkGizmo *gizmo, + GtkCssStyleChange *change); struct _GtkGizmo { @@ -45,6 +47,7 @@ struct _GtkGizmo GtkGizmoContainsFunc contains_func; GtkGizmoFocusFunc focus_func; GtkGizmoGrabFocusFunc grab_focus_func; + GtkGizmoCssChangedFunc css_changed_func; }; struct _GtkGizmoClass @@ -71,5 +74,7 @@ GtkWidget *gtk_gizmo_new_with_role (const char *css_name, GtkGizmoFocusFunc focus_func, GtkGizmoGrabFocusFunc grab_focus_func); +void gtk_gizmo_set_css_changed_func (GtkGizmo *gizmo, + GtkGizmoCssChangedFunc css_changed_func); #endif From aa1d08687d5e395649b59640567efb40f6203053 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sat, 13 Feb 2021 19:05:26 -0500 Subject: [PATCH 06/11] popover: Handle shadow changes When the box-shadow css property of the contents widget changes, queue a resize on the popover. --- gtk/gtkpopover.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/gtk/gtkpopover.c b/gtk/gtkpopover.c index 217c2f30a2..bf9be24709 100644 --- a/gtk/gtkpopover.c +++ b/gtk/gtkpopover.c @@ -879,6 +879,15 @@ node_style_changed_cb (GtkCssNode *node, gtk_widget_queue_draw (widget); } +static void +contents_css_changed (GtkGizmo *contents, + GtkCssStyleChange *change) +{ + if (change == NULL || + gtk_css_style_change_changes_property (change, GTK_CSS_PROPERTY_BOX_SHADOW)) + gtk_widget_queue_resize (gtk_widget_get_parent (GTK_WIDGET (contents))); +} + static void gtk_popover_init (GtkPopover *popover) { @@ -914,6 +923,7 @@ gtk_popover_init (GtkPopover *popover) priv->contents_widget = gtk_gizmo_new ("contents", NULL, NULL, NULL, NULL, (GtkGizmoFocusFunc)gtk_widget_focus_child, (GtkGizmoGrabFocusFunc)gtk_widget_grab_focus_child); + gtk_gizmo_set_css_changed_func (GTK_GIZMO (priv->contents_widget), contents_css_changed); gtk_widget_set_layout_manager (priv->contents_widget, gtk_bin_layout_new ()); gtk_widget_set_parent (priv->contents_widget, GTK_WIDGET (popover)); gtk_widget_set_overflow (priv->contents_widget, GTK_OVERFLOW_HIDDEN); From ad9c813ed1091e4d0263f2f477708108b4ff6591 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sat, 13 Feb 2021 21:10:10 -0500 Subject: [PATCH 07/11] x11: Apply popup shadow with --- gdk/x11/gdksurface-x11.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/gdk/x11/gdksurface-x11.c b/gdk/x11/gdksurface-x11.c index 1ab71a7cce..92a0a23d6c 100644 --- a/gdk/x11/gdksurface-x11.c +++ b/gdk/x11/gdksurface-x11.c @@ -1814,6 +1814,12 @@ gdk_x11_surface_layout_popup (GdkSurface *surface, gdk_x11_monitor_get_workarea); gdk_x11_monitor_get_workarea (monitor, &bounds); + gdk_popup_layout_get_shadow_width (layout, + &impl->shadow_left, + &impl->shadow_right, + &impl->shadow_top, + &impl->shadow_bottom); + gdk_surface_layout_popup_helper (surface, width, height, From e0c48e74bd34810a7dabf3d5bab81b31f4b47002 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sat, 13 Feb 2021 21:10:52 -0500 Subject: [PATCH 08/11] win32: Apply popup shadow width --- gdk/win32/gdksurface-win32.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/gdk/win32/gdksurface-win32.c b/gdk/win32/gdksurface-win32.c index c690fed206..cd113bc4df 100644 --- a/gdk/win32/gdksurface-win32.c +++ b/gdk/win32/gdksurface-win32.c @@ -1201,6 +1201,12 @@ gdk_win32_surface_move (GdkSurface *surface, gdk_win32_surface_move_resize_internal (surface, TRUE, x, y, -1, -1); } +static void gdk_win32_surface_set_shadow_width (GdkSurface *window, + int left, + int right, + int top, + int bottom); + static void gdk_win32_surface_layout_popup (GdkSurface *surface, int width, @@ -1212,11 +1218,23 @@ gdk_win32_surface_layout_popup (GdkSurface *surface, GdkRectangle bounds; GdkRectangle final_rect; int x, y; + int shadow_left, shadow_right, shadow_top, shadow_bottom; monitor = gdk_surface_get_layout_monitor (surface, layout, gdk_win32_monitor_get_workarea); gdk_win32_monitor_get_workarea (monitor, &bounds); + gdk_popup_layout_get_shadow_width (layout, + &shadow_left, + &shadow_right, + &shadow_top, + &shadow_bottom); + gdk_win32_surface_set_shadow_width (surface, + shadow_left, + shadow_right, + shadow_top, + shadow_bottom); + gdk_surface_layout_popup_helper (surface, width, height, From a4f17eaa8bf2794af5a7fd027a59e3622ffb91f4 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sat, 13 Feb 2021 21:12:40 -0500 Subject: [PATCH 09/11] macos: Apply popup shadow width --- gdk/macos/gdkmacospopupsurface.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/gdk/macos/gdkmacospopupsurface.c b/gdk/macos/gdkmacospopupsurface.c index 23c44a70a8..60239d72ed 100644 --- a/gdk/macos/gdkmacospopupsurface.c +++ b/gdk/macos/gdkmacospopupsurface.c @@ -66,6 +66,12 @@ gdk_macos_popup_surface_layout (GdkMacosPopupSurface *self, monitor = _gdk_macos_surface_get_best_monitor (GDK_MACOS_SURFACE (self)); gdk_macos_monitor_get_workarea (monitor, &bounds); + gdk_popup_layout_get_shadow_width (layout, + &self->parent_instance.shadow_left, + &self->parent_instance.shadow_right, + &self->parent_instance.shadow_top, + &self->parent_instance.shadow_bottom); + gdk_surface_layout_popup_helper (GDK_SURFACE (self), width, height, From 7ebc3a9489a55191a1d4f16296d75ffefdfa19c8 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 14 Feb 2021 11:14:23 -0500 Subject: [PATCH 10/11] Update popover css docs Mention that shadows are now possible. --- gtk/gtkpopover.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/gtk/gtkpopover.c b/gtk/gtkpopover.c index bf9be24709..b00cdd3827 100644 --- a/gtk/gtkpopover.c +++ b/gtk/gtkpopover.c @@ -83,14 +83,15 @@ * to differentiate from plain popovers. * * When styling a popover directly, the popover node should usually - * not have any background. + * not have any background. The visible part of the popover can have a shadow. + * To specify it in CSS, set the box-shadow of the contents node. * * Note that, in order to accomplish appropriate arrow visuals, #GtkPopover uses * custom drawing for the arrow node. This makes it possible for the arrow to * change its shape dynamically, but it also limits the possibilities of styling * it using CSS. In particular, the arrow gets drawn over the content node's - * border so they look like one shape, which means that the border-width of - * the content node and the arrow node should be the same. The arrow also does + * shadow border so they look like one shape, which means that the border width + * of the content node and the arrow node should be the same. The arrow also does * not support any border shape other than solid, no border-radius, only one * border width (border-bottom-width is used) and no box-shadow. */ From 52a2891933907b9b9e5d718bb6faa69b6aa3d96c Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 14 Feb 2021 11:22:44 -0500 Subject: [PATCH 11/11] window: Add some more details to css docs Mention more of the supported style classes. --- gtk/gtkwindow.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/gtk/gtkwindow.c b/gtk/gtkwindow.c index ac7f050738..b4de2cc97c 100644 --- a/gtk/gtkwindow.c +++ b/gtk/gtkwindow.c @@ -114,7 +114,7 @@ * # CSS nodes * * |[ - * window.background + * window.background [.csd / .solid-csd / .ssd] [.maximized / .fullscreen / .tiled] * ├── * ╰── .titlebar [.default-decoration] * ]| @@ -125,9 +125,11 @@ * client-side decorations are in use), .solid-csd (for client-side decorations * without invisible borders), .ssd (used by mutter when rendering server-side * decorations). GtkWindow also represents window states with the following - * style classes on the main node: .tiled, .maximized, .fullscreen. Specialized - * types of window often add their own discriminating style classes, such as - * .popup or .tooltip. + * style classes on the main node: .maximized, .fullscreen, .tiled (when supported, + * also .tiled-top, .tiled-left, .tiled-right, .tiled-bottom). + * + * GtkWindow subclasses often add their own discriminating style classes, + * such as .dialog, .popup or .tooltip. * * Generally, some CSS properties don't make sense on the toplevel window node, * such as margins or padding. When client-side decorations without invisible