forked from AuroraMiddleware/gtk
fe028e2789
Instead of supporting every angle, just support top, right, bottom and left and round the angle to one of those directions. Adwaita overrides arrows anyway and doesn't even look at the angle, so this should not be a problem.
1638 lines
52 KiB
C
1638 lines
52 KiB
C
/* GTK - The GIMP Toolkit
|
||
* Copyright (C) 2010 Carlos Garnacho <carlosg@gnome.org>
|
||
*
|
||
* 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 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/>.
|
||
*/
|
||
|
||
#include "config.h"
|
||
|
||
#include "gtkrender.h"
|
||
#include "gtkrenderprivate.h"
|
||
|
||
#include <math.h>
|
||
|
||
#include "gtkcsscornervalueprivate.h"
|
||
#include "gtkcssenumvalueprivate.h"
|
||
#include "gtkcssimagebuiltinprivate.h"
|
||
#include "gtkcssimagevalueprivate.h"
|
||
#include "gtkcssnumbervalueprivate.h"
|
||
#include "gtkcssrgbavalueprivate.h"
|
||
#include "gtkcssshadowsvalueprivate.h"
|
||
#include "gtkcsstransformvalueprivate.h"
|
||
#include "gtkhslaprivate.h"
|
||
#include "gtkrenderbackgroundprivate.h"
|
||
#include "gtkrenderborderprivate.h"
|
||
#include "gtkstylecontextprivate.h"
|
||
|
||
#include "fallback-c89.c"
|
||
|
||
static gboolean
|
||
render_icon_image (GtkStyleContext *context,
|
||
cairo_t *cr,
|
||
double x,
|
||
double y,
|
||
double width,
|
||
double height)
|
||
{
|
||
const GtkCssValue *shadows;
|
||
cairo_matrix_t matrix, transform_matrix;
|
||
GtkCssImage *image;
|
||
|
||
image = _gtk_css_image_value_get_image (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_ICON_SOURCE));
|
||
if (image == NULL)
|
||
return TRUE;
|
||
|
||
if (GTK_IS_CSS_IMAGE_BUILTIN (image))
|
||
return FALSE;
|
||
|
||
shadows = _gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_ICON_SHADOW);
|
||
|
||
cairo_translate (cr, x, y);
|
||
|
||
if (_gtk_css_transform_value_get_matrix (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_ICON_TRANSFORM), &transform_matrix))
|
||
{
|
||
/* XXX: Implement -gtk-icon-transform-origin instead of hardcoding "50% 50%" here */
|
||
cairo_matrix_init_translate (&matrix, width / 2, height / 2);
|
||
cairo_matrix_multiply (&matrix, &transform_matrix, &matrix);
|
||
cairo_matrix_translate (&matrix, - width / 2, - height / 2);
|
||
|
||
if (_gtk_css_shadows_value_is_none (shadows))
|
||
{
|
||
cairo_transform (cr, &matrix);
|
||
_gtk_css_image_draw (image, cr, width, height);
|
||
}
|
||
else
|
||
{
|
||
cairo_push_group (cr);
|
||
cairo_transform (cr, &matrix);
|
||
_gtk_css_image_draw (image, cr, width, height);
|
||
cairo_pop_group_to_source (cr);
|
||
_gtk_css_shadows_value_paint_icon (shadows, cr);
|
||
cairo_paint (cr);
|
||
}
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
static void
|
||
gtk_do_render_check (GtkStyleContext *context,
|
||
cairo_t *cr,
|
||
gdouble x,
|
||
gdouble y,
|
||
gdouble width,
|
||
gdouble height)
|
||
{
|
||
GtkBorderStyle border_style;
|
||
GtkStateFlags state;
|
||
GtkCssImageBuiltinType image_type;
|
||
gint border_width;
|
||
|
||
state = gtk_style_context_get_state (context);
|
||
if (state & GTK_STATE_FLAG_INCONSISTENT)
|
||
image_type = GTK_CSS_IMAGE_BUILTIN_CHECK_INCONSISTENT;
|
||
else if (state & GTK_STATE_FLAG_CHECKED)
|
||
image_type = GTK_CSS_IMAGE_BUILTIN_CHECK_CHECKED;
|
||
else
|
||
image_type = GTK_CSS_IMAGE_BUILTIN_CHECK;
|
||
|
||
if (render_icon_image (context, cr, x, y, width, height))
|
||
return;
|
||
|
||
border_style = _gtk_css_border_style_value_get (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_BORDER_TOP_STYLE));
|
||
if (border_style == GTK_BORDER_STYLE_SOLID)
|
||
{
|
||
GtkBorder border;
|
||
|
||
border.top = _gtk_css_number_value_get (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_BORDER_TOP_WIDTH), 100);
|
||
border.right = _gtk_css_number_value_get (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_BORDER_RIGHT_WIDTH), 100);
|
||
border.bottom = _gtk_css_number_value_get (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_BORDER_BOTTOM_WIDTH), 100);
|
||
border.left = _gtk_css_number_value_get (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_BORDER_LEFT_WIDTH), 100);
|
||
|
||
border_width = MIN (MIN (border.top, border.bottom),
|
||
MIN (border.left, border.right));
|
||
}
|
||
else
|
||
{
|
||
border_width = 0;
|
||
}
|
||
|
||
cairo_translate (cr, x, y);
|
||
|
||
gtk_css_image_builtin_draw (_gtk_css_image_value_get_image (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_ICON_SOURCE)),
|
||
cr,
|
||
width, height,
|
||
image_type,
|
||
_gtk_css_rgba_value_get_rgba (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_COLOR)),
|
||
_gtk_css_rgba_value_get_rgba (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_BACKGROUND_COLOR)),
|
||
_gtk_css_rgba_value_get_rgba (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_BORDER_TOP_COLOR)),
|
||
border_width);
|
||
}
|
||
|
||
/**
|
||
* gtk_render_check:
|
||
* @context: a #GtkStyleContext
|
||
* @cr: a #cairo_t
|
||
* @x: X origin of the rectangle
|
||
* @y: Y origin of the rectangle
|
||
* @width: rectangle width
|
||
* @height: rectangle height
|
||
*
|
||
* Renders a checkmark (as in a #GtkCheckButton).
|
||
*
|
||
* The %GTK_STATE_FLAG_CHECKED state determines whether the check is
|
||
* on or off, and %GTK_STATE_FLAG_INCONSISTENT determines whether it
|
||
* should be marked as undefined.
|
||
*
|
||
* Typical checkmark rendering:
|
||
*
|
||
* ![](checks.png)
|
||
*
|
||
* Since: 3.0
|
||
**/
|
||
void
|
||
gtk_render_check (GtkStyleContext *context,
|
||
cairo_t *cr,
|
||
gdouble x,
|
||
gdouble y,
|
||
gdouble width,
|
||
gdouble height)
|
||
{
|
||
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
|
||
g_return_if_fail (cr != NULL);
|
||
|
||
if (width <= 0 || height <= 0)
|
||
return;
|
||
|
||
cairo_save (cr);
|
||
cairo_new_path (cr);
|
||
|
||
gtk_do_render_check (context, cr, x, y, width, height);
|
||
|
||
cairo_restore (cr);
|
||
}
|
||
|
||
static void
|
||
gtk_do_render_option (GtkStyleContext *context,
|
||
cairo_t *cr,
|
||
gdouble x,
|
||
gdouble y,
|
||
gdouble width,
|
||
gdouble height)
|
||
{
|
||
GtkBorderStyle border_style;
|
||
GtkStateFlags state;
|
||
GtkCssImageBuiltinType image_type;
|
||
gint border_width;
|
||
|
||
state = gtk_style_context_get_state (context);
|
||
if (state & GTK_STATE_FLAG_INCONSISTENT)
|
||
image_type = GTK_CSS_IMAGE_BUILTIN_OPTION_INCONSISTENT;
|
||
else if (state & GTK_STATE_FLAG_CHECKED)
|
||
image_type = GTK_CSS_IMAGE_BUILTIN_OPTION_CHECKED;
|
||
else
|
||
image_type = GTK_CSS_IMAGE_BUILTIN_OPTION;
|
||
|
||
if (render_icon_image (context, cr, x, y, width, height))
|
||
return;
|
||
|
||
border_style = _gtk_css_border_style_value_get (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_BORDER_TOP_STYLE));
|
||
if (border_style == GTK_BORDER_STYLE_SOLID)
|
||
{
|
||
GtkBorder border;
|
||
|
||
border.top = _gtk_css_number_value_get (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_BORDER_TOP_WIDTH), 100);
|
||
border.right = _gtk_css_number_value_get (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_BORDER_RIGHT_WIDTH), 100);
|
||
border.bottom = _gtk_css_number_value_get (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_BORDER_BOTTOM_WIDTH), 100);
|
||
border.left = _gtk_css_number_value_get (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_BORDER_LEFT_WIDTH), 100);
|
||
|
||
border_width = MIN (MIN (border.top, border.bottom),
|
||
MIN (border.left, border.right));
|
||
}
|
||
else
|
||
{
|
||
border_width = 0;
|
||
}
|
||
|
||
cairo_translate (cr, x, y);
|
||
|
||
gtk_css_image_builtin_draw (_gtk_css_image_value_get_image (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_ICON_SOURCE)),
|
||
cr,
|
||
width, height,
|
||
image_type,
|
||
_gtk_css_rgba_value_get_rgba (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_COLOR)),
|
||
_gtk_css_rgba_value_get_rgba (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_BACKGROUND_COLOR)),
|
||
_gtk_css_rgba_value_get_rgba (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_BORDER_TOP_COLOR)),
|
||
border_width);
|
||
}
|
||
|
||
/**
|
||
* gtk_render_option:
|
||
* @context: a #GtkStyleContext
|
||
* @cr: a #cairo_t
|
||
* @x: X origin of the rectangle
|
||
* @y: Y origin of the rectangle
|
||
* @width: rectangle width
|
||
* @height: rectangle height
|
||
*
|
||
* Renders an option mark (as in a #GtkRadioButton), the %GTK_STATE_FLAG_CHECKED
|
||
* state will determine whether the option is on or off, and
|
||
* %GTK_STATE_FLAG_INCONSISTENT whether it should be marked as undefined.
|
||
*
|
||
* Typical option mark rendering:
|
||
*
|
||
* ![](options.png)
|
||
*
|
||
* Since: 3.0
|
||
**/
|
||
void
|
||
gtk_render_option (GtkStyleContext *context,
|
||
cairo_t *cr,
|
||
gdouble x,
|
||
gdouble y,
|
||
gdouble width,
|
||
gdouble height)
|
||
{
|
||
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
|
||
g_return_if_fail (cr != NULL);
|
||
|
||
if (width <= 0 || height <= 0)
|
||
return;
|
||
|
||
cairo_save (cr);
|
||
cairo_new_path (cr);
|
||
|
||
gtk_do_render_option (context, cr, x, y, width, height);
|
||
|
||
cairo_restore (cr);
|
||
}
|
||
|
||
static void
|
||
gtk_do_render_arrow (GtkStyleContext *context,
|
||
cairo_t *cr,
|
||
gdouble angle,
|
||
gdouble x,
|
||
gdouble y,
|
||
gdouble size)
|
||
{
|
||
GtkBorderStyle border_style;
|
||
GtkCssImageBuiltinType image_type;
|
||
gint border_width;
|
||
|
||
/* map [0, 2 * pi) to [0, 4) */
|
||
angle = round (2 * angle / G_PI);
|
||
|
||
switch (((int) angle) & 3)
|
||
{
|
||
case 0:
|
||
image_type = GTK_CSS_IMAGE_BUILTIN_ARROW_UP;
|
||
break;
|
||
case 1:
|
||
image_type = GTK_CSS_IMAGE_BUILTIN_ARROW_RIGHT;
|
||
break;
|
||
case 2:
|
||
image_type = GTK_CSS_IMAGE_BUILTIN_ARROW_DOWN;
|
||
break;
|
||
case 3:
|
||
image_type = GTK_CSS_IMAGE_BUILTIN_ARROW_LEFT;
|
||
break;
|
||
default:
|
||
g_assert_not_reached ();
|
||
image_type = GTK_CSS_IMAGE_BUILTIN_ARROW_UP;
|
||
break;
|
||
}
|
||
|
||
if (render_icon_image (context, cr, x, y, size, size))
|
||
return;
|
||
|
||
border_style = _gtk_css_border_style_value_get (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_BORDER_TOP_STYLE));
|
||
if (border_style == GTK_BORDER_STYLE_SOLID)
|
||
{
|
||
GtkBorder border;
|
||
|
||
border.top = _gtk_css_number_value_get (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_BORDER_TOP_WIDTH), 100);
|
||
border.right = _gtk_css_number_value_get (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_BORDER_RIGHT_WIDTH), 100);
|
||
border.bottom = _gtk_css_number_value_get (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_BORDER_BOTTOM_WIDTH), 100);
|
||
border.left = _gtk_css_number_value_get (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_BORDER_LEFT_WIDTH), 100);
|
||
|
||
border_width = MIN (MIN (border.top, border.bottom),
|
||
MIN (border.left, border.right));
|
||
}
|
||
else
|
||
{
|
||
border_width = 0;
|
||
}
|
||
|
||
cairo_translate (cr, x, y);
|
||
|
||
gtk_css_image_builtin_draw (_gtk_css_image_value_get_image (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_ICON_SOURCE)),
|
||
cr,
|
||
size, size,
|
||
image_type,
|
||
_gtk_css_rgba_value_get_rgba (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_COLOR)),
|
||
_gtk_css_rgba_value_get_rgba (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_BACKGROUND_COLOR)),
|
||
_gtk_css_rgba_value_get_rgba (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_BORDER_TOP_COLOR)),
|
||
border_width);
|
||
}
|
||
|
||
/**
|
||
* gtk_render_arrow:
|
||
* @context: a #GtkStyleContext
|
||
* @cr: a #cairo_t
|
||
* @angle: arrow angle from 0 to 2 * %G_PI, being 0 the arrow pointing to the north
|
||
* @x: X origin of the render area
|
||
* @y: Y origin of the render area
|
||
* @size: square side for render area
|
||
*
|
||
* Renders an arrow pointing to @angle.
|
||
*
|
||
* Typical arrow rendering at 0, 1&solidus;2 π, π and 3&solidus;2 π:
|
||
*
|
||
* ![](arrows.png)
|
||
*
|
||
* Since: 3.0
|
||
**/
|
||
void
|
||
gtk_render_arrow (GtkStyleContext *context,
|
||
cairo_t *cr,
|
||
gdouble angle,
|
||
gdouble x,
|
||
gdouble y,
|
||
gdouble size)
|
||
{
|
||
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
|
||
g_return_if_fail (cr != NULL);
|
||
|
||
if (size <= 0)
|
||
return;
|
||
|
||
cairo_save (cr);
|
||
cairo_new_path (cr);
|
||
|
||
gtk_style_context_save (context);
|
||
gtk_style_context_add_class (context, GTK_STYLE_CLASS_ARROW);
|
||
|
||
gtk_do_render_arrow (context, cr, angle, x, y, size);
|
||
|
||
gtk_style_context_restore (context);
|
||
cairo_restore (cr);
|
||
}
|
||
|
||
/**
|
||
* gtk_render_background:
|
||
* @context: a #GtkStyleContext
|
||
* @cr: a #cairo_t
|
||
* @x: X origin of the rectangle
|
||
* @y: Y origin of the rectangle
|
||
* @width: rectangle width
|
||
* @height: rectangle height
|
||
*
|
||
* Renders the background of an element.
|
||
*
|
||
* Typical background rendering, showing the effect of
|
||
* `background-image`, `border-width` and `border-radius`:
|
||
*
|
||
* ![](background.png)
|
||
*
|
||
* Since: 3.0.
|
||
**/
|
||
void
|
||
gtk_render_background (GtkStyleContext *context,
|
||
cairo_t *cr,
|
||
gdouble x,
|
||
gdouble y,
|
||
gdouble width,
|
||
gdouble height)
|
||
{
|
||
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
|
||
g_return_if_fail (cr != NULL);
|
||
|
||
if (width <= 0 || height <= 0)
|
||
return;
|
||
|
||
cairo_save (cr);
|
||
cairo_new_path (cr);
|
||
|
||
gtk_css_style_render_background (gtk_style_context_lookup_style (context),
|
||
cr, x, y, width, height,
|
||
gtk_style_context_get_junction_sides (context));
|
||
|
||
cairo_restore (cr);
|
||
}
|
||
|
||
/**
|
||
* gtk_render_frame:
|
||
* @context: a #GtkStyleContext
|
||
* @cr: a #cairo_t
|
||
* @x: X origin of the rectangle
|
||
* @y: Y origin of the rectangle
|
||
* @width: rectangle width
|
||
* @height: rectangle height
|
||
*
|
||
* Renders a frame around the rectangle defined by @x, @y, @width, @height.
|
||
*
|
||
* Examples of frame rendering, showing the effect of `border-image`,
|
||
* `border-color`, `border-width`, `border-radius` and junctions:
|
||
*
|
||
* ![](frames.png)
|
||
*
|
||
* Since: 3.0
|
||
**/
|
||
void
|
||
gtk_render_frame (GtkStyleContext *context,
|
||
cairo_t *cr,
|
||
gdouble x,
|
||
gdouble y,
|
||
gdouble width,
|
||
gdouble height)
|
||
{
|
||
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
|
||
g_return_if_fail (cr != NULL);
|
||
|
||
if (width <= 0 || height <= 0)
|
||
return;
|
||
|
||
cairo_save (cr);
|
||
cairo_new_path (cr);
|
||
|
||
gtk_css_style_render_border (gtk_style_context_lookup_style (context),
|
||
cr,
|
||
x, y, width, height,
|
||
0,
|
||
gtk_style_context_get_junction_sides (context));
|
||
|
||
|
||
cairo_restore (cr);
|
||
}
|
||
|
||
static void
|
||
gtk_do_render_expander (GtkStyleContext *context,
|
||
cairo_t *cr,
|
||
gdouble x,
|
||
gdouble y,
|
||
gdouble width,
|
||
gdouble height)
|
||
{
|
||
GtkBorderStyle border_style;
|
||
GtkCssImageBuiltinType image_type;
|
||
GtkStateFlags state;
|
||
gint border_width;
|
||
|
||
state = gtk_style_context_get_state (context);
|
||
if (gtk_style_context_has_class (context, "horizontal"))
|
||
{
|
||
if (state & GTK_STATE_FLAG_DIR_RTL)
|
||
image_type = (state & GTK_STATE_FLAG_CHECKED)
|
||
? GTK_CSS_IMAGE_BUILTIN_EXPANDER_HORIZONTAL_RIGHT_EXPANDED
|
||
: GTK_CSS_IMAGE_BUILTIN_EXPANDER_HORIZONTAL_RIGHT;
|
||
else
|
||
image_type = (state & GTK_STATE_FLAG_CHECKED)
|
||
? GTK_CSS_IMAGE_BUILTIN_EXPANDER_HORIZONTAL_LEFT_EXPANDED
|
||
: GTK_CSS_IMAGE_BUILTIN_EXPANDER_HORIZONTAL_LEFT;
|
||
}
|
||
else
|
||
{
|
||
if (state & GTK_STATE_FLAG_DIR_RTL)
|
||
image_type = (state & GTK_STATE_FLAG_CHECKED)
|
||
? GTK_CSS_IMAGE_BUILTIN_EXPANDER_VERTICAL_RIGHT_EXPANDED
|
||
: GTK_CSS_IMAGE_BUILTIN_EXPANDER_VERTICAL_RIGHT;
|
||
else
|
||
image_type = (state & GTK_STATE_FLAG_CHECKED)
|
||
? GTK_CSS_IMAGE_BUILTIN_EXPANDER_VERTICAL_LEFT_EXPANDED
|
||
: GTK_CSS_IMAGE_BUILTIN_EXPANDER_VERTICAL_LEFT;
|
||
}
|
||
|
||
if (render_icon_image (context, cr, x, y, width, height))
|
||
return;
|
||
|
||
border_style = _gtk_css_border_style_value_get (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_BORDER_TOP_STYLE));
|
||
if (border_style == GTK_BORDER_STYLE_SOLID)
|
||
{
|
||
GtkBorder border;
|
||
|
||
border.top = _gtk_css_number_value_get (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_BORDER_TOP_WIDTH), 100);
|
||
border.right = _gtk_css_number_value_get (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_BORDER_RIGHT_WIDTH), 100);
|
||
border.bottom = _gtk_css_number_value_get (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_BORDER_BOTTOM_WIDTH), 100);
|
||
border.left = _gtk_css_number_value_get (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_BORDER_LEFT_WIDTH), 100);
|
||
|
||
border_width = MIN (MIN (border.top, border.bottom),
|
||
MIN (border.left, border.right));
|
||
}
|
||
else
|
||
{
|
||
border_width = 0;
|
||
}
|
||
|
||
cairo_translate (cr, x, y);
|
||
|
||
gtk_css_image_builtin_draw (_gtk_css_image_value_get_image (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_ICON_SOURCE)),
|
||
cr,
|
||
width, height,
|
||
image_type,
|
||
_gtk_css_rgba_value_get_rgba (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_COLOR)),
|
||
_gtk_css_rgba_value_get_rgba (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_BACKGROUND_COLOR)),
|
||
_gtk_css_rgba_value_get_rgba (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_BORDER_TOP_COLOR)),
|
||
border_width);
|
||
}
|
||
|
||
/**
|
||
* gtk_render_expander:
|
||
* @context: a #GtkStyleContext
|
||
* @cr: a #cairo_t
|
||
* @x: X origin of the rectangle
|
||
* @y: Y origin of the rectangle
|
||
* @width: rectangle width
|
||
* @height: rectangle height
|
||
*
|
||
* Renders an expander (as used in #GtkTreeView and #GtkExpander) in the area
|
||
* defined by @x, @y, @width, @height. The state %GTK_STATE_FLAG_CHECKED
|
||
* determines whether the expander is collapsed or expanded.
|
||
*
|
||
* Typical expander rendering:
|
||
*
|
||
* ![](expanders.png)
|
||
*
|
||
* Since: 3.0
|
||
**/
|
||
void
|
||
gtk_render_expander (GtkStyleContext *context,
|
||
cairo_t *cr,
|
||
gdouble x,
|
||
gdouble y,
|
||
gdouble width,
|
||
gdouble height)
|
||
{
|
||
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
|
||
g_return_if_fail (cr != NULL);
|
||
|
||
if (width <= 0 || height <= 0)
|
||
return;
|
||
|
||
cairo_save (cr);
|
||
cairo_new_path (cr);
|
||
|
||
gtk_do_render_expander (context, cr, x, y, width, height);
|
||
|
||
cairo_restore (cr);
|
||
}
|
||
|
||
/**
|
||
* gtk_render_focus:
|
||
* @context: a #GtkStyleContext
|
||
* @cr: a #cairo_t
|
||
* @x: X origin of the rectangle
|
||
* @y: Y origin of the rectangle
|
||
* @width: rectangle width
|
||
* @height: rectangle height
|
||
*
|
||
* Renders a focus indicator on the rectangle determined by @x, @y, @width, @height.
|
||
*
|
||
* Typical focus rendering:
|
||
*
|
||
* ![](focus.png)
|
||
*
|
||
* Since: 3.0
|
||
**/
|
||
void
|
||
gtk_render_focus (GtkStyleContext *context,
|
||
cairo_t *cr,
|
||
gdouble x,
|
||
gdouble y,
|
||
gdouble width,
|
||
gdouble height)
|
||
{
|
||
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
|
||
g_return_if_fail (cr != NULL);
|
||
|
||
if (width <= 0 || height <= 0)
|
||
return;
|
||
|
||
cairo_save (cr);
|
||
cairo_new_path (cr);
|
||
|
||
gtk_css_style_render_outline (gtk_style_context_lookup_style (context),
|
||
cr,
|
||
x, y, width, height);
|
||
|
||
cairo_restore (cr);
|
||
}
|
||
|
||
static void
|
||
prepare_context_for_layout (cairo_t *cr,
|
||
gdouble x,
|
||
gdouble y,
|
||
PangoLayout *layout)
|
||
{
|
||
const PangoMatrix *matrix;
|
||
|
||
matrix = pango_context_get_matrix (pango_layout_get_context (layout));
|
||
|
||
cairo_move_to (cr, x, y);
|
||
|
||
if (matrix)
|
||
{
|
||
cairo_matrix_t cairo_matrix;
|
||
|
||
cairo_matrix_init (&cairo_matrix,
|
||
matrix->xx, matrix->yx,
|
||
matrix->xy, matrix->yy,
|
||
matrix->x0, matrix->y0);
|
||
|
||
cairo_transform (cr, &cairo_matrix);
|
||
}
|
||
}
|
||
|
||
static void
|
||
gtk_do_render_layout (GtkStyleContext *context,
|
||
cairo_t *cr,
|
||
gdouble x,
|
||
gdouble y,
|
||
PangoLayout *layout)
|
||
{
|
||
const GdkRGBA *fg_color;
|
||
|
||
cairo_save (cr);
|
||
fg_color = _gtk_css_rgba_value_get_rgba (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_COLOR));
|
||
|
||
prepare_context_for_layout (cr, x, y, layout);
|
||
|
||
_gtk_css_shadows_value_paint_layout (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_TEXT_SHADOW),
|
||
cr, layout);
|
||
|
||
gdk_cairo_set_source_rgba (cr, fg_color);
|
||
pango_cairo_show_layout (cr, layout);
|
||
|
||
cairo_restore (cr);
|
||
}
|
||
|
||
/**
|
||
* gtk_render_layout:
|
||
* @context: a #GtkStyleContext
|
||
* @cr: a #cairo_t
|
||
* @x: X origin
|
||
* @y: Y origin
|
||
* @layout: the #PangoLayout to render
|
||
*
|
||
* Renders @layout on the coordinates @x, @y
|
||
*
|
||
* Since: 3.0
|
||
**/
|
||
void
|
||
gtk_render_layout (GtkStyleContext *context,
|
||
cairo_t *cr,
|
||
gdouble x,
|
||
gdouble y,
|
||
PangoLayout *layout)
|
||
{
|
||
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
|
||
g_return_if_fail (PANGO_IS_LAYOUT (layout));
|
||
g_return_if_fail (cr != NULL);
|
||
|
||
cairo_save (cr);
|
||
cairo_new_path (cr);
|
||
|
||
gtk_do_render_layout (context, cr, x, y, layout);
|
||
|
||
cairo_restore (cr);
|
||
}
|
||
|
||
static void
|
||
gtk_do_render_line (GtkStyleContext *context,
|
||
cairo_t *cr,
|
||
gdouble x0,
|
||
gdouble y0,
|
||
gdouble x1,
|
||
gdouble y1)
|
||
{
|
||
const GdkRGBA *color;
|
||
|
||
cairo_save (cr);
|
||
|
||
color = _gtk_css_rgba_value_get_rgba (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_COLOR));
|
||
|
||
cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
|
||
cairo_set_line_width (cr, 1);
|
||
|
||
cairo_move_to (cr, x0 + 0.5, y0 + 0.5);
|
||
cairo_line_to (cr, x1 + 0.5, y1 + 0.5);
|
||
|
||
gdk_cairo_set_source_rgba (cr, color);
|
||
cairo_stroke (cr);
|
||
|
||
cairo_restore (cr);
|
||
}
|
||
|
||
/**
|
||
* gtk_render_line:
|
||
* @context: a #GtkStyleContext
|
||
* @cr: a #cairo_t
|
||
* @x0: X coordinate for the origin of the line
|
||
* @y0: Y coordinate for the origin of the line
|
||
* @x1: X coordinate for the end of the line
|
||
* @y1: Y coordinate for the end of the line
|
||
*
|
||
* Renders a line from (x0, y0) to (x1, y1).
|
||
*
|
||
* Since: 3.0
|
||
**/
|
||
void
|
||
gtk_render_line (GtkStyleContext *context,
|
||
cairo_t *cr,
|
||
gdouble x0,
|
||
gdouble y0,
|
||
gdouble x1,
|
||
gdouble y1)
|
||
{
|
||
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
|
||
g_return_if_fail (cr != NULL);
|
||
|
||
cairo_save (cr);
|
||
cairo_new_path (cr);
|
||
|
||
gtk_do_render_line (context, cr, x0, y0, x1, y1);
|
||
|
||
cairo_restore (cr);
|
||
}
|
||
|
||
static void
|
||
gtk_do_render_slider (GtkStyleContext *context,
|
||
cairo_t *cr,
|
||
gdouble x,
|
||
gdouble y,
|
||
gdouble width,
|
||
gdouble height,
|
||
GtkOrientation orientation)
|
||
{
|
||
GtkCssStyle *style;
|
||
GtkJunctionSides junction;
|
||
|
||
style = gtk_style_context_lookup_style (context);
|
||
junction = gtk_style_context_get_junction_sides (context);
|
||
|
||
gtk_css_style_render_background (style,
|
||
cr,
|
||
x, y, width, height,
|
||
junction);
|
||
gtk_css_style_render_border (style,
|
||
cr,
|
||
x, y, width, height,
|
||
0,
|
||
junction);
|
||
}
|
||
|
||
/**
|
||
* gtk_render_slider:
|
||
* @context: a #GtkStyleContext
|
||
* @cr: a #cairo_t
|
||
* @x: X origin of the rectangle
|
||
* @y: Y origin of the rectangle
|
||
* @width: rectangle width
|
||
* @height: rectangle height
|
||
* @orientation: orientation of the slider
|
||
*
|
||
* Renders a slider (as in #GtkScale) in the rectangle defined by @x, @y,
|
||
* @width, @height. @orientation defines whether the slider is vertical
|
||
* or horizontal.
|
||
*
|
||
* Typical slider rendering:
|
||
*
|
||
* ![](sliders.png)
|
||
*
|
||
* Since: 3.0
|
||
**/
|
||
void
|
||
gtk_render_slider (GtkStyleContext *context,
|
||
cairo_t *cr,
|
||
gdouble x,
|
||
gdouble y,
|
||
gdouble width,
|
||
gdouble height,
|
||
GtkOrientation orientation)
|
||
{
|
||
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
|
||
g_return_if_fail (cr != NULL);
|
||
|
||
if (width <= 0 || height <= 0)
|
||
return;
|
||
|
||
cairo_save (cr);
|
||
cairo_new_path (cr);
|
||
|
||
gtk_do_render_slider (context, cr, x, y, width, height, orientation);
|
||
|
||
cairo_restore (cr);
|
||
}
|
||
|
||
static void
|
||
gtk_css_style_render_frame_gap (GtkCssStyle *style,
|
||
cairo_t *cr,
|
||
gdouble x,
|
||
gdouble y,
|
||
gdouble width,
|
||
gdouble height,
|
||
GtkPositionType gap_side,
|
||
gdouble xy0_gap,
|
||
gdouble xy1_gap,
|
||
GtkJunctionSides junction)
|
||
{
|
||
gint border_width;
|
||
GtkCssValue *corner[4];
|
||
gdouble x0, y0, x1, y1, xc = 0.0, yc = 0.0, wc = 0.0, hc = 0.0;
|
||
GtkBorder border;
|
||
|
||
border.top = _gtk_css_number_value_get (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_BORDER_TOP_WIDTH), 100);
|
||
border.right = _gtk_css_number_value_get (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_BORDER_RIGHT_WIDTH), 100);
|
||
border.bottom = _gtk_css_number_value_get (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_BORDER_BOTTOM_WIDTH), 100);
|
||
border.left = _gtk_css_number_value_get (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_BORDER_LEFT_WIDTH), 100);
|
||
corner[GTK_CSS_TOP_LEFT] = gtk_css_style_get_value (style, GTK_CSS_PROPERTY_BORDER_TOP_LEFT_RADIUS);
|
||
corner[GTK_CSS_TOP_RIGHT] = gtk_css_style_get_value (style, GTK_CSS_PROPERTY_BORDER_TOP_RIGHT_RADIUS);
|
||
corner[GTK_CSS_BOTTOM_LEFT] = gtk_css_style_get_value (style, GTK_CSS_PROPERTY_BORDER_BOTTOM_LEFT_RADIUS);
|
||
corner[GTK_CSS_BOTTOM_RIGHT] = gtk_css_style_get_value (style, GTK_CSS_PROPERTY_BORDER_BOTTOM_RIGHT_RADIUS);
|
||
|
||
border_width = MIN (MIN (border.top, border.bottom),
|
||
MIN (border.left, border.right));
|
||
|
||
cairo_save (cr);
|
||
|
||
switch (gap_side)
|
||
{
|
||
case GTK_POS_TOP:
|
||
xc = x + xy0_gap + border_width;
|
||
yc = y;
|
||
wc = MAX (xy1_gap - xy0_gap - 2 * border_width, 0);
|
||
hc = border_width;
|
||
|
||
if (xy0_gap < _gtk_css_corner_value_get_x (corner[GTK_CSS_TOP_LEFT], width))
|
||
junction |= GTK_JUNCTION_CORNER_TOPLEFT;
|
||
|
||
if (xy1_gap > width - _gtk_css_corner_value_get_x (corner[GTK_CSS_TOP_RIGHT], width))
|
||
junction |= GTK_JUNCTION_CORNER_TOPRIGHT;
|
||
break;
|
||
case GTK_POS_BOTTOM:
|
||
xc = x + xy0_gap + border_width;
|
||
yc = y + height - border_width;
|
||
wc = MAX (xy1_gap - xy0_gap - 2 * border_width, 0);
|
||
hc = border_width;
|
||
|
||
if (xy0_gap < _gtk_css_corner_value_get_x (corner[GTK_CSS_BOTTOM_LEFT], width))
|
||
junction |= GTK_JUNCTION_CORNER_BOTTOMLEFT;
|
||
|
||
if (xy1_gap > width - _gtk_css_corner_value_get_x (corner[GTK_CSS_BOTTOM_RIGHT], width))
|
||
junction |= GTK_JUNCTION_CORNER_BOTTOMRIGHT;
|
||
|
||
break;
|
||
case GTK_POS_LEFT:
|
||
xc = x;
|
||
yc = y + xy0_gap + border_width;
|
||
wc = border_width;
|
||
hc = MAX (xy1_gap - xy0_gap - 2 * border_width, 0);
|
||
|
||
if (xy0_gap < _gtk_css_corner_value_get_y (corner[GTK_CSS_TOP_LEFT], height))
|
||
junction |= GTK_JUNCTION_CORNER_TOPLEFT;
|
||
|
||
if (xy1_gap > height - _gtk_css_corner_value_get_y (corner[GTK_CSS_BOTTOM_LEFT], height))
|
||
junction |= GTK_JUNCTION_CORNER_BOTTOMLEFT;
|
||
|
||
break;
|
||
case GTK_POS_RIGHT:
|
||
xc = x + width - border_width;
|
||
yc = y + xy0_gap + border_width;
|
||
wc = border_width;
|
||
hc = MAX (xy1_gap - xy0_gap - 2 * border_width, 0);
|
||
|
||
if (xy0_gap < _gtk_css_corner_value_get_y (corner[GTK_CSS_TOP_RIGHT], height))
|
||
junction |= GTK_JUNCTION_CORNER_TOPRIGHT;
|
||
|
||
if (xy1_gap > height - _gtk_css_corner_value_get_y (corner[GTK_CSS_BOTTOM_RIGHT], height))
|
||
junction |= GTK_JUNCTION_CORNER_BOTTOMRIGHT;
|
||
|
||
break;
|
||
}
|
||
|
||
cairo_clip_extents (cr, &x0, &y0, &x1, &y1);
|
||
cairo_rectangle (cr, x0, y0, x1 - x0, yc - y0);
|
||
cairo_rectangle (cr, x0, yc, xc - x0, hc);
|
||
cairo_rectangle (cr, xc + wc, yc, x1 - (xc + wc), hc);
|
||
cairo_rectangle (cr, x0, yc + hc, x1 - x0, y1 - (yc + hc));
|
||
cairo_clip (cr);
|
||
|
||
gtk_css_style_render_border (style, cr,
|
||
x, y, width, height,
|
||
0, junction);
|
||
|
||
cairo_restore (cr);
|
||
}
|
||
|
||
/**
|
||
* gtk_render_frame_gap:
|
||
* @context: a #GtkStyleContext
|
||
* @cr: a #cairo_t
|
||
* @x: X origin of the rectangle
|
||
* @y: Y origin of the rectangle
|
||
* @width: rectangle width
|
||
* @height: rectangle height
|
||
* @gap_side: side where the gap is
|
||
* @xy0_gap: initial coordinate (X or Y depending on @gap_side) for the gap
|
||
* @xy1_gap: end coordinate (X or Y depending on @gap_side) for the gap
|
||
*
|
||
* Renders a frame around the rectangle defined by (@x, @y, @width, @height),
|
||
* leaving a gap on one side. @xy0_gap and @xy1_gap will mean X coordinates
|
||
* for %GTK_POS_TOP and %GTK_POS_BOTTOM gap sides, and Y coordinates for
|
||
* %GTK_POS_LEFT and %GTK_POS_RIGHT.
|
||
*
|
||
* Typical rendering of a frame with a gap:
|
||
*
|
||
* ![](frame-gap.png)
|
||
*
|
||
* Since: 3.0
|
||
**/
|
||
void
|
||
gtk_render_frame_gap (GtkStyleContext *context,
|
||
cairo_t *cr,
|
||
gdouble x,
|
||
gdouble y,
|
||
gdouble width,
|
||
gdouble height,
|
||
GtkPositionType gap_side,
|
||
gdouble xy0_gap,
|
||
gdouble xy1_gap)
|
||
{
|
||
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
|
||
g_return_if_fail (cr != NULL);
|
||
g_return_if_fail (xy0_gap <= xy1_gap);
|
||
g_return_if_fail (xy0_gap >= 0);
|
||
|
||
if (width <= 0 || height <= 0)
|
||
return;
|
||
|
||
if (gap_side == GTK_POS_LEFT ||
|
||
gap_side == GTK_POS_RIGHT)
|
||
g_return_if_fail (xy1_gap <= height);
|
||
else
|
||
g_return_if_fail (xy1_gap <= width);
|
||
|
||
cairo_save (cr);
|
||
cairo_new_path (cr);
|
||
|
||
gtk_css_style_render_frame_gap (gtk_style_context_lookup_style (context),
|
||
cr,
|
||
x, y, width, height, gap_side,
|
||
xy0_gap, xy1_gap,
|
||
gtk_style_context_get_junction_sides (context));
|
||
|
||
|
||
cairo_restore (cr);
|
||
}
|
||
|
||
static void
|
||
gtk_css_style_render_extension (GtkCssStyle *style,
|
||
cairo_t *cr,
|
||
gdouble x,
|
||
gdouble y,
|
||
gdouble width,
|
||
gdouble height,
|
||
GtkPositionType gap_side)
|
||
{
|
||
GtkJunctionSides junction = 0;
|
||
guint hidden_side = 0;
|
||
|
||
switch (gap_side)
|
||
{
|
||
case GTK_POS_LEFT:
|
||
junction = GTK_JUNCTION_LEFT;
|
||
hidden_side = (1 << GTK_CSS_LEFT);
|
||
break;
|
||
case GTK_POS_RIGHT:
|
||
junction = GTK_JUNCTION_RIGHT;
|
||
hidden_side = (1 << GTK_CSS_RIGHT);
|
||
break;
|
||
case GTK_POS_TOP:
|
||
junction = GTK_JUNCTION_TOP;
|
||
hidden_side = (1 << GTK_CSS_TOP);
|
||
break;
|
||
case GTK_POS_BOTTOM:
|
||
junction = GTK_JUNCTION_BOTTOM;
|
||
hidden_side = (1 << GTK_CSS_BOTTOM);
|
||
break;
|
||
}
|
||
|
||
gtk_css_style_render_background (style,
|
||
cr,
|
||
x, y,
|
||
width, height,
|
||
junction);
|
||
|
||
gtk_css_style_render_border (style, cr,
|
||
x, y, width, height,
|
||
hidden_side, junction);
|
||
}
|
||
|
||
/**
|
||
* gtk_render_extension:
|
||
* @context: a #GtkStyleContext
|
||
* @cr: a #cairo_t
|
||
* @x: X origin of the rectangle
|
||
* @y: Y origin of the rectangle
|
||
* @width: rectangle width
|
||
* @height: rectangle height
|
||
* @gap_side: side where the gap is
|
||
*
|
||
* Renders a extension (as in a #GtkNotebook tab) in the rectangle
|
||
* defined by @x, @y, @width, @height. The side where the extension
|
||
* connects to is defined by @gap_side.
|
||
*
|
||
* Typical extension rendering:
|
||
*
|
||
* ![](extensions.png)
|
||
*
|
||
* Since: 3.0
|
||
**/
|
||
void
|
||
gtk_render_extension (GtkStyleContext *context,
|
||
cairo_t *cr,
|
||
gdouble x,
|
||
gdouble y,
|
||
gdouble width,
|
||
gdouble height,
|
||
GtkPositionType gap_side)
|
||
{
|
||
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
|
||
g_return_if_fail (cr != NULL);
|
||
|
||
if (width <= 0 || height <= 0)
|
||
return;
|
||
|
||
cairo_save (cr);
|
||
cairo_new_path (cr);
|
||
|
||
gtk_css_style_render_extension (gtk_style_context_lookup_style (context),
|
||
cr,
|
||
x, y, width, height,
|
||
gap_side);
|
||
|
||
cairo_restore (cr);
|
||
}
|
||
|
||
static void
|
||
gtk_do_render_handle (GtkStyleContext *context,
|
||
cairo_t *cr,
|
||
gdouble x,
|
||
gdouble y,
|
||
gdouble width,
|
||
gdouble height)
|
||
{
|
||
GtkCssImageBuiltinType type;
|
||
GtkBorderStyle border_style;
|
||
gint border_width;
|
||
|
||
gtk_render_background (context, cr, x, y, width, height);
|
||
gtk_render_frame (context, cr, x, y, width, height);
|
||
|
||
if (render_icon_image (context, cr, x, y, width, height))
|
||
return;
|
||
|
||
border_style = _gtk_css_border_style_value_get (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_BORDER_TOP_STYLE));
|
||
if (border_style == GTK_BORDER_STYLE_SOLID)
|
||
{
|
||
GtkBorder border;
|
||
|
||
border.top = _gtk_css_number_value_get (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_BORDER_TOP_WIDTH), 100);
|
||
border.right = _gtk_css_number_value_get (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_BORDER_RIGHT_WIDTH), 100);
|
||
border.bottom = _gtk_css_number_value_get (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_BORDER_BOTTOM_WIDTH), 100);
|
||
border.left = _gtk_css_number_value_get (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_BORDER_LEFT_WIDTH), 100);
|
||
|
||
border_width = MIN (MIN (border.top, border.bottom),
|
||
MIN (border.left, border.right));
|
||
}
|
||
else
|
||
{
|
||
border_width = 0;
|
||
}
|
||
|
||
cairo_translate (cr, x, y);
|
||
|
||
if (gtk_style_context_has_class (context, GTK_STYLE_CLASS_GRIP))
|
||
{
|
||
GtkJunctionSides sides = gtk_style_context_get_junction_sides (context);
|
||
|
||
/* order is important here for when too many (or too few) sides are set */
|
||
if ((sides & GTK_JUNCTION_CORNER_BOTTOMRIGHT) == GTK_JUNCTION_CORNER_BOTTOMRIGHT)
|
||
type = GTK_CSS_IMAGE_BUILTIN_GRIP_BOTTOMRIGHT;
|
||
else if ((sides & GTK_JUNCTION_CORNER_TOPRIGHT) == GTK_JUNCTION_CORNER_TOPRIGHT)
|
||
type = GTK_CSS_IMAGE_BUILTIN_GRIP_TOPRIGHT;
|
||
else if ((sides & GTK_JUNCTION_CORNER_BOTTOMLEFT) == GTK_JUNCTION_CORNER_BOTTOMLEFT)
|
||
type = GTK_CSS_IMAGE_BUILTIN_GRIP_BOTTOMLEFT;
|
||
else if ((sides & GTK_JUNCTION_CORNER_TOPLEFT) == GTK_JUNCTION_CORNER_TOPLEFT)
|
||
type = GTK_CSS_IMAGE_BUILTIN_GRIP_TOPLEFT;
|
||
else if (sides & GTK_JUNCTION_RIGHT)
|
||
type = GTK_CSS_IMAGE_BUILTIN_GRIP_RIGHT;
|
||
else if (sides & GTK_JUNCTION_BOTTOM)
|
||
type = GTK_CSS_IMAGE_BUILTIN_GRIP_BOTTOM;
|
||
else if (sides & GTK_JUNCTION_TOP)
|
||
type = GTK_CSS_IMAGE_BUILTIN_GRIP_TOP;
|
||
else if (sides & GTK_JUNCTION_LEFT)
|
||
type = GTK_CSS_IMAGE_BUILTIN_GRIP_LEFT;
|
||
else
|
||
type = GTK_CSS_IMAGE_BUILTIN_GRIP_BOTTOMRIGHT;
|
||
}
|
||
else if (gtk_style_context_has_class (context, GTK_STYLE_CLASS_PANE_SEPARATOR))
|
||
{
|
||
type = GTK_CSS_IMAGE_BUILTIN_PANE_SEPARATOR;
|
||
}
|
||
else
|
||
{
|
||
type = GTK_CSS_IMAGE_BUILTIN_HANDLE;
|
||
}
|
||
|
||
gtk_css_image_builtin_draw (_gtk_css_image_value_get_image (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_ICON_SOURCE)),
|
||
cr,
|
||
width, height,
|
||
type,
|
||
_gtk_css_rgba_value_get_rgba (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_COLOR)),
|
||
_gtk_css_rgba_value_get_rgba (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_BACKGROUND_COLOR)),
|
||
_gtk_css_rgba_value_get_rgba (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_BORDER_TOP_COLOR)),
|
||
border_width);
|
||
}
|
||
|
||
/**
|
||
* gtk_render_handle:
|
||
* @context: a #GtkStyleContext
|
||
* @cr: a #cairo_t
|
||
* @x: X origin of the rectangle
|
||
* @y: Y origin of the rectangle
|
||
* @width: rectangle width
|
||
* @height: rectangle height
|
||
*
|
||
* Renders a handle (as in #GtkHandleBox, #GtkPaned and
|
||
* #GtkWindow’s resize grip), in the rectangle
|
||
* determined by @x, @y, @width, @height.
|
||
*
|
||
* Handles rendered for the paned and grip classes:
|
||
*
|
||
* ![](handles.png)
|
||
*
|
||
* Since: 3.0
|
||
**/
|
||
void
|
||
gtk_render_handle (GtkStyleContext *context,
|
||
cairo_t *cr,
|
||
gdouble x,
|
||
gdouble y,
|
||
gdouble width,
|
||
gdouble height)
|
||
{
|
||
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
|
||
g_return_if_fail (cr != NULL);
|
||
|
||
if (width <= 0 || height <= 0)
|
||
return;
|
||
|
||
cairo_save (cr);
|
||
cairo_new_path (cr);
|
||
|
||
gtk_do_render_handle (context, cr, x, y, width, height);
|
||
|
||
cairo_restore (cr);
|
||
}
|
||
|
||
void
|
||
gtk_render_paint_spinner (cairo_t *cr,
|
||
gdouble radius,
|
||
gdouble progress)
|
||
{
|
||
guint num_steps, step;
|
||
gdouble half;
|
||
gint i;
|
||
|
||
num_steps = 12;
|
||
|
||
if (progress >= 0)
|
||
step = (guint) (progress * num_steps);
|
||
else
|
||
step = 0;
|
||
|
||
cairo_save (cr);
|
||
|
||
cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
|
||
cairo_set_line_width (cr, 2.0);
|
||
|
||
half = num_steps / 2;
|
||
|
||
for (i = 0; i < num_steps; i++)
|
||
{
|
||
gint inset = 0.7 * radius;
|
||
|
||
/* transparency is a function of time and intial value */
|
||
gdouble t = 1.0 - (gdouble) ((i + step) % num_steps) / num_steps;
|
||
gdouble xscale = - sin (i * G_PI / half);
|
||
gdouble yscale = - cos (i * G_PI / half);
|
||
|
||
cairo_push_group (cr);
|
||
|
||
cairo_move_to (cr,
|
||
(radius - inset) * xscale,
|
||
(radius - inset) * yscale);
|
||
cairo_line_to (cr,
|
||
radius * xscale,
|
||
radius * yscale);
|
||
|
||
cairo_stroke (cr);
|
||
|
||
cairo_pop_group_to_source (cr);
|
||
cairo_paint_with_alpha (cr, t);
|
||
}
|
||
|
||
cairo_restore (cr);
|
||
}
|
||
|
||
static void
|
||
gtk_do_render_activity (GtkStyleContext *context,
|
||
cairo_t *cr,
|
||
gdouble x,
|
||
gdouble y,
|
||
gdouble width,
|
||
gdouble height)
|
||
{
|
||
GtkBorderStyle border_style;
|
||
gint border_width;
|
||
|
||
if (render_icon_image (context, cr, x, y, width, height))
|
||
return;
|
||
|
||
border_style = _gtk_css_border_style_value_get (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_BORDER_TOP_STYLE));
|
||
if (border_style == GTK_BORDER_STYLE_SOLID)
|
||
{
|
||
GtkBorder border;
|
||
|
||
border.top = _gtk_css_number_value_get (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_BORDER_TOP_WIDTH), 100);
|
||
border.right = _gtk_css_number_value_get (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_BORDER_RIGHT_WIDTH), 100);
|
||
border.bottom = _gtk_css_number_value_get (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_BORDER_BOTTOM_WIDTH), 100);
|
||
border.left = _gtk_css_number_value_get (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_BORDER_LEFT_WIDTH), 100);
|
||
|
||
border_width = MIN (MIN (border.top, border.bottom),
|
||
MIN (border.left, border.right));
|
||
}
|
||
else
|
||
{
|
||
border_width = 0;
|
||
}
|
||
|
||
cairo_translate (cr, x, y);
|
||
|
||
gtk_css_image_builtin_draw (_gtk_css_image_value_get_image (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_ICON_SOURCE)),
|
||
cr,
|
||
width, height,
|
||
GTK_CSS_IMAGE_BUILTIN_SPINNER,
|
||
_gtk_css_rgba_value_get_rgba (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_COLOR)),
|
||
_gtk_css_rgba_value_get_rgba (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_BACKGROUND_COLOR)),
|
||
_gtk_css_rgba_value_get_rgba (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_BORDER_TOP_COLOR)),
|
||
border_width);
|
||
}
|
||
|
||
/**
|
||
* gtk_render_activity:
|
||
* @context: a #GtkStyleContext
|
||
* @cr: a #cairo_t
|
||
* @x: X origin of the rectangle
|
||
* @y: Y origin of the rectangle
|
||
* @width: rectangle width
|
||
* @height: rectangle height
|
||
*
|
||
* Renders an activity indicator (such as in #GtkSpinner).
|
||
* The state %GTK_STATE_FLAG_CHECKED determines whether there is
|
||
* activity going on.
|
||
*
|
||
* Since: 3.0
|
||
**/
|
||
void
|
||
gtk_render_activity (GtkStyleContext *context,
|
||
cairo_t *cr,
|
||
gdouble x,
|
||
gdouble y,
|
||
gdouble width,
|
||
gdouble height)
|
||
{
|
||
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
|
||
g_return_if_fail (cr != NULL);
|
||
|
||
if (width <= 0 || height <= 0)
|
||
return;
|
||
|
||
cairo_save (cr);
|
||
cairo_new_path (cr);
|
||
|
||
gtk_do_render_activity (context, cr, x, y, width, height);
|
||
|
||
cairo_restore (cr);
|
||
}
|
||
|
||
static void
|
||
colorshift_source (cairo_t *cr,
|
||
gdouble shift)
|
||
{
|
||
cairo_pattern_t *source;
|
||
|
||
cairo_save (cr);
|
||
cairo_paint (cr);
|
||
|
||
source = cairo_pattern_reference (cairo_get_source (cr));
|
||
|
||
cairo_set_source_rgb (cr, shift, shift, shift);
|
||
cairo_set_operator (cr, CAIRO_OPERATOR_COLOR_DODGE);
|
||
|
||
cairo_mask (cr, source);
|
||
|
||
cairo_pattern_destroy (source);
|
||
cairo_restore (cr);
|
||
}
|
||
|
||
static GdkPixbuf *
|
||
scale_or_ref (GdkPixbuf *src,
|
||
gint width,
|
||
gint height)
|
||
{
|
||
if (width == gdk_pixbuf_get_width (src) &&
|
||
height == gdk_pixbuf_get_height (src))
|
||
return g_object_ref (src);
|
||
else
|
||
return gdk_pixbuf_scale_simple (src,
|
||
width, height,
|
||
GDK_INTERP_BILINEAR);
|
||
}
|
||
|
||
static GdkPixbuf *
|
||
gtk_do_render_icon_pixbuf (GtkStyleContext *context,
|
||
const GtkIconSource *source,
|
||
GtkIconSize size)
|
||
{
|
||
GdkPixbuf *scaled;
|
||
GdkPixbuf *stated;
|
||
GdkPixbuf *base_pixbuf;
|
||
GtkStateFlags state;
|
||
gint width = 1;
|
||
gint height = 1;
|
||
cairo_t *cr;
|
||
cairo_surface_t *surface;
|
||
gboolean wildcarded;
|
||
GtkCssImageEffect image_effect;
|
||
|
||
G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
|
||
base_pixbuf = gtk_icon_source_get_pixbuf (source);
|
||
G_GNUC_END_IGNORE_DEPRECATIONS;
|
||
|
||
state = gtk_style_context_get_state (context);
|
||
|
||
g_return_val_if_fail (base_pixbuf != NULL, NULL);
|
||
|
||
if (size != (GtkIconSize) -1 &&
|
||
!gtk_icon_size_lookup (size, &width, &height))
|
||
{
|
||
g_warning (G_STRLOC ": invalid icon size '%d'", size);
|
||
return NULL;
|
||
}
|
||
|
||
/* If the size was wildcarded, and we're allowed to scale, then scale; otherwise,
|
||
* leave it alone.
|
||
*/
|
||
G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
|
||
wildcarded = gtk_icon_source_get_size_wildcarded (source);
|
||
G_GNUC_END_IGNORE_DEPRECATIONS;
|
||
if (size != (GtkIconSize) -1 && wildcarded)
|
||
scaled = scale_or_ref (base_pixbuf, width, height);
|
||
else
|
||
scaled = g_object_ref (base_pixbuf);
|
||
|
||
/* If the state was wildcarded, then generate a state. */
|
||
G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
|
||
wildcarded = gtk_icon_source_get_state_wildcarded (source);
|
||
G_GNUC_END_IGNORE_DEPRECATIONS;
|
||
|
||
if (!wildcarded)
|
||
return scaled;
|
||
|
||
image_effect = _gtk_css_image_effect_value_get
|
||
(_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_GTK_IMAGE_EFFECT));
|
||
|
||
if (image_effect == GTK_CSS_IMAGE_EFFECT_DIM ||
|
||
state & GTK_STATE_FLAG_INSENSITIVE)
|
||
{
|
||
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
|
||
gdk_pixbuf_get_width (scaled),
|
||
gdk_pixbuf_get_height (scaled));
|
||
cr = cairo_create (surface);
|
||
gdk_cairo_set_source_pixbuf (cr, scaled, 0, 0);
|
||
cairo_paint_with_alpha (cr, 0.5);
|
||
|
||
cairo_destroy (cr);
|
||
|
||
g_object_unref (scaled);
|
||
stated = gdk_pixbuf_get_from_surface (surface, 0, 0,
|
||
cairo_image_surface_get_width (surface),
|
||
cairo_image_surface_get_height (surface));
|
||
cairo_surface_destroy (surface);
|
||
}
|
||
else if (image_effect == GTK_CSS_IMAGE_EFFECT_HIGHLIGHT ||
|
||
state & GTK_STATE_FLAG_PRELIGHT)
|
||
{
|
||
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
|
||
gdk_pixbuf_get_width (scaled),
|
||
gdk_pixbuf_get_height (scaled));
|
||
|
||
cr = cairo_create (surface);
|
||
gdk_cairo_set_source_pixbuf (cr, scaled, 0, 0);
|
||
colorshift_source (cr, 0.10);
|
||
|
||
cairo_destroy (cr);
|
||
|
||
g_object_unref (scaled);
|
||
stated = gdk_pixbuf_get_from_surface (surface, 0, 0,
|
||
cairo_image_surface_get_width (surface),
|
||
cairo_image_surface_get_height (surface));
|
||
cairo_surface_destroy (surface);
|
||
}
|
||
else
|
||
stated = scaled;
|
||
|
||
return stated;
|
||
}
|
||
|
||
/**
|
||
* gtk_render_icon_pixbuf:
|
||
* @context: a #GtkStyleContext
|
||
* @source: the #GtkIconSource specifying the icon to render
|
||
* @size: (type int): the size to render the icon at. A size of (GtkIconSize) -1
|
||
* means render at the size of the source and don’t scale.
|
||
*
|
||
* Renders the icon specified by @source at the given @size, returning the result
|
||
* in a pixbuf.
|
||
*
|
||
* Returns: (transfer full): a newly-created #GdkPixbuf containing the rendered icon
|
||
*
|
||
* Since: 3.0
|
||
*
|
||
* Deprecated: 3.10: Use gtk_icon_theme_load_icon() instead.
|
||
**/
|
||
GdkPixbuf *
|
||
gtk_render_icon_pixbuf (GtkStyleContext *context,
|
||
const GtkIconSource *source,
|
||
GtkIconSize size)
|
||
{
|
||
g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), NULL);
|
||
g_return_val_if_fail (size > GTK_ICON_SIZE_INVALID || size == -1, NULL);
|
||
g_return_val_if_fail (source != NULL, NULL);
|
||
|
||
return gtk_do_render_icon_pixbuf (context, source, size);
|
||
}
|
||
|
||
static void
|
||
gtk_do_render_icon (GtkStyleContext *context,
|
||
cairo_t *cr,
|
||
GdkPixbuf *pixbuf,
|
||
gdouble x,
|
||
gdouble y)
|
||
{
|
||
cairo_save (cr);
|
||
|
||
gdk_cairo_set_source_pixbuf (cr, pixbuf, x, y);
|
||
|
||
_gtk_css_shadows_value_paint_icon (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_ICON_SHADOW), cr);
|
||
|
||
cairo_paint (cr);
|
||
|
||
cairo_restore (cr);
|
||
}
|
||
|
||
/**
|
||
* gtk_render_icon:
|
||
* @context: a #GtkStyleContext
|
||
* @cr: a #cairo_t
|
||
* @pixbuf: a #GdkPixbuf containing the icon to draw
|
||
* @x: X position for the @pixbuf
|
||
* @y: Y position for the @pixbuf
|
||
*
|
||
* Renders the icon in @pixbuf at the specified @x and @y coordinates.
|
||
*
|
||
* Since: 3.2
|
||
**/
|
||
void
|
||
gtk_render_icon (GtkStyleContext *context,
|
||
cairo_t *cr,
|
||
GdkPixbuf *pixbuf,
|
||
gdouble x,
|
||
gdouble y)
|
||
{
|
||
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
|
||
g_return_if_fail (cr != NULL);
|
||
|
||
cairo_save (cr);
|
||
cairo_new_path (cr);
|
||
|
||
gtk_do_render_icon (context, cr, pixbuf, x, y);
|
||
|
||
cairo_restore (cr);
|
||
}
|
||
|
||
static void
|
||
gtk_do_render_icon_surface (GtkStyleContext *context,
|
||
cairo_t *cr,
|
||
cairo_surface_t *surface,
|
||
gdouble x,
|
||
gdouble y)
|
||
{
|
||
cairo_save (cr);
|
||
|
||
cairo_set_source_surface (cr, surface, x, y);
|
||
|
||
_gtk_css_shadows_value_paint_icon (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_ICON_SHADOW), cr);
|
||
|
||
cairo_paint (cr);
|
||
|
||
cairo_restore (cr);
|
||
}
|
||
|
||
/**
|
||
* gtk_render_icon_surface:
|
||
* @context: a #GtkStyleContext
|
||
* @cr: a #cairo_t
|
||
* @surface: a #cairo_surface_t containing the icon to draw
|
||
* @x: X position for the @icon
|
||
* @y: Y position for the @incon
|
||
*
|
||
* Renders the icon in @surface at the specified @x and @y coordinates.
|
||
*
|
||
* Since: 3.10
|
||
**/
|
||
void
|
||
gtk_render_icon_surface (GtkStyleContext *context,
|
||
cairo_t *cr,
|
||
cairo_surface_t *surface,
|
||
gdouble x,
|
||
gdouble y)
|
||
{
|
||
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
|
||
g_return_if_fail (cr != NULL);
|
||
|
||
cairo_save (cr);
|
||
cairo_new_path (cr);
|
||
|
||
gtk_do_render_icon_surface (context, cr, surface, x, y);
|
||
|
||
cairo_restore (cr);
|
||
}
|
||
|
||
/*
|
||
* gtk_render_content_path:
|
||
* @context: style context to get style information from
|
||
* @cr: cairo context to add path to
|
||
* @x: x coordinate of CSS box
|
||
* @y: y coordinate of CSS box
|
||
* @width: width of CSS box
|
||
* @height: height of CSS box
|
||
*
|
||
* Adds the path of the content box to @cr for a given border box.
|
||
* This function respects rounded corners.
|
||
*
|
||
* This is useful if you are drawing content that is supposed to
|
||
* fill the whole content area, like the color buttons in
|
||
* #GtkColorChooserDialog.
|
||
**/
|
||
void
|
||
gtk_render_content_path (GtkStyleContext *context,
|
||
cairo_t *cr,
|
||
double x,
|
||
double y,
|
||
double width,
|
||
double height)
|
||
{
|
||
GtkRoundedBox box;
|
||
|
||
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
|
||
g_return_if_fail (cr != NULL);
|
||
|
||
_gtk_rounded_box_init_rect (&box, x, y, width, height);
|
||
_gtk_rounded_box_apply_border_radius_for_style (&box, gtk_style_context_lookup_style (context), 0);
|
||
|
||
_gtk_rounded_box_shrink (&box,
|
||
_gtk_css_number_value_get (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_BORDER_TOP_WIDTH), 100)
|
||
+ _gtk_css_number_value_get (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_PADDING_TOP), 100),
|
||
_gtk_css_number_value_get (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_BORDER_RIGHT_WIDTH), 100)
|
||
+ _gtk_css_number_value_get (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_PADDING_RIGHT), 100),
|
||
_gtk_css_number_value_get (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_BORDER_BOTTOM_WIDTH), 100)
|
||
+ _gtk_css_number_value_get (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_PADDING_BOTTOM), 100),
|
||
_gtk_css_number_value_get (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_BORDER_LEFT_WIDTH), 100)
|
||
+ _gtk_css_number_value_get (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_PADDING_LEFT), 100));
|
||
|
||
_gtk_rounded_box_path (&box, cr);
|
||
}
|