Compare commits

...

5 Commits

Author SHA1 Message Date
Benjamin Otte
37c76aad11 button: Implement clip 2014-05-23 19:33:14 +02:00
Benjamin Otte
2a111dd1d0 widget: Include box-shadow in the clip
This allows all widgets that implement clip to use box shadows.
2014-05-23 19:33:14 +02:00
Benjamin Otte
5b86526a2e widget: Add _gtk_widget_set_css_clip()
... and use it in GtkBox and GtkGrid.
2014-05-23 19:33:14 +02:00
Benjamin Otte
15a3f58bf9 spinner: Implement clip
We now allow you to gtk-icon-transform() the spinner outside of the
spinners allocation.
2014-05-23 19:33:14 +02:00
Benjamin Otte
6cdd7c002b API: widget: Add gtk_widget_set_clip() API
And handle the fact that drawing bounds are now handled by this API and
the corresponding gtk_widget_get_clip().

Also add _gtk_widget_supports_clip() function to check if a widget has
been ported to the new world.
2014-05-23 19:33:14 +02:00
11 changed files with 314 additions and 20 deletions

View File

@@ -5475,6 +5475,8 @@ gtk_widget_get_allocated_height
gtk_widget_get_allocation
gtk_widget_set_allocation
gtk_widget_get_allocated_baseline
gtk_widget_get_clip
gtk_widget_set_clip
gtk_widget_get_app_paintable
gtk_widget_get_can_default
gtk_widget_set_can_default

View File

@@ -791,6 +791,8 @@ gtk_box_size_allocate_no_center (GtkWidget *widget,
i++;
}
}
_gtk_widget_set_simple_clip (widget);
}
static void
@@ -1158,6 +1160,8 @@ gtk_box_size_allocate_with_center (GtkWidget *widget,
child_allocation.height = center_size;
}
gtk_widget_size_allocate_with_baseline (priv->center->widget, &child_allocation, baseline);
_gtk_widget_set_simple_clip (widget);
}
static void

View File

@@ -1761,6 +1761,8 @@ gtk_button_size_allocate (GtkWidget *widget,
gtk_widget_size_allocate_with_baseline (child, &child_allocation, baseline);
}
_gtk_widget_set_simple_clip (widget);
}
static gboolean

View File

