diff options
Diffstat (limited to 'demos/gtk-demo/gtkshaderstack.c')
-rw-r--r-- | demos/gtk-demo/gtkshaderstack.c | 356 |
1 files changed, 356 insertions, 0 deletions
diff --git a/demos/gtk-demo/gtkshaderstack.c b/demos/gtk-demo/gtkshaderstack.c new file mode 100644 index 0000000000..0f9a7b0c20 --- /dev/null +++ b/demos/gtk-demo/gtkshaderstack.c @@ -0,0 +1,356 @@ +#include "gtkshaderstack.h" + +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; + } + + 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); +} + +static void +clicked_cb (GtkGestureClick *gesture, + guint n_pressed, + double x, + double y, + gpointer data) +{ + GtkShaderStack *self = GTK_SHADER_STACK (data); + guint button; + + button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture)); + + stop_transition (self); + + self->backwards = button == GDK_BUTTON_SECONDARY; + 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) +{ + GtkEventController *controller; + + 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; + + 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), self); + gtk_event_controller_set_propagation_phase (controller, GTK_PHASE_BUBBLE); + gtk_widget_add_controller (GTK_WIDGET (self), controller); +} + +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 + { + 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; + } + + gtk_snapshot_push_glshader (snapshot, + self->shader, + &GRAPHENE_RECT_INIT(0, 0, width, height), + 2, + "progress", &progress, + NULL); + gtk_widget_snapshot_child (widget, next, snapshot); + gtk_snapshot_pop (snapshot); /* Fallback */ + gtk_widget_snapshot_child (widget, current, snapshot); + gtk_snapshot_pop (snapshot); /* current child */ + gtk_widget_snapshot_child (widget, next, snapshot); + gtk_snapshot_pop (snapshot); /* next child */ + } +} + +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); +} |