summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthias Clasen <mclasen@redhat.com>2021-02-14 17:22:50 +0000
committerMatthias Clasen <mclasen@redhat.com>2021-02-14 17:22:50 +0000
commitb0a82b1dfe2bba205fb5584e89fd4714bfad91ee (patch)
tree461b5ec6b5b112efd4a080ef03b9855cd4dba7f1
parent04fb1421dc05836b18a19407c133e9452d34285a (diff)
parent52a2891933907b9b9e5d718bb6faa69b6aa3d96c (diff)
downloadgtk+-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.xml4
-rw-r--r--docs/reference/gdk/gdk4-sections.txt2
-rw-r--r--gdk/gdkpopuplayout.c70
-rw-r--r--gdk/gdkpopuplayout.h14
-rw-r--r--gdk/macos/gdkmacospopupsurface.c6
-rw-r--r--gdk/wayland/gdksurface-wayland.c54
-rw-r--r--gdk/win32/gdksurface-win32.c18
-rw-r--r--gdk/x11/gdksurface-x11.c6
-rw-r--r--gtk/gtkgizmo.c20
-rw-r--r--gtk/gtkgizmoprivate.h7
-rw-r--r--gtk/gtkpopover.c145
-rw-r--r--gtk/gtkwindow.c10
-rw-r--r--tests/meson.build1
-rw-r--r--tests/testpopup.c131
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;
+}