diff options
author | Matthias Clasen <mclasen@redhat.com> | 2019-02-21 19:27:41 -0500 |
---|---|---|
committer | Matthias Clasen <mclasen@redhat.com> | 2019-02-21 19:31:41 -0500 |
commit | b70cd64d6dea4c02cf53489e41f6ff6567cf1b6a (patch) | |
tree | 40b6eab42a6d8c76ec1289227c48ee04fa4ff805 | |
parent | 6711aa2a4eee7eaa16f15e963b1e508811688350 (diff) | |
download | gtk+-b70cd64d6dea4c02cf53489e41f6ff6567cf1b6a.tar.gz |
gtk-demo: Move the blur demo code here
We want to remove this from the GtkOverlay code.
-rw-r--r-- | demos/gtk-demo/bluroverlay.c | 482 | ||||
-rw-r--r-- | demos/gtk-demo/bluroverlay.h | 65 | ||||
-rw-r--r-- | demos/gtk-demo/demo.gresource.xml | 1 | ||||
-rw-r--r-- | demos/gtk-demo/meson.build | 2 | ||||
-rw-r--r-- | demos/gtk-demo/transparent.c | 9 |
5 files changed, 553 insertions, 6 deletions
diff --git a/demos/gtk-demo/bluroverlay.c b/demos/gtk-demo/bluroverlay.c new file mode 100644 index 0000000000..f6e268c6e6 --- /dev/null +++ b/demos/gtk-demo/bluroverlay.c @@ -0,0 +1,482 @@ +/* + * bluroverlay.c + * This file is part of gtk + * + * Copyright (C) 2011 - Ignacio Casal Quinteiro, Mike Krüger + * + * 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 "bluroverlay.h" + +/* + * This is a cut-down copy of gtkoverlay.c with a custom snapshot + * function that support a limited form of blur-under. + */ +typedef struct _BlurOverlayChild BlurOverlayChild; + +struct _BlurOverlayChild +{ + double blur; +}; + +enum { + GET_CHILD_POSITION, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; +static GQuark child_data_quark = 0; + +G_DEFINE_TYPE (BlurOverlay, blur_overlay, GTK_TYPE_BIN) + +static void +blur_overlay_set_overlay_child (GtkWidget *widget, + BlurOverlayChild *child_data) +{ + g_object_set_qdata_full (G_OBJECT (widget), child_data_quark, child_data, g_free); +} + +static BlurOverlayChild * +blur_overlay_get_overlay_child (GtkWidget *widget) +{ + return (BlurOverlayChild *) g_object_get_qdata (G_OBJECT (widget), child_data_quark); +} + +static void +blur_overlay_measure (GtkWidget *widget, + GtkOrientation orientation, + int for_size, + int *minimum, + int *natural, + int *minimum_baseline, + int *natural_baseline) +{ + GtkWidget *child; + + for (child = gtk_widget_get_first_child (widget); + child != NULL; + child = gtk_widget_get_next_sibling (child)) + { + int child_min, child_nat, child_min_baseline, child_nat_baseline; + + gtk_widget_measure (child, + orientation, + for_size, + &child_min, &child_nat, + &child_min_baseline, &child_nat_baseline); + + *minimum = MAX (*minimum, child_min); + *natural = MAX (*natural, child_nat); + if (child_min_baseline > -1) + *minimum_baseline = MAX (*minimum_baseline, child_min_baseline); + if (child_nat_baseline > -1) + *natural_baseline = MAX (*natural_baseline, child_nat_baseline); + } +} + +static void +blur_overlay_compute_child_allocation (BlurOverlay *overlay, + GtkWidget *widget, + BlurOverlayChild *child, + GtkAllocation *widget_allocation) +{ + GtkAllocation allocation; + gboolean result; + + g_signal_emit (overlay, signals[GET_CHILD_POSITION], + 0, widget, &allocation, &result); + + widget_allocation->x = allocation.x; + widget_allocation->y = allocation.y; + widget_allocation->width = allocation.width; + widget_allocation->height = allocation.height; +} + +static GtkAlign +effective_align (GtkAlign align, + GtkTextDirection direction) +{ + switch (align) + { + case GTK_ALIGN_START: + return direction == GTK_TEXT_DIR_RTL ? GTK_ALIGN_END : GTK_ALIGN_START; + case GTK_ALIGN_END: + return direction == GTK_TEXT_DIR_RTL ? GTK_ALIGN_START : GTK_ALIGN_END; + case GTK_ALIGN_FILL: + case GTK_ALIGN_CENTER: + case GTK_ALIGN_BASELINE: + default: + return align; + } +} + +static void +blur_overlay_child_update_style_classes (BlurOverlay *overlay, + GtkWidget *child, + GtkAllocation *child_allocation) +{ + int width, height; + GtkAlign valign, halign; + gboolean is_left, is_right, is_top, is_bottom; + gboolean has_left, has_right, has_top, has_bottom; + GtkStyleContext *context; + + context = gtk_widget_get_style_context (child); + has_left = gtk_style_context_has_class (context, GTK_STYLE_CLASS_LEFT); + has_right = gtk_style_context_has_class (context, GTK_STYLE_CLASS_RIGHT); + has_top = gtk_style_context_has_class (context, GTK_STYLE_CLASS_TOP); + has_bottom = gtk_style_context_has_class (context, GTK_STYLE_CLASS_BOTTOM); + + is_left = is_right = is_top = is_bottom = FALSE; + + width = gtk_widget_get_width (GTK_WIDGET (overlay)); + height = gtk_widget_get_height (GTK_WIDGET (overlay)); + + halign = effective_align (gtk_widget_get_halign (child), + gtk_widget_get_direction (child)); + + if (halign == GTK_ALIGN_START) + is_left = (child_allocation->x == 0); + else if (halign == GTK_ALIGN_END) + is_right = (child_allocation->x + child_allocation->width == width); + + valign = gtk_widget_get_valign (child); + + if (valign == GTK_ALIGN_START) + is_top = (child_allocation->y == 0); + else if (valign == GTK_ALIGN_END) + is_bottom = (child_allocation->y + child_allocation->height == height); + + if (has_left && !is_left) + gtk_style_context_remove_class (context, GTK_STYLE_CLASS_LEFT); + else if (!has_left && is_left) + gtk_style_context_add_class (context, GTK_STYLE_CLASS_LEFT); + + if (has_right && !is_right) + gtk_style_context_remove_class (context, GTK_STYLE_CLASS_RIGHT); + else if (!has_right && is_right) + gtk_style_context_add_class (context, GTK_STYLE_CLASS_RIGHT); + + if (has_top && !is_top) + gtk_style_context_remove_class (context, GTK_STYLE_CLASS_TOP); + else if (!has_top && is_top) + gtk_style_context_add_class (context, GTK_STYLE_CLASS_TOP); + + if (has_bottom && !is_bottom) + gtk_style_context_remove_class (context, GTK_STYLE_CLASS_BOTTOM); + else if (!has_bottom && is_bottom) + gtk_style_context_add_class (context, GTK_STYLE_CLASS_BOTTOM); +} + +static void +blur_overlay_child_allocate (BlurOverlay *overlay, + GtkWidget *widget, + BlurOverlayChild *child) +{ + GtkAllocation child_allocation; + + if (!gtk_widget_get_visible (widget)) + return; + + blur_overlay_compute_child_allocation (overlay, widget, child, &child_allocation); + + blur_overlay_child_update_style_classes (overlay, widget, &child_allocation); + gtk_widget_size_allocate (widget, &child_allocation, -1); +} + +static void +blur_overlay_size_allocate (GtkWidget *widget, + int width, + int height, + int baseline) +{ + BlurOverlay *overlay = BLUR_OVERLAY (widget); + GtkWidget *child; + GtkWidget *main_widget; + + main_widget = gtk_bin_get_child (GTK_BIN (overlay)); + if (main_widget && gtk_widget_get_visible (main_widget)) + gtk_widget_size_allocate (main_widget, + &(GtkAllocation) { + 0, 0, + width, height + }, -1); + + for (child = gtk_widget_get_first_child (widget); + child != NULL; + child = gtk_widget_get_next_sibling (child)) + { + if (child != main_widget) + { + BlurOverlayChild *child_data = blur_overlay_get_overlay_child (child); + blur_overlay_child_allocate (overlay, child, child_data); + } + } +} + +static gboolean +blur_overlay_get_child_position (BlurOverlay *overlay, + GtkWidget *widget, + GtkAllocation *alloc) +{ + GtkRequisition min, req; + GtkAlign halign; + GtkTextDirection direction; + int width, height; + + gtk_widget_get_preferred_size (widget, &min, &req); + width = gtk_widget_get_width (GTK_WIDGET (overlay)); + height = gtk_widget_get_height (GTK_WIDGET (overlay)); + + alloc->x = 0; + alloc->width = MAX (min.width, MIN (width, req.width)); + + direction = gtk_widget_get_direction (widget); + + halign = gtk_widget_get_halign (widget); + switch (effective_align (halign, direction)) + { + case GTK_ALIGN_START: + /* nothing to do */ + break; + case GTK_ALIGN_FILL: + alloc->width = MAX (alloc->width, width); + break; + case GTK_ALIGN_CENTER: + alloc->x += width / 2 - alloc->width / 2; + break; + case GTK_ALIGN_END: + alloc->x += width - alloc->width; + break; + case GTK_ALIGN_BASELINE: + default: + g_assert_not_reached (); + break; + } + + alloc->y = 0; + alloc->height = MAX (min.height, MIN (height, req.height)); + + switch (gtk_widget_get_valign (widget)) + { + case GTK_ALIGN_START: + /* nothing to do */ + break; + case GTK_ALIGN_FILL: + alloc->height = MAX (alloc->height, height); + break; + case GTK_ALIGN_CENTER: + alloc->y += height / 2 - alloc->height / 2; + break; + case GTK_ALIGN_END: + alloc->y += height - alloc->height; + break; + case GTK_ALIGN_BASELINE: + default: + g_assert_not_reached (); + break; + } + + return TRUE; +} + +static void +blur_overlay_add (GtkContainer *container, + GtkWidget *widget) +{ + BlurOverlay *overlay = BLUR_OVERLAY (container); + gtk_widget_insert_after (widget, GTK_WIDGET (container), NULL); + overlay->main_widget = widget; +} + +static void +blur_overlay_remove (GtkContainer *container, + GtkWidget *widget) +{ + BlurOverlay *overlay = BLUR_OVERLAY (container); + gtk_widget_unparent (widget); + if (overlay->main_widget == widget) + overlay->main_widget = NULL; +} + +static void +blur_overlay_forall (GtkContainer *overlay, + GtkCallback callback, + gpointer callback_data) +{ + GtkWidget *child; + + child = gtk_widget_get_first_child (GTK_WIDGET (overlay)); + while (child != NULL) + { + GtkWidget *next = gtk_widget_get_next_sibling (child); + + (* callback) (child, callback_data); + + child = next; + } +} + +static void +blur_overlay_snapshot (GtkWidget *widget, + GtkSnapshot *snapshot) +{ + GtkWidget *main_widget; + GskRenderNode *main_widget_node = NULL; + GtkWidget *child; + GtkAllocation main_alloc; + cairo_region_t *clip = NULL; + int i; + + main_widget = BLUR_OVERLAY (widget)->main_widget; + gtk_widget_get_allocation (widget, &main_alloc); + + for (child = gtk_widget_get_first_child (widget); + child != NULL; + child = gtk_widget_get_next_sibling (child)) + { + BlurOverlayChild *child_info = blur_overlay_get_overlay_child (child); + double blur = 0; + if (child_info) + blur = child_info->blur; + + if (blur > 0) + { + GtkAllocation alloc; + graphene_rect_t bounds; + + if (main_widget_node == NULL) + { + GtkSnapshot *child_snapshot; + + child_snapshot = gtk_snapshot_new (); + gtk_widget_snapshot_child (widget, main_widget, child_snapshot); + main_widget_node = gtk_snapshot_free_to_node (child_snapshot); + } + + gtk_widget_get_allocation (child, &alloc); + graphene_rect_init (&bounds, alloc.x, alloc.y, alloc.width, alloc.height); + gtk_snapshot_push_blur (snapshot, blur); + gtk_snapshot_push_clip (snapshot, &bounds); + gtk_snapshot_append_node (snapshot, main_widget_node); + gtk_snapshot_pop (snapshot); + gtk_snapshot_pop (snapshot); + + if (clip == NULL) + { + cairo_rectangle_int_t rect; + rect.x = rect.y = 0; + rect.width = main_alloc.width; + rect.height = main_alloc.height; + clip = cairo_region_create_rectangle (&rect); + } + cairo_region_subtract_rectangle (clip, (cairo_rectangle_int_t *)&alloc); + } + } + + if (clip == NULL) + { + for (child = gtk_widget_get_first_child (widget); + child != NULL; + child = gtk_widget_get_next_sibling (child)) + { + gtk_widget_snapshot_child (widget, child, snapshot); + } + return; + } + + for (i = 0; i < cairo_region_num_rectangles (clip); i++) + { + cairo_rectangle_int_t rect; + graphene_rect_t bounds; + + cairo_region_get_rectangle (clip, i, &rect); + graphene_rect_init (&bounds, rect.x, rect.y, rect.width, rect.height); + gtk_snapshot_push_clip (snapshot, &bounds); + gtk_snapshot_append_node (snapshot, main_widget_node); + gtk_snapshot_pop (snapshot); + } + + cairo_region_destroy (clip); + + for (child = gtk_widget_get_first_child (widget); + child != NULL; + child = gtk_widget_get_next_sibling (child)) + { + if (child != main_widget) + gtk_widget_snapshot_child (widget, child, snapshot); + } + + gsk_render_node_unref (main_widget_node); +} + +static void +blur_overlay_class_init (BlurOverlayClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass); + + widget_class->measure = blur_overlay_measure; + widget_class->size_allocate = blur_overlay_size_allocate; + widget_class->snapshot = blur_overlay_snapshot; + + container_class->add = blur_overlay_add; + container_class->remove = blur_overlay_remove; + container_class->forall = blur_overlay_forall; + + klass->get_child_position = blur_overlay_get_child_position; + + signals[GET_CHILD_POSITION] = + g_signal_new ("get-child-position", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (BlurOverlayClass, get_child_position), + g_signal_accumulator_true_handled, NULL, + NULL, + G_TYPE_BOOLEAN, 2, + GTK_TYPE_WIDGET, + GDK_TYPE_RECTANGLE | G_SIGNAL_TYPE_STATIC_SCOPE); + + child_data_quark = g_quark_from_static_string ("gtk-overlay-child-data"); + + gtk_widget_class_set_css_name (widget_class, "overlay"); +} + +static void +blur_overlay_init (BlurOverlay *overlay) +{ + gtk_widget_set_has_surface (GTK_WIDGET (overlay), FALSE); +} + +GtkWidget * +blur_overlay_new (void) +{ + return g_object_new (BLUR_TYPE_OVERLAY, NULL); +} + +void +blur_overlay_add_overlay (BlurOverlay *overlay, + GtkWidget *widget, + double blur) +{ + BlurOverlayChild *child = g_new0 (BlurOverlayChild, 1); + + gtk_widget_insert_before (widget, GTK_WIDGET (overlay), NULL); + + child->blur = blur; + + blur_overlay_set_overlay_child (widget, child); +} diff --git a/demos/gtk-demo/bluroverlay.h b/demos/gtk-demo/bluroverlay.h new file mode 100644 index 0000000000..e3fc27c7a8 --- /dev/null +++ b/demos/gtk-demo/bluroverlay.h @@ -0,0 +1,65 @@ +/* + * bluroverlay.h + * This file is part of gtk + * + * Copyright (C) 2011 - Ignacio Casal Quinteiro, Mike Krüger + * + * 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/>. + */ + +#ifndef __BLUR_OVERLAY_H__ +#define __BLUR_OVERLAY_H__ + +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +#define BLUR_TYPE_OVERLAY (blur_overlay_get_type ()) +#define BLUR_OVERLAY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), BLUR_TYPE_OVERLAY, BlurOverlay)) +#define BLUR_OVERLAY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), BLUR_TYPE_OVERLAY, BlurOverlayClass)) +#define BLUR_IS_OVERLAY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), BLUR_TYPE_OVERLAY)) +#define BLUR_IS_OVERLAY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), BLUR_TYPE_OVERLAY)) +#define BLUR_OVERLAY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), BLUR_TYPE_OVERLAY, BlurOverlayClass)) + +typedef struct _BlurOverlay BlurOverlay; +typedef struct _BlurOverlayClass BlurOverlayClass; + +struct _BlurOverlay +{ + GtkBin parent_instance; + + GtkWidget *main_widget; +}; + +struct _BlurOverlayClass +{ + GtkBinClass parent_class; + + gboolean (*get_child_position) (BlurOverlay *overlay, + GtkWidget *widget, + GtkAllocation *allocation); +}; + +GDK_AVAILABLE_IN_ALL +GType blur_overlay_get_type (void) G_GNUC_CONST; +GDK_AVAILABLE_IN_ALL +GtkWidget *blur_overlay_new (void); +GDK_AVAILABLE_IN_ALL +void blur_overlay_add_overlay (BlurOverlay *overlay, + GtkWidget *widget, + double blur); + +G_END_DECLS + +#endif /* __BLUR_OVERLAY_H__ */ diff --git a/demos/gtk-demo/demo.gresource.xml b/demos/gtk-demo/demo.gresource.xml index 1294f73f41..0a49548a48 100644 --- a/demos/gtk-demo/demo.gresource.xml +++ b/demos/gtk-demo/demo.gresource.xml @@ -247,6 +247,7 @@ </gresource> <gresource prefix="/transparent"> <file>portland-rose.jpg</file> + <file>bluroverlay.c</file> </gresource> <gresource prefix="/markup"> <file>markup.txt</file> diff --git a/demos/gtk-demo/meson.build b/demos/gtk-demo/meson.build index 7116a411e2..83d0176250 100644 --- a/demos/gtk-demo/meson.build +++ b/demos/gtk-demo/meson.build @@ -75,7 +75,7 @@ demos = files([ gtkdemo_deps = [ libgtk_dep, ] -extra_demo_sources = files(['main.c', 'gtkfishbowl.c', 'fontplane.c', 'gtkgears.c', 'puzzlepiece.c']) +extra_demo_sources = files(['main.c', 'gtkfishbowl.c', 'fontplane.c', 'gtkgears.c', 'puzzlepiece.c', 'bluroverlay.c']) if harfbuzz_dep.found() and pangoft_dep.found() demos += files('font_features.c') diff --git a/demos/gtk-demo/transparent.c b/demos/gtk-demo/transparent.c index 6d5e80750b..9f1e989856 100644 --- a/demos/gtk-demo/transparent.c +++ b/demos/gtk-demo/transparent.c @@ -4,6 +4,7 @@ */ #include <gtk/gtk.h> +#include "bluroverlay.h" GtkWidget * do_transparent (GtkWidget *do_widget) @@ -27,7 +28,7 @@ do_transparent (GtkWidget *do_widget) gtk_window_set_title (GTK_WINDOW (window), "Transparency"); - overlay = gtk_overlay_new (); + overlay = blur_overlay_new (); gtk_container_add (GTK_CONTAINER (window), overlay); button = gtk_button_new_with_label ("Don't click this button!"); @@ -38,8 +39,7 @@ do_transparent (GtkWidget *do_widget) gtk_widget_set_halign (button, GTK_ALIGN_FILL); gtk_widget_set_valign (button, GTK_ALIGN_START); - gtk_overlay_add_overlay (GTK_OVERLAY (overlay), button); - gtk_container_child_set (GTK_CONTAINER (overlay), button, "blur", 5.0, NULL); + blur_overlay_add_overlay (BLUR_OVERLAY (overlay), button, 5.0); button = gtk_button_new_with_label ("Maybe this one?"); label = gtk_bin_get_child (GTK_BIN (button)); @@ -49,8 +49,7 @@ do_transparent (GtkWidget *do_widget) gtk_widget_set_halign (button, GTK_ALIGN_FILL); gtk_widget_set_valign (button, GTK_ALIGN_END); - gtk_overlay_add_overlay (GTK_OVERLAY (overlay), button); - gtk_container_child_set (GTK_CONTAINER (overlay), button, "blur", 5.0, NULL); + blur_overlay_add_overlay (BLUR_OVERLAY (overlay), button, 5.0); picture = gtk_picture_new_for_resource ("/transparent/portland-rose.jpg"); gtk_container_add (GTK_CONTAINER (overlay), picture); |