/* * 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); }
static void _gtk_theming_background_paint_color (GtkThemingBackground *bg, cairo_t *cr, const GdkRGBA *bg_color, GtkCssValue *background_image) { gint n_values = _gtk_css_array_value_get_n_values (background_image); GtkCssArea clip = _gtk_css_area_value_get (_gtk_css_array_value_get_nth (_gtk_style_context_peek_property (bg->context, GTK_CSS_PROPERTY_BACKGROUND_CLIP), n_values - 1)); _gtk_rounded_box_path (&bg->boxes[clip], cr); gdk_cairo_set_source_rgba (cr, bg_color); cairo_fill (cr); }
static void _gtk_theming_background_paint_color (GtkThemingBackground *bg, cairo_t *cr, GtkCssValue *background_image) { GtkRoundedBox clip_box; gint n_values = _gtk_css_array_value_get_n_values (background_image); GtkCssArea clip = _gtk_css_area_value_get (_gtk_css_array_value_get_nth (_gtk_style_context_peek_property (bg->context, GTK_CSS_PROPERTY_BACKGROUND_CLIP), n_values - 1)); clip_box = bg->border_box; _gtk_theming_background_apply_clip (bg, &clip_box, clip); cairo_save (cr); _gtk_rounded_box_path (&clip_box, cr); cairo_clip (cr); gdk_cairo_set_source_rgba (cr, &bg->bg_color); cairo_paint (cr); cairo_restore (cr); }
static void render_frame_stroke (cairo_t *cr, GtkRoundedBox *border_box, const double border_width[4], GdkRGBA colors[4], guint hidden_side, GtkBorderStyle stroke_style) { gboolean different_colors, different_borders; GtkRoundedBox stroke_box; guint i; different_colors = !gdk_rgba_equal (&colors[0], &colors[1]) || !gdk_rgba_equal (&colors[0], &colors[2]) || !gdk_rgba_equal (&colors[0], &colors[3]); different_borders = border_width[0] != border_width[1] || border_width[0] != border_width[2] || border_width[0] != border_width[3] ; stroke_box = *border_box; _gtk_rounded_box_shrink (&stroke_box, border_width[GTK_CSS_TOP] / 2.0, border_width[GTK_CSS_RIGHT] / 2.0, border_width[GTK_CSS_BOTTOM] / 2.0, border_width[GTK_CSS_LEFT] / 2.0); if (!different_colors && !different_borders && hidden_side == 0) { double length = 0; /* FAST PATH: * Mostly expected to trigger for focus rectangles */ for (i = 0; i < 4; i++) { length += _gtk_rounded_box_guess_length (&stroke_box, i); } _gtk_rounded_box_path (&stroke_box, cr); gdk_cairo_set_source_rgba (cr, &colors[0]); set_stroke_style (cr, border_width[0], stroke_style, length); cairo_stroke (cr); } else { GtkRoundedBox padding_box; padding_box = *border_box; _gtk_rounded_box_shrink (&padding_box, border_width[GTK_CSS_TOP], border_width[GTK_CSS_RIGHT], border_width[GTK_CSS_BOTTOM], border_width[GTK_CSS_LEFT]); for (i = 0; i < 4; i++) { if (hidden_side & (1 << i)) continue; if (border_width[i] == 0) continue; cairo_save (cr); if (i == 0) _gtk_rounded_box_path_top (border_box, &padding_box, cr); else if (i == 1) _gtk_rounded_box_path_right (border_box, &padding_box, cr); else if (i == 2) _gtk_rounded_box_path_bottom (border_box, &padding_box, cr); else if (i == 3) _gtk_rounded_box_path_left (border_box, &padding_box, cr); cairo_clip (cr); _gtk_rounded_box_path_side (&stroke_box, cr, i); gdk_cairo_set_source_rgba (cr, &colors[i]); set_stroke_style (cr, border_width[i], stroke_style, _gtk_rounded_box_guess_length (&stroke_box, i)); cairo_stroke (cr); cairo_restore (cr); } } }
static void render_frame_fill (cairo_t *cr, GtkRoundedBox *border_box, const double border_width[4], GdkRGBA colors[4], guint hidden_side) { GtkRoundedBox padding_box; guint i, j; padding_box = *border_box; _gtk_rounded_box_shrink (&padding_box, border_width[GTK_CSS_TOP], border_width[GTK_CSS_RIGHT], border_width[GTK_CSS_BOTTOM], border_width[GTK_CSS_LEFT]); if (hidden_side == 0 && gdk_rgba_equal (&colors[0], &colors[1]) && gdk_rgba_equal (&colors[0], &colors[2]) && gdk_rgba_equal (&colors[0], &colors[3])) { gdk_cairo_set_source_rgba (cr, &colors[0]); _gtk_rounded_box_path (border_box, cr); _gtk_rounded_box_path (&padding_box, cr); cairo_fill (cr); } else { for (i = 0; i < 4; i++) { if (hidden_side & (1 << i)) continue; for (j = 0; j < 4; j++) { if (hidden_side & (1 << j)) continue; if (i == j || (gdk_rgba_equal (&colors[i], &colors[j]))) { /* We were already painted when i == j */ if (i > j) break; if (j == 0) _gtk_rounded_box_path_top (border_box, &padding_box, cr); else if (j == 1) _gtk_rounded_box_path_right (border_box, &padding_box, cr); else if (j == 2) _gtk_rounded_box_path_bottom (border_box, &padding_box, cr); else if (j == 3) _gtk_rounded_box_path_left (border_box, &padding_box, cr); } } /* We were already painted when i == j */ if (i > j) continue; gdk_cairo_set_source_rgba (cr, &colors[i]); cairo_fill (cr); } } }
static void _gtk_theming_background_paint_layer (GtkThemingBackground *bg, guint idx, cairo_t *cr) { GtkCssRepeatStyle hrepeat, vrepeat; const GtkCssValue *pos, *repeat; GtkCssImage *image; const GtkRoundedBox *origin; double image_width, image_height; double width, height; pos = _gtk_css_array_value_get_nth (_gtk_style_context_peek_property (bg->context, GTK_CSS_PROPERTY_BACKGROUND_POSITION), idx); repeat = _gtk_css_array_value_get_nth (_gtk_style_context_peek_property (bg->context, GTK_CSS_PROPERTY_BACKGROUND_REPEAT), idx); hrepeat = _gtk_css_background_repeat_value_get_x (repeat); vrepeat = _gtk_css_background_repeat_value_get_y (repeat); image = _gtk_css_image_value_get_image ( _gtk_css_array_value_get_nth ( _gtk_style_context_peek_property (bg->context, GTK_CSS_PROPERTY_BACKGROUND_IMAGE), idx)); origin = &bg->boxes[ _gtk_css_area_value_get ( _gtk_css_array_value_get_nth ( _gtk_style_context_peek_property (bg->context, GTK_CSS_PROPERTY_BACKGROUND_ORIGIN), idx))]; width = origin->box.width; height = origin->box.height; if (image == NULL || width <= 0 || height <= 0) return; _gtk_css_bg_size_value_compute_size (_gtk_css_array_value_get_nth (_gtk_style_context_peek_property (bg->context, GTK_CSS_PROPERTY_BACKGROUND_SIZE), idx), image, width, height, &image_width, &image_height); if (image_width <= 0 || image_height <= 0) return; /* optimization */ if (image_width == width) hrepeat = GTK_CSS_REPEAT_STYLE_NO_REPEAT; if (image_height == height) vrepeat = GTK_CSS_REPEAT_STYLE_NO_REPEAT; cairo_save (cr); _gtk_rounded_box_path ( &bg->boxes[ _gtk_css_area_value_get ( _gtk_css_array_value_get_nth ( _gtk_style_context_peek_property (bg->context, GTK_CSS_PROPERTY_BACKGROUND_CLIP), idx))], cr); cairo_clip (cr); cairo_translate (cr, origin->box.x, origin->box.y); if (hrepeat == GTK_CSS_REPEAT_STYLE_NO_REPEAT && vrepeat == GTK_CSS_REPEAT_STYLE_NO_REPEAT) { cairo_translate (cr, _gtk_css_position_value_get_x (pos, width - image_width), _gtk_css_position_value_get_y (pos, height - image_height)); /* shortcut for normal case */ _gtk_css_image_draw (image, cr, image_width, image_height); } else { int surface_width, surface_height; cairo_rectangle_t fill_rect; cairo_surface_t *surface; cairo_t *cr2; /* If ‘background-repeat’ is ‘round’ for one (or both) dimensions, * there is a second step. The UA must scale the image in that * dimension (or both dimensions) so that it fits a whole number of * times in the background positioning area. In the case of the width * (height is analogous): * * If X ≠ 0 is the width of the image after step one and W is the width * of the background positioning area, then the rounded width * X' = W / round(W / X) where round() is a function that returns the * nearest natural number (integer greater than zero). * * If ‘background-repeat’ is ‘round’ for one dimension only and if * ‘background-size’ is ‘auto’ for the other dimension, then there is * a third step: that other dimension is scaled so that the original * aspect ratio is restored. */ if (hrepeat == GTK_CSS_REPEAT_STYLE_ROUND) { double n = round (width / image_width); n = MAX (1, n); if (vrepeat != GTK_CSS_REPEAT_STYLE_ROUND /* && vsize == auto (it is by default) */) image_height *= width / (image_width * n); image_width = width / n; } if (vrepeat == GTK_CSS_REPEAT_STYLE_ROUND) { double n = round (height / image_height); n = MAX (1, n); if (hrepeat != GTK_CSS_REPEAT_STYLE_ROUND /* && hsize == auto (it is by default) */) image_width *= height / (image_height * n); image_height = height / n; } /* if hrepeat or vrepeat is 'space', we create a somewhat larger surface * to store the extra space. */ if (hrepeat == GTK_CSS_REPEAT_STYLE_SPACE) { double n = floor (width / image_width); surface_width = n ? round (width / n) : 0; } else surface_width = round (image_width); if (vrepeat == GTK_CSS_REPEAT_STYLE_SPACE) { double n = floor (height / image_height); surface_height = n ? round (height / n) : 0; } else surface_height = round (image_height); surface = cairo_surface_create_similar (cairo_get_target (cr), CAIRO_CONTENT_COLOR_ALPHA, surface_width, surface_height); cr2 = cairo_create (surface); cairo_translate (cr2, 0.5 * (surface_width - image_width), 0.5 * (surface_height - image_height)); _gtk_css_image_draw (image, cr2, image_width, image_height); cairo_destroy (cr2); cairo_set_source_surface (cr, surface, _gtk_css_position_value_get_x (pos, width - image_width), _gtk_css_position_value_get_y (pos, height - image_height)); cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_REPEAT); cairo_surface_destroy (surface); if (hrepeat == GTK_CSS_REPEAT_STYLE_NO_REPEAT) { fill_rect.x = _gtk_css_position_value_get_x (pos, width - image_width); fill_rect.width = image_width; } else { fill_rect.x = 0; fill_rect.width = width; } if (vrepeat == GTK_CSS_REPEAT_STYLE_NO_REPEAT) { fill_rect.y = _gtk_css_position_value_get_y (pos, height - image_height); fill_rect.height = image_height; } else { fill_rect.y = 0; fill_rect.height = height; } cairo_rectangle (cr, fill_rect.x, fill_rect.y, fill_rect.width, fill_rect.height); cairo_fill (cr); } cairo_restore (cr); }
static gboolean swatch_draw (GtkWidget *widget, cairo_t *cr) { GtkColorSwatch *swatch = (GtkColorSwatch*)widget; GtkThemingBackground background; gdouble width, height; GtkStyleContext *context; GtkStateFlags state; GtkIconTheme *theme; GtkIconInfo *icon_info = NULL; theme = gtk_icon_theme_get_default (); context = gtk_widget_get_style_context (widget); state = gtk_widget_get_state_flags (widget); width = gtk_widget_get_allocated_width (widget); height = gtk_widget_get_allocated_height (widget); cairo_save (cr); gtk_style_context_save (context); gtk_style_context_set_state (context, state); _gtk_theming_background_init_from_context (&background, context, 0, 0, width, height, GTK_JUNCTION_NONE); if (swatch->priv->has_color) { cairo_pattern_t *pattern; cairo_matrix_t matrix; if (swatch->priv->use_alpha) { cairo_save (cr); _gtk_rounded_box_path (&background.padding_box, cr); cairo_clip_preserve (cr); cairo_set_source_rgb (cr, 0.33, 0.33, 0.33); cairo_fill_preserve (cr); pattern = _gtk_color_chooser_get_checkered_pattern (); cairo_matrix_init_scale (&matrix, 0.125, 0.125); cairo_pattern_set_matrix (pattern, &matrix); cairo_set_source_rgb (cr, 0.66, 0.66, 0.66); cairo_mask (cr, pattern); cairo_pattern_destroy (pattern); cairo_restore (cr); background.bg_color = swatch->priv->color; } else { background.bg_color = swatch->priv->color; background.bg_color.alpha = 1.0; } _gtk_theming_background_render (&background, cr); } else _gtk_theming_background_render (&background, cr); gtk_render_frame (context, cr, 0, 0, width, height); if (gtk_widget_has_visible_focus (widget)) { cairo_set_line_width (cr, 2); if (swatch->priv->has_color && INTENSITY (swatch->priv->color.red, swatch->priv->color.green, swatch->priv->color.blue) < 0.5) cairo_set_source_rgba (cr, 1., 1., 1., 0.4); else cairo_set_source_rgba (cr, 0., 0., 0., 0.4); _gtk_rounded_box_shrink (&background.padding_box, 3, 3, 3, 3); _gtk_rounded_box_path (&background.padding_box, cr); cairo_stroke (cr); } if (swatch->priv->icon) { icon_info = gtk_icon_theme_lookup_icon (theme, swatch->priv->icon, 16, GTK_ICON_LOOKUP_GENERIC_FALLBACK | GTK_ICON_LOOKUP_USE_BUILTIN); } else if ((state & GTK_STATE_FLAG_SELECTED) != 0) { GdkRGBA bg, border; GtkBorder border_width; GIcon *gicon; gtk_style_context_add_class (context, "color-active-badge"); _gtk_theming_background_init_from_context (&background, context, (width - 2 * ACTIVE_BADGE_RADIUS) / 2, (height - 2 * ACTIVE_BADGE_RADIUS) / 2, 2 * ACTIVE_BADGE_RADIUS, 2* ACTIVE_BADGE_RADIUS, GTK_JUNCTION_NONE); if (_gtk_theming_background_has_background_image (&background)) { _gtk_theming_background_render (&background, cr); } else { gtk_style_context_get_background_color (context, state, &bg); gtk_style_context_get_border_color (context, state, &border); gtk_style_context_get_border (context, state, &border_width); cairo_new_sub_path (cr); cairo_arc (cr, width / 2, height / 2, ACTIVE_BADGE_RADIUS, 0, 2 * G_PI); cairo_close_path (cr); gdk_cairo_set_source_rgba (cr, &bg); cairo_fill_preserve (cr); gdk_cairo_set_source_rgba (cr, &border); cairo_set_line_width (cr, border_width.left); cairo_stroke (cr); gicon = g_themed_icon_new ("object-select-symbolic"); /* fallback for themes that don't have object-select-symbolic */ g_themed_icon_append_name (G_THEMED_ICON (gicon), "gtk-apply"); icon_info = gtk_icon_theme_lookup_by_gicon (theme, gicon, 16, GTK_ICON_LOOKUP_GENERIC_FALLBACK | GTK_ICON_LOOKUP_USE_BUILTIN); g_object_unref (gicon); } } if (icon_info != NULL) { GdkPixbuf *pixbuf; pixbuf = gtk_icon_info_load_symbolic_for_context (icon_info, context, NULL, NULL); if (pixbuf != NULL) { gtk_render_icon (context, cr, pixbuf, (width - gdk_pixbuf_get_width (pixbuf)) / 2, (height - gdk_pixbuf_get_height (pixbuf)) / 2); g_object_unref (pixbuf); } g_object_unref (icon_info); } cairo_restore (cr); gtk_style_context_restore (context); return FALSE; }