Compare commits

...

16 Commits

Author SHA1 Message Date
Benjamin Otte
4757dad35b testsuite: Add a broken test
This also adds some build magic so all tests that contain "-3d" in them
won't be added to the Cairo renderer.

Of course, this new test is such a test.
2019-03-21 06:47:36 +01:00
Benjamin Otte
4a0b73ce58 transform: Add gsk_transform_from_string()
It uses the CSS parser, so we can also use it for parsing transform nodes.
2019-03-21 06:46:23 +01:00
Benjamin Otte
e25621d46a Move working tests to new directory
These are tests that are working on both GL and Cairo now.

Some tests got black boxes over the areas that aren't easy to compare.
2019-03-19 09:59:04 +01:00
Benjamin Otte
2054facb21 testsuite: Port GL tests to new syntax
Now that the syntax tries to follow the CSS specs more, we need to
update our tests for the changes.
2019-03-19 09:55:47 +01:00
Benjamin Otte
dae0dabb51 testsuite: Add tests for the new node parser
These are just a few crashes I encountered while developing it.
2019-03-19 09:55:47 +01:00
Benjamin Otte
0fa5efa628 rendernode: Redo the rendernode parser
Resurrect the CSS parser from the tokenizer branch and make it handle a
render node syntax.

The syntax is different from the old syntax. This is done so that it
follows CSS specification conventions as close as possible and can
tehrefor be parsed by a CSS parser.
2019-03-19 09:55:47 +01:00
Benjamin Otte
9a875a7515 testsuite: Don't pretend we're a GTest
This is a meson test, not a GTest thing. So:

- Use g_print(), not g_test_message
That makes meson test --verbose print the actual log messsages.

- Don't g_assert() all the time
Instead, run tests through to the end and just return a non-0 exit
status.
2019-03-19 08:59:31 +01:00
Benjamin Otte
e95d171163 csstokenizer: Add gsk_css_token_is_preserved() 2019-03-19 08:59:31 +01:00
Benjamin Otte
aba832559f tokenizer: Pass error arg to read_token()
Instead of an error vfunc, have the tokenizer vfunc take a GError
argument. Note that even when an error is returned, there is still a
token to be read.
2019-03-19 08:59:31 +01:00
Benjamin Otte
7ef0e00659 rendernodeparser: Rebase on top of the CSS tokenizer 2019-03-19 08:59:31 +01:00
Benjamin Otte
8497327b7c css: Add GtkCssTokenizer
This is copied from an old branch of mine.
2019-03-19 08:59:31 +01:00
Benjamin Otte
f4a76d5bcb Add gtk4-node-editor
It's meant to be a little editor for render nodes so we can do testing
with it.
2019-03-19 08:59:31 +01:00
Benjamin Otte
74eadf4181 Add GtkDiffPaintable and use it in the magnifier
This is supposed to show diffs, but works only semi-well because we
fallback on GL for blend nodes, so the diffs are actually with cairo
renderings. Oops.
2019-03-19 08:59:31 +01:00
Benjamin Otte
e0dc257bd3 magnifier: Add renderings with alternative renderers
That's pretty rough around the edges, but it mostly works.
Apart from sizing, screw listbox sizing.
2019-03-19 08:59:31 +01:00
Timm Bäder
2f58d65abd Parse render nodes from text files 2019-03-19 08:58:14 +01:00
Timm Bäder
b76344bfac testsuite: Port gl tests to text-based format 2019-03-19 08:58:14 +01:00
89 changed files with 5870 additions and 80 deletions

View File

@@ -1,3 +1,4 @@
subdir('gtk-demo')
subdir('icon-browser')
subdir('node-editor')
subdir('widget-factory')

28
demos/node-editor/main.c Normal file
View File

@@ -0,0 +1,28 @@
/*
* Copyright © 2019 Benjamin Otte
*
* 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.1 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/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#include "config.h"
#include <node-editor-application.h>
int
main (int argc, char *argv[])
{
return g_application_run (G_APPLICATION (node_editor_application_new ()), argc, argv);
}

View File

@@ -0,0 +1,17 @@
node_editor_sources = [
'main.c',
'node-editor-application.c',
'node-editor-window.c',
]
node_editor_resources = gnome.compile_resources('node_editor_resources',
'node-editor.gresource.xml',
source_dir: '.')
executable('gtk4-node-editor',
node_editor_sources, node_editor_resources,
dependencies: libgtk_dep,
include_directories: confinc,
gui_app: true,
link_args: extra_demo_ldflags,
install: false)

View File

@@ -0,0 +1,114 @@
/*
* Copyright © 2019 Benjamin Otte
*
* 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.1 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/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#include "config.h"
#include "node-editor-application.h"
#include "node-editor-window.h"
struct _NodeEditorApplication
{
GtkApplication parent;
};
struct _NodeEditorApplicationClass
{
GtkApplicationClass parent_class;
};
G_DEFINE_TYPE(NodeEditorApplication, node_editor_application, GTK_TYPE_APPLICATION);
static void
node_editor_application_init (NodeEditorApplication *app)
{
}
static void
quit_activated (GSimpleAction *action,
GVariant *parameter,
gpointer data)
{
g_application_quit (G_APPLICATION (data));
}
static GActionEntry app_entries[] =
{
{ "quit", quit_activated, NULL, NULL, NULL }
};
static void
node_editor_application_startup (GApplication *app)
{
const gchar *quit_accels[2] = { "<Ctrl>Q", NULL };
G_APPLICATION_CLASS (node_editor_application_parent_class)->startup (app);
g_action_map_add_action_entries (G_ACTION_MAP (app),
app_entries, G_N_ELEMENTS (app_entries),
app);
gtk_application_set_accels_for_action (GTK_APPLICATION (app),
"app.quit",
quit_accels);
}
static void
node_editor_application_activate (GApplication *app)
{
NodeEditorWindow *win;
win = node_editor_window_new (NODE_EDITOR_APPLICATION (app));
gtk_window_present (GTK_WINDOW (win));
}
static void
node_editor_application_open (GApplication *app,
GFile **files,
gint n_files,
const gchar *hint)
{
NodeEditorWindow *win;
gint i;
for (i = 0; i < n_files; i++)
{
win = node_editor_window_new (NODE_EDITOR_APPLICATION (app));
node_editor_window_load (win, files[i]);
gtk_window_present (GTK_WINDOW (win));
}
}
static void
node_editor_application_class_init (NodeEditorApplicationClass *class)
{
GApplicationClass *application_class = G_APPLICATION_CLASS (class);
application_class->startup = node_editor_application_startup;
application_class->activate = node_editor_application_activate;
application_class->open = node_editor_application_open;
}
NodeEditorApplication *
node_editor_application_new (void)
{
return g_object_new (NODE_EDITOR_APPLICATION_TYPE,
"application-id", "org.gtk.gtk4.NodeEditor",
"flags", G_APPLICATION_HANDLES_OPEN,
NULL);
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright © 2019 Benjamin Otte
*
* 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.1 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/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __NODE_EDITOR_APPLICATION_H__
#define __NODE_EDITOR_APPLICATION_H__
#include <gtk/gtk.h>
#define NODE_EDITOR_APPLICATION_TYPE (node_editor_application_get_type ())
#define NODE_EDITOR_APPLICATION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NODE_EDITOR_APPLICATION_TYPE, NodeEditorApplication))
typedef struct _NodeEditorApplication NodeEditorApplication;
typedef struct _NodeEditorApplicationClass NodeEditorApplicationClass;
GType node_editor_application_get_type (void);
NodeEditorApplication *node_editor_application_new (void);
#endif /* __NODE_EDITOR_APPLICATION_H__ */

View File

