mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2025-01-07 03:10:07 +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.
366 lines
9.1 KiB
C
366 lines
9.1 KiB
C
#include "gtkshaderstack.h"
|
|
|
|
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
|
|
|
|
struct _GtkShaderStack
|
|
{
|
|
GtkWidget parent_instance;
|
|
|
|
GskGLShader *shader;
|
|
GPtrArray *children;
|
|
int current;
|
|
int next;
|
|
gboolean backwards;
|
|
|
|
guint tick_id;
|
|
float time;
|
|
float duration;
|
|
gint64 start_time;
|
|
};
|
|
|
|
struct _GtkShaderStackClass
|
|
{
|
|
GtkWidgetClass parent_class;
|
|
};
|
|
|
|
|
|
enum {
|
|
PROP_DURATION = 1,
|
|
NUM_PROPERTIES
|
|
};
|
|
|
|
static GParamSpec *properties[NUM_PROPERTIES] = { NULL };
|
|
|
|
G_DEFINE_TYPE (GtkShaderStack, gtk_shader_stack, GTK_TYPE_WIDGET)
|
|
|
|
static void
|
|
gtk_shader_stack_finalize (GObject *object)
|
|
{
|
|
GtkShaderStack *self = GTK_SHADER_STACK (object);
|
|
|
|
g_object_unref (self->shader);
|
|
|
|
G_OBJECT_CLASS (gtk_shader_stack_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
update_child_visible (GtkShaderStack *self)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < self->children->len; i++)
|
|
{
|
|
GtkWidget *child = g_ptr_array_index (self->children, i);
|
|
|
|
gtk_widget_set_child_visible (child,
|
|
i == self->current || i == self->next);
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
transition_cb (GtkWidget *widget,
|
|
GdkFrameClock *clock,
|
|
gpointer unused)
|
|
{
|
|
GtkShaderStack *self = GTK_SHADER_STACK (widget);
|
|
gint64 frame_time;
|
|
|
|
frame_time = gdk_frame_clock_get_frame_time (clock);
|
|
|
|
if (self->start_time == 0)
|
|
self->start_time = frame_time;
|
|
|
|
self->time = (frame_time - self->start_time) / (float)G_USEC_PER_SEC;
|
|
|
|
gtk_widget_queue_draw (widget);
|
|
|
|
if (self->time >= self->duration)
|
|
{
|
|
self->current = self->next;
|
|
self->next = -1;
|
|
|
|
update_child_visible (self);
|
|
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
else
|
|
return G_SOURCE_CONTINUE;
|
|
}
|
|
|
|
static void
|
|
start_transition (GtkShaderStack *self)
|
|
{
|
|
self->start_time = 0;
|
|
self->tick_id = gtk_widget_add_tick_callback (GTK_WIDGET (self),
|
|
transition_cb,
|
|
NULL, NULL);
|
|
}
|
|
|
|
static void
|
|
stop_transition (GtkShaderStack *self)
|
|
{
|
|
if (self->tick_id != 0)
|
|
{
|
|
gtk_widget_remove_tick_callback (GTK_WIDGET (self), self->tick_id);
|
|
self->tick_id = 0;
|
|
}
|
|
|
|
if (self->next != -1)
|
|
self->current = self->next;
|
|
self->next = -1;
|
|
|
|
update_child_visible (self);
|
|
}
|
|
|
|
static void
|
|
gtk_shader_stack_dispose (GObject *object)
|
|
{
|
|
GtkShaderStack *self = GTK_SHADER_STACK (object);
|
|
|
|
stop_transition (self);
|
|
|
|
g_clear_pointer (&self->children, g_ptr_array_unref);
|
|
|
|
G_OBJECT_CLASS (gtk_shader_stack_parent_class)->dispose (object);
|
|
}
|
|
|
|
void
|
|
gtk_shader_stack_transition (GtkShaderStack *self,
|
|
gboolean forward)
|
|
{
|
|
stop_transition (self);
|
|
|
|
self->backwards = !forward;
|
|
if (self->backwards)
|
|
self->next = (self->current + self->children->len - 1) % self->children->len;
|
|
else
|
|
self->next = (self->current + 1) % self->children->len;
|
|
|
|
update_child_visible (self);
|
|
|
|
start_transition (self);
|
|
}
|
|
|
|
static void
|
|
gtk_shader_stack_init (GtkShaderStack *self)
|
|
{
|
|
self->children = g_ptr_array_new_with_free_func ((GDestroyNotify)gtk_widget_unparent);
|
|
self->current = -1;
|
|
self->next = -1;
|
|
self->backwards = FALSE;
|
|
self->duration = 1.0;
|
|
}
|
|
|
|
static void
|
|
gtk_shader_stack_measure (GtkWidget *widget,
|
|
GtkOrientation orientation,
|
|
int for_size,
|
|
int *minimum,
|
|
int *natural,
|
|
int *minimum_baseline,
|
|
int *natural_baseline)
|
|
{
|
|
GtkShaderStack *self = GTK_SHADER_STACK (widget);
|
|
int i;
|
|
|
|
*minimum = 0;
|
|
*natural = 0;
|
|
|
|
for (i = 0; i < self->children->len; i++)
|
|
{
|
|
GtkWidget *child = g_ptr_array_index (self->children, i);
|
|
int child_min, child_nat;
|
|
|
|
if (gtk_widget_get_visible (child))
|
|
{
|
|
gtk_widget_measure (child, orientation, for_size, &child_min, &child_nat, NULL, NULL);
|
|
|
|
*minimum = MAX (*minimum, child_min);
|
|
*natural = MAX (*natural, child_nat);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
gtk_shader_stack_size_allocate (GtkWidget *widget,
|
|
int width,
|
|
int height,
|
|
int baseline)
|
|
{
|
|
GtkShaderStack *self = GTK_SHADER_STACK (widget);
|
|
GtkAllocation child_allocation;
|
|
GtkWidget *child;
|
|
int i;
|
|
|
|
child_allocation.x = 0;
|
|
child_allocation.y = 0;
|
|
child_allocation.width = width;
|
|
child_allocation.height = height;
|
|
|
|
for (i = 0; i < self->children->len; i++)
|
|
{
|
|
child = g_ptr_array_index (self->children, i);
|
|
if (gtk_widget_get_visible (child))
|
|
gtk_widget_size_allocate (child, &child_allocation, -1);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gtk_shader_stack_snapshot (GtkWidget *widget,
|
|
GtkSnapshot *snapshot)
|
|
{
|
|
GtkShaderStack *self = GTK_SHADER_STACK (widget);
|
|
int width, height;
|
|
GtkWidget *current, *next;
|
|
|
|
width = gtk_widget_get_width (widget);
|
|
height = gtk_widget_get_height (widget);
|
|
|
|
current = g_ptr_array_index (self->children, self->current);
|
|
|
|
if (self->next == -1)
|
|
{
|
|
gtk_widget_snapshot_child (widget, current, snapshot);
|
|
}
|
|
else
|
|
{
|
|
GtkNative *native = gtk_widget_get_native (widget);
|
|
GskRenderer *renderer = gtk_native_get_renderer (native);
|
|
float progress;
|
|
|
|
next = g_ptr_array_index (self->children, self->next);
|
|
|
|
progress = self->time / self->duration;
|
|
|
|
if (self->backwards)
|
|
{
|
|
GtkWidget *tmp = next;
|
|
next = current;
|
|
current = tmp;
|
|
progress = 1. - progress;
|
|
}
|
|
|
|
if (gsk_gl_shader_compile (self->shader, renderer, NULL))
|
|
{
|
|
gtk_snapshot_push_gl_shader (snapshot,
|
|
self->shader,
|
|
&GRAPHENE_RECT_INIT(0, 0, width, height),
|
|
gsk_gl_shader_format_args (self->shader,
|
|
"progress", progress,
|
|
NULL));
|
|
|
|
gtk_widget_snapshot_child (widget, current, snapshot);
|
|
gtk_snapshot_gl_shader_pop_texture (snapshot); /* current child */
|
|
gtk_widget_snapshot_child (widget, next, snapshot);
|
|
gtk_snapshot_gl_shader_pop_texture (snapshot); /* next child */
|
|
gtk_snapshot_pop (snapshot);
|
|
}
|
|
else
|
|
{
|
|
/* Non-shader fallback */
|
|
gtk_widget_snapshot_child (widget, current, snapshot);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
gtk_shader_stack_get_property (GObject *object,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
GtkShaderStack *self = GTK_SHADER_STACK (object);
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_DURATION:
|
|
g_value_set_float (value, self->duration);
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gtk_shader_stack_set_property (GObject *object,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
GtkShaderStack *self = GTK_SHADER_STACK (object);
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_DURATION:
|
|
self->duration = g_value_get_float (value);
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gtk_shader_stack_class_init (GtkShaderStackClass *class)
|
|
{
|
|
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
|
|
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
|
|
|
object_class->finalize = gtk_shader_stack_finalize;
|
|
object_class->dispose = gtk_shader_stack_dispose;
|
|
object_class->get_property = gtk_shader_stack_get_property;
|
|
object_class->set_property = gtk_shader_stack_set_property;
|
|
|
|
widget_class->snapshot = gtk_shader_stack_snapshot;
|
|
widget_class->measure = gtk_shader_stack_measure;
|
|
widget_class->size_allocate = gtk_shader_stack_size_allocate;
|
|
|
|
properties[PROP_DURATION] =
|
|
g_param_spec_float ("duration", "Duration", "Duration",
|
|
0.1, 3.0, 1.0,
|
|
G_PARAM_READWRITE);
|
|
|
|
g_object_class_install_properties (object_class, NUM_PROPERTIES, properties);
|
|
}
|
|
|
|
GtkWidget *
|
|
gtk_shader_stack_new (void)
|
|
{
|
|
return g_object_new (GTK_TYPE_SHADER_STACK, NULL);
|
|
}
|
|
|
|
void
|
|
gtk_shader_stack_set_shader (GtkShaderStack *self,
|
|
GskGLShader *shader)
|
|
{
|
|
g_set_object (&self->shader, shader);
|
|
}
|
|
|
|
void
|
|
gtk_shader_stack_add_child (GtkShaderStack *self,
|
|
GtkWidget *child)
|
|
{
|
|
g_ptr_array_add (self->children, child);
|
|
gtk_widget_set_parent (child, GTK_WIDGET (self));
|
|
gtk_widget_queue_resize (GTK_WIDGET (self));
|
|
|
|
if (self->current == -1)
|
|
self->current = 0;
|
|
else
|
|
gtk_widget_set_child_visible (child, FALSE);
|
|
}
|
|
|
|
void
|
|
gtk_shader_stack_set_active (GtkShaderStack *self,
|
|
int index)
|
|
{
|
|
stop_transition (self);
|
|
self->current = MIN (index, self->children->len);
|
|
update_child_visible (self);
|
|
}
|
|
|
|
G_GNUC_END_IGNORE_DEPRECATIONS
|