Compare commits

...

39 Commits

Author SHA1 Message Date
Timm Bäder
59aede163f widget: Remove allocation member
x and y are always 0 now, so only save the size.
2019-02-14 08:07:54 +01:00
Timm Bäder
427457f030 widget: Fix get_allocation
This was always returning 0/0 as the position, breaking things like the
emoji chooser.
2019-02-14 07:49:27 +01:00
Timm Bäder
b2351f8fef widget: Remove _gtk_widget_get_allocation
Getting the allocation is kind of discouraged until we have figured out
what exactly we need to expose, so stop having an internal function for
it. Most of the calls should be replaced by gtk_widget_compute_bounds.
2019-02-14 07:40:51 +01:00
Timm Bäder
c73e0709e4 flowbox: Implement get_child_at_pos properly
With transforms in the mix, checking if the coordinate is inside the
widget "allocation" makes even less sense. Just use gtk_widget_pick()
and walk up until we find a GtkFlowBoxChild.
2019-02-14 07:13:02 +01:00
Timm Bäder
8bafd03cdd testsuite: Disable more pick() tests
This doesn't work without a toplevel window...
2019-02-14 07:13:02 +01:00
Timm Bäder
ea94dab6d3 Add & use GTK_CSS_AFFECTS_TRANSFORM 2019-02-14 06:57:23 +01:00
Timm Bäder
17f6169ac3 Fix shadowing warnings 2019-02-14 06:57:23 +01:00
Timm Bäder
e2094ee517 widget: Apply CSS transform in size_allocate_transformed 2019-02-14 06:57:23 +01:00
Timm Bäder
6f06d5b8b8 csstransformvalue: Export is_none
We'll use it later in gtkwidget.c
2019-02-14 06:57:23 +01:00
Timm Bäder
06a8dd7cb1 css: Add transform style property 2019-02-14 06:57:23 +01:00
Timm Bäder
fc2ce9a39a Add flipping test 2019-02-14 06:57:23 +01:00
Timm Bäder
6e2f024285 widget: Fix queue_allocate on transformed widgets
If we just use gtk_widget_size_allocate here, we lose previously set
transformations.
2019-02-14 06:57:23 +01:00
Timm Bäder
87bca8399b Add shrinking test case 2019-02-14 06:57:23 +01:00
Timm Bäder
64558335b1 gl renderer: Remove an incorrect comment
Quite some other nodes use more than one quad these days.
2019-02-14 06:57:23 +01:00
Timm Bäder
fcfab338ec gl renderer: Flip the framebuffer when dumping it 2019-02-14 06:57:23 +01:00
Timm Bäder
cdbb8d1e0f gl renderer: Fix offscreen-drawn transform nodes
We need to vertically flip them if they are indeed drawn offscreen, like
everything else. Also use the child bounds for the offscreen texture,
because that's what we draw offscreen.
2019-02-14 06:57:23 +01:00
Timm Bäder
bcd6f0b75f testwidgettransforms: Set transformation in size_allocate 2019-02-14 06:57:23 +01:00
Timm Bäder
57cd3391f6 widget: Only push a transform if we need to 2019-02-14 06:57:23 +01:00
Timm Bäder
63ef6a06f6 testwidgettransforms: Add toggle for picking 2019-02-14 06:57:23 +01:00
Timm Bäder
cf0d69d90e Add size_allocate_transformed to the docs 2019-02-14 06:57:23 +01:00
Timm Bäder
1345839921 testformentry: Pass transform in size_allocate 2019-02-14 06:57:23 +01:00
Timm Bäder
88278e84d2 compute_bounds: Add preconditions 2019-02-14 06:57:23 +01:00
Timm Bäder
1f1be208a4 testsuite: Fix translate test for size_allocate changes
gtk_widget_set_transform is still a hack that will eventually go away
but it's also still useful for testing.
2019-02-14 06:57:23 +01:00
Timm Bäder
997e4381e1 widget: Handle position in transformation matrix
Most unoptimized version so far.
2019-02-14 06:57:23 +01:00
Timm Bäder
13d9848da7 Add form entry test 2019-02-14 06:57:23 +01:00
Timm Bäder
a80a17deba tests: Add some transform test with CSS values 2019-02-14 06:57:23 +01:00
Timm Bäder
cce4acab90 widget: Fix translate_coordinates for CSS values
As well as compute_bounds. In these cases, we need to consider the CSS
values (margin, border padding) specially.
2019-02-14 06:57:23 +01:00
Timm Bäder
7c8b1fd6d1 widget Add gtk_widget_get_transform 2019-02-14 06:57:23 +01:00
Timm Bäder
3e646ec6ca Add test for widget transforms 2019-02-14 06:57:23 +01:00
Timm Bäder
43ac1e7ca1 Add picking/translation/compute_bounds unit tests 2019-02-14 06:57:23 +01:00
Timm Bäder
edd8183a2c widget: Consider widget transformations in translate_coordinatesf 2019-02-14 06:57:23 +01:00
Timm Bäder
379efea52c widget: Look at transform in compute_bounds
We now return an axis-aligned bounding box for the given widget, in the
coordinate space of the target widget.
2019-02-14 06:57:23 +01:00
Timm Bäder
530d5493ec widget: Make translate_coordinatesf public 2019-02-14 06:57:23 +01:00
Timm Bäder
bd410a2da8 widget: Don't use compute_bounds in _real_contains
compute_bounds will look at the widget transformation and return an
axis-aligned rectangle containing the given widget.
2019-02-14 06:57:22 +01:00
Timm Bäder
2a7fc5f24e widget: Care about transformation in translate_coordinatesf 2019-02-14 06:57:22 +01:00
Timm Bäder
0a938cca39 widget: Use translate_coordinates in pick()
In case converting the coordinate into the child coordinate space
consists of more than just a translation.
2019-02-14 06:57:22 +01:00
Timm Bäder
07e48d3972 widget: Add gtk_widget_set_transform
This might not stay until the end but for now it's good enough to test
widget transforms.
2019-02-14 06:57:22 +01:00
Timm Bäder
4d3a07c516 widget: Push transform node when neccessary 2019-02-14 06:57:22 +01:00
Timm Bäder
020857e347 widget: Add transform matrix
Unused for now.
2019-02-14 06:57:22 +01:00
21 changed files with 2074 additions and 389 deletions

View File

@@ -4409,6 +4409,7 @@ GtkTickCallback
gtk_widget_add_tick_callback
gtk_widget_remove_tick_callback
gtk_widget_size_allocate
gtk_widget_size_allocate_transformed
gtk_widget_add_accelerator
gtk_widget_remove_accelerator
gtk_widget_set_accel_path

View File