@@ -0,0 +1,408 @@
/*
* Copyright © 2019 Benjamin Otte
*
* 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.1 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/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#include "config.h"
#include "node-editor-window.h"
#include "gsk/gskrendernodeparserprivate.h"
struct _NodeEditorWindow
{
GtkApplicationWindow parent;
guint text_timeout;
GtkWidget *picture;
GtkWidget *text_view;
GtkTextBuffer *text_buffer;
};
struct _NodeEditorWindowClass
{
GtkApplicationWindowClass parent_class;
};
G_DEFINE_TYPE(NodeEditorWindow, node_editor_window, GTK_TYPE_APPLICATION_WINDOW);
static gchar *
get_current_text (GtkTextBuffer *buffer)
{
GtkTextIter start, end;
gtk_text_buffer_get_start_iter (buffer, &start);
gtk_text_buffer_get_end_iter (buffer, &end);
gtk_text_buffer_remove_all_tags (buffer, &start, &end);
return gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
}
static void
update_node (NodeEditorWindow *self)
{
GskRenderNode *node;
GError *error = NULL;
char *text;
GBytes *bytes;
text = get_current_text (self->text_buffer);
bytes = g_bytes_new_take (text, strlen (text));
node = gsk_render_node_deserialize (bytes, &error);
g_bytes_unref (bytes);
if (node)
{
/* XXX: Is this code necessary or can we have API to turn nodes into paintables? */
GtkSnapshot *snapshot;
GdkPaintable *paintable;
graphene_rect_t bounds;
snapshot = gtk_snapshot_new ();
gsk_render_node_get_bounds (node, &bounds);
gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (- bounds.origin.x, - bounds.origin.y));
gtk_snapshot_append_node (snapshot, node);
gsk_render_node_unref (node);
paintable = gtk_snapshot_free_to_paintable (snapshot, &bounds.size);
gtk_picture_set_paintable (GTK_PICTURE (self->picture), paintable);
g_clear_object (&paintable);
}
else
{
gtk_picture_set_paintable (GTK_PICTURE (self->picture), NULL);
g_clear_error (&error);
}
}
static gboolean
update_timeout (gpointer data)
{
NodeEditorWindow *self = data;
self->text_timeout = 0;
update_node (self);
return G_SOURCE_REMOVE;
}
static void
text_changed (GtkTextBuffer *buffer,
NodeEditorWindow *self)
{
if (self->text_timeout != 0)
g_source_remove (self->text_timeout);
self->text_timeout = g_timeout_add (100, update_timeout, self);
}
static gboolean
query_tooltip_cb (GtkWidget *widget,
gint x,
gint y,
gboolean keyboard_tip,
GtkTooltip *tooltip,
NodeEditorWindow *self)
{
GtkTextIter iter;
//GList *l;
if (keyboard_tip)
{
gint offset;
g_object_get (self->text_buffer, "cursor-position", &offset, NULL);
gtk_text_buffer_get_iter_at_offset (self->text_buffer, &iter, offset);
}
else
{
gint bx, by, trailing;
gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (self->text_view), GTK_TEXT_WINDOW_TEXT,
x, y, &bx, &by);
gtk_text_view_get_iter_at_position (GTK_TEXT_VIEW (self->text_view), &iter, &trailing, bx, by);
}
#if 0
for (l = ce->priv->errors; l; l = l->next)
{
CssError *css_error = l->data;
if (gtk_text_iter_in_range (&iter, &css_error->start, &css_error->end))
{
gtk_tooltip_set_text (tooltip, css_error->error->message);
return TRUE;
}
}
#endif
return FALSE;
}
gboolean
node_editor_window_load (NodeEditorWindow *self,
GFile *file)
{
GtkTextIter start, end;
GBytes *bytes;
bytes = g_file_load_bytes (file, NULL, NULL, NULL);
if (bytes == NULL)
return FALSE;
if (!g_utf8_validate (g_bytes_get_data (bytes, NULL), g_bytes_get_size (bytes), NULL))
{
g_bytes_unref (bytes);
return FALSE;
}
gtk_text_buffer_get_start_iter (self->text_buffer, &start);
gtk_text_buffer_get_end_iter (self->text_buffer, &end);
gtk_text_buffer_insert (self->text_buffer,
&end,
g_bytes_get_data (bytes, NULL),
g_bytes_get_size (bytes));
return TRUE;
}
static void
open_response_cb (GtkWidget *dialog,
gint response,
NodeEditorWindow *self)
{
gtk_widget_hide (dialog);
if (response == GTK_RESPONSE_ACCEPT)
{
GFile *file;
file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
node_editor_window_load (self, file);
g_object_unref (file);
}
gtk_widget_destroy (dialog);
}
static void
open_cb (GtkWidget *button,
NodeEditorWindow *self)
{
GtkWidget *dialog;
dialog = gtk_file_chooser_dialog_new ("",
GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (button))),
GTK_FILE_CHOOSER_ACTION_OPEN,
"_Cancel", GTK_RESPONSE_CANCEL,
"_Load", GTK_RESPONSE_ACCEPT,
NULL);
gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog), TRUE);
g_signal_connect (dialog, "response", G_CALLBACK (open_response_cb), self);
gtk_widget_show (dialog);
}
static void
save_response_cb (GtkWidget *dialog,
gint response,
NodeEditorWindow *self)
{
gtk_widget_hide (dialog);
if (response == GTK_RESPONSE_ACCEPT)
{
char *text, *filename;
GError *error = NULL;
text = get_current_text (self->text_buffer);
filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
if (!g_file_set_contents (filename, text, -1, &error))
{
GtkWidget *dialog;
dialog = gtk_message_dialog_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (self))),
GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_INFO,
GTK_BUTTONS_OK,
"Saving failed");
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
"%s", error->message);
g_signal_connect (dialog, "response", G_CALLBACK (gtk_widget_destroy), NULL);
gtk_widget_show (dialog);
g_error_free (error);
}
g_free (filename);
}
gtk_widget_destroy (dialog);
}
static void
save_cb (GtkWidget *button,
NodeEditorWindow *self)
{
GtkWidget *dialog;
dialog = gtk_file_chooser_dialog_new ("",
GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (button))),
GTK_FILE_CHOOSER_ACTION_SAVE,
"_Cancel", GTK_RESPONSE_CANCEL,
"_Save", GTK_RESPONSE_ACCEPT,
NULL);
gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog), TRUE);
g_signal_connect (dialog, "response", G_CALLBACK (save_response_cb), self);
gtk_widget_show (dialog);
}
static GdkTexture *
create_texture (NodeEditorWindow *self)
{
GdkPaintable *paintable;
GtkSnapshot *snapshot;
GskRenderer *renderer;
GskRenderNode *node;
GdkTexture *texture;
paintable = gtk_picture_get_paintable (GTK_PICTURE (self->picture));
if (paintable == NULL ||
gdk_paintable_get_intrinsic_width (paintable) <= 0 ||
gdk_paintable_get_intrinsic_height (paintable) <= 0)
return NULL;
snapshot = gtk_snapshot_new ();
gdk_paintable_snapshot (paintable, snapshot, gdk_paintable_get_intrinsic_width (paintable), gdk_paintable_get_intrinsic_height (paintable));
node = gtk_snapshot_free_to_node (snapshot);
if (node == NULL)
return NULL;
/* ahem */
renderer = GTK_ROOT_GET_IFACE (gtk_widget_get_root (GTK_WIDGET (self)))->get_renderer (gtk_widget_get_root (GTK_WIDGET (self)));
texture = gsk_renderer_render_texture (renderer, node, NULL);
gsk_render_node_unref (node);
return texture;
}
static void
export_image_response_cb (GtkWidget *dialog,
gint response,
GdkTexture *texture)
{
gtk_widget_hide (dialog);
if (response == GTK_RESPONSE_ACCEPT)
{
char *filename;
filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
if (!gdk_texture_save_to_png (texture, filename))
{
GtkWidget *message_dialog;
message_dialog = gtk_message_dialog_new (GTK_WINDOW (gtk_window_get_transient_for (GTK_WINDOW (dialog))),
GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_INFO,
GTK_BUTTONS_OK,
"Exporting to image failed");
g_signal_connect (message_dialog, "response", G_CALLBACK (gtk_widget_destroy), NULL);
gtk_widget_show (message_dialog);
}
g_free (filename);
}
gtk_widget_destroy (dialog);
g_object_unref (texture);
}
static void
export_image_cb (GtkWidget *button,
NodeEditorWindow *self)
{
GdkTexture *texture;
GtkWidget *dialog;
texture = create_texture (self);
if (texture == NULL)
return;
dialog = gtk_file_chooser_dialog_new ("",
GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (button))),
GTK_FILE_CHOOSER_ACTION_SAVE,
"_Cancel", GTK_RESPONSE_CANCEL,
"_Save", GTK_RESPONSE_ACCEPT,
NULL);
gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog), TRUE);
g_signal_connect (dialog, "response", G_CALLBACK (export_image_response_cb), texture);
gtk_widget_show (dialog);
}
static void
node_editor_window_finalize (GObject *object)
{
NodeEditorWindow *self = NODE_EDITOR_WINDOW (object);
if (self->text_timeout != 0)
g_source_remove (self->text_timeout);
G_OBJECT_CLASS (node_editor_window_parent_class)->finalize (object);
}
static void
node_editor_window_class_init (NodeEditorWindowClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
object_class->finalize = node_editor_window_finalize;
gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (class),
"/org/gtk/gtk4/node-editor/node-editor-window.ui");
gtk_widget_class_bind_template_child (widget_class, NodeEditorWindow, text_buffer);
gtk_widget_class_bind_template_child (widget_class, NodeEditorWindow, text_view);
gtk_widget_class_bind_template_child (widget_class, NodeEditorWindow, picture);
gtk_widget_class_bind_template_callback (widget_class, text_changed);
gtk_widget_class_bind_template_callback (widget_class, query_tooltip_cb);
gtk_widget_class_bind_template_callback (widget_class, open_cb);
gtk_widget_class_bind_template_callback (widget_class, save_cb);
gtk_widget_class_bind_template_callback (widget_class, export_image_cb);
}
static void
node_editor_window_init (NodeEditorWindow *self)
{
gtk_widget_init_template (GTK_WIDGET (self));
}
NodeEditorWindow *
node_editor_window_new (NodeEditorApplication *application)
{
return g_object_new (NODE_EDITOR_WINDOW_TYPE,
"application", application,
NULL);
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright © 2019 Benjamin Otte
*
* 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.1 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/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __NODE_EDITOR_WINDOW_H__
#define __NODE_EDITOR_WINDOW_H__
#include <gtk/gtk.h>
#include "node-editor-application.h"
#define NODE_EDITOR_WINDOW_TYPE (node_editor_window_get_type ())
#define NODE_EDITOR_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NODE_EDITOR_WINDOW_TYPE, NodeEditorWindow))
typedef struct _NodeEditorWindow NodeEditorWindow;
typedef struct _NodeEditorWindowClass NodeEditorWindowClass;
GType node_editor_window_get_type (void);
NodeEditorWindow * node_editor_window_new (NodeEditorApplication *application);
gboolean node_editor_window_load (NodeEditorWindow *self,
GFile *file);
#endif /* __NODE_EDITOR_WINDOW_H__ */

View File

@@ -0,0 +1,118 @@
<interface>
<object class="GtkTextTagTable" id="tags">
<child type="tag">
<object class="GtkTextTag">
<property name="name">warning</property>
<property name="underline">single</property>
<property name="underline-rgba">darkorange</property>
</object>
</child>
<child type="tag">
<object class="GtkTextTag">
<property name="name">error</property>
<property name="underline">error</property>
</object>
</child>
</object>
<object class="GtkTextBuffer" id="text_buffer">
<property name="tag-table">tags</property>
<signal name="changed" handler="text_changed"/>
</object>
<template class="NodeEditorWindow" parent="GtkApplicationWindow">
<style>
<class name="devel"/>
</style>
<property name="title" translatable="yes">GTK Node Editor</property>
<property name="default-width">1024</property>
<property name="default-height">768</property>
<child type="titlebar">
<object class="GtkHeaderBar" id="header">
<property name="title" translatable="yes">GTK Node Editor</property>
<property name="show-title-buttons">1</property>
<child>
<object class="GtkBox">
<child>
<object class="GtkButton">
<property name="icon-name">document-open-symbolic</property>
<signal name="clicked" handler="open_cb"/>
</object>
</child>
<child>
<object class="GtkButton">
<property name="icon-name">document-save-symbolic</property>
<signal name="clicked" handler="save_cb"/>
</object>
</child>
<child>
<object class="GtkButton">
<property name="icon-name">insert-image-symbolic</property>
<signal name="clicked" handler="export_image_cb"/>
</object>
</child>
</object>
<packing>
<property name="pack-type">start</property>
</packing>
</child>
<child type="title">
<object class="GtkLabel">
<property name="label" translatable="yes">GTK Node Editor</property>
</object>
</child>
<child>
<object class="GtkToggleButton">
<property name="icon-name">view-more-symbolic</property>
</object>
<packing>
<property name="pack-type">end</property>
</packing>
</child>
</object>
</child>
<child>
<object class="GtkPaned">
<property name="position">99999</property>
<child>
<object class="GtkScrolledWindow">
<property name="hscrollbar-policy">never</property>
<property name="expand">1</property>
<child>
<object class="GtkTextView" id="text_view">
<property name="buffer">text_buffer</property>
<property name="wrap-mode">word</property>
<property name="monospace">1</property>
<property name="has-focus">1</property>
<property name="left-margin">6</property>
<property name="right-margin">6</property>
<property name="has-tooltip">1</property>
<signal name="query-tooltip" handler="query_tooltip_cb"/>
</object>
</child>
</object>
</child>
<child>
<object class="GtkScrolledWindow">
<property name="expand">1</property>
<property name="min-content-height">100</property>
<property name="min-content-width">100</property>
<child>
<object class="GtkViewport">
<child>
<object class="GtkPicture" id="picture">
<property name="can-shrink">false</property>
<property name="halign">center</property>
<property name="valign">center</property>
</object>
</child>
</object>
</child>
</object>
<packing>
<property name="shrink">false</property>
</packing>
</child>
</object>
</child>
</template>
</interface>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<gresources>
<gresource prefix="/org/gtk/gtk4/node-editor">
<file preprocess="xml-stripblanks">node-editor-window.ui</file>
</gresource>
</gresources>

View File

@@ -157,6 +157,8 @@ gsk_transform_get_category
<SUBSECTION>
gsk_transform_print
gsk_transform_to_string
gsk_transform_from_string
<SUBSECTION>
gsk_transform_to_matrix
gsk_transform_to_2d
gsk_transform_to_affine

444
gsk/gskcssparser.c Normal file
View File

@@ -0,0 +1,444 @@
/*
* Copyright © 2019 Benjamin Otte
*
* 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.1 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/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#include "config.h"
#include "gskcssparserprivate.h"
#define GTK_COMPILATION
#include "gtk/gtkcssprovider.h"
struct _GskCssParser
{
volatile int ref_count;
GskCssParserErrorFunc error_func;
gpointer user_data;
GDestroyNotify user_destroy;
GSList *sources;
GSList *blocks;
GskCssLocation location;
GskCssToken token;
};
GskCssParser *
gsk_css_parser_new (GskCssParserErrorFunc error_func,
gpointer user_data,
GDestroyNotify user_destroy)
{
GskCssParser *self;
self = g_slice_new0 (GskCssParser);
self->ref_count = 1;
self->error_func = error_func;
self->user_data = user_data;
self->user_destroy = user_destroy;
return self;
}
static void
gsk_css_parser_finalize (GskCssParser *self)
{
g_slist_free_full (self->sources, (GDestroyNotify) gsk_css_tokenizer_unref);
if (self->user_destroy)
self->user_destroy (self->user_data);
g_slice_free (GskCssParser, self);
}
GskCssParser *
gsk_css_parser_ref (GskCssParser *self)
{
g_atomic_int_inc (&self->ref_count);
return self;
}
void
gsk_css_parser_unref (GskCssParser *self)
{
if (g_atomic_int_dec_and_test (&self->ref_count))
gsk_css_parser_finalize (self);
}
void
gsk_css_parser_add_tokenizer (GskCssParser *self,
GskCssTokenizer *tokenizer)
{
self->sources = g_slist_prepend (self->sources, gsk_css_tokenizer_ref (tokenizer));
}
void
gsk_css_parser_add_bytes (GskCssParser *self,
GBytes *bytes)
{
GskCssTokenizer *tokenizer;
tokenizer = gsk_css_tokenizer_new (bytes);
gsk_css_parser_add_tokenizer (self, tokenizer);
gsk_css_tokenizer_unref (tokenizer);
}
static void
gsk_css_parser_ensure_token (GskCssParser *self)
{
GskCssTokenizer *tokenizer;
GError *error = NULL;
if (!gsk_css_token_is (&self->token, GSK_CSS_TOKEN_EOF))
return;
if (self->sources == NULL)
return;
tokenizer = self->sources->data;
self->location = *gsk_css_tokenizer_get_location (tokenizer);
if (!gsk_css_tokenizer_read_token (tokenizer, &self->token, &error))
{
g_clear_error (&error);
}
}
const GskCssToken *
gsk_css_parser_peek_token (GskCssParser *self)
{
static const GskCssToken eof_token = { GSK_CSS_TOKEN_EOF, };
gsk_css_parser_ensure_token (self);
if (self->blocks && gsk_css_token_is (&self->token, GPOINTER_TO_UINT (self->blocks->data)))
return &eof_token;
return &self->token;
}
const GskCssToken *
gsk_css_parser_get_token (GskCssParser *self)
{
const GskCssToken *token;
for (token = gsk_css_parser_peek_token (self);
gsk_css_token_is (token, GSK_CSS_TOKEN_COMMENT) ||
gsk_css_token_is (token, GSK_CSS_TOKEN_WHITESPACE);
token = gsk_css_parser_peek_token (self))
{
gsk_css_parser_consume_token (self);
}
return token;
}
void
gsk_css_parser_consume_token (GskCssParser *self)
{
gsk_css_parser_ensure_token (self);
/* unpreserved tokens MUST be consumed via start_block() */
g_assert (gsk_css_token_is_preserved (&self->token, NULL));
gsk_css_token_clear (&self->token);
}
void
gsk_css_parser_start_block (GskCssParser *self)
{
GskCssTokenType end_token_type;
gsk_css_parser_ensure_token (self);
if (gsk_css_token_is_preserved (&self->token, &end_token_type))
{
g_critical ("gsk_css_parser_start_block() may only be called for non-preserved tokens");
return;
}
self->blocks = g_slist_prepend (self->blocks, GUINT_TO_POINTER (end_token_type));
gsk_css_token_clear (&self->token);
}
void
gsk_css_parser_end_block (GskCssParser *self)
{
g_return_if_fail (self->blocks != NULL);
gsk_css_parser_skip_until (self, GSK_CSS_TOKEN_EOF);
if (gsk_css_token_is (&self->token, GSK_CSS_TOKEN_EOF))
gsk_css_parser_warn_syntax (self, "Unterminated block at end of document");
self->blocks = g_slist_remove (self->blocks, self->blocks->data);
gsk_css_token_clear (&self->token);
}
/*
* gsk_css_parser_skip:
* @self: a #GskCssParser
*
* Skips a component value.
*
* This means that if the token is a preserved token, only
* this token will be skipped. If the token starts a block,
* the whole block will be skipped.
**/
void
gsk_css_parser_skip (GskCssParser *self)
{
const GskCssToken *token;
token = gsk_css_parser_get_token (self);
if (gsk_css_token_is_preserved (token, NULL))
{
gsk_css_parser_consume_token (self);
}
else
{
gsk_css_parser_start_block (self);
gsk_css_parser_end_block (self);
}
}
/*
* gsk_css_parser_skip_until:
* @self: a #GskCssParser
* @token_type: type of token to skip to
*
* Repeatedly skips a token until a certain type is reached.
* After this called, gsk_css_parser_get_token() will either
* return a token of this type or the eof token.
*
* This function is useful for resyncing a parser after encountering
* an error.
*
* If you want to skip until the end, use %GSK_TOKEN_TYPE_EOF
* as the token type.
**/
void
gsk_css_parser_skip_until (GskCssParser *self,
GskCssTokenType token_type)
{
const GskCssToken *token;
for (token = gsk_css_parser_get_token (self);
!gsk_css_token_is (token, token_type) &&
!gsk_css_token_is (token, GSK_CSS_TOKEN_EOF);
token = gsk_css_parser_get_token (self))
{
gsk_css_parser_skip (self);
}
}
void
gsk_css_parser_emit_error (GskCssParser *self,
const GError *error)
{
self->error_func (self,
&self->location,
&self->token,
error,
self->user_data);
}
void
gsk_css_parser_error_syntax (GskCssParser *self,
const char *format,
...)
{
va_list args;
GError *error;
va_start (args, format);
error = g_error_new_valist (GTK_CSS_PROVIDER_ERROR,
GTK_CSS_PROVIDER_ERROR_SYNTAX,
format, args);
gsk_css_parser_emit_error (self, error);
g_error_free (error);
va_end (args);
}
void
gsk_css_parser_error_value (GskCssParser *self,
const char *format,
...)
{
va_list args;
GError *error;
va_start (args, format);
error = g_error_new_valist (GTK_CSS_PROVIDER_ERROR,
GTK_CSS_PROVIDER_ERROR_UNKNOWN_VALUE,
format, args);
gsk_css_parser_emit_error (self, error);
g_error_free (error);
va_end (args);
}
void
gsk_css_parser_warn_syntax (GskCssParser *self,
const char *format,
...)
{
va_list args;
GError *error;
va_start (args, format);
error = g_error_new_valist (GTK_CSS_PROVIDER_ERROR,
GTK_CSS_PROVIDER_WARN_GENERAL,
format, args);
gsk_css_parser_emit_error (self, error);
g_error_free (error);
va_end (args);
}
gboolean
gsk_css_parser_consume_function (GskCssParser *self,
guint min_args,
guint max_args,
guint (* parse_func) (GskCssParser *, guint, gpointer),
gpointer data)
{
const GskCssToken *token;
gboolean result = FALSE;
char *function_name;
guint arg;
token = gsk_css_parser_get_token (self);
g_return_val_if_fail (gsk_css_token_is (token, GSK_CSS_TOKEN_FUNCTION), FALSE);
function_name = g_strdup (token->string.string);
gsk_css_parser_start_block (self);
arg = 0;
while (TRUE)
{
guint parse_args = parse_func (self, arg, data);
if (parse_args == 0)
break;
arg += parse_args;
token = gsk_css_parser_get_token (self);
if (gsk_css_token_is (token, GSK_CSS_TOKEN_EOF))
{
if (arg < min_args)
{
gsk_css_parser_error_syntax (self, "%s() requires at least %u arguments", function_name, min_args);
break;
}
else
{
result = TRUE;
break;
}
}
else if (gsk_css_token_is (token, GSK_CSS_TOKEN_COMMA))
{
if (arg >= max_args)
{
gsk_css_parser_error_syntax (self, "Expected ')' at end of %s()", function_name);
break;
}
gsk_css_parser_consume_token (self);
continue;
}
else
{
gsk_css_parser_error_syntax (self, "Unexpected data at end of %s() argument", function_name);
break;
}
}
gsk_css_parser_end_block (self);
g_free (function_name);
return result;
}
/**
* gsk_css_parser_consume_if:
* @self: a #GskCssParser
* @token_type: type of token to check for
*
* Consumes the next token if it matches the given @token_type.
*
* This function can be used in loops like this:
* do {
* ... parse one element ...
* } while (gsk_css_parser_consume_if (parser, GSK_CSS_TOKEN_COMMA);
*
* Returns: %TRUE if a token was consumed
**/
gboolean
gsk_css_parser_consume_if (GskCssParser *self,
GskCssTokenType token_type)
{
const GskCssToken *token;
token = gsk_css_parser_get_token (self);
if (!gsk_css_token_is (token, token_type))
return FALSE;
gsk_css_parser_consume_token (self);
return TRUE;
}
gboolean
gsk_css_parser_consume_number (GskCssParser *self,
double *number)
{
const GskCssToken *token;
token = gsk_css_parser_get_token (self);
if (gsk_css_token_is (token, GSK_CSS_TOKEN_SIGNED_NUMBER) ||
gsk_css_token_is (token, GSK_CSS_TOKEN_SIGNLESS_NUMBER) ||
gsk_css_token_is (token, GSK_CSS_TOKEN_SIGNED_INTEGER) ||
gsk_css_token_is (token, GSK_CSS_TOKEN_SIGNLESS_INTEGER))
{
*number = token->number.number;
gsk_css_parser_consume_token (self);
return TRUE;
}
/* FIXME: Implement calc() */
return FALSE;
}
gboolean
gsk_css_parser_consume_percentage (GskCssParser *self,
double *number)
{
const GskCssToken *token;
token = gsk_css_parser_get_token (self);
if (gsk_css_token_is (token, GSK_CSS_TOKEN_PERCENTAGE))
{
*number = token->number.number;
gsk_css_parser_consume_token (self);
return TRUE;
}
/* FIXME: Implement calc() */
return FALSE;
}

