forked from AuroraMiddleware/gtk
a754579e44
We were forcing the size to be at least min-height, but we left the baselines as they were, which had the effect of making text e.g in entries 'stick to the top'. With this change, we adjust the baseline to keep the ratio of baseline to height unchanged.
952 lines
30 KiB
C
952 lines
30 KiB
C
/*
|
|
* Copyright © 2015 Red Hat Inc.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
* Authors: Benjamin Otte <otte@gnome.org>
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include "gtkcssgadgetprivate.h"
|
|
|
|
#include <math.h>
|
|
|
|
#include "gtkcssnumbervalueprivate.h"
|
|
#include "gtkcssshadowsvalueprivate.h"
|
|
#include "gtkcssstyleprivate.h"
|
|
#include "gtkcssstylepropertyprivate.h"
|
|
#include "gtkcsswidgetnodeprivate.h"
|
|
#include "gtkrenderbackgroundprivate.h"
|
|
#include "gtkrenderborderprivate.h"
|
|
#include "gtkdebug.h"
|
|
|
|
/*
|
|
* Gadgets are 'next-generation widgets' - they combine a CSS node
|
|
* for style matching with geometry management and drawing. Each gadget
|
|
* corresponds to 'CSS box'. Compared to traditional widgets, they are more
|
|
* like building blocks - a typical GTK+ widget will have multiple gadgets,
|
|
* for example a check button has its main gadget, and sub-gadgets for
|
|
* the checkmark and the text.
|
|
*
|
|
* Gadgets are not themselves hierarchically organized, but it is common
|
|
* to have a 'main' gadget, which gets used by the widgets size_allocate,
|
|
* get_preferred_width, etc. and draw callbacks, and which in turn calls out
|
|
* to the sub-gadgets. This call tree might extend further if there are
|
|
* sub-sub-gadgets that a allocated relative to sub-gadgets. In typical
|
|
* situations, the callback chain will reflect the tree structure of the
|
|
* gadgets CSS nodes.
|
|
*
|
|
* Geometry management - Gadgets implement much of the CSS box model for you:
|
|
* margins, border, padding, shadows, min-width/height are all applied automatically.
|
|
*
|
|
* Drawing - Gadgets implement standardized CSS drawing for you: background,
|
|
* shadows and border are drawn before any custom drawing, and the focus outline
|
|
* is (optionally) drawn afterwards.
|
|
*
|
|
* Invalidation - Gadgets sit 'between' widgets and CSS nodes, and connect
|
|
* to the nodes ::style-changed signal and trigger appropriate invalidations
|
|
* on the widget side.
|
|
*/
|
|
|
|
typedef struct _GtkCssGadgetPrivate GtkCssGadgetPrivate;
|
|
struct _GtkCssGadgetPrivate {
|
|
GtkCssNode *node;
|
|
GtkWidget *owner;
|
|
GtkAllocation allocated_size;
|
|
gint allocated_baseline;
|
|
};
|
|
|
|
enum {
|
|
PROP_0,
|
|
PROP_NODE,
|
|
PROP_OWNER,
|
|
/* add more */
|
|
NUM_PROPERTIES
|
|
};
|
|
|
|
static GParamSpec *properties[NUM_PROPERTIES];
|
|
|
|
G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GtkCssGadget, gtk_css_gadget, G_TYPE_OBJECT,
|
|
G_ADD_PRIVATE (GtkCssGadget))
|
|
|
|
static void
|
|
gtk_css_gadget_real_get_preferred_size (GtkCssGadget *gadget,
|
|
GtkOrientation orientation,
|
|
gint for_size,
|
|
gint *minimum,
|
|
gint *natural,
|
|
gint *minimum_baseline,
|
|
gint *natural_baseline)
|
|
{
|
|
*minimum = 0;
|
|
*natural = 0;
|
|
|
|
if (minimum_baseline)
|
|
*minimum_baseline = 0;
|
|
if (natural_baseline)
|
|
*natural_baseline = 0;
|
|
}
|
|
|
|
static void
|
|
gtk_css_gadget_real_allocate (GtkCssGadget *gadget,
|
|
const GtkAllocation *allocation,
|
|
int baseline,
|
|
GtkAllocation *out_clip)
|
|
{
|
|
*out_clip = *allocation;
|
|
}
|
|
|
|
static gboolean
|
|
gtk_css_gadget_real_draw (GtkCssGadget *gadget,
|
|
cairo_t *cr,
|
|
int x,
|
|
int y,
|
|
int width,
|
|
int height)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
gtk_css_gadget_real_style_changed (GtkCssGadget *gadget,
|
|
GtkCssStyleChange *change)
|
|
{
|
|
if (gtk_css_style_change_affects (change, GTK_CSS_AFFECTS_SIZE))
|
|
gtk_css_gadget_queue_resize (gadget);
|
|
else if (gtk_css_style_change_affects (change, GTK_CSS_AFFECTS_CLIP))
|
|
gtk_css_gadget_queue_allocate (gadget);
|
|
else if (gtk_css_style_change_affects (change, GTK_CSS_AFFECTS_REDRAW))
|
|
gtk_css_gadget_queue_draw (gadget);
|
|
}
|
|
|
|
static void
|
|
gtk_css_gadget_get_property (GObject *object,
|
|
guint property_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
GtkCssGadgetPrivate *priv = gtk_css_gadget_get_instance_private (GTK_CSS_GADGET (object));
|
|
|
|
switch (property_id)
|
|
{
|
|
case PROP_NODE:
|
|
g_value_set_object (value, priv->node);
|
|
break;
|
|
|
|
case PROP_OWNER:
|
|
g_value_set_object (value, priv->owner);
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gtk_css_gadget_node_style_changed_cb (GtkCssNode *node,
|
|
GtkCssStyleChange *change,
|
|
GtkCssGadget *gadget)
|
|
{
|
|
GtkCssGadgetClass *klass = GTK_CSS_GADGET_GET_CLASS (gadget);
|
|
|
|
klass->style_changed (gadget, change);
|
|
}
|
|
|
|
static gboolean
|
|
gtk_css_gadget_should_connect_style_changed (GtkCssNode *node)
|
|
{
|
|
/* Delegate to WidgetClass->style_changed */
|
|
if (GTK_IS_CSS_WIDGET_NODE (node))
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
gtk_css_gadget_unset_node (GtkCssGadget *gadget)
|
|
{
|
|
GtkCssGadgetPrivate *priv = gtk_css_gadget_get_instance_private (gadget);
|
|
|
|
if (priv->node)
|
|
{
|
|
if (gtk_css_gadget_should_connect_style_changed (priv->node))
|
|
{
|
|
if (g_signal_handlers_disconnect_by_func (priv->node, gtk_css_gadget_node_style_changed_cb, gadget) != 1)
|
|
{
|
|
g_assert_not_reached ();
|
|
}
|
|
}
|
|
g_object_unref (priv->node);
|
|
priv->node = NULL;
|
|
}
|
|
}
|
|
|
|
void
|
|
gtk_css_gadget_set_node (GtkCssGadget *gadget,
|
|
GtkCssNode *node)
|
|
{
|
|
GtkCssGadgetPrivate *priv = gtk_css_gadget_get_instance_private (gadget);
|
|
|
|
gtk_css_gadget_unset_node (gadget);
|
|
|
|
if (node != NULL)
|
|
priv->node = g_object_ref (node);
|
|
else
|
|
priv->node = gtk_css_node_new ();
|
|
|
|
if (gtk_css_gadget_should_connect_style_changed (priv->node))
|
|
{
|
|
g_signal_connect_after (priv->node,
|
|
"style-changed",
|
|
G_CALLBACK (gtk_css_gadget_node_style_changed_cb),
|
|
gadget);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gtk_css_gadget_set_property (GObject *object,
|
|
guint property_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
GtkCssGadget *gadget = GTK_CSS_GADGET (object);
|
|
GtkCssGadgetPrivate *priv = gtk_css_gadget_get_instance_private (gadget);
|
|
|
|
switch (property_id)
|
|
{
|
|
case PROP_NODE:
|
|
gtk_css_gadget_set_node (gadget, g_value_get_object (value));
|
|
break;
|
|
|
|
case PROP_OWNER:
|
|
priv->owner = g_value_get_object (value);
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
gtk_css_gadget_finalize (GObject *object)
|
|
{
|
|
GtkCssGadget *gadget = GTK_CSS_GADGET (object);
|
|
|
|
gtk_css_gadget_unset_node (gadget);
|
|
|
|
G_OBJECT_CLASS (gtk_css_gadget_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
gtk_css_gadget_class_init (GtkCssGadgetClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
object_class->get_property = gtk_css_gadget_get_property;
|
|
object_class->set_property = gtk_css_gadget_set_property;
|
|
object_class->finalize = gtk_css_gadget_finalize;
|
|
|
|
klass->get_preferred_size = gtk_css_gadget_real_get_preferred_size;
|
|
klass->allocate = gtk_css_gadget_real_allocate;
|
|
klass->draw = gtk_css_gadget_real_draw;
|
|
klass->style_changed = gtk_css_gadget_real_style_changed;
|
|
|
|
properties[PROP_NODE] = g_param_spec_object ("node", "Node",
|
|
"CSS node",
|
|
GTK_TYPE_CSS_NODE,
|
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
|
|
G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
|
properties[PROP_OWNER] = g_param_spec_object ("owner", "Owner",
|
|
"Widget that created and owns this gadget",
|
|
GTK_TYPE_WIDGET,
|
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
|
|
G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
|
|
|
|
|
g_object_class_install_properties (object_class, NUM_PROPERTIES, properties);
|
|
}
|
|
|
|
static void
|
|
gtk_css_gadget_init (GtkCssGadget *gadget)
|
|
{
|
|
|
|
}
|
|
|
|
/**
|
|
* gtk_css_gadget_get_node:
|
|
* @gadget: a #GtkCssGadget
|
|
*
|
|
* Get the CSS node for this gadget.
|
|
*
|
|
* Returns: (transfer none): the CSS node
|
|
*/
|
|
GtkCssNode *
|
|
gtk_css_gadget_get_node (GtkCssGadget *gadget)
|
|
{
|
|
GtkCssGadgetPrivate *priv = gtk_css_gadget_get_instance_private (gadget);
|
|
|
|
return priv->node;
|
|
}
|
|
|
|
/**
|
|
* gtk_css_gadget_get_style:
|
|
* @gadget: a #GtkCssGadget
|
|
*
|
|
* Get the CSS style for this gadget.
|
|
*
|
|
* Returns: (transfer none): the CSS style
|
|
*/
|
|
GtkCssStyle *
|
|
gtk_css_gadget_get_style (GtkCssGadget *gadget)
|
|
{
|
|
GtkCssGadgetPrivate *priv = gtk_css_gadget_get_instance_private (gadget);
|
|
|
|
return gtk_css_node_get_style (priv->node);
|
|
}
|
|
|
|
/**
|
|
* gtk_css_gadget_get_owner:
|
|
* @gadget: a #GtkCssGadget
|
|
*
|
|
* Get the widget to which this gadget belongs.
|
|
*
|
|
* Returns: (transfer none): the widget to which @gadget belongs
|
|
*/
|
|
GtkWidget *
|
|
gtk_css_gadget_get_owner (GtkCssGadget *gadget)
|
|
{
|
|
GtkCssGadgetPrivate *priv = gtk_css_gadget_get_instance_private (gadget);
|
|
|
|
return priv->owner;
|
|
}
|
|
|
|
void
|
|
gtk_css_gadget_set_visible (GtkCssGadget *gadget,
|
|
gboolean visible)
|
|
{
|
|
GtkCssGadgetPrivate *priv = gtk_css_gadget_get_instance_private (gadget);
|
|
|
|
gtk_css_node_set_visible (priv->node, visible);
|
|
}
|
|
|
|
gboolean
|
|
gtk_css_gadget_get_visible (GtkCssGadget *gadget)
|
|
{
|
|
GtkCssGadgetPrivate *priv = gtk_css_gadget_get_instance_private (gadget);
|
|
|
|
return gtk_css_node_get_visible (priv->node);
|
|
}
|
|
|
|
/**
|
|
* gtk_css_gadget_add_class:
|
|
* @gadget: a #GtkCssGadget
|
|
* @name: class name to use in CSS matching
|
|
*
|
|
* Adds a style class to the gadgets CSS node.
|
|
*/
|
|
void
|
|
gtk_css_gadget_add_class (GtkCssGadget *gadget,
|
|
const char *name)
|
|
{
|
|
GtkCssGadgetPrivate *priv = gtk_css_gadget_get_instance_private (gadget);
|
|
GQuark quark;
|
|
|
|
quark = g_quark_from_string (name);
|
|
|
|
gtk_css_node_add_class (priv->node, quark);
|
|
}
|
|
|
|
/**
|
|
* gtk_css_gadget_remove_class:
|
|
* @gadget: a #GtkCssGadget
|
|
* @name: class name
|
|
*
|
|
* Removes a style class from the gadgets CSS node.
|
|
*/
|
|
void
|
|
gtk_css_gadget_remove_class (GtkCssGadget *gadget,
|
|
const char *name)
|
|
{
|
|
GtkCssGadgetPrivate *priv = gtk_css_gadget_get_instance_private (gadget);
|
|
GQuark quark;
|
|
|
|
quark = g_quark_try_string (name);
|
|
if (quark == 0)
|
|
return;
|
|
|
|
gtk_css_node_remove_class (priv->node, quark);
|
|
}
|
|
|
|
/**
|
|
* gtk_css_gadget_set_state:
|
|
* @gadget: a #GtkCssGadget
|
|
* @state: The new state
|
|
*
|
|
* Sets the state of the gadget's CSS node.
|
|
*/
|
|
void
|
|
gtk_css_gadget_set_state (GtkCssGadget *gadget,
|
|
GtkStateFlags state)
|
|
{
|
|
GtkCssGadgetPrivate *priv = gtk_css_gadget_get_instance_private (gadget);
|
|
|
|
gtk_css_node_set_state (priv->node, state);
|
|
}
|
|
|
|
/**
|
|
* gtk_css_gadget_add_state:
|
|
* @gadget: a #GtkCssGadget
|
|
* @state: The state to add
|
|
*
|
|
* Adds the given states to the states of gadget's CSS node. Other states
|
|
* will be kept as they are.
|
|
*/
|
|
void
|
|
gtk_css_gadget_add_state (GtkCssGadget *gadget,
|
|
GtkStateFlags state)
|
|
{
|
|
GtkCssGadgetPrivate *priv = gtk_css_gadget_get_instance_private (gadget);
|
|
|
|
gtk_css_node_set_state (priv->node, gtk_css_node_get_state (priv->node) | state);
|
|
}
|
|
|
|
/**
|
|
* gtk_css_gadget_remove_state:
|
|
* @gadget: a #GtkCssGadget
|
|
* @state: The state to remove
|
|
*
|
|
* Adds the given states to the states of gadget's CSS node. Other states
|
|
* will be kept as they are.
|
|
*/
|
|
void
|
|
gtk_css_gadget_remove_state (GtkCssGadget *gadget,
|
|
GtkStateFlags state)
|
|
{
|
|
GtkCssGadgetPrivate *priv = gtk_css_gadget_get_instance_private (gadget);
|
|
|
|
gtk_css_node_set_state (priv->node, gtk_css_node_get_state (priv->node) & ~state);
|
|
}
|
|
|
|
static gint
|
|
get_number (GtkCssStyle *style,
|
|
guint property)
|
|
{
|
|
double d = _gtk_css_number_value_get (gtk_css_style_get_value (style, property), 100);
|
|
|
|
if (d < 1)
|
|
return ceil (d);
|
|
else
|
|
return floor (d);
|
|
}
|
|
|
|
static void
|
|
get_box_margin (GtkCssStyle *style,
|
|
GtkBorder *margin)
|
|
{
|
|
margin->top = get_number (style, GTK_CSS_PROPERTY_MARGIN_TOP);
|
|
margin->left = get_number (style, GTK_CSS_PROPERTY_MARGIN_LEFT);
|
|
margin->bottom = get_number (style, GTK_CSS_PROPERTY_MARGIN_BOTTOM);
|
|
margin->right = get_number (style, GTK_CSS_PROPERTY_MARGIN_RIGHT);
|
|
}
|
|
|
|
static void
|
|
get_box_border (GtkCssStyle *style,
|
|
GtkBorder *border)
|
|
{
|
|
border->top = get_number (style, GTK_CSS_PROPERTY_BORDER_TOP_WIDTH);
|
|
border->left = get_number (style, GTK_CSS_PROPERTY_BORDER_LEFT_WIDTH);
|
|
border->bottom = get_number (style, GTK_CSS_PROPERTY_BORDER_BOTTOM_WIDTH);
|
|
border->right = get_number (style, GTK_CSS_PROPERTY_BORDER_RIGHT_WIDTH);
|
|
}
|
|
|
|
static void
|
|
get_box_padding (GtkCssStyle *style,
|
|
GtkBorder *border)
|
|
{
|
|
border->top = get_number (style, GTK_CSS_PROPERTY_PADDING_TOP);
|
|
border->left = get_number (style, GTK_CSS_PROPERTY_PADDING_LEFT);
|
|
border->bottom = get_number (style, GTK_CSS_PROPERTY_PADDING_BOTTOM);
|
|
border->right = get_number (style, GTK_CSS_PROPERTY_PADDING_RIGHT);
|
|
}
|
|
|
|
/**
|
|
* gtk_css_gadget_get_preferred_size:
|
|
* @gadget: the #GtkCssGadget whose size is requested
|
|
* @orientation: whether a width (ie horizontal) or height (ie vertical) size is requested
|
|
* @for_size: the available size in the opposite direction, or -1
|
|
* @minimum: (nullable): return location for the minimum size
|
|
* @natural: (nullable): return location for the natural size
|
|
* @minimum_baseline: (nullable): return location for the baseline at minimum size
|
|
* @natural_baseline: (nullable): return location for the baseline at natural size
|
|
*
|
|
* Gets the gadgets minimum and natural size (and, optionally, baseline)
|
|
* in the given orientation for the specified size in the opposite direction.
|
|
*
|
|
* The returned values include CSS padding, border and margin in addition to the
|
|
* gadgets content size, and respect the CSS min-with or min-height properties.
|
|
*
|
|
* The @for_size is assumed to include CSS padding, border and margins as well.
|
|
*/
|
|
void
|
|
gtk_css_gadget_get_preferred_size (GtkCssGadget *gadget,
|
|
GtkOrientation orientation,
|
|
gint for_size,
|
|
gint *minimum,
|
|
gint *natural,
|
|
gint *minimum_baseline,
|
|
gint *natural_baseline)
|
|
{
|
|
GtkCssStyle *style;
|
|
GtkBorder margin, border, padding;
|
|
int min_size, extra_size, extra_opposite, extra_baseline;
|
|
int unused_minimum, unused_natural;
|
|
int forced_minimum, forced_natural;
|
|
|
|
if (minimum == NULL)
|
|
minimum = &unused_minimum;
|
|
if (natural == NULL)
|
|
natural = &unused_natural;
|
|
|
|
if (!gtk_css_gadget_get_visible (gadget))
|
|
{
|
|
*minimum = 0;
|
|
*natural = 0;
|
|
if (minimum_baseline)
|
|
*minimum_baseline = -1;
|
|
if (natural_baseline)
|
|
*natural_baseline = -1;
|
|
return;
|
|
}
|
|
|
|
style = gtk_css_gadget_get_style (gadget);
|
|
get_box_margin (style, &margin);
|
|
get_box_border (style, &border);
|
|
get_box_padding (style, &padding);
|
|
if (orientation == GTK_ORIENTATION_HORIZONTAL)
|
|
{
|
|
extra_size = margin.left + margin.right + border.left + border.right + padding.left + padding.right;
|
|
extra_opposite = margin.top + margin.bottom + border.top + border.bottom + padding.top + padding.bottom;
|
|
extra_baseline = margin.left + border.left + padding.left;
|
|
min_size = get_number (style, GTK_CSS_PROPERTY_MIN_WIDTH);
|
|
}
|
|
else
|
|
{
|
|
extra_size = margin.top + margin.bottom + border.top + border.bottom + padding.top + padding.bottom;
|
|
extra_opposite = margin.left + margin.right + border.left + border.right + padding.left + padding.right;
|
|
extra_baseline = margin.top + border.top + padding.top;
|
|
min_size = get_number (style, GTK_CSS_PROPERTY_MIN_HEIGHT);
|
|
}
|
|
|
|
if (for_size > -1)
|
|
for_size = MAX (0, for_size - extra_opposite);
|
|
|
|
if (minimum_baseline)
|
|
*minimum_baseline = -1;
|
|
if (natural_baseline)
|
|
*natural_baseline = -1;
|
|
|
|
GTK_CSS_GADGET_GET_CLASS (gadget)->get_preferred_size (gadget,
|
|
orientation,
|
|
for_size,
|
|
minimum, natural,
|
|
minimum_baseline, natural_baseline);
|
|
|
|
g_warn_if_fail (*minimum <= *natural);
|
|
|
|
forced_minimum = MAX (*minimum, min_size);
|
|
forced_natural = MAX (*natural, min_size);
|
|
|
|
if (minimum_baseline && *minimum_baseline > -1)
|
|
{
|
|
if (*minimum > 0)
|
|
*minimum_baseline = *minimum_baseline * forced_minimum / *minimum;
|
|
*minimum_baseline = MAX (0, *minimum_baseline + extra_baseline);
|
|
}
|
|
if (natural_baseline && *natural_baseline > -1)
|
|
{
|
|
if (*natural > 0)
|
|
*natural_baseline = *natural_baseline * forced_natural / *natural;
|
|
*natural_baseline = MAX (0, *natural_baseline + extra_baseline);
|
|
}
|
|
|
|
*minimum = MAX (0, forced_minimum + extra_size);
|
|
*natural = MAX (0, forced_natural + extra_size);
|
|
|
|
}
|
|
|
|
/**
|
|
* gtk_css_gadget_allocate:
|
|
* @gadget: the #GtkCssGadget to allocate
|
|
* @allocation: the allocation
|
|
* @baseline: the baseline for the allocation
|
|
* @out_clip: (out): return location for the gadgets clip region
|
|
*
|
|
* Allocates the gadget.
|
|
*
|
|
* The @allocation is assumed to include CSS padding, border and margin.
|
|
* The gadget content will be allocated a smaller area that excludes these.
|
|
* The @out_clip includes the shadow extents of the gadget in addition to
|
|
* any content clip.
|
|
*/
|
|
void
|
|
gtk_css_gadget_allocate (GtkCssGadget *gadget,
|
|
const GtkAllocation *allocation,
|
|
int baseline,
|
|
GtkAllocation *out_clip)
|
|
{
|
|
GtkCssGadgetPrivate *priv = gtk_css_gadget_get_instance_private (gadget);
|
|
GtkAllocation content_allocation;
|
|
GtkAllocation tmp_clip;
|
|
GtkAllocation content_clip = { 0, 0, 0, 0 };
|
|
GtkBorder margin, border, padding, shadow, extents;
|
|
GtkCssStyle *style;
|
|
|
|
g_return_if_fail (out_clip != NULL);
|
|
|
|
if (!gtk_css_gadget_get_visible (gadget))
|
|
{
|
|
out_clip->x = 0;
|
|
out_clip->y = 0;
|
|
out_clip->width = 0;
|
|
out_clip->height = 0;
|
|
return;
|
|
}
|
|
|
|
priv->allocated_size = *allocation;
|
|
priv->allocated_baseline = baseline;
|
|
|
|
style = gtk_css_gadget_get_style (gadget);
|
|
get_box_margin (style, &margin);
|
|
get_box_border (style, &border);
|
|
get_box_padding (style, &padding);
|
|
extents.top = margin.top + border.top + padding.top;
|
|
extents.right = margin.right + border.right + padding.right;
|
|
extents.bottom = margin.bottom + border.bottom + padding.bottom;
|
|
extents.left = margin.left + border.left + padding.left;
|
|
|
|
content_allocation.x = allocation->x + extents.left;
|
|
content_allocation.y = allocation->y + extents.top;
|
|
content_allocation.width = allocation->width - extents.left - extents.right;
|
|
content_allocation.height = allocation->height - extents.top - extents.bottom;
|
|
|
|
if (baseline >= 0)
|
|
baseline -= extents.top;
|
|
|
|
if (content_allocation.width < 0)
|
|
{
|
|
g_warning ("Negative content width %d (allocation %d, extents %dx%d) "
|
|
"while allocating gadget (node %s, owner %s)\n",
|
|
content_allocation.width, allocation->width,
|
|
extents.left, extents.right,
|
|
gtk_css_node_get_name (gtk_css_gadget_get_node (gadget)),
|
|
G_OBJECT_TYPE_NAME (gtk_css_gadget_get_owner (gadget)));
|
|
content_allocation.width = 0;
|
|
}
|
|
|
|
if (content_allocation.height < 0)
|
|
{
|
|
g_warning ("Negative content height %d (allocation %d, extents %dx%d) "
|
|
"while allocating gadget (node %s, owner %s)\n",
|
|
content_allocation.height, allocation->height,
|
|
extents.top, extents.bottom,
|
|
gtk_css_node_get_name (gtk_css_gadget_get_node (gadget)),
|
|
G_OBJECT_TYPE_NAME (gtk_css_gadget_get_owner (gadget)));
|
|
content_allocation.height = 0;
|
|
}
|
|
|
|
GTK_CSS_GADGET_GET_CLASS (gadget)->allocate (gadget, &content_allocation, baseline, &content_clip);
|
|
|
|
_gtk_css_shadows_value_get_extents (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_BOX_SHADOW), &shadow);
|
|
|
|
out_clip->x = allocation->x + margin.left - shadow.left;
|
|
out_clip->y = allocation->y + margin.top - shadow.top;
|
|
out_clip->width = MAX (0, allocation->width - margin.left - margin.right + shadow.left + shadow.right);
|
|
out_clip->height = MAX (0, allocation->height - margin.top - margin.bottom + shadow.top + shadow.bottom);
|
|
|
|
if (content_clip.width > 0 && content_clip.height > 0)
|
|
gdk_rectangle_union (&content_clip, out_clip, out_clip);
|
|
|
|
if (gtk_css_style_render_outline_get_clip (style,
|
|
allocation->x + margin.left,
|
|
allocation->y + margin.top,
|
|
allocation->width - margin.left - margin.right,
|
|
allocation->height - margin.top - margin.bottom,
|
|
&tmp_clip))
|
|
gdk_rectangle_union (&tmp_clip, out_clip, out_clip);
|
|
}
|
|
|
|
/**
|
|
* gtk_css_gadget_draw:
|
|
* @gadget: The gadget to draw
|
|
* @cr: The cairo context to draw to
|
|
*
|
|
* Will draw the gadget at the position allocated via
|
|
* gtk_css_gadget_allocate(). It is your responsibility to make
|
|
* sure that those 2 coordinate systems match.
|
|
*
|
|
* The drawing virtual function will be passed an untransformed @cr.
|
|
* This is important because functions like
|
|
* gtk_container_propagate_draw() depend on that.
|
|
*/
|
|
void
|
|
gtk_css_gadget_draw (GtkCssGadget *gadget,
|
|
cairo_t *cr)
|
|
{
|
|
GtkCssGadgetPrivate *priv = gtk_css_gadget_get_instance_private (gadget);
|
|
GtkBorder margin, border, padding;
|
|
gboolean draw_focus = FALSE;
|
|
GtkCssStyle *style;
|
|
int x, y, width, height;
|
|
int contents_x, contents_y, contents_width, contents_height;
|
|
|
|
if (!gtk_css_gadget_get_visible (gadget))
|
|
return;
|
|
|
|
x = priv->allocated_size.x;
|
|
y = priv->allocated_size.y;
|
|
if (priv->owner && !gtk_widget_get_has_window (priv->owner))
|
|
{
|
|
GtkAllocation widget_alloc;
|
|
gtk_widget_get_allocation (priv->owner, &widget_alloc);
|
|
x -= widget_alloc.x;
|
|
y -= widget_alloc.y;
|
|
}
|
|
width = priv->allocated_size.width;
|
|
height = priv->allocated_size.height;
|
|
|
|
style = gtk_css_gadget_get_style (gadget);
|
|
get_box_margin (style, &margin);
|
|
get_box_border (style, &border);
|
|
get_box_padding (style, &padding);
|
|
|
|
gtk_css_style_render_background (style,
|
|
cr,
|
|
x + margin.left,
|
|
y + margin.top,
|
|
width - margin.left - margin.right,
|
|
height - margin.top - margin.bottom,
|
|
gtk_css_node_get_junction_sides (priv->node));
|
|
gtk_css_style_render_border (style,
|
|
cr,
|
|
x + margin.left,
|
|
y + margin.top,
|
|
width - margin.left - margin.right,
|
|
height - margin.top - margin.bottom,
|
|
0,
|
|
gtk_css_node_get_junction_sides (priv->node));
|
|
|
|
contents_x = x + margin.left + border.left + padding.left;
|
|
contents_y = y + margin.top + border.top + padding.top;
|
|
contents_width = width - margin.left - margin.right - border.left - border.right - padding.left - padding.right;
|
|
contents_height = height - margin.top - margin.bottom - border.top - border.bottom - padding.top - padding.bottom;
|
|
|
|
if (contents_width > 0 && contents_height > 0)
|
|
draw_focus = GTK_CSS_GADGET_GET_CLASS (gadget)->draw (gadget,
|
|
cr,
|
|
contents_x, contents_y,
|
|
contents_width, contents_height);
|
|
|
|
if (draw_focus)
|
|
gtk_css_style_render_outline (style,
|
|
cr,
|
|
x + margin.left,
|
|
y + margin.top,
|
|
width - margin.left - margin.right,
|
|
height - margin.top - margin.bottom);
|
|
|
|
#if G_ENABLE_DEBUG
|
|
if (GTK_DEBUG_CHECK (BASELINES))
|
|
{
|
|
int baseline = priv->allocated_baseline;
|
|
|
|
if (baseline != -1)
|
|
{
|
|
if (priv->owner && !gtk_widget_get_has_window (priv->owner))
|
|
{
|
|
GtkAllocation widget_alloc;
|
|
gtk_widget_get_allocation (priv->owner, &widget_alloc);
|
|
baseline -= widget_alloc.y;
|
|
}
|
|
cairo_save (cr);
|
|
cairo_new_path (cr);
|
|
cairo_move_to (cr, x + margin.left, priv->allocated_baseline + 0.5);
|
|
cairo_rel_line_to (cr, width - margin.left - margin.right, 0);
|
|
cairo_set_line_width (cr, 1.0);
|
|
cairo_set_source_rgba (cr, 1.0, 0, 0.25, 0.25);
|
|
cairo_stroke (cr);
|
|
cairo_restore (cr);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void
|
|
gtk_css_node_style_changed_for_widget (GtkCssNode *node,
|
|
GtkCssStyle *old_style,
|
|
GtkCssStyle *new_style,
|
|
GtkWidget *widget)
|
|
{
|
|
static GtkBitmask *affects_size = NULL;
|
|
GtkBitmask *changes;
|
|
|
|
changes = _gtk_bitmask_new ();
|
|
changes = gtk_css_style_add_difference (changes, old_style, new_style);
|
|
|
|
if (G_UNLIKELY (affects_size == NULL))
|
|
affects_size = _gtk_css_style_property_get_mask_affecting (GTK_CSS_AFFECTS_SIZE | GTK_CSS_AFFECTS_CLIP);
|
|
|
|
if (_gtk_bitmask_intersects (changes, affects_size))
|
|
gtk_widget_queue_resize (widget);
|
|
else
|
|
gtk_widget_queue_draw (widget);
|
|
|
|
_gtk_bitmask_free (changes);
|
|
}
|
|
|
|
void
|
|
gtk_css_gadget_queue_resize (GtkCssGadget *gadget)
|
|
{
|
|
g_return_if_fail (GTK_IS_CSS_GADGET (gadget));
|
|
|
|
gtk_widget_queue_resize (gtk_css_gadget_get_owner (gadget));
|
|
}
|
|
|
|
void
|
|
gtk_css_gadget_queue_allocate (GtkCssGadget *gadget)
|
|
{
|
|
g_return_if_fail (GTK_IS_CSS_GADGET (gadget));
|
|
|
|
gtk_widget_queue_allocate (gtk_css_gadget_get_owner (gadget));
|
|
}
|
|
|
|
void
|
|
gtk_css_gadget_queue_draw (GtkCssGadget *gadget)
|
|
{
|
|
g_return_if_fail (GTK_IS_CSS_GADGET (gadget));
|
|
|
|
/* XXX: Only invalidate clip here */
|
|
gtk_widget_queue_draw (gtk_css_gadget_get_owner (gadget));
|
|
}
|
|
|
|
void
|
|
gtk_css_gadget_get_margin_allocation (GtkCssGadget *gadget,
|
|
GtkAllocation *allocation,
|
|
int *baseline)
|
|
{
|
|
GtkCssGadgetPrivate *priv = gtk_css_gadget_get_instance_private (gadget);
|
|
|
|
g_return_if_fail (GTK_IS_CSS_GADGET (gadget));
|
|
|
|
if (!gtk_css_gadget_get_visible (gadget))
|
|
{
|
|
if (allocation)
|
|
allocation->x = allocation->y = allocation->width = allocation->height = 0;
|
|
if (baseline)
|
|
*baseline = -1;
|
|
return;
|
|
}
|
|
|
|
if (allocation)
|
|
*allocation = priv->allocated_size;
|
|
if (baseline)
|
|
*baseline = priv->allocated_baseline;
|
|
}
|
|
|
|
void
|
|
gtk_css_gadget_get_border_allocation (GtkCssGadget *gadget,
|
|
GtkAllocation *allocation,
|
|
int *baseline)
|
|
{
|
|
GtkCssGadgetPrivate *priv = gtk_css_gadget_get_instance_private (gadget);
|
|
GtkBorder margin;
|
|
|
|
g_return_if_fail (GTK_IS_CSS_GADGET (gadget));
|
|
|
|
if (!gtk_css_gadget_get_visible (gadget))
|
|
{
|
|
if (allocation)
|
|
allocation->x = allocation->y = allocation->width = allocation->height = 0;
|
|
if (baseline)
|
|
*baseline = -1;
|
|
return;
|
|
}
|
|
|
|
get_box_margin (gtk_css_gadget_get_style (gadget), &margin);
|
|
|
|
if (allocation)
|
|
{
|
|
allocation->x = priv->allocated_size.x + margin.left;
|
|
allocation->y = priv->allocated_size.y + margin.top;
|
|
allocation->width = MAX (0, priv->allocated_size.width - margin.left - margin.right);
|
|
allocation->height = MAX (0, priv->allocated_size.height - margin.top - margin.bottom);
|
|
}
|
|
if (baseline)
|
|
{
|
|
if (priv->allocated_baseline >= 0)
|
|
*baseline = priv->allocated_baseline - margin.top;
|
|
else
|
|
*baseline = -1;
|
|
}
|
|
}
|
|
|
|
void
|
|
gtk_css_gadget_get_content_allocation (GtkCssGadget *gadget,
|
|
GtkAllocation *allocation,
|
|
int *baseline)
|
|
{
|
|
GtkCssGadgetPrivate *priv = gtk_css_gadget_get_instance_private (gadget);
|
|
GtkBorder margin, border, padding, extents;
|
|
GtkCssStyle *style;
|
|
|
|
g_return_if_fail (GTK_IS_CSS_GADGET (gadget));
|
|
|
|
if (!gtk_css_gadget_get_visible (gadget))
|
|
{
|
|
if (allocation)
|
|
allocation->x = allocation->y = allocation->width = allocation->height = 0;
|
|
if (baseline)
|
|
*baseline = -1;
|
|
return;
|
|
}
|
|
|
|
style = gtk_css_gadget_get_style (gadget);
|
|
get_box_margin (style, &margin);
|
|
get_box_border (style, &border);
|
|
get_box_padding (style, &padding);
|
|
extents.top = margin.top + border.top + padding.top;
|
|
extents.right = margin.right + border.right + padding.right;
|
|
extents.bottom = margin.bottom + border.bottom + padding.bottom;
|
|
extents.left = margin.left + border.left + padding.left;
|
|
|
|
if (allocation)
|
|
{
|
|
allocation->x = priv->allocated_size.x + extents.left;
|
|
allocation->y = priv->allocated_size.y + extents.top;
|
|
allocation->width = MAX (0, priv->allocated_size.width - extents.left - extents.right);
|
|
allocation->height = MAX (0, priv->allocated_size.height - extents.top - extents.bottom);
|
|
}
|
|
|
|
if (baseline)
|
|
{
|
|
if (priv->allocated_baseline >= 0)
|
|
*baseline = priv->allocated_baseline - extents.top;
|
|
else
|
|
*baseline = -1;
|
|
}
|
|
}
|