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.
This commit is contained in:
Matthias Clasen
2021-01-31 19:26:33 -05:00
parent aec738745a
commit b948799778

View File

@@ -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)