diff options
author | Matthias Clasen <mclasen@redhat.com> | 2021-02-14 17:22:50 +0000 |
---|---|---|
committer | Matthias Clasen <mclasen@redhat.com> | 2021-02-14 17:22:50 +0000 |
commit | b0a82b1dfe2bba205fb5584e89fd4714bfad91ee (patch) | |
tree | 461b5ec6b5b112efd4a080ef03b9855cd4dba7f1 | |
parent | 04fb1421dc05836b18a19407c133e9452d34285a (diff) | |
parent | 52a2891933907b9b9e5d718bb6faa69b6aa3d96c (diff) | |
download | gtk+-b0a82b1dfe2bba205fb5584e89fd4714bfad91ee.tar.gz |
Merge branch 'popup-shadow-width' into 'master'
Support shadows on popovers
See merge request GNOME/gtk!3089
-rw-r--r-- | docs/reference/gdk/gdk4-docs.xml | 4 | ||||
-rw-r--r-- | docs/reference/gdk/gdk4-sections.txt | 2 | ||||
-rw-r--r-- | gdk/gdkpopuplayout.c | 70 | ||||
-rw-r--r-- | gdk/gdkpopuplayout.h | 14 | ||||
-rw-r--r-- | gdk/macos/gdkmacospopupsurface.c | 6 | ||||
-rw-r--r-- | gdk/wayland/gdksurface-wayland.c | 54 | ||||
-rw-r--r-- | gdk/win32/gdksurface-win32.c | 18 | ||||
-rw-r--r-- | gdk/x11/gdksurface-x11.c | 6 | ||||
-rw-r--r-- | gtk/gtkgizmo.c | 20 | ||||
-rw-r--r-- | gtk/gtkgizmoprivate.h | 7 | ||||
-rw-r--r-- | gtk/gtkpopover.c | 145 | ||||
-rw-r--r-- | gtk/gtkwindow.c | 10 | ||||
-rw-r--r-- | tests/meson.build | 1 | ||||
-rw-r--r-- | tests/testpopup.c | 131 |
14 files changed, 440 insertions, 48 deletions
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 @@ <title>Index of all symbols</title> <xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include> </index> + <index id="api-index-4-2" role="4.2"> + <title>Index of new symbols in 4.2</title> + <xi:include href="xml/api-index-4.2.xml"><xi:fallback /></xi:include> + </index> <index id="api-index-deprecated" role="deprecated"> <title>Index of deprecated symbols</title> <xi:include href="xml/api-index-deprecated.xml"><xi:fallback /></xi:include> 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 <SUBSECTION Standard> 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__ */ 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, 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; + + 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, - impl->next_layout.popup.x, - impl->next_layout.popup.y, - impl->next_layout.configured_width, - impl->next_layout.configured_height); + 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; 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, 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, diff --git a/gtk/gtkgizmo.c b/gtk/gtkgizmo.c index 00162645b3..45b23b20eb 100644 --- a/gtk/gtkgizmo.c +++ b/gtk/gtkgizmo.c @@ -83,6 +83,16 @@ gtk_gizmo_grab_focus (GtkWidget *widget) } 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) { GtkGizmo *self = GTK_GIZMO (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 diff --git a/gtk/gtkpopover.c b/gtk/gtkpopover.c index 6d10169073..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. */ @@ -126,6 +127,7 @@ #include "gtkstylecontextprivate.h" #include "gtkroundedboxprivate.h" #include "gsk/gskroundedrectprivate.h" +#include "gtkcssshadowvalueprivate.h" #define MNEMONICS_DELAY 300 /* ms */ @@ -436,6 +438,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 +451,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 +560,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 +582,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; @@ -871,6 +881,15 @@ node_style_changed_cb (GtkCssNode *node, } 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) { GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover); @@ -905,6 +924,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); @@ -1107,6 +1127,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 +1153,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 = shadow_width.top; + base = tip + TAIL_HEIGHT + border_top; + } + else if (pos == GTK_POS_RIGHT) { - tip = 0; - base = TAIL_HEIGHT + border_top; + 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 +1314,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 +1358,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 +1390,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 +1408,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 +1436,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) 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 * * |[<!-- language="plain" --> - * window.background + * window.background [.csd / .solid-csd / .ssd] [.maximized / .fullscreen / .tiled] * ├── <child> * ╰── <titlebar child>.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 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 <gtk/gtk.h> + +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; +} |