mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2024-09-19 21:40:22 +00:00
32625381fa
The new renderers don't support them due to the required complexity of integrating them with Vulkan and the assumptions those nodes make about the renderer (the GL renderer exports its internal APIs into the GLShader). There haven't been any complaints that I'm aware of since 4.14 was released where the default renderer does not support the nodes, so usage in public seems to be close to nonexistant. The 2 uses I know of were workarounds about missing features in GTK that have stopped since GTK now supports them: 1. GStreamer used in to do premultiplication when the old GL renderer did not do so in hardware but on the CPU. 2. Adwaita used it for masking before the mask node wa added in 4.10.
367 lines
12 KiB
C
367 lines
12 KiB
C
/* OpenGL/Transitions and Effects
|
|
* #Keywords: OpenGL, shader, effect
|
|
*
|
|
* Create transitions between pages using a custom fragment shader.
|
|
*
|
|
* The example transitions here are taken from gl-transitions.com, and you
|
|
* can edit the shader code itself on the last page of the stack.
|
|
*
|
|
* The transitions work with arbitrary content. We use images, shaders
|
|
* GL areas and plain old widgets to demonstrate this.
|
|
*
|
|
* The demo also shows some over-the-top effects like wobbly widgets,
|
|
* and animated backgrounds.
|
|
*/
|
|
|
|
#include <math.h>
|
|
#include <gtk/gtk.h>
|
|
#include "gtkshaderstack.h"
|
|
#include "gtkshaderbin.h"
|
|
#include "gtkshadertoy.h"
|
|
#include "gskshaderpaintable.h"
|
|
|
|
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
|
|
|
|
static GtkWidget *demo_window = NULL;
|
|
|
|
static void
|
|
close_window (GtkWidget *widget)
|
|
{
|
|
/* Reset the state */
|
|
demo_window = NULL;
|
|
}
|
|
|
|
static void
|
|
text_changed (GtkTextBuffer *buffer,
|
|
GtkWidget *button)
|
|
{
|
|
gtk_widget_set_visible (button, TRUE);
|
|
}
|
|
|
|
static void
|
|
apply_text (GtkWidget *button,
|
|
GtkTextBuffer *buffer)
|
|
{
|
|
GtkWidget *stack;
|
|
GskGLShader *shader;
|
|
GtkTextIter start, end;
|
|
char *text;
|
|
|
|
stack = g_object_get_data (G_OBJECT (button), "the-stack");
|
|
|
|
gtk_text_buffer_get_bounds (buffer, &start, &end);
|
|
text = gtk_text_buffer_get_text (buffer, &start, &end, TRUE);
|
|
|
|
GBytes *bytes = g_bytes_new_take (text, strlen (text));
|
|
shader = gsk_gl_shader_new_from_bytes (bytes);
|
|
|
|
gtk_shader_stack_set_shader (GTK_SHADER_STACK (stack), shader);
|
|
|
|
g_object_unref (shader);
|
|
g_bytes_unref (bytes);
|
|
|
|
gtk_widget_set_visible (button, FALSE);
|
|
}
|
|
|
|
static void
|
|
go_back (GtkWidget *button,
|
|
GtkWidget *stack)
|
|
{
|
|
gtk_shader_stack_transition (GTK_SHADER_STACK (stack), FALSE);
|
|
}
|
|
|
|
static void
|
|
go_forward (GtkWidget *button,
|
|
GtkWidget *stack)
|
|
{
|
|
gtk_shader_stack_transition (GTK_SHADER_STACK (stack), TRUE);
|
|
}
|
|
|
|
static void
|
|
clicked_cb (GtkGestureClick *gesture,
|
|
guint n_pressed,
|
|
double x,
|
|
double y,
|
|
gpointer data)
|
|
{
|
|
gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
|
|
}
|
|
|
|
static GtkWidget *
|
|
ripple_bin_new (void)
|
|
{
|
|
GtkWidget *bin = gtk_shader_bin_new ();
|
|
static GskGLShader *shader = NULL;
|
|
|
|
if (shader == NULL)
|
|
shader = gsk_gl_shader_new_from_resource ("/gltransition/ripple.glsl");
|
|
|
|
gtk_shader_bin_add_shader (GTK_SHADER_BIN (bin), shader, GTK_STATE_FLAG_PRELIGHT, GTK_STATE_FLAG_PRELIGHT, 20);
|
|
|
|
return bin;
|
|
}
|
|
|
|
static GtkWidget *
|
|
new_shadertoy (const char *path)
|
|
{
|
|
GBytes *shader;
|
|
GtkWidget *toy;
|
|
|
|
toy = gtk_shadertoy_new ();
|
|
shader = g_resources_lookup_data (path, 0, NULL);
|
|
gtk_shadertoy_set_image_shader (GTK_SHADERTOY (toy),
|
|
g_bytes_get_data (shader, NULL));
|
|
g_bytes_unref (shader);
|
|
|
|
return toy;
|
|
}
|
|
|
|
static gboolean
|
|
update_paintable (GtkWidget *widget,
|
|
GdkFrameClock *frame_clock,
|
|
gpointer user_data)
|
|
{
|
|
GskShaderPaintable *paintable;
|
|
gint64 frame_time;
|
|
|
|
paintable = GSK_SHADER_PAINTABLE (gtk_picture_get_paintable (GTK_PICTURE (widget)));
|
|
frame_time = gdk_frame_clock_get_frame_time (frame_clock);
|
|
gsk_shader_paintable_update_time (paintable, 0, frame_time);
|
|
|
|
return G_SOURCE_CONTINUE;
|
|
}
|
|
|
|
static GtkWidget *
|
|
make_shader_stack (const char *name,
|
|
const char *resource_path,
|
|
int active_child,
|
|
GtkWidget *scale)
|
|
{
|
|
GtkWidget *stack, *child, *widget, *vbox, *hbox, *bin;
|
|
GtkWidget *label, *button, *tv;
|
|
GskGLShader *shader;
|
|
GObjectClass *class;
|
|
GParamSpecFloat *pspec;
|
|
GtkAdjustment *adjustment;
|
|
GtkTextBuffer *buffer;
|
|
GBytes *bytes;
|
|
GtkEventController *controller;
|
|
GdkPaintable *paintable;
|
|
|
|
stack = gtk_shader_stack_new ();
|
|
shader = gsk_gl_shader_new_from_resource (resource_path);
|
|
gtk_shader_stack_set_shader (GTK_SHADER_STACK (stack), shader);
|
|
g_object_unref (shader);
|
|
|
|
child = gtk_picture_new_for_resource ("/css_blendmodes/ducky.png");
|
|
gtk_picture_set_can_shrink (GTK_PICTURE (child), TRUE);
|
|
gtk_shader_stack_add_child (GTK_SHADER_STACK (stack), child);
|
|
|
|
shader = gsk_gl_shader_new_from_resource ("/gltransition/cogs2.glsl");
|
|
paintable = gsk_shader_paintable_new (shader, NULL);
|
|
|
|
child = gtk_picture_new_for_paintable (paintable);
|
|
gtk_widget_add_tick_callback (child, update_paintable, NULL, NULL);
|
|
gtk_picture_set_can_shrink (GTK_PICTURE (child), TRUE);
|
|
gtk_shader_stack_add_child (GTK_SHADER_STACK (stack), child);
|
|
|
|
child = gtk_picture_new_for_resource ("/transparent/portland-rose.jpg");
|
|
gtk_picture_set_can_shrink (GTK_PICTURE (child), TRUE);
|
|
gtk_shader_stack_add_child (GTK_SHADER_STACK (stack), child);
|
|
|
|
child = new_shadertoy ("/shadertoy/neon.glsl");
|
|
gtk_shader_stack_add_child (GTK_SHADER_STACK (stack), child);
|
|
|
|
child = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
|
|
|
|
class = g_type_class_ref (GTK_TYPE_SHADER_STACK);
|
|
pspec = G_PARAM_SPEC_FLOAT (g_object_class_find_property (class, "duration"));
|
|
|
|
adjustment = gtk_range_get_adjustment (GTK_RANGE (scale));
|
|
if (gtk_adjustment_get_lower (adjustment) == 0.0 &&
|
|
gtk_adjustment_get_upper (adjustment) == 0.0)
|
|
{
|
|
gtk_adjustment_configure (adjustment,
|
|
pspec->default_value,
|
|
pspec->minimum,
|
|
pspec->maximum,
|
|
0.1, 0.5, 0);
|
|
}
|
|
|
|
g_type_class_unref (class);
|
|
|
|
g_object_bind_property (adjustment, "value",
|
|
stack, "duration",
|
|
G_BINDING_DEFAULT);
|
|
|
|
widget = gtk_scrolled_window_new ();
|
|
gtk_scrolled_window_set_has_frame (GTK_SCROLLED_WINDOW (widget), TRUE);
|
|
gtk_widget_set_hexpand (widget, TRUE);
|
|
gtk_widget_set_vexpand (widget, TRUE);
|
|
|
|
controller = GTK_EVENT_CONTROLLER (gtk_gesture_click_new ());
|
|
gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (controller), 0);
|
|
g_signal_connect (controller, "released", G_CALLBACK (clicked_cb), NULL);
|
|
gtk_event_controller_set_propagation_phase (controller, GTK_PHASE_BUBBLE);
|
|
gtk_widget_add_controller (GTK_WIDGET (widget), controller);
|
|
|
|
tv = gtk_text_view_new ();
|
|
gtk_text_view_set_left_margin (GTK_TEXT_VIEW (tv), 4);
|
|
gtk_text_view_set_right_margin (GTK_TEXT_VIEW (tv), 4);
|
|
gtk_text_view_set_top_margin (GTK_TEXT_VIEW (tv), 4);
|
|
gtk_text_view_set_bottom_margin (GTK_TEXT_VIEW (tv), 4);
|
|
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
|
|
bytes = g_resources_lookup_data (resource_path, 0, NULL);
|
|
gtk_text_buffer_set_text (buffer,
|
|
(const char *)g_bytes_get_data (bytes, NULL),
|
|
g_bytes_get_size (bytes));
|
|
g_bytes_unref (bytes);
|
|
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (widget), tv);
|
|
|
|
gtk_box_append (GTK_BOX (child), widget);
|
|
|
|
gtk_shader_stack_add_child (GTK_SHADER_STACK (stack), child);
|
|
|
|
gtk_shader_stack_set_active (GTK_SHADER_STACK (stack), active_child);
|
|
|
|
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
|
|
|
|
widget = gtk_center_box_new ();
|
|
label = gtk_label_new (name);
|
|
gtk_widget_add_css_class (label, "title-4");
|
|
gtk_widget_set_size_request (label, -1, 26);
|
|
gtk_center_box_set_center_widget (GTK_CENTER_BOX (widget), label);
|
|
|
|
button = gtk_button_new_from_icon_name ("view-refresh-symbolic");
|
|
g_signal_connect (buffer, "changed", G_CALLBACK (text_changed), button);
|
|
g_object_set_data (G_OBJECT (button), "the-stack", stack);
|
|
g_signal_connect (button, "clicked", G_CALLBACK (apply_text), buffer);
|
|
gtk_widget_set_halign (button, GTK_ALIGN_CENTER);
|
|
gtk_widget_set_valign (button, GTK_ALIGN_CENTER);
|
|
gtk_widget_add_css_class (button, "small");
|
|
gtk_widget_set_visible (button, FALSE);
|
|
gtk_center_box_set_end_widget (GTK_CENTER_BOX (widget), button);
|
|
|
|
gtk_box_append (GTK_BOX (vbox), widget);
|
|
|
|
GtkWidget *bin2 = ripple_bin_new ();
|
|
gtk_shader_bin_set_child (GTK_SHADER_BIN (bin2), stack);
|
|
|
|
gtk_box_append (GTK_BOX (vbox), bin2);
|
|
|
|
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
|
|
gtk_widget_set_halign (hbox, GTK_ALIGN_CENTER);
|
|
|
|
gtk_box_append (GTK_BOX (vbox), hbox);
|
|
|
|
button = gtk_button_new_from_icon_name ("go-previous-symbolic");
|
|
g_signal_connect (button, "clicked", G_CALLBACK (go_back), stack);
|
|
bin = ripple_bin_new ();
|
|
gtk_shader_bin_set_child (GTK_SHADER_BIN (bin), button);
|
|
gtk_box_append (GTK_BOX (hbox), bin);
|
|
|
|
button = gtk_button_new_from_icon_name ("go-next-symbolic");
|
|
g_signal_connect (button, "clicked", G_CALLBACK (go_forward), stack);
|
|
bin = ripple_bin_new ();
|
|
gtk_shader_bin_set_child (GTK_SHADER_BIN (bin), button);
|
|
gtk_box_append (GTK_BOX (hbox), bin);
|
|
|
|
return vbox;
|
|
}
|
|
|
|
static void
|
|
remove_provider (gpointer data)
|
|
{
|
|
GtkStyleProvider *provider = GTK_STYLE_PROVIDER (data);
|
|
|
|
gtk_style_context_remove_provider_for_display (gdk_display_get_default (), provider);
|
|
g_object_unref (provider);
|
|
}
|
|
|
|
static GtkWidget *
|
|
create_gltransition_window (GtkWidget *do_widget)
|
|
{
|
|
GtkWidget *window, *headerbar, *scale, *outer_grid, *grid, *background;
|
|
GdkPaintable *paintable;
|
|
GtkCssProvider *provider;
|
|
|
|
window = gtk_window_new ();
|
|
gtk_window_set_display (GTK_WINDOW (window), gtk_widget_get_display (do_widget));
|
|
gtk_window_set_title (GTK_WINDOW (window), "Transitions and Effects");
|
|
headerbar = gtk_header_bar_new ();
|
|
scale = gtk_scale_new (GTK_ORIENTATION_HORIZONTAL, NULL);
|
|
gtk_scale_set_draw_value (GTK_SCALE (scale), FALSE);
|
|
gtk_widget_set_size_request (scale, 100, -1);
|
|
gtk_widget_set_tooltip_text (scale, "Transition duration");
|
|
gtk_header_bar_pack_end (GTK_HEADER_BAR (headerbar), scale);
|
|
gtk_window_set_titlebar (GTK_WINDOW (window), headerbar);
|
|
gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
|
|
g_signal_connect (window, "destroy", G_CALLBACK (close_window), NULL);
|
|
|
|
outer_grid = gtk_grid_new ();
|
|
gtk_window_set_child (GTK_WINDOW (window), outer_grid);
|
|
|
|
paintable = gsk_shader_paintable_new (gsk_gl_shader_new_from_resource ("/gltransition/background.glsl"), NULL);
|
|
background = gtk_picture_new_for_paintable (paintable);
|
|
gtk_widget_add_tick_callback (background, update_paintable, NULL, NULL);
|
|
|
|
gtk_grid_attach (GTK_GRID (outer_grid),
|
|
background,
|
|
0, 0, 1, 1);
|
|
|
|
grid = gtk_grid_new ();
|
|
gtk_grid_attach (GTK_GRID (outer_grid),
|
|
grid,
|
|
0, 0, 1, 1);
|
|
|
|
gtk_widget_set_halign (grid, GTK_ALIGN_CENTER);
|
|
gtk_widget_set_valign (grid, GTK_ALIGN_CENTER);
|
|
gtk_widget_set_margin_start (grid, 12);
|
|
gtk_widget_set_margin_end (grid, 12);
|
|
gtk_widget_set_margin_top (grid, 12);
|
|
gtk_widget_set_margin_bottom (grid, 12);
|
|
gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
|
|
gtk_grid_set_column_spacing (GTK_GRID (grid), 6);
|
|
gtk_grid_set_row_homogeneous (GTK_GRID (grid), TRUE);
|
|
gtk_grid_set_column_homogeneous (GTK_GRID (grid), TRUE);
|
|
|
|
gtk_grid_attach (GTK_GRID (grid),
|
|
make_shader_stack ("Wind", "/gltransition/wind.glsl", 0, scale),
|
|
0, 0, 1, 1);
|
|
gtk_grid_attach (GTK_GRID (grid),
|
|
make_shader_stack ("Radial", "/gltransition/radial.glsl", 1, scale),
|
|
1, 0, 1, 1);
|
|
gtk_grid_attach (GTK_GRID (grid),
|
|
make_shader_stack ("Crosswarp", "/gltransition/crosswarp.glsl", 2, scale),
|
|
0, 1, 1, 1);
|
|
gtk_grid_attach (GTK_GRID (grid),
|
|
make_shader_stack ("Kaleidoscope", "/gltransition/kaleidoscope.glsl", 3, scale),
|
|
1, 1, 1, 1);
|
|
|
|
provider = gtk_css_provider_new ();
|
|
gtk_css_provider_load_from_string (provider, "button.small { padding: 0; }");
|
|
gtk_style_context_add_provider_for_display (gdk_display_get_default (),
|
|
GTK_STYLE_PROVIDER (provider),
|
|
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
|
|
|
|
g_object_set_data_full (G_OBJECT (window), "provider", provider, remove_provider);
|
|
|
|
return window;
|
|
}
|
|
|
|
GtkWidget *
|
|
do_gltransition (GtkWidget *do_widget)
|
|
{
|
|
if (!demo_window)
|
|
demo_window = create_gltransition_window (do_widget);
|
|
|
|
if (!gtk_widget_get_visible (demo_window))
|
|
gtk_widget_set_visible (demo_window, TRUE);
|
|
else
|
|
gtk_window_destroy (GTK_WINDOW (demo_window));
|
|
|
|
return demo_window;
|
|
}
|
|
|
|
G_GNUC_END_IGNORE_DEPRECATIONS
|