83
gsk/gskcssparserprivate.h Normal file
View File

@@ -0,0 +1,83 @@
/*
* Copyright © 2019 Benjamin Otte
*
* 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.1 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/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __GSK_CSS_PARSER_H__
#define __GSK_CSS_PARSER_H__
#include "gskcsstokenizerprivate.h"
G_BEGIN_DECLS
typedef struct _GskCssParser GskCssParser;
typedef void (* GskCssParserErrorFunc) (GskCssParser *parser,
const GskCssLocation *location,
const GskCssToken *token,
const GError *error,
gpointer user_data);
GskCssParser * gsk_css_parser_new (GskCssParserErrorFunc error_func,
gpointer user_data,
GDestroyNotify user_destroy);
GskCssParser * gsk_css_parser_ref (GskCssParser *self);
void gsk_css_parser_unref (GskCssParser *self);
void gsk_css_parser_add_tokenizer (GskCssParser *self,
GskCssTokenizer *tokenizer);
void gsk_css_parser_add_bytes (GskCssParser *self,
GBytes *bytes);
const GskCssToken * gsk_css_parser_peek_token (GskCssParser *self);
const GskCssToken * gsk_css_parser_get_token (GskCssParser *self);
void gsk_css_parser_consume_token (GskCssParser *self);
void gsk_css_parser_start_block (GskCssParser *self);
void gsk_css_parser_end_block (GskCssParser *self);
void gsk_css_parser_skip (GskCssParser *self);
void gsk_css_parser_skip_until (GskCssParser *self,
GskCssTokenType token_type);
void gsk_css_parser_emit_error (GskCssParser *self,
const GError *error);
void gsk_css_parser_error_syntax (GskCssParser *self,
const char *format,
...) G_GNUC_PRINTF(2, 3);
void gsk_css_parser_error_value (GskCssParser *self,
const char *format,
...) G_GNUC_PRINTF(2, 3);
void gsk_css_parser_warn_syntax (GskCssParser *self,
const char *format,
...) G_GNUC_PRINTF(2, 3);
gboolean gsk_css_parser_consume_if (GskCssParser *self,
GskCssTokenType token_type);
gboolean gsk_css_parser_consume_number (GskCssParser *self,
double *number);
gboolean gsk_css_parser_consume_percentage (GskCssParser *self,
double *number);
gboolean gsk_css_parser_consume_function (GskCssParser *self,
guint min_args,
guint max_args,
guint (* parse_func) (GskCssParser *, guint, gpointer),
gpointer data);
G_END_DECLS
#endif /* __GSK_CSS_PARSER_H__ */