@@ -134,16 +134,27 @@ print_render_node_tree (GskRenderNode *root, int level)
static void G_GNUC_UNUSED
dump_framebuffer (const char *filename, int w, int h)
{
int stride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, w);
const int stride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, w);
guchar *data = g_malloc (h * stride);
guchar *flipped = g_malloc (h * stride);
cairo_surface_t *s;
int i;
glReadPixels (0, 0, w, h, GL_BGRA, GL_UNSIGNED_BYTE, data);
s = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_ARGB32, w, h, stride);
for (i = 0; i < h; i ++)
{
memcpy (flipped + (stride * i),
data + ((h - 1 - i) * stride),
stride);
}
g_free (data);
s = cairo_image_surface_create_for_data (flipped, CAIRO_FORMAT_ARGB32, w, h, stride);
cairo_surface_write_to_png (s, filename);
cairo_surface_destroy (s);
g_free (data);
g_free (flipped);
}
static gboolean
@@ -464,11 +475,11 @@ render_text_node (GskGLRenderer *self,
const PangoFont *font = gsk_text_node_peek_font (node);
const PangoGlyphInfo *glyphs = gsk_text_node_peek_glyphs (node);
const float text_scale = ops_get_scale (builder);
guint num_glyphs = gsk_text_node_get_num_glyphs (node);
const guint num_glyphs = gsk_text_node_get_num_glyphs (node);
const float x = gsk_text_node_get_x (node) + builder->dx;
const float y = gsk_text_node_get_y (node) + builder->dy;
int i;
int x_position = 0;
float x = gsk_text_node_get_x (node) + builder->dx;
float y = gsk_text_node_get_y (node) + builder->dy;
/* If the font has color glyphs, we don't need to recolor anything */
if (!force_color && font_has_color_glyphs (font))
@@ -481,8 +492,6 @@ render_text_node (GskGLRenderer *self,
ops_set_color (builder, color);
}
/* We use one quad per character, unlike the other nodes which
* use at most one quad altogether */
for (i = 0; i < num_glyphs; i++)
{
const PangoGlyphInfo *gi = &glyphs[i];
@@ -811,35 +820,54 @@ render_transform_node (GskGLRenderer *self,
}
else
{
const float min_x = node->bounds.origin.x;
const float min_y = node->bounds.origin.y;
const float max_x = min_x + node->bounds.size.width;
const float max_y = min_y + node->bounds.size.height;
const GskQuadVertex vertex_data[GL_N_VERTICES] = {
{ { min_x, min_y }, { 0, 1 }, },
{ { min_x, max_y }, { 0, 0 }, },
{ { max_x, min_y }, { 1, 1 }, },
{ { max_x, max_y }, { 1, 0 }, },
{ { min_x, max_y }, { 0, 0 }, },
{ { max_x, min_y }, { 1, 1 }, },
};
const float min_x = child->bounds.origin.x;
const float min_y = child->bounds.origin.y;
const float max_x = min_x + child->bounds.size.width;
const float max_y = min_y + child->bounds.size.height;
int texture_id;
gboolean is_offscreen;
/* For non-trivial transforms, we draw everything on a texture and then
* draw the texture transformed. */
/* TODO: We should compute a modelview containing only the "non-trivial"
* part (e.g. the rotation) and use that. We want to keep the scale
* for the texture.
*/
add_offscreen_ops (self, builder,
&node->bounds,
&child->bounds,
child,
&texture_id, &is_offscreen,
&texture_id,
&is_offscreen,
RESET_CLIP | RESET_OPACITY);
ops_set_texture (builder, texture_id);
ops_set_program (builder, &self->blit_program);
ops_draw (builder, vertex_data);
if (is_offscreen)
{
ops_draw (builder, (GskQuadVertex[GL_N_VERTICES]) {
{ { min_x, min_y }, { 0, 1 }, },
{ { min_x, max_y }, { 0, 0 }, },
{ { max_x, min_y }, { 1, 1 }, },
{ { max_x, max_y }, { 1, 0 }, },
{ { min_x, max_y }, { 0, 0 }, },
{ { max_x, min_y }, { 1, 1 }, },
});
}
else
{
ops_draw (builder, (GskQuadVertex[GL_N_VERTICES]) {
{ { min_x, min_y }, { 0, 0 }, },
{ { min_x, max_y }, { 0, 1 }, },
{ { max_x, min_y }, { 1, 0 }, },
{ { max_x, max_y }, { 1, 1 }, },
{ { min_x, max_y }, { 0, 1 }, },
{ { max_x, min_y }, { 1, 0 }, },
});
}
}
ops_pop_modelview (builder);
}

View File

@@ -961,7 +961,7 @@ gtk_entry_accessible_get_character_extents (AtkText *text,
pango_layout_index_to_pos (gtk_entry_get_layout (entry), index, &char_rect);
pango_extents_to_pixels (&char_rect, NULL);
_gtk_widget_get_allocation (widget, &allocation);
gtk_widget_get_allocation (widget, &allocation);
surface = gtk_widget_get_surface (widget);
gdk_surface_get_origin (surface, &x_surface, &y_surface);

View File

@@ -1601,6 +1601,14 @@ _gtk_css_style_property_init_properties (void)
transform_value_parse,
NULL,
_gtk_css_transform_value_new_none ());
gtk_css_style_property_register ("transform",
GTK_CSS_PROPERTY_TRANSFORM,
G_TYPE_NONE,
GTK_STYLE_PROPERTY_ANIMATED,
GTK_CSS_AFFECTS_TRANSFORM,
transform_value_parse,
NULL,
_gtk_css_transform_value_new_none ());
gtk_css_style_property_register ("-gtk-icon-filter",
GTK_CSS_PROPERTY_ICON_FILTER,
G_TYPE_NONE,
@@ -1609,7 +1617,6 @@ _gtk_css_style_property_init_properties (void)
filter_value_parse,
NULL,
gtk_css_filter_value_new_none ());
gtk_css_style_property_register ("border-spacing",
GTK_CSS_PROPERTY_BORDER_SPACING,
G_TYPE_NONE,

View File

@@ -75,7 +75,6 @@ struct _GtkCssValue {
};
static GtkCssValue * gtk_css_transform_value_alloc (guint n_values);
static gboolean gtk_css_transform_value_is_none (const GtkCssValue *value);
static void
gtk_css_transform_clear (GtkCssTransform *transform)
@@ -733,7 +732,7 @@ _gtk_css_transform_value_new_none (void)
return _gtk_css_value_ref (&none_singleton);
}
static gboolean
gboolean
gtk_css_transform_value_is_none (const GtkCssValue *value)
{
return value->n_transforms == 0;

View File

@@ -27,6 +27,7 @@ G_BEGIN_DECLS
GtkCssValue * _gtk_css_transform_value_new_none (void);
GtkCssValue * _gtk_css_transform_value_parse (GtkCssParser *parser);
gboolean gtk_css_transform_value_is_none (const GtkCssValue *transform);
gboolean gtk_css_transform_value_get_matrix (const GtkCssValue *transform,
graphene_matrix_t *matrix);

View File

@@ -115,6 +115,7 @@ typedef guint64 GtkCssChange;
* @GTK_CSS_AFFECTS_POSTEFFECT: An effect is applied after drawing that changes
* @GTK_CSS_AFFECTS_TEXT: Affects anything related to text rendering.
* @GTK_CSS_AFFECTS_REDRAW: Affects anything that requires redraw.
* @GTK_CSS_AFFECTS_TRANSFORM: Affects the element transformation.
*
* The generic effects that a CSS property can have. If a value is
* set, then the property will have an influence on that feature.
@@ -133,7 +134,8 @@ typedef enum {
GTK_CSS_AFFECTS_SYMBOLIC_ICON = (1 << 8),
GTK_CSS_AFFECTS_OUTLINE = (1 << 9),
GTK_CSS_AFFECTS_SIZE = (1 << 10),
GTK_CSS_AFFECTS_POSTEFFECT = (1 << 11)
GTK_CSS_AFFECTS_POSTEFFECT = (1 << 11),
GTK_CSS_AFFECTS_TRANSFORM = (1 << 12),
} GtkCssAffects;
#define GTK_CSS_AFFECTS_REDRAW (GTK_CSS_AFFECTS_CONTENT | \
@@ -220,6 +222,7 @@ enum { /*< skip >*/
GTK_CSS_PROPERTY_ICON_SHADOW,
GTK_CSS_PROPERTY_ICON_STYLE,
GTK_CSS_PROPERTY_ICON_TRANSFORM,
GTK_CSS_PROPERTY_TRANSFORM,
GTK_CSS_PROPERTY_ICON_FILTER,
GTK_CSS_PROPERTY_BORDER_SPACING,
GTK_CSS_PROPERTY_MIN_WIDTH,

View File

@@ -3918,7 +3918,7 @@ gtk_flow_box_get_child_at_index (GtkFlowBox *box,
* @y: the y coordinate of the child
*
* Gets the child in the (@x, @y) position. Both @x and @y are
* assumed to be relative to the allocation of @box.
* assumed to be relative to the origin of @box.
*
* Returns: (transfer none) (nullable): the child widget, which will
* always be a #GtkFlowBoxChild or %NULL in case no child widget
@@ -3929,24 +3929,12 @@ gtk_flow_box_get_child_at_pos (GtkFlowBox *box,
gint x,
gint y)
{
GtkWidget *child;
GSequenceIter *iter;
GtkAllocation allocation;
GtkWidget *child = gtk_widget_pick (GTK_WIDGET (box), x, y);
for (iter = g_sequence_get_begin_iter (BOX_PRIV (box)->children);
!g_sequence_iter_is_end (iter);
iter = g_sequence_iter_next (iter))
{
child = g_sequence_get (iter);
if (!child_is_visible (child))
continue;
if (!child)
return NULL;
gtk_widget_get_allocation (child, &allocation);
if (gdk_rectangle_contains_point (&allocation, x, y))
return GTK_FLOW_BOX_CHILD (child);
}
return NULL;
return (GtkFlowBoxChild *)gtk_widget_get_ancestor (child, GTK_TYPE_FLOW_BOX_CHILD);
}
/**

View File

@@ -94,13 +94,6 @@ void gtk_propagate_event_internal (GtkWidget *widget,
GdkEvent *event,
GtkWidget *topmost);
gboolean gtk_widget_translate_coordinatesf (GtkWidget *src_widget,
GtkWidget *dest_widget,
double src_x,
double src_y,
double *dest_x,
double *dest_y);
GtkWidget * _gtk_toplevel_pick (GtkWindow *toplevel,
gdouble x,
gdouble y,

View File

@@ -120,6 +120,103 @@ get_box_padding (GtkCssStyle *style,
border->right = get_number (style, GTK_CSS_PROPERTY_PADDING_RIGHT);
}
/* translate initial/final into start/end */
static GtkAlign
effective_align (GtkAlign align,
GtkTextDirection direction)
{
switch (align)
{
case GTK_ALIGN_START:
return direction == GTK_TEXT_DIR_RTL ? GTK_ALIGN_END : GTK_ALIGN_START;
case GTK_ALIGN_END:
return direction == GTK_TEXT_DIR_RTL ? GTK_ALIGN_START : GTK_ALIGN_END;
case GTK_ALIGN_FILL:
case GTK_ALIGN_CENTER:
case GTK_ALIGN_BASELINE:
default:
return align;
}
}
static void
adjust_for_align (GtkAlign align,
gint *natural_size,
gint *allocated_pos,
gint *allocated_size)
{
switch (align)
{
case GTK_ALIGN_BASELINE:
case GTK_ALIGN_FILL:
default:
/* change nothing */
break;
case GTK_ALIGN_START:
/* keep *allocated_pos where it is */
*allocated_size = MIN (*allocated_size, *natural_size);
break;
case GTK_ALIGN_END:
if (*allocated_size > *natural_size)
{
*allocated_pos += (*allocated_size - *natural_size);
*allocated_size = *natural_size;
}
break;
case GTK_ALIGN_CENTER:
if (*allocated_size > *natural_size)
{
*allocated_pos += (*allocated_size - *natural_size) / 2;
*allocated_size = MIN (*allocated_size, *natural_size);
}
break;
}
}
static void
adjust_for_margin(gint start_margin,
gint end_margin,
gint *minimum_size,
gint *natural_size,
gint *allocated_pos,
gint *allocated_size)
{
*minimum_size -= (start_margin + end_margin);
*natural_size -= (start_margin + end_margin);
*allocated_pos += start_margin;
*allocated_size -= (start_margin + end_margin);
}
static void
gtk_widget_adjust_size_allocation (GtkWidget *widget,
GtkOrientation orientation,
gint *minimum_size,
gint *natural_size,
gint *allocated_pos,
gint *allocated_size)
{
GtkWidgetPrivate *priv = widget->priv;
if (orientation == GTK_ORIENTATION_HORIZONTAL)
{
adjust_for_margin (priv->margin.left,
priv->margin.right,
minimum_size, natural_size,
allocated_pos, allocated_size);
adjust_for_align (effective_align (priv->halign, _gtk_widget_get_direction (widget)),
natural_size, allocated_pos, allocated_size);
}
else
{
adjust_for_margin (priv->margin.top,
priv->margin.bottom,
minimum_size, natural_size,
allocated_pos, allocated_size);
adjust_for_align (effective_align (priv->valign, GTK_TEXT_DIR_NONE),
natural_size, allocated_pos, allocated_size);
}
}
static void
gtk_widget_query_size_for_orientation (GtkWidget *widget,
GtkOrientation orientation,

File diff suppressed because it is too large Load Diff

View File

@@ -406,6 +406,12 @@ GDK_AVAILABLE_IN_ALL
void gtk_widget_size_allocate (GtkWidget *widget,
const GtkAllocation *allocation,
int baseline);
GDK_AVAILABLE_IN_ALL
void gtk_widget_size_allocate_transformed (GtkWidget *widget,
int width,
int height,
int baseline,
const graphene_matrix_t *transform);
GDK_AVAILABLE_IN_ALL
GtkSizeRequestMode gtk_widget_get_request_mode (GtkWidget *widget);
@@ -746,6 +752,14 @@ gboolean gtk_widget_translate_coordinates (GtkWidget *src_widget,
gint *dest_x,
gint *dest_y);
GDK_AVAILABLE_IN_ALL
gboolean gtk_widget_translate_coordinatesf (GtkWidget *src_widget,
GtkWidget *dest_widget,
double src_x,
double src_y,
double *dest_x,
double *dest_y);
GDK_AVAILABLE_IN_ALL
gboolean gtk_widget_contains (GtkWidget *widget,
gdouble x,
@@ -1050,6 +1064,13 @@ void gtk_widget_snapshot_child (GtkWidget *widget,
GtkWidget *child,
GtkSnapshot *snapshot);
GDK_AVAILABLE_IN_ALL
void gtk_widget_set_transform (GtkWidget *widget,
const graphene_matrix_t *transform);
GDK_AVAILABLE_IN_ALL
void gtk_widget_get_transform (GtkWidget *widget,
graphene_matrix_t *out_transform);
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkWidget, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkRequisition, gtk_requisition_free)

View File

@@ -141,9 +141,14 @@ struct _GtkWidgetPrivate
/* The widget's allocated size */
GtkAllocation allocated_size;
gint allocated_size_baseline;
GtkAllocation allocation;
int allocated_width;
int allocated_height;
gint allocated_baseline;
graphene_matrix_t allocated_transform;
graphene_matrix_t transform;
guint has_transform : 1;
/* The widget's requested sizes */
SizeRequestCache requests;
@@ -292,12 +297,6 @@ void gtk_widget_adjust_size_request (GtkWidget *widg
GtkOrientation orientation,
gint *minimum_size,
gint *natural_size);
void gtk_widget_adjust_size_allocation (GtkWidget *widget,
GtkOrientation orientation,
gint *minimum_size,
gint *natural_size,
gint *allocated_pos,
gint *allocated_size);
void gtk_widget_adjust_baseline_request (GtkWidget *widget,
gint *minimum_baseline,
gint *natural_baseline);
@@ -329,6 +328,11 @@ void gtk_widget_get_origin_relative_to_parent (GtkWidget *wi
int *origin_x,
int *origin_y);
gboolean gtk_widget_emit_event_signals (GtkWidget *widget,
const GdkEvent *event);
void gtk_widget_init_legacy_controller (GtkWidget *widget);
void gtk_widget_cancel_event_sequence (GtkWidget *widget,
GtkGesture *gesture,
GdkEventSequence *sequence,
@@ -451,13 +455,6 @@ _gtk_widget_get_surface (GtkWidget *widget)
return widget->priv->surface;
}
static inline void
_gtk_widget_get_allocation (GtkWidget *widget,
GtkAllocation *allocation)
{
*allocation = widget->priv->allocation;
}
static inline GtkWidget *
_gtk_widget_get_prev_sibling (GtkWidget *widget)
{

View File

@@ -1724,7 +1724,7 @@ edge_under_coordinates (GtkWindow *window,
(priv->edge_constraints & constraints) != constraints)
return FALSE;
_gtk_widget_get_allocation (GTK_WIDGET (window), &allocation);
gtk_widget_get_allocation (GTK_WIDGET (window), &allocation);
context = _gtk_widget_get_style_context (GTK_WIDGET (window));
gtk_style_context_save_to_node (context, priv->decoration_node);
@@ -5224,7 +5224,7 @@ gtk_window_move (GtkWindow *window,
{
GtkAllocation allocation;
_gtk_widget_get_allocation (widget, &allocation);
gtk_widget_get_allocation (widget, &allocation);
/* we have now sent a request with this position
* with currently-active constraints, so toggle flag.
@@ -6017,7 +6017,7 @@ popover_get_rect (GtkWindowPopover *popover,
gdouble min, max;
gtk_widget_get_preferred_size (popover->widget, NULL, &req);
_gtk_widget_get_allocation (GTK_WIDGET (window), &win_alloc);
gtk_widget_get_allocation (GTK_WIDGET (window), &win_alloc);
get_shadow_width (window, &win_border);
win_alloc.x += win_border.left;
@@ -6385,11 +6385,11 @@ gtk_window_realize (GtkWidget *widget)
if (!priv->client_decorated && gtk_window_should_use_csd (window))
create_decoration (widget);
_gtk_widget_get_allocation (widget, &allocation);
gtk_widget_get_allocation (widget, &allocation);
/* ensure widget tree is properly size allocated */
if (allocation.x == -1 &&
allocation.y == -1 &&
if (allocation.x == 0 &&
allocation.y == 0 &&
allocation.width == 1 &&
allocation.height == 1)
{
@@ -6411,12 +6411,12 @@ gtk_window_realize (GtkWidget *widget)
if (priv->hardcoded_surface)
{
surface = priv->hardcoded_surface;
_gtk_widget_get_allocation (widget, &allocation);
gtk_widget_get_allocation (widget, &allocation);
gdk_surface_resize (surface, allocation.width, allocation.height);
}
else
{
_gtk_widget_get_allocation (widget, &allocation);
gtk_widget_get_allocation (widget, &allocation);
switch (priv->type)
{
@@ -6787,7 +6787,7 @@ gtk_window_configure (GtkWindow *window,
* have been a queued resize from child widgets, and so we
* need to reallocate our children in case *they* changed.
*/
_gtk_widget_get_allocation (widget, &allocation);
gtk_widget_get_allocation (widget, &allocation);
if (priv->configure_request_count == 0 &&
(allocation.width == width && allocation.height == height))
{
@@ -7008,7 +7008,7 @@ get_active_region_type (GtkWindow *window, gint x, gint y)
gtk_widget_get_visible (priv->title_box) &&
gtk_widget_get_child_visible (priv->title_box))
{
_gtk_widget_get_allocation (priv->title_box, &allocation);
gtk_widget_get_allocation (priv->title_box, &allocation);
if (allocation.x <= x && allocation.x + allocation.width > x &&
allocation.y <= y && allocation.y + allocation.height > y)
return GTK_WINDOW_REGION_TITLE;
@@ -7386,7 +7386,7 @@ gtk_window_style_updated (GtkWidget *widget)
GtkAllocation allocation;
GtkBorder window_border;
_gtk_widget_get_allocation (widget, &allocation);
gtk_widget_get_allocation (widget, &allocation);
get_shadow_width (window, &window_border);
update_opaque_region (window, &window_border, &allocation);
@@ -7932,7 +7932,7 @@ gtk_window_compute_configure_request (GtkWindow *window,
gdk_surface_get_origin (surface, &ox, &oy);
_gtk_widget_get_allocation (parent_widget, &allocation);
gtk_widget_get_allocation (parent_widget, &allocation);
x = ox + (allocation.width - w) / 2;
y = oy + (allocation.height - h) / 2;

View File

@@ -41,6 +41,7 @@ gtk_tests = [
['testflowbox'],
['testfontchooser'],
['testfontoptions'],
['testformentry'],
['testframe'],
['testfullscreen'],
['testgiconpixbuf'],
@@ -122,6 +123,7 @@ gtk_tests = [
['testpopupat'],
['testgaction'],
['testwidgetfocus'],
['testwidgettransforms'],
['testcenterbox'],
['testgridbaseline'],
['showrendernode'],
@@ -129,6 +131,8 @@ gtk_tests = [
['testoutsetshadowdrawing'],
['testblur'],
['testtexture'],
['testshrinking'],
['testflipping'],
]
if os_unix

209
tests/testflipping.c Normal file
View File

@@ -0,0 +1,209 @@
#include<gtk/gtk.h>
typedef struct _GtkFlip GtkFlip;
typedef struct _GtkFlipClass GtkFlipClass;
#define GTK_TYPE_FLIP (gtk_flip_get_type ())
#define GTK_FLIP(obj) (G_TYPE_CHECK_INSTANCE_CAST(obj, GTK_TYPE_FLIP, GtkFlip))
#define GTK_FLIP_CLASS(cls) (G_TYPE_CHECK_CLASS_CAST(cls, GTK_TYPE_FLIP, GtkFlipClass))
struct _GtkFlip
{
GtkWidget parent_instance;
GtkWidget *child;
guint flipped : 1;
};
struct _GtkFlipClass
{
GtkWidgetClass parent_class;
};
GType gtk_flip_get_type (void) G_GNUC_CONST;
G_DEFINE_TYPE(GtkFlip, gtk_flip, GTK_TYPE_WIDGET);
#define OPPOSITE_ORIENTATION(o) (1 - o)
static void
gtk_flip_measure (GtkWidget *widget,
GtkOrientation orientation,
int for_size,
int *minimum,
int *natural,
int *minimum_baseline,
int *natural_baseline)
{
GtkFlip *self = (GtkFlip *)widget;
if (self->flipped)
gtk_widget_measure (self->child, OPPOSITE_ORIENTATION (orientation), for_size,
minimum, natural, NULL, NULL);
else
gtk_widget_measure (self->child, orientation, for_size, minimum, natural, NULL, NULL);
}
static void
gtk_flip_size_allocate (GtkWidget *widget,
int width,
int height,
int baseline)
{
GtkFlip *self = (GtkFlip *)widget;
int child_width;
int child_height;
graphene_matrix_t transform;
gtk_widget_measure (self->child, GTK_ORIENTATION_HORIZONTAL, -1, &child_width, NULL, NULL, NULL);
gtk_widget_measure (self->child, GTK_ORIENTATION_VERTICAL, -1, &child_height, NULL, NULL, NULL);
if (self->flipped)
{
graphene_matrix_init_rotate (&transform, 90, graphene_vec3_z_axis ());
graphene_matrix_translate (&transform,
&GRAPHENE_POINT3D_INIT (child_height, 0, 0));
}
else
{
graphene_matrix_init_identity (&transform);
}
gtk_widget_size_allocate_transformed (self->child,
child_width,
child_height,
-1, &transform);
}
static void
gtk_flip_finalize (GObject *object)
{
GtkFlip *self = (GtkFlip *)object;
gtk_widget_unparent (self->child);
G_OBJECT_CLASS (gtk_flip_parent_class)->finalize (object);
}
static void
gtk_flip_init (GtkFlip *self)
{
gtk_widget_set_has_surface (GTK_WIDGET (self), FALSE);
}
static void
gtk_flip_class_init (GtkFlipClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
object_class->finalize = gtk_flip_finalize;
widget_class->measure = gtk_flip_measure;
widget_class->size_allocate = gtk_flip_size_allocate;
}
static GtkWidget *
gtk_flip_new (GtkWidget *child)
{
GtkFlip *s = GTK_FLIP (g_object_new (GTK_TYPE_FLIP, NULL));
s->child = child;
gtk_widget_set_parent (child, GTK_WIDGET (s));
return GTK_WIDGET (s);
}
static void
gtk_flip_flip (GtkFlip *self)
{
self->flipped = !self->flipped;
gtk_widget_queue_resize (GTK_WIDGET (self));
}
/* Stub definition of MyTextView which is used in the
* widget-factory.ui file. We just need this so the
* test keeps working
*/
typedef struct
{
GtkTextView tv;
} MyTextView;
typedef GtkTextViewClass MyTextViewClass;
G_DEFINE_TYPE (MyTextView, my_text_view, GTK_TYPE_TEXT_VIEW)
static void
my_text_view_init (MyTextView *tv) {}
static void
my_text_view_class_init (MyTextViewClass *tv_class) {}
/* Copied from tests/scrolling-performance.c */
GtkWidget *
create_widget_factory_content (void)
{
GError *error = NULL;
GtkBuilder *builder;
GtkWidget *result;
g_type_ensure (my_text_view_get_type ());
builder = gtk_builder_new ();
gtk_builder_add_from_file (builder,
"../demos/widget-factory/widget-factory.ui",
&error);
if (error != NULL)
g_error ("Failed to create widgets: %s", error->message);
result = GTK_WIDGET (gtk_builder_get_object (builder, "box1"));
g_object_ref (result);
gtk_container_remove (GTK_CONTAINER (gtk_widget_get_parent (result)),
result);
g_object_unref (builder);
return result;
}
static void
flip_button_clicked_cb (GtkButton *source,
gpointer user_data)
{
GtkFlip *flip = user_data;
gtk_flip_flip (flip);
}
int
main (int argc, char **argv)
{
GtkWidget *window;
GtkWidget *flip;
GtkWidget *to_flip;
GtkWidget *hb;
GtkWidget *flip_button;
gtk_init ();
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
hb = gtk_header_bar_new ();
gtk_window_set_titlebar (GTK_WINDOW (window), hb);
flip_button = gtk_button_new_with_label ("Flip");
gtk_container_add (GTK_CONTAINER (hb), flip_button);
to_flip = create_widget_factory_content ();
g_object_set (G_OBJECT (to_flip), "margin", 0, NULL);
flip = gtk_flip_new (to_flip);
g_signal_connect (flip_button, "clicked", G_CALLBACK (flip_button_clicked_cb), flip);
gtk_container_add (GTK_CONTAINER (window), flip);
gtk_widget_show (window);
gtk_main ();
return 0;
}

229
tests/testformentry.c Normal file
View File

@@ -0,0 +1,229 @@
#include <gtk/gtk.h>
#define GTK_TYPE_FORM_ENTRY (gtk_form_entry_get_type ())
G_DECLARE_FINAL_TYPE (GtkFormEntry, gtk_form_entry, GTK, FORM_ENTRY, GtkWidget)
#define FINAL_SCALE 0.7
#define TRANSITION_DURATION (200 * 1000)
struct _GtkFormEntry
{
GtkWidget parent_instance;
GtkWidget *entry;
GtkWidget *placeholder;
double placeholder_scale;
};
G_DEFINE_TYPE (GtkFormEntry, gtk_form_entry, GTK_TYPE_WIDGET)
static void
gtk_form_entry_size_allocate (GtkWidget *widget,
int width,
int height,
int baseline)
{
GtkFormEntry *self = (GtkFormEntry *)widget;
int placeholder_height;
int top;
gtk_widget_measure (self->placeholder, GTK_ORIENTATION_VERTICAL, -1,
&placeholder_height, NULL, NULL, NULL);
top = placeholder_height * FINAL_SCALE;
gtk_widget_size_allocate (self->entry,
&(GtkAllocation) {
0, top,
width, height - top
}, -1);
/* Placeholder allocation depends on self->placeholder_scale.
* If that's 1.0, we don't scale it and center it on the
* GtkEntry. Otherwise, we move it up until y = 0. */
{
const int max_y = top + ((height - top) / 2) - (placeholder_height / 2);
const double t = self->placeholder_scale; /* TODO: Interpolate */
const int y = 0 + (t * max_y);
int x;
graphene_matrix_t m;
/* Get 0 in entry coords so we can position the placeholder there. */
gtk_widget_translate_coordinates (self->entry, widget, 0, 0, &x, NULL);
x *= t;
graphene_matrix_init_scale (&m,
CLAMP (t, FINAL_SCALE, 1.0),
CLAMP (t, FINAL_SCALE, 1.0),
1);
graphene_matrix_translate (&m,
&GRAPHENE_POINT3D_INIT (x, y, 0));
gtk_widget_size_allocate_transformed (self->placeholder,
width,
placeholder_height,
-1,
&m);
}
}
static void
gtk_form_entry_measure (GtkWidget *widget,
GtkOrientation orientation,
int for_size,
int *minimum,
int *natural,
int *minimum_baseline,
int *natural_baseline)
{
GtkFormEntry *self = (GtkFormEntry *)widget;
if (orientation == GTK_ORIENTATION_HORIZONTAL)
{
int min1, nat1;
int min2, nat2;
gtk_widget_measure (self->entry, orientation, for_size,
&min1, &nat1, NULL, NULL);
gtk_widget_measure (self->placeholder, orientation, for_size,
&min2, &nat2, NULL, NULL);
*minimum = MAX (min1, min2);
*natural = MAX (nat1, nat2);
}
else /* VERTICAL */
{
int min, nat;
int pmin, pnat;
gtk_widget_measure (self->entry, orientation, -1,
&min, &nat, NULL, NULL);
gtk_widget_measure (self->placeholder, orientation, -1,
&pmin, &pnat, NULL, NULL);
*minimum = min + (pmin * FINAL_SCALE);
*natural = nat + (pnat * FINAL_SCALE);
}
}
static gboolean
tick_cb (GtkWidget *widget,
GdkFrameClock *frame_clock,
gpointer user_data)
{
GtkFormEntry *self = user_data;
self->placeholder_scale -= 0.02;
gtk_widget_queue_allocate (GTK_WIDGET (self));
gtk_widget_queue_draw (self->placeholder);
if (self->placeholder_scale <= 0)
{
self->placeholder_scale = 0;
return G_SOURCE_REMOVE;
}
return G_SOURCE_CONTINUE;
}
static void
gtk_form_entry_focused (GtkEventControllerKey *controller,
gpointer user_data)
{
GtkFormEntry *self = user_data;
gtk_widget_add_tick_callback (GTK_WIDGET (self), tick_cb, self, NULL);
}
static void
gtk_form_entry_unfocused (GtkEventControllerKey *controller,
gpointer user_data)
{
GtkFormEntry *self = user_data;
self->placeholder_scale = 1.0;
gtk_widget_queue_allocate (GTK_WIDGET (self));
gtk_widget_queue_draw (self->placeholder);
}
static void
gtk_form_entry_class_init (GtkFormEntryClass *klass)
{
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
widget_class->measure = gtk_form_entry_measure;
widget_class->size_allocate = gtk_form_entry_size_allocate;
}
static void
gtk_form_entry_init (GtkFormEntry *self)
{
GtkEventController *key_controller;
gtk_widget_set_has_surface (GTK_WIDGET (self), FALSE);
self->entry = gtk_entry_new ();
self->placeholder = gtk_label_new ("");
gtk_label_set_xalign (GTK_LABEL (self->placeholder), 0);
self->placeholder_scale = 1.0;
gtk_widget_set_parent (self->entry, GTK_WIDGET (self));
gtk_widget_set_parent (self->placeholder, GTK_WIDGET (self));
key_controller = gtk_event_controller_key_new ();
g_signal_connect (key_controller, "focus-in", G_CALLBACK (gtk_form_entry_focused), self);
g_signal_connect (key_controller, "focus-out", G_CALLBACK (gtk_form_entry_unfocused), self);
gtk_widget_add_controller (self->entry, key_controller);
}
GtkWidget *
gtk_form_entry_new (const char *text)
{
GtkWidget *w = g_object_new (GTK_TYPE_FORM_ENTRY, NULL);
gtk_label_set_text (GTK_LABEL (((GtkFormEntry*)w)->placeholder), text);
return w;
}
int
main (int argc, char **argv)
{
GtkWidget *window;
GtkWidget *box;
GtkWidget *form_entry1;
GtkWidget *form_entry2;
gtk_init ();
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
form_entry1 = gtk_form_entry_new ("First Name");
form_entry2 = gtk_form_entry_new ("Last Name");
gtk_container_add (GTK_CONTAINER (box), form_entry1);
gtk_container_add (GTK_CONTAINER (box), form_entry2);
gtk_widget_set_halign (box, GTK_ALIGN_CENTER);
gtk_widget_set_valign (box, GTK_ALIGN_CENTER);
gtk_container_add (GTK_CONTAINER (window), box);
gtk_window_set_default_size ((GtkWindow *)window, 200, 200);
g_signal_connect (window, "close-request", G_CALLBACK (gtk_main_quit), NULL);
gtk_widget_show (window);
gtk_main ();
return 0;
}

184
tests/testshrinking.c Normal file
View File

@@ -0,0 +1,184 @@
#include<gtk/gtk.h>
typedef struct _GtkShrink GtkShrink;
typedef struct _GtkShrinkClass GtkShrinkClass;
#define GTK_TYPE_SHRINK (gtk_shrink_get_type ())
#define GTK_SHRINK(obj) (G_TYPE_CHECK_INSTANCE_CAST(obj, GTK_TYPE_SHRINK, GtkShrink))
#define GTK_SHRINK_CLASS(cls) (G_TYPE_CHECK_CLASS_CAST(cls, GTK_TYPE_SHRINK, GtkShrinkClass))
struct _GtkShrink
{
GtkWidget parent_instance;
GtkWidget *child;
};
struct _GtkShrinkClass
{
GtkWidgetClass parent_class;
};
GType gtk_shrink_get_type (void) G_GNUC_CONST;
G_DEFINE_TYPE(GtkShrink, gtk_shrink, GTK_TYPE_WIDGET);
static void
gtk_shrink_measure (GtkWidget *widget,
GtkOrientation orientation,
int for_size,
int *minimum,
int *natural,
int *minimum_baseline,
int *natural_baseline)
{
GtkShrink *self = (GtkShrink *)widget;
*minimum = 0;
gtk_widget_measure (self->child, orientation, for_size, NULL, natural, NULL, NULL);
}
static void
gtk_shrink_size_allocate (GtkWidget *widget,
int width,
int height,
int baseline)
{
GtkShrink *self = (GtkShrink *)widget;
int child_width;
int child_height;
float scale_x;
float scale_y;
graphene_matrix_t transform;
gtk_widget_measure (self->child, GTK_ORIENTATION_HORIZONTAL, -1, &child_width, NULL, NULL, NULL);
gtk_widget_measure (self->child, GTK_ORIENTATION_VERTICAL, -1, &child_height, NULL, NULL, NULL);
if (width < child_width)
scale_x = (float)width / (float)child_width;
else
scale_x = 1.0f;
if (height < child_height)
scale_y = (float)height / (float)child_height;
else
scale_y = 1.0f;
graphene_matrix_init_scale (&transform, scale_x, scale_y, 1.0f);
gtk_widget_size_allocate_transformed (self->child,
MAX (width, child_width),
MAX (height, child_height),
-1, &transform);
}
static void
gtk_shrink_finalize (GObject *object)
{
GtkShrink *self = (GtkShrink *)object;
gtk_widget_unparent (self->child);
G_OBJECT_CLASS (gtk_shrink_parent_class)->finalize (object);
}
static void
gtk_shrink_init (GtkShrink *self)
{
gtk_widget_set_has_surface (GTK_WIDGET (self), FALSE);
}
static void
gtk_shrink_class_init (GtkShrinkClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
object_class->finalize = gtk_shrink_finalize;
widget_class->measure = gtk_shrink_measure;
widget_class->size_allocate = gtk_shrink_size_allocate;
}
static GtkWidget *
gtk_shrink_new (GtkWidget *child)
{
GtkShrink *s = GTK_SHRINK (g_object_new (GTK_TYPE_SHRINK, NULL));
s->child = child;
gtk_widget_set_parent (child, GTK_WIDGET (s));
return GTK_WIDGET (s);
}
/* Stub definition of MyTextView which is used in the
* widget-factory.ui file. We just need this so the
* test keeps working
*/
typedef struct
{
GtkTextView tv;
} MyTextView;
typedef GtkTextViewClass MyTextViewClass;
G_DEFINE_TYPE (MyTextView, my_text_view, GTK_TYPE_TEXT_VIEW)
static void
my_text_view_init (MyTextView *tv) {}
static void
my_text_view_class_init (MyTextViewClass *tv_class) {}
/* Copied from tests/scrolling-performance.c */
GtkWidget *
create_widget_factory_content (void)
{
GError *error = NULL;
GtkBuilder *builder;
GtkWidget *result;
g_type_ensure (my_text_view_get_type ());
builder = gtk_builder_new ();
gtk_builder_add_from_file (builder,
"../demos/widget-factory/widget-factory.ui",
&error);
if (error != NULL)
g_error ("Failed to create widgets: %s", error->message);
result = GTK_WIDGET (gtk_builder_get_object (builder, "box1"));
g_object_ref (result);
gtk_container_remove (GTK_CONTAINER (gtk_widget_get_parent (result)),
result);
g_object_unref (builder);
return result;
}
int
main (int argc, char **argv)
{
GtkWidget *window;
GtkWidget *shrink;
GtkWidget *to_shrink;
gtk_init ();
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
to_shrink = create_widget_factory_content ();
g_object_set (G_OBJECT (to_shrink), "margin", 0, NULL);
shrink = gtk_shrink_new (to_shrink);
gtk_container_add (GTK_CONTAINER (window), shrink);
gtk_widget_show (window);
gtk_main ();
return 0;
}

View File

@@ -0,0 +1,349 @@
#include <gtk/gtk.h>
static const char *css =
"test>button {"
" all: unset; "
" background-color: white;"
" border: 20px solid black;"
" padding: 20px;"
" margin: 40px;"
"}"
"test>button:hover {"
" background-color: blue;"
" border-color: red;"
"}"
"test image {"
" background-color: teal;"
"}"
;
/* Just so we can avoid a signal */
GtkWidget *transform_tester;
GtkWidget *test_widget;
GtkWidget *test_child;
float scale = 1;
gboolean do_picking = TRUE;
graphene_matrix_t global_transform;
static const GdkRGBA RED = {1, 0, 0, 0.4};
static const GdkRGBA GREEN = {0, 1, 0, 0.4};
static const GdkRGBA BLUE = {0, 0, 1, 0.4};
static const GdkRGBA BLACK = {0, 0, 0, 1};
/* ######################################################################### */
/* ############################## MatrixChooser ############################ */
/* ######################################################################### */
#define GTK_TYPE_MATRIX_CHOOSER (gtk_matrix_chooser_get_type ())
G_DECLARE_FINAL_TYPE (GtkMatrixChooser, gtk_matrix_chooser, GTK, MATRIX_CHOOSER, GtkWidget)
struct _GtkMatrixChooser
{
GtkWidget parent_instance;
};
G_DEFINE_TYPE (GtkMatrixChooser, gtk_matrix_chooser, GTK_TYPE_WIDGET)
static void
gtk_matrix_chooser_init (GtkMatrixChooser *self)
{
gtk_widget_set_has_surface (GTK_WIDGET (self), FALSE);
}
static void
gtk_matrix_chooser_class_init (GtkMatrixChooserClass *klass)
{
}
/* ######################################################################### */
/* ############################# TransformTester ########################### */
/* ######################################################################### */
#define TEST_WIDGET_MIN_SIZE 100
#define GTK_TYPE_TRANSFORM_TESTER (gtk_transform_tester_get_type ())
G_DECLARE_FINAL_TYPE (GtkTransformTester, gtk_transform_tester, GTK, TRANSFORM_TESTER, GtkWidget);
struct _GtkTransformTester
{
GtkWidget parent_instance;
GtkWidget *test_widget;
int pick_increase;
};
G_DEFINE_TYPE (GtkTransformTester, gtk_transform_tester, GTK_TYPE_WIDGET);
static void
gtk_transform_tester_measure (GtkWidget *widget,
GtkOrientation orientation,
int for_size,
int *minimum,
int *natural,
int *minimum_baseline,
int *natural_baseline)
{
GtkTransformTester *self = (GtkTransformTester *)widget;
if (self->test_widget)
{
gtk_widget_measure (self->test_widget, orientation, for_size,
minimum, natural, NULL, NULL);
}
}
static void
gtk_transform_tester_size_allocate (GtkWidget *widget,
int width,
int height,
int baseline)
{
GtkTransformTester *self = (GtkTransformTester *)widget;
int w, h;
if (!self->test_widget)
return;
scale += 2.5f;
gtk_widget_measure (self->test_widget, GTK_ORIENTATION_HORIZONTAL, -1,
&w, NULL, NULL, NULL);
gtk_widget_measure (self->test_widget, GTK_ORIENTATION_VERTICAL, w,
&h, NULL, NULL, NULL);
graphene_matrix_init_identity (&global_transform);
graphene_matrix_translate (&global_transform, &(graphene_point3d_t){ -w/2.0f, -h/2.0f, 0});
graphene_matrix_rotate (&global_transform, scale,
graphene_vec3_z_axis ());
graphene_matrix_translate (&global_transform, &(graphene_point3d_t){ width / 2.0f, height / 2.0f, 0});
gtk_widget_size_allocate_transformed (self->test_widget,
w, h,
-1,
&global_transform);
}
static void
gtk_transform_tester_snapshot (GtkWidget *widget,
GtkSnapshot *snapshot)
{
GtkTransformTester *self = (GtkTransformTester *)widget;
const int width = gtk_widget_get_width (widget);
const int height = gtk_widget_get_height (widget);
const int inc = self->pick_increase;
graphene_rect_t child_bounds;
graphene_rect_t self_bounds;
int x, y;
GTK_WIDGET_CLASS (gtk_transform_tester_parent_class)->snapshot (widget, snapshot);
if (!do_picking)
return;
gtk_widget_compute_bounds (self->test_widget, widget, &child_bounds);
gtk_widget_compute_bounds (self->test_widget, self->test_widget, &self_bounds);
{
const struct {
graphene_point_t coords;
GdkRGBA color;
} points[4] = {
{ self_bounds.origin, {1, 0, 0, 1} },
{ GRAPHENE_POINT_INIT (self_bounds.origin.x + self_bounds.size.width, self_bounds.origin.y), {0, 1, 0, 1} },
{ GRAPHENE_POINT_INIT (self_bounds.origin.x + self_bounds.size.width, self_bounds.origin.y + self_bounds.size.height), {0, 0, 1, 1} },
{ GRAPHENE_POINT_INIT (self_bounds.origin.x, self_bounds.origin.y + self_bounds.size.height), {1, 0, 1, 1} }
};
for (x = 0; x < G_N_ELEMENTS (points); x ++)
{
double px, py;
gtk_widget_translate_coordinatesf (self->test_widget, widget,
points[x].coords.x, points[x].coords.y,
&px, &py);
gtk_snapshot_append_color (snapshot, &points[x].color,
&GRAPHENE_RECT_INIT (px, py,
4,
4));
}
}
/* Now add custom drawing */
for (x = 0; x < width; x += inc)
{
for (y = 0; y < height; y += inc)
{
const float px = x;
const float py = y;
GtkWidget *picked;
#if 1
picked = gtk_widget_pick (widget, px, py);
#else
{
double dx, dy;
gtk_widget_translate_coordinatesf (widget, self->test_widget, px, py, &dx, &dy);
picked = gtk_widget_pick (self->test_widget, dx, dy);
}
#endif
if (picked == self->test_widget)
gtk_snapshot_append_color (snapshot, &GREEN,
&GRAPHENE_RECT_INIT (px - (inc / 2), py - (inc / 2), inc, inc));
else if (picked == test_child)
gtk_snapshot_append_color (snapshot, &BLUE,
&GRAPHENE_RECT_INIT (px - (inc / 2), py - (inc / 2), inc, inc));
else
gtk_snapshot_append_color (snapshot, &RED,
&GRAPHENE_RECT_INIT (px - (inc / 2), py - (inc / 2), inc, inc));
}
}
gtk_snapshot_append_color (snapshot, &BLACK,
&GRAPHENE_RECT_INIT (child_bounds.origin.x,
child_bounds.origin.y,
child_bounds.size.width,
1));
gtk_snapshot_append_color (snapshot, &BLACK,
&GRAPHENE_RECT_INIT (child_bounds.origin.x + child_bounds.size.width,
child_bounds.origin.y,
1,
child_bounds.size.height));
gtk_snapshot_append_color (snapshot, &BLACK,
&GRAPHENE_RECT_INIT (child_bounds.origin.x,
child_bounds.origin.y + child_bounds.size.height,
child_bounds.size.width,
1));
gtk_snapshot_append_color (snapshot, &BLACK,
&GRAPHENE_RECT_INIT (child_bounds.origin.x,
child_bounds.origin.y,
1,
child_bounds.size.height));
}
static void
gtk_transform_tester_init (GtkTransformTester *self)
{
gtk_widget_set_has_surface (GTK_WIDGET (self), FALSE);
self->pick_increase = 4;
}
static void
gtk_transform_tester_class_init (GtkTransformTesterClass *klass)
{
GtkWidgetClass *widget_class = (GtkWidgetClass *)klass;
widget_class->measure = gtk_transform_tester_measure;
widget_class->size_allocate = gtk_transform_tester_size_allocate;
widget_class->snapshot = gtk_transform_tester_snapshot;
gtk_widget_class_set_css_name (widget_class, "test");
}
static gboolean
tick_cb (GtkWidget *widget,
GdkFrameClock *frame_clock,
gpointer user_data)
{
gtk_widget_queue_allocate (widget);
return G_SOURCE_CONTINUE;
}
static void
gtk_transform_tester_set_test_widget (GtkTransformTester *self,
GtkWidget *test_widget)
{
g_assert (!self->test_widget);
self->test_widget = test_widget;
gtk_widget_set_parent (test_widget, (GtkWidget *)self);
gtk_widget_add_tick_callback (GTK_WIDGET (self), tick_cb, NULL, NULL);
}
static void
toggled_cb (GtkToggleButton *source,
gpointer user_data)
{
do_picking = gtk_toggle_button_get_active (source);
}
int
main (int argc, char **argv)
{
GtkWidget *window;
GtkWidget *matrix_chooser;
GtkWidget *box;
GtkWidget *titlebar;
GtkWidget *toggle_button;
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),
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
matrix_chooser = g_object_new (GTK_TYPE_MATRIX_CHOOSER, NULL);
transform_tester = g_object_new (GTK_TYPE_TRANSFORM_TESTER, NULL);
box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
titlebar = gtk_header_bar_new ();
gtk_window_set_titlebar (GTK_WINDOW (window), titlebar);
gtk_header_bar_set_show_title_buttons (GTK_HEADER_BAR (titlebar), TRUE);
toggle_button = gtk_toggle_button_new ();
gtk_button_set_label (GTK_BUTTON (toggle_button), "Picking");
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle_button), do_picking);
g_signal_connect (toggle_button, "toggled", G_CALLBACK (toggled_cb), NULL);
gtk_container_add (GTK_CONTAINER (titlebar), toggle_button);
test_widget = gtk_button_new ();
gtk_widget_set_size_request (test_widget, TEST_WIDGET_MIN_SIZE, TEST_WIDGET_MIN_SIZE);
gtk_widget_set_halign (test_widget, GTK_ALIGN_CENTER);
gtk_widget_set_valign (test_widget, GTK_ALIGN_CENTER);
test_child = gtk_image_new_from_icon_name ("corebird");
gtk_widget_set_halign (test_child, GTK_ALIGN_CENTER);
gtk_widget_set_valign (test_child, GTK_ALIGN_CENTER);
gtk_widget_set_size_request (test_child, TEST_WIDGET_MIN_SIZE / 2, TEST_WIDGET_MIN_SIZE / 2);
gtk_container_add (GTK_CONTAINER (test_widget), test_child);
gtk_transform_tester_set_test_widget (GTK_TRANSFORM_TESTER (transform_tester), test_widget);
gtk_widget_set_vexpand (transform_tester, TRUE);
gtk_container_add (GTK_CONTAINER (box), transform_tester);
gtk_container_add (GTK_CONTAINER (box), matrix_chooser);
gtk_container_add (GTK_CONTAINER (window), box);
gtk_window_set_default_size ((GtkWindow *)window, 200, 200);
g_signal_connect (window, "close-request", G_CALLBACK (gtk_main_quit), NULL);
gtk_widget_show (window);
gtk_main ();
return 0;
}

View File

@@ -53,6 +53,7 @@ tests = [
['textbuffer'],
['textiter'],
['treelistmodel'],
['translate'],
['treemodel', ['treemodel.c', 'liststore.c', 'treestore.c', 'filtermodel.c',
'modelrefcount.c', 'sortmodel.c', 'gtktreemodelrefcount.c']],
['treepath'],

424
testsuite/gtk/translate.c Normal file
View File

@@ -0,0 +1,424 @@
#include <gtk/gtk.h>
#define BORDER_WIDTH 30
static const char *css =
"button, box {"
" all: unset; "
"}"
".with-border {"
" border: 30px solid white;"
"}"
;
static void
same_widget (void)
{
GtkWidget *a = gtk_button_new ();
int i;
for (i = -1000; i < 1000; i ++)
{
int rx, ry;
gtk_widget_translate_coordinates (a, a, i, i, &rx, &ry);
g_assert_cmpint (rx, ==, i);
g_assert_cmpint (ry, ==, i);
}
}
static void
compute_bounds (void)
{
const int WIDTH = 200;
const int HEIGHT = 100;
GtkWidget *a = gtk_button_new ();
graphene_matrix_t transform;
graphene_rect_t bounds;
graphene_matrix_init_scale (&transform, 2, 1, 1);
gtk_widget_measure (a, GTK_ORIENTATION_HORIZONTAL, -1, NULL, NULL, NULL, NULL);
gtk_widget_size_allocate (a, &(GtkAllocation){0, 0, WIDTH, HEIGHT}, -1);
gtk_widget_compute_bounds (a, a, &bounds);
g_assert_cmpfloat (bounds.origin.x, ==, 0);
g_assert_cmpfloat (bounds.origin.y, ==, 0);
g_assert_cmpfloat_with_epsilon (bounds.size.width, WIDTH, 1);
g_assert_cmpfloat_with_epsilon (bounds.size.height, HEIGHT, 1);
gtk_widget_set_transform (a, &transform);
gtk_widget_compute_bounds (a, a, &bounds);
g_assert_cmpfloat_with_epsilon (bounds.size.width, WIDTH, 1);
g_assert_cmpfloat_with_epsilon (bounds.size.height, HEIGHT, 1);
}
static void
compute_bounds_with_parent (void)
{
const int WIDTH = 200;
const int HEIGHT = 100;
GtkWidget *box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
GtkWidget *a = gtk_button_new ();
graphene_matrix_t transform;
graphene_rect_t bounds;
gtk_widget_set_hexpand (a, FALSE);
gtk_widget_set_vexpand (a, FALSE);
gtk_widget_set_halign (a, GTK_ALIGN_START);
gtk_widget_set_valign (a, GTK_ALIGN_START);
gtk_widget_set_size_request (a, WIDTH, HEIGHT);
gtk_widget_set_margin_start (a, 25);
gtk_container_add (GTK_CONTAINER (box), a);
gtk_widget_measure (a, GTK_ORIENTATION_HORIZONTAL, -1, NULL, NULL, NULL, NULL);
gtk_widget_measure (box, GTK_ORIENTATION_HORIZONTAL, -1, NULL, NULL, NULL, NULL);
gtk_widget_size_allocate (box, &(GtkAllocation){0, 0, WIDTH * 10, HEIGHT * 10}, -1);
gtk_widget_compute_bounds (a, box, &bounds);
g_assert_cmpfloat_with_epsilon (bounds.origin.x, 25, 1);
g_assert_cmpfloat_with_epsilon (bounds.origin.y, 0, 1);
g_assert_cmpfloat_with_epsilon (bounds.size.width, WIDTH, 1);
g_assert_cmpfloat_with_epsilon (bounds.size.height, HEIGHT, 1);
/* Now set a transform and check that the bounds returned by compute_bounds
* have the proper values */
graphene_matrix_init_scale (&transform, 2, 1, 1);
gtk_widget_set_transform (a, &transform);
gtk_widget_compute_bounds (a, box, &bounds);
/* FIXME: Positions here are borked */
/*g_assert_cmpfloat_with_epsilon (bounds.origin.x, 25, 1);*/
/*g_assert_cmpfloat_with_epsilon (bounds.origin.y, 0, 1);*/
g_assert_cmpfloat_with_epsilon (bounds.size.width, WIDTH * 2, 1);
g_assert_cmpfloat_with_epsilon (bounds.size.height, HEIGHT, 1);
/*g_message ("RESULT: %f, %f, %f, %f",*/
/*bounds.origin.x, bounds.origin.y,*/
/*bounds.size.width, bounds.size.height);*/
}
static void
translate_with_parent (void)
{
const int WIDTH = 200;
const int HEIGHT = 100;
const float x_scale = 2.0f;
const int x_margin = 25;
GtkWidget *parent = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
GtkWidget *child = gtk_button_new ();
graphene_matrix_t transform;
int i;
gtk_widget_set_hexpand (child, FALSE);
gtk_widget_set_vexpand (child, FALSE);
gtk_widget_set_halign (child, GTK_ALIGN_START);
gtk_widget_set_valign (child, GTK_ALIGN_START);
gtk_widget_set_size_request (child, WIDTH, HEIGHT);
gtk_widget_set_margin_start (child, x_margin);
gtk_container_add (GTK_CONTAINER (parent), child);
gtk_widget_measure (child, GTK_ORIENTATION_HORIZONTAL, -1, NULL, NULL, NULL, NULL);
gtk_widget_measure (parent, GTK_ORIENTATION_HORIZONTAL, -1, NULL, NULL, NULL, NULL);
gtk_widget_size_allocate (parent, &(GtkAllocation){0, 0, WIDTH * 10, HEIGHT * 10}, -1);
/* First we have no transformation. We take a coordinate and translate it from parent
* to child, then back from child to parent and check if we get our original coordinate. */
for (i = 0; i < 100; i ++)
{
double cx, cy;
double px, py;
gtk_widget_translate_coordinatesf (parent, child, i, i, &cx, &cy);
/* Back up */
gtk_widget_translate_coordinatesf (child, parent, cx, cy, &px, &py);
g_assert_cmpfloat_with_epsilon (px, i, 0.1f);
g_assert_cmpfloat_with_epsilon (py, i, 0.1f);
}
graphene_matrix_init_scale (&transform, x_scale, 1, 1);
gtk_widget_set_transform (child, &transform);
/* Same thing... */
for (i = 1; i < 100; i ++)
{
double cx, cy;
double px, py;
gtk_widget_translate_coordinatesf (parent, child, i, i, &cx, &cy);
/*g_message ("### %d/%d in child coords: %f/%f", i, i, cx, cy);*/
/*g_assert_cmpfloat_with_epsilon (cx, (-x_margin+i) / x_scale, 0.1f);*/
/*g_assert_cmpfloat_with_epsilon (cy, i, 0.1f);*/
/* Back up */
gtk_widget_translate_coordinatesf (child, parent, cx, cy, &px, &py);
/*g_message ("%f, %f", px, py);*/
/*g_message ("%f/%f in parent coords: %f/%f", cx, cy, px, py);*/
g_assert_cmpfloat_with_epsilon (px, i, 0.1f);
g_assert_cmpfloat_with_epsilon (py, i, 0.1f);
}
/* Now try a translation... */
gtk_widget_set_margin_start (child, 0);
gtk_widget_measure (child, GTK_ORIENTATION_HORIZONTAL, -1, NULL, NULL, NULL, NULL);
gtk_widget_measure (parent, GTK_ORIENTATION_HORIZONTAL, -1, NULL, NULL, NULL, NULL);
gtk_widget_size_allocate (parent, &(GtkAllocation){0, 0, WIDTH * 10, HEIGHT * 10}, -1);
graphene_matrix_init_translate (&transform,
&(graphene_point3d_t){20, 0, 0});
gtk_widget_set_transform (child, &transform);
{
double dx, dy;
gtk_widget_translate_coordinatesf (parent, child, 0, 0, &dx, &dy);
g_assert_cmpfloat_with_epsilon (dx, -20, 0.1);
g_assert_cmpfloat_with_epsilon (dy, 0, 0.1);
}
}
static void
translate_with_css (void)
{
const int WIDTH = 200;
const int HEIGHT = 100;
GtkWidget *parent = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
GtkWidget *child = gtk_button_new ();
graphene_matrix_t transform;
gtk_style_context_add_class (gtk_widget_get_style_context (child), "with-border");
gtk_widget_set_hexpand (child, FALSE);
gtk_widget_set_vexpand (child, FALSE);
gtk_widget_set_halign (child, GTK_ALIGN_START);
gtk_widget_set_valign (child, GTK_ALIGN_START);
gtk_widget_set_size_request (child, WIDTH, HEIGHT);
/*gtk_widget_set_margin_start (child, x_margin);*/
gtk_container_add (GTK_CONTAINER (parent), child);
gtk_widget_measure (child, GTK_ORIENTATION_HORIZONTAL, -1, NULL, NULL, NULL, NULL);
gtk_widget_measure (parent, GTK_ORIENTATION_HORIZONTAL, -1, NULL, NULL, NULL, NULL);
gtk_widget_size_allocate (parent, &(GtkAllocation){0, 0, WIDTH * 10, HEIGHT * 10}, -1);
/* Basic checks without a transformation */
{
double dx, dy;
gtk_widget_translate_coordinatesf (child, parent, 0, 0, &dx, &dy);
g_assert_cmpfloat_with_epsilon (dx, BORDER_WIDTH, 0.1);
g_assert_cmpfloat_with_epsilon (dy, BORDER_WIDTH, 0.1);
gtk_widget_translate_coordinatesf (parent, child, 0, 0, &dx, &dy);
g_assert_cmpfloat_with_epsilon (dx, - BORDER_WIDTH, 0.1);
g_assert_cmpfloat_with_epsilon (dy, - BORDER_WIDTH, 0.1);
}
graphene_matrix_init_scale (&transform, 2, 2, 1);
gtk_widget_set_transform (child, &transform);
/* Since the border is also scaled, the values should be double from above. */
{
double px, py;
double cx, cy;
/*g_message (">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");*/
gtk_widget_translate_coordinatesf (child, parent, 0, 0, &px, &py);
/*g_message ("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<");*/
g_assert_cmpfloat_with_epsilon (px, BORDER_WIDTH * 2, 0.1);
g_assert_cmpfloat_with_epsilon (py, BORDER_WIDTH * 2, 0.1);
gtk_widget_translate_coordinatesf (parent, child, px, py, &cx, &cy);
g_assert_cmpfloat_with_epsilon (cx, 0, 0.1);
g_assert_cmpfloat_with_epsilon (cy, 0, 0.1);
}
}
static void
pick (void)
{
const int WIDTH = 200;
const int HEIGHT = 100;
GtkWidget *parent = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
GtkWidget *child = gtk_button_new ();
graphene_matrix_t transform;
gtk_widget_set_hexpand (child, TRUE);
gtk_widget_set_vexpand (child, TRUE);
gtk_widget_set_halign (child, GTK_ALIGN_FILL);
gtk_widget_set_valign (child, GTK_ALIGN_FILL);
gtk_container_add (GTK_CONTAINER (parent), child);
gtk_widget_measure (child, GTK_ORIENTATION_HORIZONTAL, -1, NULL, NULL, NULL, NULL);
gtk_widget_measure (parent, GTK_ORIENTATION_HORIZONTAL, -1, NULL, NULL, NULL, NULL);
gtk_widget_size_allocate (parent, &(GtkAllocation){0, 0, WIDTH, HEIGHT}, -1);
g_assert (gtk_widget_get_width (child) == WIDTH);
g_assert (gtk_widget_get_height (child) == HEIGHT);
/* We scale the child widget to only half its size on the x axis,
* which means doing a pick on the left half of the parent should
* return the child but a pick on the right half should return the
* parent. */
graphene_matrix_init_scale (&transform, 0.5, 1, 1);
gtk_widget_set_transform (child, &transform);
/* XXX These are disabled because gtk_widget_pick()
* checks for gtk_widget_is_drawable(), which is not the case here
* since the widgets aren't mapped!
*/
/*g_assert (gtk_widget_pick (parent, WIDTH * 0.25, HEIGHT / 2) == child);*/
/*g_assert (gtk_widget_pick (parent, WIDTH * 0.75, HEIGHT / 2) == parent);*/
/* Now we test translations by simply offsetting the child widget by its own size,
* which will move it to the left and entirely out of the parent's allocation. */
graphene_matrix_init_translate (&transform,
&(graphene_point3d_t){ - WIDTH, 0, 0 });
gtk_widget_set_transform (child, &transform);
/* ... which means that picking on the parent with any positive x coordinate will
* yield the parent widget, while negative x coordinates (up until -WIDTH) will
* yield the child */
/*g_assert (gtk_widget_pick (parent, WIDTH * 0.1, 0) == parent);*/
/*g_assert (gtk_widget_pick (parent, WIDTH * 0.9, 0) == parent);*/
/*double dx, dy;*/
/*gtk_widget_translate_coordinatesf (parent, child, - WIDTH * 0.1, 0, &dx, &dy);*/
/*g_message ("translate: %f, %f", dx, dy);*/
/*g_assert (gtk_widget_pick (parent, -WIDTH * 0.1, 0) == child);*/
/*g_assert (gtk_widget_pick (parent, -WIDTH * 0.9, 0) == child);*/
}
#if 0
static void
single_widget_scale (void)
{
GtkWidget *p = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
GtkWidget *w = gtk_button_new ();
graphene_matrix_t transform;
GtkWidget *picked;
int x, y;
gtk_container_add (GTK_CONTAINER (p), w);
gtk_widget_set_hexpand (w, TRUE);
gtk_widget_set_vexpand (w, TRUE);
graphene_matrix_init_scale (&transform, 0.5f, 0.5f, 1);
gtk_widget_set_transform (w, &transform);
/* Just to shut up the GtkWidget warning... */
gtk_widget_measure (p, GTK_ORIENTATION_HORIZONTAL, -1, NULL, NULL, NULL, NULL);
gtk_widget_size_allocate (p, &(GtkAllocation) {0, 0, 100, 100}, -1);
gtk_widget_translate_coordinates (p, w, 0, 0, &x, &y);
g_assert_cmpint (x, ==, 0);
g_assert_cmpint (y, ==, 0);
gtk_widget_translate_coordinates (p, w, 10, 10, &x, &y);
g_assert_cmpint (x, ==, 10 / 2);
g_assert_cmpint (y, ==, 10 / 2);
gtk_widget_translate_coordinates (p, w, 100, 100, &x, &y);
g_assert_cmpint (x, ==, 100 / 2);
g_assert_cmpint (y, ==, 100 / 2);
picked = gtk_widget_pick (p, 0, 0);
g_assert (picked == w);
picked = gtk_widget_pick (p, 51, 51);
g_assert (picked == p);
}
static void
single_widget_rotate (void)
{
GtkWidget *p = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
GtkWidget *w = gtk_button_new ();
graphene_matrix_t transform;
GtkWidget *picked;
int x, y;
gtk_container_add (GTK_CONTAINER (p), w);
gtk_widget_set_hexpand (w, TRUE);
gtk_widget_set_vexpand (w, TRUE);
graphene_matrix_init_rotate (&transform,
45.0, /* Deg */
graphene_vec3_z_axis ());
gtk_widget_set_transform (w, &transform);
/* Just to shut up the GtkWidget warning... */
gtk_widget_measure (p, GTK_ORIENTATION_HORIZONTAL, -1, NULL, NULL, NULL, NULL);
gtk_widget_size_allocate (p, &(GtkAllocation) {0, 0, 100, 100}, -1);
gtk_widget_translate_coordinates (p, w, 0, 0, &x, &y);
g_assert_cmpint (x, ==, 0);
g_assert_cmpint (y, ==, 0);
picked = gtk_widget_pick (p, 0, 0);
g_assert (picked == w);
picked = gtk_widget_pick (p, 0, 100);
g_assert (picked == w);
/* Now it gets interesting... */
/* This should return the button parent since the button is rotated away from the
* y axis on top */
picked = gtk_widget_pick (p, 20, 0);
g_assert (picked == p);
picked = gtk_widget_pick (p, 50, 10);
g_assert (picked == p);
picked = gtk_widget_pick (p, 100, 100);
g_assert (picked == p);
}
#endif
int
main (int argc, char **argv)
{
GtkCssProvider *provider;
gtk_init ();
// TODO: Do this only conditionally and/or per-testcase.
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),
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/translate/same-widget", same_widget);
g_test_add_func ("/translate/compute-bounds", compute_bounds);
g_test_add_func ("/translate/compute-bounds-with-parent", compute_bounds_with_parent);
g_test_add_func ("/translate/translate-with-parent", translate_with_parent);
g_test_add_func ("/translate/translate-with-css", translate_with_css);
g_test_add_func ("/translate/pick", pick);
return g_test_run ();
}