@@ -1004,7 +1004,7 @@ _gtk_css_style_property_init_properties (void)
gtk_css_style_property_register ("-gtk-icon-transform",
GTK_CSS_PROPERTY_ICON_TRANSFORM,
G_TYPE_NONE,
GTK_STYLE_PROPERTY_ANIMATED | GTK_STYLE_PROPERTY_NO_RESIZE,
GTK_STYLE_PROPERTY_ANIMATED,
transform_value_parse,
NULL,
NULL,
@@ -1013,7 +1013,7 @@ _gtk_css_style_property_init_properties (void)
gtk_css_style_property_register ("box-shadow",
GTK_CSS_PROPERTY_BOX_SHADOW,
G_TYPE_NONE,
GTK_STYLE_PROPERTY_ANIMATED | GTK_STYLE_PROPERTY_NO_RESIZE,
GTK_STYLE_PROPERTY_ANIMATED,
shadow_value_parse,
NULL,
NULL,

View File

@@ -24,6 +24,7 @@
#include "gtkorientableprivate.h"
#include "gtksizerequest.h"
#include "gtkwidgetprivate.h"
#include "gtkprivate.h"
#include "gtkintl.h"
@@ -1677,6 +1678,8 @@ gtk_grid_size_allocate (GtkWidget *widget,
gtk_grid_request_position (&request, 1);
gtk_grid_request_allocate_children (&request);
_gtk_widget_set_simple_clip (widget);
}
static gboolean

View File

@@ -35,6 +35,7 @@
#include "gtkintl.h"
#include "gtkstylecontext.h"
#include "gtkstylecontextprivate.h"
#include "gtkwidgetprivate.h"
#include "a11y/gtkspinneraccessible.h"
@@ -67,6 +68,8 @@ struct _GtkSpinnerPrivate
static gboolean gtk_spinner_draw (GtkWidget *widget,
cairo_t *cr);
static void gtk_spinner_size_allocate (GtkWidget *widget,
GtkAllocation *allocation);
static void gtk_spinner_get_property (GObject *object,
guint param_id,
GValue *value,
@@ -98,6 +101,7 @@ gtk_spinner_class_init (GtkSpinnerClass *klass)
gobject_class->set_property = gtk_spinner_set_property;
widget_class = GTK_WIDGET_CLASS(klass);
widget_class->size_allocate = gtk_spinner_size_allocate;
widget_class->draw = gtk_spinner_draw;
widget_class->get_preferred_width = gtk_spinner_get_preferred_width;
widget_class->get_preferred_height = gtk_spinner_get_preferred_height;
@@ -186,6 +190,29 @@ gtk_spinner_get_preferred_height (GtkWidget *widget,
*natural_size = SPINNER_SIZE;
}
static void
gtk_spinner_size_allocate (GtkWidget *widget,
GtkAllocation *allocation)
{
GtkStyleContext *context;
GtkAllocation clip;
gint size;
context = gtk_widget_get_style_context (widget);
size = MIN (allocation->width, allocation->height);
_gtk_style_context_get_icon_extents (context,
&clip,
allocation->x + (allocation->width - size) / 2,
allocation->y + (allocation->height - size) / 2,
size, size);
gdk_rectangle_union (&clip, allocation, &clip);
gtk_widget_set_allocation (widget, allocation);
gtk_widget_set_clip (widget, &clip);
}
static gboolean
gtk_spinner_draw (GtkWidget *widget,
cairo_t *cr)

View File

@@ -28,8 +28,11 @@
#include "gtkcsscornervalueprivate.h"
#include "gtkcssenginevalueprivate.h"
#include "gtkcssenumvalueprivate.h"
#include "gtkcssimagevalueprivate.h"
#include "gtkcssnumbervalueprivate.h"
#include "gtkcssrgbavalueprivate.h"
#include "gtkcssshadowsvalueprivate.h"
#include "gtkcsstransformvalueprivate.h"
#include "gtkdebug.h"
#include "gtkstylepropertiesprivate.h"
#include "gtktypebuiltins.h"
@@ -4680,6 +4683,82 @@ _gtk_style_context_get_changes (GtkStyleContext *context)
return context->priv->invalidating_context;
}
void
gtk_cairo_rectangle_transform (cairo_rectangle_int_t *dest,
const cairo_rectangle_int_t *src,
const cairo_matrix_t *matrix)
{
double x1, x2, x3, x4;
double y1, y2, y3, y4;
g_return_if_fail (dest != NULL);
g_return_if_fail (src != NULL);
g_return_if_fail (matrix != NULL);
x1 = src->x;
y1 = src->y;
x2 = src->x + src->width;
y2 = src->y;
x3 = src->x + src->width;
y3 = src->y + src->height;
x4 = src->x;
y4 = src->y + src->height;
cairo_matrix_transform_point (matrix, &x1, &y1);
cairo_matrix_transform_point (matrix, &x2, &y2);
cairo_matrix_transform_point (matrix, &x3, &y3);
cairo_matrix_transform_point (matrix, &x4, &y4);
dest->x = floor (MIN (MIN (x1, x2), MIN (x3, x4)));
dest->y = floor (MIN (MIN (y1, y2), MIN (y3, y4)));
dest->width = ceil (MAX (MAX (x1, x2), MAX (x3, x4))) - dest->x;
dest->height = ceil (MAX (MAX (y1, y2), MAX (y3, y4))) - dest->y;
}
void
_gtk_style_context_get_icon_extents (GtkStyleContext *context,
GdkRectangle *extents,
gint x,
gint y,
gint width,
gint height)
{
cairo_matrix_t transform_matrix, matrix;
GtkBorder border;
GdkRectangle rect;
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
g_return_if_fail (extents != NULL);
rect.x = x;
rect.y = y;
rect.width = width;
rect.height = height;
/* strictly speaking we should return an empty rect here,
* but most code still draws a fallback in this case */
if (_gtk_css_image_value_get_image (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_ICON_SOURCE)) == NULL)
return;
if (!_gtk_css_transform_value_get_matrix (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_ICON_TRANSFORM), &transform_matrix))
return;
cairo_matrix_init_translate (&matrix, x + width / 2.0, y + height / 2.0);
cairo_matrix_multiply (&matrix, &transform_matrix, &matrix);
/* need to round to full pixels */
rect.x = - (width + 1) / 2;
rect.y = - (height + 1) / 2;
rect.width = (width + 1) & ~1;
rect.height = (height + 1) & ~1;
gtk_cairo_rectangle_transform (extents, &rect, &matrix);
_gtk_css_shadows_value_get_extents (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_ICON_SHADOW), &border);
extents->x -= border.left;
extents->y -= border.top;
extents->width += border.left + border.right;
extents->height += border.top + border.bottom;
}
GtkIconLookupFlags
_gtk_style_context_get_icon_lookup_flags (GtkStyleContext *context)
{

View File

@@ -56,6 +56,12 @@ void _gtk_style_context_get_cursor_color (GtkStyleContext
void _gtk_style_context_update_animating (GtkStyleContext *context);
void _gtk_style_context_get_icon_extents (GtkStyleContext *context,
GdkRectangle *extents,
gint x,
gint y,
gint width,
gint height);
GtkIconLookupFlags _gtk_style_context_get_icon_lookup_flags (GtkStyleContext *context);
G_END_DECLS

View File

@@ -39,6 +39,7 @@
#include "gtkclipboard.h"
#include "gtkcssstylepropertyprivate.h"
#include "gtkcssnumbervalueprivate.h"
#include "gtkcssshadowsvalueprivate.h"
#include "gtkintl.h"
#include "gtkmarshalers.h"
#include "gtkselectionprivate.h"
@@ -430,6 +431,7 @@ struct _GtkWidgetPrivate
guint multidevice : 1;
guint has_shape_mask : 1;
guint in_reparent : 1;
guint supports_clip : 1;
/* Queue-resize related flags */
guint alloc_needed : 1;
@@ -476,6 +478,7 @@ struct _GtkWidgetPrivate
/* The widget's allocated size */
GtkAllocation allocation;
gint allocated_baseline;
GtkAllocation clip;
/* The widget's requested sizes */
SizeRequestCache requests;
@@ -4218,10 +4221,10 @@ gtk_widget_queue_draw_child (GtkWidget *widget)
parent = priv->parent;
if (parent && gtk_widget_is_drawable (parent))
gtk_widget_queue_draw_area (parent,
priv->allocation.x,
priv->allocation.y,
priv->allocation.width,
priv->allocation.height);
priv->clip.x,
priv->clip.y,
priv->clip.width,
priv->clip.height);
}
/**
@@ -4628,7 +4631,7 @@ gtk_widget_map (GtkWidget *widget)
g_signal_emit (widget, widget_signals[MAP], 0);
if (!gtk_widget_get_has_window (widget))
gdk_window_invalidate_rect (priv->window, &priv->allocation, FALSE);
gdk_window_invalidate_rect (priv->window, &priv->clip, FALSE);
if (widget->priv->context)
_gtk_style_context_update_animating (widget->priv->context);
@@ -4658,7 +4661,7 @@ gtk_widget_unmap (GtkWidget *widget)
gtk_widget_push_verify_invariants (widget);
if (!gtk_widget_get_has_window (widget))
gdk_window_invalidate_rect (priv->window, &priv->allocation, FALSE);
gdk_window_invalidate_rect (priv->window, &priv->clip, FALSE);
_gtk_tooltip_hide (widget);
if (widget->priv->context)
@@ -5267,7 +5270,7 @@ gtk_widget_queue_draw (GtkWidget *widget)
g_return_if_fail (GTK_IS_WIDGET (widget));
gtk_widget_get_allocation (widget, &rect);
gtk_widget_get_clip (widget, &rect);
if (!gtk_widget_get_has_window (widget))
gtk_widget_queue_draw_area (widget,
@@ -5504,7 +5507,7 @@ gtk_widget_size_allocate_with_baseline (GtkWidget *widget,
{
GtkWidgetPrivate *priv;
GdkRectangle real_allocation;
GdkRectangle old_allocation;
GdkRectangle old_allocation, old_clip;
GdkRectangle adjusted_allocation;
gboolean alloc_needed;
gboolean size_changed;
@@ -5561,6 +5564,7 @@ gtk_widget_size_allocate_with_baseline (GtkWidget *widget,
priv->alloc_needed = FALSE;
old_allocation = priv->allocation;
old_clip = priv->clip;
old_baseline = priv->allocated_baseline;
real_allocation = *allocation;
@@ -5652,20 +5656,35 @@ gtk_widget_size_allocate_with_baseline (GtkWidget *widget,
if (!alloc_needed && !size_changed && !position_changed && !baseline_changed)
goto out;
memset (&priv->clip, 0, sizeof (priv->clip));
priv->supports_clip = FALSE;
priv->allocated_baseline = baseline;
g_signal_emit (widget, widget_signals[SIZE_ALLOCATE], 0, &real_allocation);
/* Size allocation is god... after consulting god, no further requests or allocations are needed */
priv->alloc_needed = FALSE;
if (priv->supports_clip)
{
size_changed |= (old_clip.width != priv->clip.width ||
old_clip.height != priv->clip.height);
position_changed |= (old_clip.x != priv->clip.x ||
old_clip.y != priv->clip.y);
}
else
{
priv->clip = priv->allocation;
}
if (gtk_widget_get_mapped (widget) && priv->redraw_on_alloc)
{
if (!gtk_widget_get_has_window (widget) && position_changed)
{
/* Invalidate union(old_allaction,priv->allocation) in priv->window
/* Invalidate union(old_clip,priv->clip) in priv->window
*/
cairo_region_t *invalidate = cairo_region_create_rectangle (&priv->allocation);
cairo_region_union_rectangle (invalidate, &old_allocation);
cairo_region_t *invalidate = cairo_region_create_rectangle (&priv->clip);
cairo_region_union_rectangle (invalidate, &old_clip);
gdk_window_invalidate_region (priv->window, invalidate, FALSE);
cairo_region_destroy (invalidate);
@@ -5673,10 +5692,10 @@ gtk_widget_size_allocate_with_baseline (GtkWidget *widget,
if (size_changed || baseline_changed)
{
/* Invalidate union(old_allaction,priv->allocation) in priv->window and descendents owned by widget
/* Invalidate union(old_clip,priv->clip) in priv->window and descendents owned by widget
*/
cairo_region_t *invalidate = cairo_region_create_rectangle (&priv->allocation);
cairo_region_union_rectangle (invalidate, &old_allocation);
cairo_region_t *invalidate = cairo_region_create_rectangle (&priv->clip);
cairo_region_union_rectangle (invalidate, &old_clip);
gtk_widget_invalidate_widget_windows (widget, invalidate);
cairo_region_destroy (invalidate);
@@ -5686,7 +5705,7 @@ gtk_widget_size_allocate_with_baseline (GtkWidget *widget,
if ((size_changed || position_changed || baseline_changed) && priv->parent &&
gtk_widget_get_realized (priv->parent) && _gtk_container_get_reallocate_redraws (GTK_CONTAINER (priv->parent)))
{
cairo_region_t *invalidate = cairo_region_create_rectangle (&priv->parent->priv->allocation);
cairo_region_t *invalidate = cairo_region_create_rectangle (&priv->parent->priv->clip);
gtk_widget_invalidate_widget_windows (priv->parent, invalidate);
cairo_region_destroy (invalidate);
}
@@ -6522,9 +6541,10 @@ _gtk_widget_draw_internal (GtkWidget *widget,
if (clip_to_size)
{
cairo_rectangle (cr,
0, 0,
widget->priv->allocation.width,
widget->priv->allocation.height);
widget->priv->clip.x - widget->priv->allocation.x,
widget->priv->clip.y - widget->priv->allocation.y,
widget->priv->clip.width,
widget->priv->clip.height);
cairo_clip (cr);
}
@@ -8466,6 +8486,7 @@ _gtk_widget_set_visible_flag (GtkWidget *widget,
priv->allocation.y = -1;
priv->allocation.width = 1;
priv->allocation.height = 1;
memset (&priv->clip, 0, sizeof (priv->clip));
}
}
@@ -14880,6 +14901,148 @@ gtk_widget_get_has_tooltip (GtkWidget *widget)
return has_tooltip;
}
/**
* gtk_widget_get_clip:
* @widget: a #GtkWidget
* @clip: (out): a pointer to a #GtkAllocation to copy to
*
* Retrieves the widgets clip area.
*
* The clip area is the area in which all of @widget's drawing will
* happen. Other toolkits call it the bounding box.
*
* Historically, in GTK the clip area has been equal to the allocation
* retrieved via gtk_widget_get_allocation().
*
* Since: 3.14
*/
void
gtk_widget_get_clip (GtkWidget *widget,
GtkAllocation *clip)
{
GtkWidgetPrivate *priv;
g_return_if_fail (GTK_IS_WIDGET (widget));
g_return_if_fail (clip != NULL);
priv = widget->priv;
*clip = priv->clip;
}
/**
* gtk_widget_set_clip:
* @widget: a #GtkWidget
* @clip: a pointer to a #GtkAllocation to copy from
*
* Sets the widgets clip. This must not be used
* directly, but from within a widgets size_allocate method.
*
* The clip set should be the area that @widget draws on. If @widget is a
* #GtkContainer, the area must contain all children's clips.
*
* If this function is not called by @widget during a size_allocate handler,
* it is assumed to be equal to the allocation. However, if the function is
* not called, certain features that might extend a widget's allocation will
* not be available:
*
* * The GtkWidget::draw signal will be clipped to the widget's allocation
* to avoid overdraw.
* * Calling gtk_render_background() will not draw outset shadows.
*
* It is therefor suggested that you always call gtk_widget_set_clip() during
* a size_allocate handler.
*
* Since: 3.14
*/
void
gtk_widget_set_clip (GtkWidget *widget,
const GtkAllocation *clip)
{
GtkWidgetPrivate *priv;
g_return_if_fail (GTK_IS_WIDGET (widget));
g_return_if_fail (gtk_widget_get_visible (widget) || gtk_widget_is_toplevel (widget));
g_return_if_fail (clip != NULL);
priv = widget->priv;
priv->clip = *clip;
priv->supports_clip = TRUE;
}
/**
* _gtk_widget_supports_clip:
* @widget: The #GtkWidget to check
*
* Returns %TRUE if the widget called gtk_widget_set_clip() during
* size allocation. See that function for details.
*
* Returns: %TRUE if the widget handles a clip separate from its allocation.
**/
gboolean
_gtk_widget_supports_clip (GtkWidget *widget)
{
g_return_val_if_fail (GTK_IS_WIDGET (widget), TRUE);
return widget->priv->supports_clip;
}
static void
union_with_clip (GtkWidget *widget,
gpointer clip)
{
GtkAllocation widget_clip;
if (!gtk_widget_is_drawable (widget))
return;
gtk_widget_get_clip (widget, &widget_clip);
gdk_rectangle_union (&widget_clip, clip, clip);
}
/**
* _gtk_widget_set_simple_clip:
* @widget:
*
* This is a convenience function for gtk_widget_set_clip(), if you
* just want to set the clip for @widget based on its allocation,
* CSS properties and - if the widget is a #GtkContainer - its
* children. gtk_widget_set_allocation() must have been called
* and all children must have been allocated with
* gtk_widget_size_allocate() before calling this function. It is
* therefor a good idea to call this function last in your
* implementation of GtkWidget::size_allocate().
*
* If your widget overdraws its contents, you cannot use this
* function and must call gtk_widget_set_clip() yourself.
**/
void
_gtk_widget_set_simple_clip (GtkWidget *widget)
{
GtkStyleContext *context;
GtkAllocation clip;
GtkBorder extents;
context = gtk_widget_get_style_context (widget);
gtk_widget_get_allocation (widget, &clip);
_gtk_css_shadows_value_get_extents (_gtk_style_context_peek_property (context,
GTK_CSS_PROPERTY_BOX_SHADOW),
&extents);
clip.x -= extents.left;
clip.y -= extents.top;
clip.width += extents.left + extents.right;
clip.height += extents.top + extents.bottom;
if (GTK_IS_CONTAINER (widget))
gtk_container_forall (GTK_CONTAINER (widget), union_with_clip, &clip);
gtk_widget_set_clip (widget, &clip);
}
/**
* gtk_widget_get_allocation:
* @widget: a #GtkWidget

View File

@@ -945,6 +945,12 @@ void gtk_widget_get_allocation (GtkWidget *widget,
GDK_AVAILABLE_IN_ALL
void gtk_widget_set_allocation (GtkWidget *widget,
const GtkAllocation *allocation);
GDK_AVAILABLE_IN_3_14
void gtk_widget_set_clip (GtkWidget *widget,
const GtkAllocation *allocation);
GDK_AVAILABLE_IN_3_14
void gtk_widget_get_clip (GtkWidget *widget,
GtkAllocation *allocation);
GDK_DEPRECATED_IN_3_0_FOR(gtk_widget_get_preferred_width & gtk_widget_get_preferred_height)

View File

@@ -132,6 +132,8 @@ void _gtk_widget_buildable_finish_accelerator (GtkWidget *widget,
GtkStyle * _gtk_widget_get_style (GtkWidget *widget);
void _gtk_widget_set_style (GtkWidget *widget,
GtkStyle *style);
gboolean _gtk_widget_supports_clip (GtkWidget *widget);
void _gtk_widget_set_simple_clip (GtkWidget *widget);
typedef gboolean (*GtkCapturedEventHandler) (GtkWidget *widget, GdkEvent *event);