1454
gsk/gskcsstokenizer.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,153 @@
/* GSK - The GIMP Toolkit
* Copyright (C) 2011 Benjamin Otte <otte@gnome.org>
*
* 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 __GSK_CSS_TOKENIZER_PRIVATE_H__
#define __GSK_CSS_TOKENIZER_PRIVATE_H__
#include <glib.h>
G_BEGIN_DECLS
typedef enum {
/* no content */
GSK_CSS_TOKEN_EOF,
GSK_CSS_TOKEN_WHITESPACE,
GSK_CSS_TOKEN_OPEN_PARENS,
GSK_CSS_TOKEN_CLOSE_PARENS,
GSK_CSS_TOKEN_OPEN_SQUARE,
GSK_CSS_TOKEN_CLOSE_SQUARE,
GSK_CSS_TOKEN_OPEN_CURLY,
GSK_CSS_TOKEN_CLOSE_CURLY,
GSK_CSS_TOKEN_COMMA,
GSK_CSS_TOKEN_COLON,
GSK_CSS_TOKEN_SEMICOLON,
GSK_CSS_TOKEN_CDO,
GSK_CSS_TOKEN_CDC,
GSK_CSS_TOKEN_INCLUDE_MATCH,
GSK_CSS_TOKEN_DASH_MATCH,
GSK_CSS_TOKEN_PREFIX_MATCH,
GSK_CSS_TOKEN_SUFFIX_MATCH,
GSK_CSS_TOKEN_SUBSTRING_MATCH,
GSK_CSS_TOKEN_COLUMN,
GSK_CSS_TOKEN_BAD_STRING,
GSK_CSS_TOKEN_BAD_URL,
GSK_CSS_TOKEN_COMMENT,
/* delim */
GSK_CSS_TOKEN_DELIM,
/* string */
GSK_CSS_TOKEN_STRING,
GSK_CSS_TOKEN_IDENT,
GSK_CSS_TOKEN_FUNCTION,
GSK_CSS_TOKEN_AT_KEYWORD,
GSK_CSS_TOKEN_HASH_UNRESTRICTED,
GSK_CSS_TOKEN_HASH_ID,
GSK_CSS_TOKEN_URL,
/* number */
GSK_CSS_TOKEN_SIGNED_INTEGER,
GSK_CSS_TOKEN_SIGNLESS_INTEGER,
GSK_CSS_TOKEN_SIGNED_NUMBER,
GSK_CSS_TOKEN_SIGNLESS_NUMBER,
GSK_CSS_TOKEN_PERCENTAGE,
/* dimension */
GSK_CSS_TOKEN_SIGNED_INTEGER_DIMENSION,
GSK_CSS_TOKEN_SIGNLESS_INTEGER_DIMENSION,
GSK_CSS_TOKEN_DIMENSION
} GskCssTokenType;
typedef union _GskCssToken GskCssToken;
typedef struct _GskCssTokenizer GskCssTokenizer;
typedef struct _GskCssLocation GskCssLocation;
typedef struct _GskCssStringToken GskCssStringToken;
typedef struct _GskCssDelimToken GskCssDelimToken;
typedef struct _GskCssNumberToken GskCssNumberToken;
typedef struct _GskCssDimensionToken GskCssDimensionToken;
typedef void (* GskCssTokenizerErrorFunc) (GskCssTokenizer *parser,
const GskCssToken *token,
const GError *error,
gpointer user_data);
struct _GskCssStringToken {
GskCssTokenType type;
char *string;
};
struct _GskCssDelimToken {
GskCssTokenType type;
gunichar delim;
};
struct _GskCssNumberToken {
GskCssTokenType type;
double number;
};
struct _GskCssDimensionToken {
GskCssTokenType type;
double value;
char *dimension;
};
union _GskCssToken {
GskCssTokenType type;
GskCssStringToken string;
GskCssDelimToken delim;
GskCssNumberToken number;
GskCssDimensionToken dimension;
};
struct _GskCssLocation
{
gsize bytes;
gsize chars;
gsize lines;
gsize line_bytes;
gsize line_chars;
};
void gsk_css_token_clear (GskCssToken *token);
gboolean gsk_css_token_is_finite (const GskCssToken *token);
gboolean gsk_css_token_is_preserved (const GskCssToken *token,
GskCssTokenType *out_closing);
#define gsk_css_token_is(token, _type) ((token)->type == (_type))
gboolean gsk_css_token_is_ident (const GskCssToken *token,
const char *ident);
gboolean gsk_css_token_is_function (const GskCssToken *token,
const char *ident);
gboolean gsk_css_token_is_delim (const GskCssToken *token,
gunichar delim);
void gsk_css_token_print (const GskCssToken *token,
GString *string);
char * gsk_css_token_to_string (const GskCssToken *token);
GskCssTokenizer * gsk_css_tokenizer_new (GBytes *bytes);
GskCssTokenizer * gsk_css_tokenizer_ref (GskCssTokenizer *tokenizer);
void gsk_css_tokenizer_unref (GskCssTokenizer *tokenizer);
const GskCssLocation * gsk_css_tokenizer_get_location (GskCssTokenizer *tokenizer);
gboolean gsk_css_tokenizer_read_token (GskCssTokenizer *tokenizer,
GskCssToken *token,
GError **error);
G_END_DECLS
#endif /* __GSK_CSS_TOKENIZER_PRIVATE_H__ */

View File

