Merge branch 'popup-shadow-width' into 'master'

Support shadows on popovers

See merge request GNOME/gtk!3089
This commit is contained in:
Matthias Clasen
2021-02-14 17:22:50 +00:00
14 changed files with 440 additions and 48 deletions

View File

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

View File

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

View File

@@ -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;
}

View File

@@ -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__ */

View File

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

View File

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

View File

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

View File

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

View File

@@ -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;
}

View File

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

View File

@@ -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;
@@ -870,6 +880,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)
{
@@ -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 = 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 +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)

View File

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

View File

@@ -1,5 +1,6 @@
gtk_tests = [
# testname, optional extra sources
['testpopup'],
['testupload'],
['testtransform'],
['testdropdown'],

131
tests/testpopup.c Normal file
View File

@@ -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;
}