@@ -42,6 +42,7 @@
#include "gskdebugprivate.h"
#include "gskrendererprivate.h"
#include "gskrendernodeparserprivate.h"
#include <graphene-gobject.h>
@@ -328,19 +329,11 @@ gsk_render_node_diff (GskRenderNode *node1,
GBytes *
gsk_render_node_serialize (GskRenderNode *node)
{
GVariant *node_variant, *variant;
GBytes *result;
char *str;
node_variant = gsk_render_node_serialize_node (node);
variant = g_variant_new ("(suuv)",
GSK_RENDER_NODE_SERIALIZATION_ID,
(guint32) GSK_RENDER_NODE_SERIALIZATION_VERSION,
(guint32) gsk_render_node_get_node_type (node),
node_variant);
result = g_variant_get_data_as_bytes (variant);
g_variant_unref (variant);
str = gsk_render_node_serialize_to_string (node);
result = g_bytes_new_take (str, strlen (str));
return result;
}
@@ -397,36 +390,9 @@ GskRenderNode *
gsk_render_node_deserialize (GBytes *bytes,
GError **error)
{
char *id_string;
guint32 version, node_type;
GVariant *variant, *node_variant;
GskRenderNode *node = NULL;
variant = g_variant_new_from_bytes (G_VARIANT_TYPE ("(suuv)"), bytes, FALSE);
g_variant_get (variant, "(suuv)", &id_string, &version, &node_type, &node_variant);
if (!g_str_equal (id_string, GSK_RENDER_NODE_SERIALIZATION_ID))
{
g_set_error (error, GSK_SERIALIZATION_ERROR, GSK_SERIALIZATION_UNSUPPORTED_FORMAT,
"Data not in GskRenderNode serialization format.");
goto out;
}
if (version != GSK_RENDER_NODE_SERIALIZATION_VERSION)
{
g_set_error (error, GSK_SERIALIZATION_ERROR, GSK_SERIALIZATION_UNSUPPORTED_VERSION,
"Format version %u not supported.", version);
goto out;
}
node = gsk_render_node_deserialize_node (node_type, node_variant, error);
out:
g_free (id_string);
g_variant_unref (node_variant);
g_variant_unref (variant);
node = gsk_render_node_deserialize_from_bytes (bytes);
return node;
}

1433
gsk/gskrendernodeparser.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,10 @@
#ifndef __GSK_RENDER_NODE_PARSER_PRIVATE_H__
#define __GSK_RENDER_NODE_PARSER_PRIVATE_H__
#include "gskrendernode.h"
GskRenderNode * gsk_render_node_deserialize_from_bytes (GBytes *bytes);
char * gsk_render_node_serialize_to_string (GskRenderNode *root);
#endif

View File

@@ -1264,8 +1264,8 @@ gsk_transform_unref (GskTransform *self)
* @self: (allow-none): a #GskTransform
* @string: The string to print into
*
* Converts @self into a string representation suitable for printing that
* can later be parsed with gsk_transform_parse().
* Converts @self into a human-readable string representation suitable
* for printing that can later be parsed with gsk_transform_from_string().
**/
void
gsk_transform_print (GskTransform *self,
@@ -1651,3 +1651,254 @@ gsk_transform_transform_bounds (GskTransform *self,
break;
}
}
static guint
gsk_transform_parse_float (GskCssParser *parser,
guint n,
gpointer data)
{
float *f = data;
double d;
if (!gsk_css_parser_consume_number (parser, &d))
return 0;
f[n] = d;
return 1;
}
static guint
gsk_transform_parse_scale (GskCssParser *parser,
guint n,
gpointer data)
{
float *f = data;
double d;
if (!gsk_css_parser_consume_number (parser, &d))
return 0;
f[n] = d;
f[1] = d;
return 1;
}
gboolean
gsk_transform_parse (GskCssParser *parser,
GskTransform **out_transform)
{
const GskCssToken *token;
GskTransform *transform = NULL;
float f[16] = { 0, };
token = gsk_css_parser_get_token (parser);
if (gsk_css_token_is_ident (token, "none"))
{
gsk_css_parser_consume_token (parser);
*out_transform = NULL;
return TRUE;
}
while (TRUE)
{
if (gsk_css_token_is_function (token, "matrix"))
{
graphene_matrix_t matrix;
if (!gsk_css_parser_consume_function (parser, 6, 6, gsk_transform_parse_float, f))
goto fail;
graphene_matrix_init_from_2d (&matrix, f[0], f[1], f[2], f[3], f[4], f[5]);
transform = gsk_transform_matrix_with_category (transform,
&matrix,
GSK_TRANSFORM_CATEGORY_2D);
}
else if (gsk_css_token_is_function (token, "matrix3d"))
{
graphene_matrix_t matrix;
if (!gsk_css_parser_consume_function (parser, 16, 16, gsk_transform_parse_float, f))
goto fail;
graphene_matrix_init_from_float (&matrix, f);
transform = gsk_transform_matrix (transform, &matrix);
}
else if (gsk_css_token_is_function (token, "perspective"))
{
if (!gsk_css_parser_consume_function (parser, 1, 1, gsk_transform_parse_float, f))
goto fail;
transform = gsk_transform_perspective (transform, f[0]);
}
else if (gsk_css_token_is_function (token, "rotate") ||
gsk_css_token_is_function (token, "rotateZ"))
{
if (!gsk_css_parser_consume_function (parser, 1, 1, gsk_transform_parse_float, f))
goto fail;
transform = gsk_transform_rotate (transform, f[0]);
}
else if (gsk_css_token_is_function (token, "rotate3d"))
{
graphene_vec3_t axis;
if (!gsk_css_parser_consume_function (parser, 4, 4, gsk_transform_parse_float, f))
goto fail;
graphene_vec3_init (&axis, f[0], f[1], f[2]);
transform = gsk_transform_rotate_3d (transform, f[3], &axis);
}
else if (gsk_css_token_is_function (token, "rotateX"))
{
if (!gsk_css_parser_consume_function (parser, 1, 1, gsk_transform_parse_float, f))
goto fail;
transform = gsk_transform_rotate_3d (transform, f[0], graphene_vec3_x_axis ());
}
else if (gsk_css_token_is_function (token, "rotateY"))
{
if (!gsk_css_parser_consume_function (parser, 1, 1, gsk_transform_parse_float, f))
goto fail;
transform = gsk_transform_rotate_3d (transform, f[0], graphene_vec3_y_axis ());
}
else if (gsk_css_token_is_function (token, "scale"))
{
if (!gsk_css_parser_consume_function (parser, 1, 2, gsk_transform_parse_scale, f))
goto fail;
transform = gsk_transform_scale (transform, f[0], f[1]);
}
else if (gsk_css_token_is_function (token, "scale3d"))
{
if (!gsk_css_parser_consume_function (parser, 3, 3, gsk_transform_parse_float, f))
goto fail;
transform = gsk_transform_scale_3d (transform, f[0], f[1], f[2]);
}
else if (gsk_css_token_is_function (token, "scaleX"))
{
if (!gsk_css_parser_consume_function (parser, 1, 1, gsk_transform_parse_float, f))
goto fail;
transform = gsk_transform_scale (transform, f[0], 1.f);
}
else if (gsk_css_token_is_function (token, "scaleY"))
{
if (!gsk_css_parser_consume_function (parser, 1, 1, gsk_transform_parse_float, f))
goto fail;
transform = gsk_transform_scale (transform, 1.f, f[0]);
}
else if (gsk_css_token_is_function (token, "scaleZ"))
{
if (!gsk_css_parser_consume_function (parser, 1, 1, gsk_transform_parse_float, f))
goto fail;
transform = gsk_transform_scale_3d (transform, 1.f, 1.f, f[0]);
}
else if (gsk_css_token_is_function (token, "translate"))
{
f[1] = 0.f;
if (!gsk_css_parser_consume_function (parser, 1, 2, gsk_transform_parse_float, f))
goto fail;
transform = gsk_transform_translate (transform, &GRAPHENE_POINT_INIT (f[0], f[1]));
}
else if (gsk_css_token_is_function (token, "translate3d"))
{
if (!gsk_css_parser_consume_function (parser, 3, 3, gsk_transform_parse_float, f))
goto fail;
transform = gsk_transform_translate_3d (transform, &GRAPHENE_POINT3D_INIT (f[0], f[1], f[2]));
}
else if (gsk_css_token_is_function (token, "translateX"))
{
if (!gsk_css_parser_consume_function (parser, 1, 1, gsk_transform_parse_float, f))
goto fail;
transform = gsk_transform_translate (transform, &GRAPHENE_POINT_INIT (f[0], 0.f));
}
else if (gsk_css_token_is_function (token, "translateY"))
{
if (!gsk_css_parser_consume_function (parser, 1, 1, gsk_transform_parse_float, f))
goto fail;
transform = gsk_transform_translate (transform, &GRAPHENE_POINT_INIT (0.f, f[0]));
}
else if (gsk_css_token_is_function (token, "translateZ"))
{
if (!gsk_css_parser_consume_function (parser, 1, 1, gsk_transform_parse_float, f))
goto fail;
transform = gsk_transform_translate_3d (transform, &GRAPHENE_POINT3D_INIT (0.f, 0.f, f[0]));
}
#if 0
/* FIXME: add these */
else if (gsk_css_token_is_function (token, "skew"))
{
}
else if (gsk_css_token_is_function (token, "skewX"))
{
}
else if (gsk_css_token_is_function (token, "skewY"))
{
}
#endif
else
{
break;
}
token = gsk_css_parser_get_token (parser);
}
if (transform == NULL)
{
gsk_css_parser_error_syntax (parser, "Expected a transform");
goto fail;
}
*out_transform = transform;
return TRUE;
fail:
gsk_transform_unref (transform);
*out_transform = NULL;
return FALSE;
}
/**
* gsk_transform_from_string:
* @string: the string to parse
* @out_transform: (out): The location to put the transform in
*
* Parses the given @string into a transform and puts it in
* @out_transform. Strings printed via gsk_transform_to_string()
* can be read in again successfully using this function.
*
* If @string does not describe a valid transform, %FALSE is
* returned and %NULL is put in @out_transform.
*
* Returns: %TRUE if @string described a valid transform.
**/
gboolean
gsk_transform_from_string (const char *string,
GskTransform **out_transform)
{
GskCssParser *parser;
GBytes *bytes;
gboolean result;
g_return_val_if_fail (string != NULL, FALSE);
g_return_val_if_fail (out_transform != NULL, FALSE);
bytes = g_bytes_new_static (string, strlen (string));
parser = gsk_css_parser_new (NULL, NULL, NULL);
gsk_css_parser_add_bytes (parser, bytes);
result = gsk_transform_parse (parser, out_transform);
gsk_css_parser_unref (parser);
g_bytes_unref (bytes);
return result;
}

View File

@@ -46,6 +46,9 @@ void gsk_transform_print (GskTransform
GDK_AVAILABLE_IN_ALL
char * gsk_transform_to_string (GskTransform *self);
GDK_AVAILABLE_IN_ALL
gboolean gsk_transform_from_string (const char *string,
GskTransform **out_transform);
GDK_AVAILABLE_IN_ALL
void gsk_transform_to_matrix (GskTransform *self,
graphene_matrix_t *out_matrix);
GDK_AVAILABLE_IN_ALL

View File

@@ -23,7 +23,7 @@
#include "gsktransform.h"
#include <gsk/gsk.h>
#include "gsk/gskcssparserprivate.h"
#include "gsk/gskrendernodeprivate.h"
G_BEGIN_DECLS
@@ -32,6 +32,10 @@ GskTransform * gsk_transform_matrix_with_category (GskTransform
const graphene_matrix_t*matrix,
GskTransformCategory category);
gboolean gsk_transform_parse (GskCssParser *parser,
GskTransform **out_transform);
G_END_DECLS
#endif /* __GSK_TRANSFORM_PRIVATE_H__ */

View File

@@ -31,9 +31,12 @@ gsk_public_sources = files([
gsk_private_sources = files([
'gskcairoblur.c',
'gskcairorenderer.c',
'gskcssparser.c',
'gskcsstokenizer.c',
'gskdebug.c',
'gskprivate.c',
'gskprofiler.c',
'gskrendernodeparser.c',
'gl/gskshaderbuilder.c',
'gl/gskglprofiler.c',
'gl/gskglrenderer.c',

View File

@@ -45,6 +45,7 @@ G_BEGIN_DECLS
* @GTK_CSS_PROVIDER_ERROR_NAME: Name error.
* @GTK_CSS_PROVIDER_ERROR_DEPRECATED: Deprecation error.
* @GTK_CSS_PROVIDER_ERROR_UNKNOWN_VALUE: Unknown value.
* @GTK_CSS_PROVIDER_WARN_GENERAL: A general warning.
*
* Error codes for %GTK_CSS_PROVIDER_ERROR.
*/
@@ -55,7 +56,8 @@ typedef enum
GTK_CSS_PROVIDER_ERROR_IMPORT,
GTK_CSS_PROVIDER_ERROR_NAME,
GTK_CSS_PROVIDER_ERROR_DEPRECATED,
GTK_CSS_PROVIDER_ERROR_UNKNOWN_VALUE
GTK_CSS_PROVIDER_ERROR_UNKNOWN_VALUE,
GTK_CSS_PROVIDER_WARN_GENERAL,
} GtkCssProviderError;
GDK_AVAILABLE_IN_ALL

326
gtk/gtkdiffpaintable.c Normal file
View File

@@ -0,0 +1,326 @@
/*
* Copyright © 2019 Benjamin Otte
*
* 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.1 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/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#include "config.h"
#include "gtkdiffpaintableprivate.h"
#include "gtkintl.h"
#include "gtksnapshot.h"
struct _GtkDiffPaintable
{
GObject parent_instance;
GdkPaintable *first;
GdkPaintable *second;
};
struct _GtkDiffPaintableClass
{
GObjectClass parent_class;
};
enum {
PROP_0,
PROP_FIRST,
PROP_SECOND,
N_PROPS
};
static GParamSpec *properties[N_PROPS] = { NULL, };
static void
gtk_diff_paintable_paintable_snapshot (GdkPaintable *paintable,
GdkSnapshot *snapshot,
double width,
double height)
{
GtkDiffPaintable *self = GTK_DIFF_PAINTABLE (paintable);
float amplify[16] = { 4, 0, 0, 0,
0, 4, 0, 0,
0, 0, 4, 0,
0, 0, 0, 1 };
graphene_matrix_t matrix;
graphene_matrix_init_from_float (&matrix, amplify);
gtk_snapshot_push_color_matrix (snapshot, &matrix, graphene_vec4_zero ());
gtk_snapshot_push_blend (snapshot, GSK_BLEND_MODE_DIFFERENCE);
if (self->first)
gdk_paintable_snapshot (self->first, snapshot, width, height);
gtk_snapshot_pop (snapshot);
if (self->second)
gdk_paintable_snapshot (self->second, snapshot, width, height);
gtk_snapshot_pop (snapshot);
gtk_snapshot_pop (snapshot);
}
static int
gtk_diff_paintable_paintable_get_intrinsic_width (GdkPaintable *paintable)
{
GtkDiffPaintable *self = GTK_DIFF_PAINTABLE (paintable);
int result;
result = 0;
if (self->first)
result = MAX (result, gdk_paintable_get_intrinsic_width (self->first));
if (self->second)
result = MAX (result, gdk_paintable_get_intrinsic_width (self->second));
return result;
}
static int
gtk_diff_paintable_paintable_get_intrinsic_height (GdkPaintable *paintable)
{
GtkDiffPaintable *self = GTK_DIFF_PAINTABLE (paintable);
int result;
result = 0;
if (self->first)
result = MAX (result, gdk_paintable_get_intrinsic_height (self->first));
if (self->second)
result = MAX (result, gdk_paintable_get_intrinsic_height (self->second));
return result;
}
static void
gtk_diff_paintable_paintable_init (GdkPaintableInterface *iface)
{
iface->snapshot = gtk_diff_paintable_paintable_snapshot;
iface->get_intrinsic_width = gtk_diff_paintable_paintable_get_intrinsic_width;
iface->get_intrinsic_height = gtk_diff_paintable_paintable_get_intrinsic_height;
}
G_DEFINE_TYPE_EXTENDED (GtkDiffPaintable, gtk_diff_paintable, G_TYPE_OBJECT, 0,
G_IMPLEMENT_INTERFACE (GDK_TYPE_PAINTABLE,
gtk_diff_paintable_paintable_init))
static void
gtk_diff_paintable_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GtkDiffPaintable *self = GTK_DIFF_PAINTABLE (object);
switch (prop_id)
{
case PROP_FIRST:
gtk_diff_paintable_set_first (self, g_value_get_object (value));
break;
case PROP_SECOND:
gtk_diff_paintable_set_second (self, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gtk_diff_paintable_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GtkDiffPaintable *self = GTK_DIFF_PAINTABLE (object);
switch (prop_id)
{
case PROP_FIRST:
g_value_set_object (value, self->first);
break;
case PROP_SECOND:
g_value_set_object (value, self->second);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gtk_diff_paintable_unset_paintable (GtkDiffPaintable *self,
GdkPaintable **paintable)
{
guint flags;
if (*paintable == NULL)
return;
flags = gdk_paintable_get_flags (*paintable);
if ((flags & GDK_PAINTABLE_STATIC_CONTENTS) == 0)
g_signal_handlers_disconnect_by_func (*paintable,
gdk_paintable_invalidate_contents,
self);
if ((flags & GDK_PAINTABLE_STATIC_SIZE) == 0)
g_signal_handlers_disconnect_by_func (*paintable,
gdk_paintable_invalidate_size,
self);
g_clear_object (paintable);
}
static void
gtk_diff_paintable_dispose (GObject *object)
{
GtkDiffPaintable *self = GTK_DIFF_PAINTABLE (object);
gtk_diff_paintable_unset_paintable (self, &self->first);
gtk_diff_paintable_unset_paintable (self, &self->second);
G_OBJECT_CLASS (gtk_diff_paintable_parent_class)->dispose (object);
}
static void
gtk_diff_paintable_class_init (GtkDiffPaintableClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->get_property = gtk_diff_paintable_get_property;
gobject_class->set_property = gtk_diff_paintable_set_property;
gobject_class->dispose = gtk_diff_paintable_dispose;
properties[PROP_FIRST] =
g_param_spec_object ("first",
P_("First"),
P_("First paintable to diff"),
GDK_TYPE_PAINTABLE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
properties[PROP_SECOND] =
g_param_spec_object ("second",
P_("Second"),
P_("Second paintable to diff"),
GDK_TYPE_PAINTABLE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
g_object_class_install_properties (gobject_class, N_PROPS, properties);
}
static void
gtk_diff_paintable_init (GtkDiffPaintable *self)
{
}
GdkPaintable *
gtk_diff_paintable_new (GdkPaintable *first,
GdkPaintable *second)
{
g_return_val_if_fail (first == NULL || GDK_IS_PAINTABLE (first), NULL);
g_return_val_if_fail (second == NULL || GDK_IS_PAINTABLE (second), NULL);
return g_object_new (GTK_TYPE_DIFF_PAINTABLE,
"first", first,
"second", second,
NULL);
}
static gboolean
gtk_diff_paintable_set_paintable (GtkDiffPaintable *self,
GdkPaintable **paintable,
GdkPaintable *new_paintable)
{
if (*paintable == new_paintable)
return FALSE;
gtk_diff_paintable_unset_paintable (self, paintable);
if (new_paintable)
{
const guint flags = gdk_paintable_get_flags (new_paintable);
*paintable = g_object_ref (new_paintable);
if ((flags & GDK_PAINTABLE_STATIC_CONTENTS) == 0)
g_signal_connect_swapped (new_paintable,
"invalidate-contents",
G_CALLBACK (gdk_paintable_invalidate_contents),
self);
if ((flags & GDK_PAINTABLE_STATIC_SIZE) == 0)
g_signal_connect_swapped (new_paintable,
"invalidate-size",
G_CALLBACK (gdk_paintable_invalidate_size),
self);
}
gdk_paintable_invalidate_size (GDK_PAINTABLE (self));
gdk_paintable_invalidate_contents (GDK_PAINTABLE (self));
return TRUE;
}
void
gtk_diff_paintable_set_first (GtkDiffPaintable *self,
GdkPaintable *paintable)
{
g_return_if_fail (GTK_IS_DIFF_PAINTABLE (self));
g_return_if_fail (paintable == NULL || GDK_IS_PAINTABLE (paintable));
if (!gtk_diff_paintable_set_paintable (self, &self->first, paintable))
return;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FIRST]);
}
GdkPaintable *
gtk_diff_paintable_get_first (GtkDiffPaintable *self)
{
g_return_val_if_fail (GTK_IS_DIFF_PAINTABLE (self), NULL);
return self->first;
}
void
gtk_diff_paintable_set_second (GtkDiffPaintable *self,
GdkPaintable *paintable)
{
g_return_if_fail (GTK_IS_DIFF_PAINTABLE (self));
g_return_if_fail (paintable == NULL || GDK_IS_PAINTABLE (paintable));
if (!gtk_diff_paintable_set_paintable (self, &self->second, paintable))
return;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SECOND]);
}
GdkPaintable *
gtk_diff_paintable_get_second (GtkDiffPaintable *self)
{
g_return_val_if_fail (GTK_IS_DIFF_PAINTABLE (self), NULL);
return self->second;
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright © 2019 Benjamin Otte
*
* 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.1 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/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __GTK_DIFF_PAINTABLE_H__
#define __GTK_DIFF_PAINTABLE_H__
#include <gsk/gsk.h>
G_BEGIN_DECLS
#define GTK_TYPE_DIFF_PAINTABLE (gtk_diff_paintable_get_type ())
G_DECLARE_FINAL_TYPE (GtkDiffPaintable, gtk_diff_paintable, GTK, DIFF_PAINTABLE, GObject)
GdkPaintable * gtk_diff_paintable_new (GdkPaintable *first,
GdkPaintable *second);
void gtk_diff_paintable_set_first (GtkDiffPaintable *self,
GdkPaintable *first);
GdkPaintable * gtk_diff_paintable_get_first (GtkDiffPaintable *self) G_GNUC_PURE;
void gtk_diff_paintable_set_second (GtkDiffPaintable *self,
GdkPaintable *second);
GdkPaintable * gtk_diff_paintable_get_second (GtkDiffPaintable *self) G_GNUC_PURE;
G_END_DECLS
#endif /* __GTK_DIFF_PAINTABLE_H__ */

321
gtk/gtkrendererpaintable.c Normal file
View File

@@ -0,0 +1,321 @@
/*
* Copyright © 2019 Benjamin Otte
*
* 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.1 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/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#include "config.h"
#include "gtkrendererpaintableprivate.h"
#include "gtkintl.h"
#include "gtksnapshot.h"
struct _GtkRendererPaintable
{
GObject parent_instance;
GskRenderer *renderer;
GdkPaintable *paintable;
};
struct _GtkRendererPaintableClass
{
GObjectClass parent_class;
};
enum {
PROP_0,
PROP_PAINTABLE,
PROP_RENDERER,
N_PROPS
};
static GParamSpec *properties[N_PROPS] = { NULL, };
static void
gtk_renderer_paintable_paintable_snapshot (GdkPaintable *paintable,
GdkSnapshot *snapshot,
double width,
double height)
{
GtkRendererPaintable *self = GTK_RENDERER_PAINTABLE (paintable);
GtkSnapshot *node_snapshot;
GskRenderNode *node;
GdkTexture *texture;
if (self->paintable == NULL)
return;
if (self->renderer == NULL ||
!gsk_renderer_is_realized (self->renderer))
{
gdk_paintable_snapshot (self->paintable, snapshot, width, height);
return;
}
node_snapshot = gtk_snapshot_new ();
gdk_paintable_snapshot (self->paintable, node_snapshot, width, height);
node = gtk_snapshot_free_to_node (node_snapshot);
texture = gsk_renderer_render_texture (self->renderer,
node,
&GRAPHENE_RECT_INIT (0, 0, width, height));
gdk_paintable_snapshot (GDK_PAINTABLE (texture), snapshot, width, height);
}
static int
gtk_renderer_paintable_paintable_get_intrinsic_width (GdkPaintable *paintable)
{
GtkRendererPaintable *self = GTK_RENDERER_PAINTABLE (paintable);
if (self->paintable == NULL)
return 0;
return gdk_paintable_get_intrinsic_width (self->paintable);
}
static int
gtk_renderer_paintable_paintable_get_intrinsic_height (GdkPaintable *paintable)
{
GtkRendererPaintable *self = GTK_RENDERER_PAINTABLE (paintable);
if (self->paintable == NULL)
return 0;
return gdk_paintable_get_intrinsic_height (self->paintable);
}
static double
gtk_renderer_paintable_paintable_get_intrinsic_aspect_ratio (GdkPaintable *paintable)
{
GtkRendererPaintable *self = GTK_RENDERER_PAINTABLE (paintable);
if (self->paintable == NULL)
return 0.0;
return gdk_paintable_get_intrinsic_aspect_ratio (self->paintable);
}
static void
gtk_renderer_paintable_paintable_init (GdkPaintableInterface *iface)
{
iface->snapshot = gtk_renderer_paintable_paintable_snapshot;
iface->get_intrinsic_width = gtk_renderer_paintable_paintable_get_intrinsic_width;
iface->get_intrinsic_height = gtk_renderer_paintable_paintable_get_intrinsic_height;
iface->get_intrinsic_aspect_ratio = gtk_renderer_paintable_paintable_get_intrinsic_aspect_ratio;
}
G_DEFINE_TYPE_EXTENDED (GtkRendererPaintable, gtk_renderer_paintable, G_TYPE_OBJECT, 0,
G_IMPLEMENT_INTERFACE (GDK_TYPE_PAINTABLE,
gtk_renderer_paintable_paintable_init))
static void
gtk_renderer_paintable_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GtkRendererPaintable *self = GTK_RENDERER_PAINTABLE (object);
switch (prop_id)
{
case PROP_PAINTABLE:
gtk_renderer_paintable_set_paintable (self, g_value_get_object (value));
break;
case PROP_RENDERER:
gtk_renderer_paintable_set_renderer (self, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gtk_renderer_paintable_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GtkRendererPaintable *self = GTK_RENDERER_PAINTABLE (object);
switch (prop_id)
{
case PROP_PAINTABLE:
g_value_set_object (value, self->paintable);
break;
case PROP_RENDERER:
g_value_set_object (value, self->renderer);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gtk_renderer_paintable_unset_paintable (GtkRendererPaintable *self)
{
guint flags;
if (self->paintable == NULL)
return;
flags = gdk_paintable_get_flags (self->paintable);
if ((flags & GDK_PAINTABLE_STATIC_CONTENTS) == 0)
g_signal_handlers_disconnect_by_func (self->paintable,
gdk_paintable_invalidate_contents,
self);
if ((flags & GDK_PAINTABLE_STATIC_SIZE) == 0)
g_signal_handlers_disconnect_by_func (self->paintable,
gdk_paintable_invalidate_size,
self);
g_clear_object (&self->paintable);
}
static void
gtk_renderer_paintable_dispose (GObject *object)
{
GtkRendererPaintable *self = GTK_RENDERER_PAINTABLE (object);
g_clear_object (&self->renderer);
gtk_renderer_paintable_unset_paintable (self);
G_OBJECT_CLASS (gtk_renderer_paintable_parent_class)->dispose (object);
}
static void
gtk_renderer_paintable_class_init (GtkRendererPaintableClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->get_property = gtk_renderer_paintable_get_property;
gobject_class->set_property = gtk_renderer_paintable_set_property;
gobject_class->dispose = gtk_renderer_paintable_dispose;
properties[PROP_PAINTABLE] =
g_param_spec_object ("paintable",
P_("Paintable"),
P_("The paintable to be shown"),
GDK_TYPE_PAINTABLE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
properties[PROP_RENDERER] =
g_param_spec_object ("renderer",
P_("Renderer"),
P_("Renderer used to render the paintable"),
GSK_TYPE_RENDERER,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
g_object_class_install_properties (gobject_class, N_PROPS, properties);
}
static void
gtk_renderer_paintable_init (GtkRendererPaintable *self)
{
}
GdkPaintable *
gtk_renderer_paintable_new (GskRenderer *renderer,
GdkPaintable *paintable)
{
g_return_val_if_fail (renderer == NULL || GSK_IS_RENDERER (renderer), NULL);
g_return_val_if_fail (paintable == NULL || GDK_IS_PAINTABLE (paintable), NULL);
return g_object_new (GTK_TYPE_RENDERER_PAINTABLE,
"renderer", renderer,
"paintable", paintable,
NULL);
}
void
gtk_renderer_paintable_set_renderer (GtkRendererPaintable *self,
GskRenderer *renderer)
{
g_return_if_fail (GTK_IS_RENDERER_PAINTABLE (self));
g_return_if_fail (renderer == NULL || GSK_IS_RENDERER (renderer));
if (!g_set_object (&self->renderer, renderer))
return;
if (self->paintable)
gdk_paintable_invalidate_contents (GDK_PAINTABLE (self));
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_RENDERER]);
}
GskRenderer *
gtk_renderer_paintable_get_renderer (GtkRendererPaintable *self)
{
g_return_val_if_fail (GTK_IS_RENDERER_PAINTABLE (self), NULL);
return self->renderer;
}
void
gtk_renderer_paintable_set_paintable (GtkRendererPaintable *self,
GdkPaintable *paintable)
{
g_return_if_fail (GTK_IS_RENDERER_PAINTABLE (self));
g_return_if_fail (paintable == NULL || GDK_IS_PAINTABLE (paintable));
if (self->paintable == paintable)
return;
gtk_renderer_paintable_unset_paintable (self);
if (paintable)
{
const guint flags = gdk_paintable_get_flags (paintable);
self->paintable = g_object_ref (paintable);
if ((flags & GDK_PAINTABLE_STATIC_CONTENTS) == 0)
g_signal_connect_swapped (paintable,
"invalidate-contents",
G_CALLBACK (gdk_paintable_invalidate_contents),
self);
if ((flags & GDK_PAINTABLE_STATIC_SIZE) == 0)
g_signal_connect_swapped (paintable,
"invalidate-size",
G_CALLBACK (gdk_paintable_invalidate_size),
self);
}
gdk_paintable_invalidate_size (GDK_PAINTABLE (self));
gdk_paintable_invalidate_contents (GDK_PAINTABLE (self));
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_PAINTABLE]);
}
GdkPaintable *
gtk_renderer_paintable_get_paintable (GtkRendererPaintable *self)
{
g_return_val_if_fail (GTK_IS_RENDERER_PAINTABLE (self), NULL);
return self->paintable;
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright © 2019 Benjamin Otte
*
* 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.1 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/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __GTK_RENDERER_PAINTABLE_H__
#define __GTK_RENDERER_PAINTABLE_H__
#include <gsk/gsk.h>
G_BEGIN_DECLS
#define GTK_TYPE_RENDERER_PAINTABLE (gtk_renderer_paintable_get_type ())
G_DECLARE_FINAL_TYPE (GtkRendererPaintable, gtk_renderer_paintable, GTK, RENDERER_PAINTABLE, GObject)
GdkPaintable * gtk_renderer_paintable_new (GskRenderer *renderer,
GdkPaintable *paintable);
void gtk_renderer_paintable_set_renderer (GtkRendererPaintable *self,
GskRenderer *renderer);
GskRenderer * gtk_renderer_paintable_get_renderer (GtkRendererPaintable *self) G_GNUC_PURE;
void gtk_renderer_paintable_set_paintable (GtkRendererPaintable *self,
GdkPaintable *paintable);
GdkPaintable * gtk_renderer_paintable_get_paintable (GtkRendererPaintable *self) G_GNUC_PURE;
G_END_DECLS
#endif /* __GTK_RENDERER_PAINTABLE_H__ */

View File

@@ -22,8 +22,22 @@
#include "gtkmagnifierprivate.h"
#include "gtklabel.h"
#include "gtkadjustment.h"
#include "gtkdiffpaintableprivate.h"
#include "gtklabel.h"
#include "gtklistbox.h"
#include "gtkpicture.h"
#include "gtkrendererpaintableprivate.h"
#include "gtkwidgetpaintable.h"
#ifdef GDK_WINDOWING_BROADWAY
#include "gsk/gskbroadwayrendererprivate.h"
#endif
#include "gsk/gskcairorendererprivate.h"
#include "gsk/gl/gskglrendererprivate.h"
#ifdef GDK_RENDERING_VULKAN
#include "gsk/vulkan/gskvulkanrendererprivate.h"
#endif
enum
{
@@ -35,37 +49,155 @@ struct _GtkInspectorMagnifierPrivate
{
GtkWidget *object;
GtkWidget *magnifier;
GtkWidget *renderer_listbox;
GtkAdjustment *adjustment;
GListStore *renderers;
GdkPaintable *paintable;
};
G_DEFINE_TYPE_WITH_PRIVATE (GtkInspectorMagnifier, gtk_inspector_magnifier, GTK_TYPE_BOX)
static void
gtk_inspector_magnifier_init (GtkInspectorMagnifier *sl)
gtk_inspector_magnifier_add_renderer (GtkInspectorMagnifier *self,
GType renderer_type,
const char *description)
{
sl->priv = gtk_inspector_magnifier_get_instance_private (sl);
gtk_widget_init_template (GTK_WIDGET (sl));
GtkInspectorMagnifierPrivate *priv = gtk_inspector_magnifier_get_instance_private (self);
GskRenderer *renderer;
GdkSurface *surface;
GdkPaintable *paintable;
surface = gtk_widget_get_surface (GTK_WIDGET (self));
g_assert (surface != NULL);
if (renderer_type == G_TYPE_NONE)
renderer = NULL;
else
{
renderer = g_object_new (renderer_type, NULL);
if (!gsk_renderer_realize (renderer, surface, NULL))
{
g_object_unref (renderer);
return;
}
}
paintable = gtk_renderer_paintable_new (renderer, priv->paintable);
g_object_set_data_full (G_OBJECT (paintable), "description", g_strdup (description), g_free);
g_object_unref (renderer);
g_list_store_append (priv->renderers, paintable);
g_object_unref (paintable);
}
static void
gtk_inspector_magnifier_realize (GtkWidget *widget)
{
GtkInspectorMagnifier *self = GTK_INSPECTOR_MAGNIFIER (widget);
GTK_WIDGET_CLASS (gtk_inspector_magnifier_parent_class)->realize (widget);
gtk_inspector_magnifier_add_renderer (self,
G_TYPE_NONE,
"Default");
gtk_inspector_magnifier_add_renderer (self,
GSK_TYPE_GL_RENDERER,
"OpenGL");
#ifdef GDK_RENDERING_VULKAN
gtk_inspector_magnifier_add_renderer (self,
GSK_TYPE_VULKAN_RENDERER,
"Vulkan");
#endif
#ifdef GDK_WINDOWING_BROADWAY
gtk_inspector_magnifier_add_renderer (self,
GSK_TYPE_BROADWAY_RENDERER,
"Broadway");
#endif
gtk_inspector_magnifier_add_renderer (self,
GSK_TYPE_CAIRO_RENDERER,
"Cairo");
}
static void
gtk_inspector_magnifier_unrealize (GtkWidget *widget)
{
GtkInspectorMagnifier *self = GTK_INSPECTOR_MAGNIFIER (widget);
GtkInspectorMagnifierPrivate *priv = gtk_inspector_magnifier_get_instance_private (self);
g_list_store_remove_all (priv->renderers);
GTK_WIDGET_CLASS (gtk_inspector_magnifier_parent_class)->unrealize (widget);
}
static GtkWidget *
gtk_inspector_magnifier_create_renderer_widget (gpointer item,
gpointer user_data)
{
GtkInspectorMagnifier *self = user_data;
GtkInspectorMagnifierPrivate *priv = gtk_inspector_magnifier_get_instance_private (self);
GdkPaintable *paintable = item;
GdkPaintable *diff;
GtkWidget *vbox, *hbox, *label, *picture;
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
gtk_widget_set_size_request (vbox, 160, 60);
label = gtk_label_new (g_object_get_data (G_OBJECT (paintable), "description"));
gtk_container_add (GTK_CONTAINER (vbox), label);
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
gtk_container_add (GTK_CONTAINER (vbox), hbox);
picture = gtk_picture_new_for_paintable (paintable);
gtk_container_add (GTK_CONTAINER (hbox), picture);
diff = gtk_diff_paintable_new (paintable, priv->paintable);
picture = gtk_picture_new_for_paintable (diff);
gtk_container_add (GTK_CONTAINER (hbox), picture);
g_object_unref (diff);
return vbox;
}
static void
gtk_inspector_magnifier_init (GtkInspectorMagnifier *self)
{
self->priv = gtk_inspector_magnifier_get_instance_private (self);
gtk_widget_init_template (GTK_WIDGET (self));
self->priv->renderers = g_list_store_new (GDK_TYPE_PAINTABLE);
gtk_list_box_bind_model (GTK_LIST_BOX (self->priv->renderer_listbox),
G_LIST_MODEL (self->priv->renderers),
gtk_inspector_magnifier_create_renderer_widget,
self,
NULL);
self->priv->paintable = gtk_widget_paintable_new (NULL);
}
void
gtk_inspector_magnifier_set_object (GtkInspectorMagnifier *sl,
GObject *object)
gtk_inspector_magnifier_set_object (GtkInspectorMagnifier *self,
GObject *object)
{
sl->priv->object = NULL;
GtkInspectorMagnifierPrivate *priv = gtk_inspector_magnifier_get_instance_private (self);
priv->object = NULL;
if (!GTK_IS_WIDGET (object) || !gtk_widget_is_visible (GTK_WIDGET (object)))
{
gtk_widget_hide (GTK_WIDGET (sl));
_gtk_magnifier_set_inspected (GTK_MAGNIFIER (sl->priv->magnifier), NULL);
gtk_widget_hide (GTK_WIDGET (self));
_gtk_magnifier_set_inspected (GTK_MAGNIFIER (priv->magnifier), NULL);
gtk_widget_paintable_set_widget (GTK_WIDGET_PAINTABLE (priv->paintable), NULL);
return;
}
gtk_widget_show (GTK_WIDGET (sl));
gtk_widget_show (GTK_WIDGET (self));
sl->priv->object = GTK_WIDGET (object);
priv->object = GTK_WIDGET (object);
_gtk_magnifier_set_inspected (GTK_MAGNIFIER (sl->priv->magnifier), GTK_WIDGET (object));
_gtk_magnifier_set_coords (GTK_MAGNIFIER (sl->priv->magnifier), 0, 0);
_gtk_magnifier_set_inspected (GTK_MAGNIFIER (priv->magnifier), GTK_WIDGET (object));
gtk_widget_paintable_set_widget (GTK_WIDGET_PAINTABLE (priv->paintable), GTK_WIDGET (object));
_gtk_magnifier_set_coords (GTK_MAGNIFIER (priv->magnifier), 0, 0);
}
static void
@@ -108,6 +240,17 @@ set_property (GObject *object,
}
}
static void
gtk_inspector_magnifier_dispose (GObject *object)
{
GtkInspectorMagnifier *self = GTK_INSPECTOR_MAGNIFIER (object);
GtkInspectorMagnifierPrivate *priv = gtk_inspector_magnifier_get_instance_private (self);
g_clear_object (&priv->renderers);
G_OBJECT_CLASS (gtk_inspector_magnifier_parent_class)->dispose (object);
}
static void
constructed (GObject *object)
{
@@ -116,6 +259,8 @@ constructed (GObject *object)
g_object_bind_property (sl->priv->adjustment, "value",
sl->priv->magnifier, "magnification",
G_BINDING_SYNC_CREATE);
G_OBJECT_CLASS (gtk_inspector_magnifier_parent_class)->constructed (object);
}
static void
@@ -126,14 +271,19 @@ gtk_inspector_magnifier_class_init (GtkInspectorMagnifierClass *klass)
object_class->get_property = get_property;
object_class->set_property = set_property;
object_class->dispose = gtk_inspector_magnifier_dispose;
object_class->constructed = constructed;
g_object_class_install_property (object_class, PROP_ADJUSTMENT,
g_param_spec_object ("adjustment", NULL, NULL,
GTK_TYPE_ADJUSTMENT, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
widget_class->realize = gtk_inspector_magnifier_realize;
widget_class->unrealize = gtk_inspector_magnifier_unrealize;
gtk_widget_class_set_template_from_resource (widget_class, "/org/gtk/libgtk/inspector/magnifier.ui");
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorMagnifier, magnifier);
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorMagnifier, renderer_listbox);
}
// vim: set et sw=2 ts=2:

View File

@@ -5,9 +5,19 @@
<object class="GtkScrolledWindow">
<property name="expand">1</property>
<child>
<object class="GtkMagnifier" id="magnifier">
<property name="visible">True</property>
<property name="resize">True</property>
<object class="GtkBox">
<child>
<object class="GtkMagnifier" id="magnifier">
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<property name="resize">True</property>
</object>
</child>
<child>
<object class="GtkListBox" id="renderer_listbox">
<property name="width-request">240</property>
</object>
</child>
</object>
</child>
</object>

View File

@@ -205,6 +205,7 @@ gtk_public_sources = files([
'gtkcssprovider.c',
'gtkcsssection.c',
'gtkdialog.c',
'gtkdiffpaintable.c',
'gtkdnd.c',
'gtkdragdest.c',
'gtkdragsource.c',
@@ -312,6 +313,7 @@ gtk_public_sources = files([
'gtkrenderbackground.c',
'gtkrenderborder.c',
'gtkrendericon.c',
'gtkrendererpaintable.c',
'gtkrendernodepaintable.c',
'gtkrevealer.c',
'gtkroot.c',

View File

@@ -49,7 +49,7 @@ save_image (cairo_surface_t *surface,
{
char *filename = get_output_file (test_name, ".node", extension);
g_test_message ("Storing test result image at %s", filename);
g_print ("Storing test result image at %s\n", filename);
g_assert (cairo_surface_write_to_png (surface, filename) == CAIRO_STATUS_SUCCESS);
g_free (filename);
}
@@ -82,8 +82,8 @@ main (int argc, char **argv)
window = gdk_surface_new_toplevel (gdk_display_get_default(), 10 , 10);
renderer = gsk_renderer_new_for_surface (window);
g_test_message ("Node file: '%s'\n", node_file);
g_test_message ("PNG file: '%s'\n", png_file);
g_print ("Node file: '%s'\n", node_file);
g_print ("PNG file: '%s'\n", png_file);
/* Load the render node from the given .node file */
{
@@ -94,7 +94,7 @@ main (int argc, char **argv)
if (!g_file_get_contents (node_file, &contents, &len, &error))
{
g_test_message ("Could not open node file: %s\n", error->message);
g_print ("Could not open node file: %s\n", error->message);
g_clear_error (&error);
g_test_fail ();
return -1;
@@ -132,7 +132,5 @@ main (int argc, char **argv)
if (diff_surface)
save_image (diff_surface, node_file, ".diff.png");
g_assert (diff_surface == NULL);
return 0;
return diff_surface == NULL ? 0 : 1;
}

View File

@@ -0,0 +1,19 @@
transform {
/* adding the perspective line here turns the matrix from a 2D
category into a 3D category. It does not have any visual effect. */
transform: perspective(200) scale(2);
child: container {
color {
bounds: 0 0 50 50;
color: transparent;
}
clip {
clip: 10 10 30 30;
child: color {
bounds: 0 0 50 50;
color: red;
}
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 269 B

View File

@@ -0,0 +1,41 @@
color {
color: white;
bounds: 50 70 80 90;
}
clip {
clip: 60 80 60 70;
child: rounded-clip {
clip: 20 50 100 100 / 50;
child: color {
bounds: 20 50 100 100;
color: red;
}
}
}
debug {
message: "clipped out area";
child: container {
color {
bounds: 115 80 5 45;
color: black;
}
color {
bounds: 110 122 5 11;
color: black;
}
color {
bounds: 100 130 10 10;
color: black;
}
color {
bounds: 91 140 11 5;
color: black;
}
color {
bounds: 60 145 35 5;
color: black;
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@@ -0,0 +1,36 @@
color {
bounds: 40 40 70 70;
color: transparent;
}
clip {
clip: 50 50 50 50;
child: opacity {
opacity: 0.4;
child: rounded-clip {
clip: 50 50 100 100 / 50;
child: color {
bounds: 50 50 100 100;
color: red;
}
}
}
}
debug {
message: "clipped out area";
child: container {
color {
bounds: 50 70 10 30;
color: black;
}
color {
bounds: 55 55 20 20;
color: black;
}
color {
bounds: 70 50 30 10;
color: black;
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -0,0 +1,8 @@
outset-shadow {
outline: 100 100 100 100;
color: black;
dx: -100;
dy: 100;
spread: 10;
blur: 0;
}

View File

Before

Width:  |  Height:  |  Size: 527 B

After

Width:  |  Height:  |  Size: 527 B

View File

@@ -0,0 +1,8 @@
outset-shadow {
outline: 100 100 100 100;
color: black;
dx: 10;
dy: 0;
spread: 10;
blur: 0;
}

View File

Before

Width:  |  Height:  |  Size: 391 B

After

Width:  |  Height:  |  Size: 391 B

View File

@@ -0,0 +1,8 @@
outset-shadow {
outline: 100 100 100 100;
color: black;
dx: 0;
dy: 10;
spread: 10;
blur: 0;
}

View File

Before

Width:  |  Height:  |  Size: 391 B

After

Width:  |  Height:  |  Size: 391 B

View File

@@ -0,0 +1,8 @@
outset-shadow {
outline: 100 100 200 200 / 7 7 0 0;
color: black;
dx: 0;
dy: 0;
spread: 1;
blur: 0;
}

View File

Before

Width:  |  Height:  |  Size: 761 B

After

Width:  |  Height:  |  Size: 761 B

View File

@@ -0,0 +1,8 @@
outset-shadow {
outline: 100 100 100 100;
color: black;
dx: 0;
dy: 0;
spread: 10;
blur: 0;
}

View File

Before

Width:  |  Height:  |  Size: 396 B

After

Width:  |  Height:  |  Size: 396 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 157 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 613 B

Binary file not shown.

View File

@@ -0,0 +1,13 @@
cross-fade {
progress: 0.5;
start: color {
bounds: 0 0 200 200;
color: white;
}
end: linear-gradient {
bounds: 100 100 200 200;
start: 200 100;
end: 200 300;
stops: 0 red, 1 blue;
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -0,0 +1,9 @@
rounded-clip {
clip: 0 0 50 100 / 25;
child: linear-gradient {
bounds: 0 0 50 100;
start: 0 0;
end: 0 100;
stops: 0 red, 1 blue;
}
}

View File

@@ -0,0 +1,14 @@
container {
linear-gradient {
bounds: 0 0 50 100;
start: 0 0;
end: 0 100;
stops: 0 red, 1 blue;
}
linear-gradient {
bounds: 50 0 50 100;
start: 50 0;
end: 100 0;
stops: 0 red, 1 blue;
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 744 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 543 B

Binary file not shown.

View File

@@ -0,0 +1,8 @@
outset-shadow {
outline: 100 100 40 40 / 5.714286 2.857143 17.142859 22.857143;
color: rgba(0, 0, 0, 0.5);
dx: 20;
dy: 20;
spread: 1;
blur: 4;
}

View File

@@ -0,0 +1,8 @@
outset-shadow {
outline: 100 100 200 200;
color: black;
dx: 0;
dy: 0;
spread: 0;
blur: 40;
}

View File

@@ -0,0 +1,8 @@
outset-shadow {
outline: 100 100 40 40 / 5.714286 2.857143 17.142859 22.857143;
color: rgba(0, 0, 0, 0.5);
dx: 0;
dy: 0;
spread: 1;
blur: 4;
}

View File

@@ -0,0 +1,8 @@
outset-shadow {
outline: 100 100 200 200 / 50 0 50 0;
color: rgba(0, 0, 0, 0.5);
dx: 10;
dy: 10;
spread: 30;
blur: 0;
}

View File

@@ -0,0 +1,8 @@
outset-shadow {
outline: 100 100 200 200 / 0 100 0 100;
color: rgba(0, 0, 0, 0.5);
dx: 10;
dy: 10;
spread: 30;
blur: 0;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 790 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1011 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 613 B

View File

@@ -18,6 +18,14 @@ compare_render = executable(
install_dir: testexecdir
)
node_parser = executable(
'node-parser',
['node-parser.c'],
dependencies: libgtk_dep,
install: get_option('install-tests'),
install_dir: testexecdir
)
test('nodes (cairo)', test_render_nodes,
args: [ '--tap', '-k' ],
env: [ 'GIO_USE_VOLUME_MONITOR=unix',
@@ -32,32 +40,19 @@ test('nodes (cairo)', test_render_nodes,
# Interesting render nodes proven to be rendered 'correctly' by the GL renderer.
gl_tests = [
['outset shadow simple', 'outset_shadow_simple'],
['outset shadow offset x', 'outset_shadow_offsetx'],
['outset shadow offset y', 'outset_shadow_offsety'],
['outset shadow offset both', 'outset_shadow_offset_both'],
['outset shadow rounded1', 'outset_shadow_rounded1'],
['outset shadow rounded2', 'outset_shadow_rounded2'],
['outset shadow rounded top', 'outset_shadow_rounded_top'],
['outset shadow blurred simple', 'outset_shadow_blurred_simple'],
['outset shadow blurred small', 'outset_shadow_blurred_small'],
['outset shadow blurred offset', 'outset_shadow_blurred_offset'],
['outset shadow transformed', 'outset_shadow_translated'],
['Transform in Shadow', 'shadow_transform1'],
['Crossfade simple', 'cross_fade'],
['Crossfade transformed', 'cross_fade_transformed'],
['Clipped crossfade', 'clipped_cross_fade'],
['Clipped rounded clip', 'clipped_rounded_clip'],
['clipped rounded clip2', 'transformed_clipped_rounded_clip'],
['gradient simple', 'gradient_simple'],
['gradient transformed', 'gradient_transformed'],
['gradient clipped', 'gradient_clipped'],
['offscreen opacity', 'opacity_clip'],
]
foreach gl_test : gl_tests
test('GL ' + gl_test[0], compare_render,
args: [join_paths(meson.current_source_dir(), 'gl', gl_test[1] + '.node'),
args: [join_paths(meson.current_source_dir(), 'gl', gl_test[1] + '.txt'),
join_paths(meson.current_source_dir(), 'gl', gl_test[1] + '.gl.png')],
env: [ 'GIO_USE_VOLUME_MONITOR=unix',
'GSETTINGS_BACKEND=memory',
@@ -70,6 +65,63 @@ foreach gl_test : gl_tests
suite: 'gsk')
endforeach
compare_render_tests = [
'clip-coordinates-3d',
'clipped_rounded_clip',
'opacity_clip',
'outset_shadow_offset_both',
'outset_shadow_offset_x',
'outset_shadow_offset_y',
'outset_shadow_rounded_top',
'outset_shadow_simple',
]
renderers = [
# name exclude term
[ 'opengl', '' ],
[ 'cairo', '-3d' ],
]
foreach renderer : renderers
foreach test : compare_render_tests
if (renderer[1] != '' and test.contains(renderer[1]))
continue
endif
test(renderer[0] + ' ' + test, compare_render,
args: [join_paths(meson.current_source_dir(), 'compare', test + '.node'),
join_paths(meson.current_source_dir(), 'compare', test + '.png')],
env: [ 'GIO_USE_VOLUME_MONITOR=unix',
'GSETTINGS_BACKEND=memory',
'GTK_CSD=1',
'G_ENABLE_DIAGNOSTIC=0',
'G_TEST_SRCDIR=@0@'.format(meson.current_source_dir()),
'G_TEST_BUILDDIR=@0@'.format(meson.current_build_dir()),
'GSK_RENDERER=' + renderer[0]
],
suite: [ 'gsk', 'gsk-compare', 'gsk-' + renderer[0], 'gsk-compare-' + renderer[0] ])
endforeach
endforeach
node_parser_tests = [
'crash1',
'crash2',
'crash3',
]
foreach test : node_parser_tests
test('parser ' + test, node_parser,
args: [join_paths(meson.current_source_dir(), 'nodeparser', test + '.node')],
env: [ 'GIO_USE_VOLUME_MONITOR=unix',
'GSETTINGS_BACKEND=memory',
'GTK_CSD=1',
'G_ENABLE_DIAGNOSTIC=0',
'G_TEST_SRCDIR=@0@'.format(meson.current_source_dir()),
'G_TEST_BUILDDIR=@0@'.format(meson.current_build_dir()),
'GSK_RENDERER=opengl'
],
suite: 'gsk')
endforeach
if have_vulkan
test('nodes (vulkan)', test_render_nodes,
args: [ '--tap', '-k' ],

View File

@@ -0,0 +1,30 @@
#include <gtk/gtk.h>
int
main (int argc, char **argv)
{
GError *error = NULL;
GskRenderNode *node;
GBytes *bytes;
GFile *file;
g_assert (argc == 2);
gtk_init ();
file = g_file_new_for_commandline_arg (argv[1]);
bytes = g_file_load_bytes (file, NULL, NULL, &error);
g_assert_no_error (error);
g_assert (bytes != NULL);
node = gsk_render_node_deserialize (bytes, &error);
if (error)
g_test_message ("Error: %s\n", error->message);
g_clear_error (&error);
g_clear_pointer (&node, gsk_render_node_unref);
g_bytes_unref (bytes);
g_object_unref (file);
return 0;
}

View File

@@ -0,0 +1,2 @@
opacity {
child = color {

View File

@@ -0,0 +1,3 @@
cross-fade {
from: color {
co

View File

@@ -0,0 +1,18 @@
cross-fade {
start: container {
color {
color: red;
bounds: 0 0 10 10;
}
color {
color: green;
bounds: 10 10 10 10;
}
}
end: color {
color: blue;
bounds: 5 5 10 10;
}
progress: 0.2;
}