Compare commits
16 Commits
wip/matthi
...
wip/otte/n
Author | SHA1 | Date | |
---|---|---|---|
|
4757dad35b | ||
|
4a0b73ce58 | ||
|
e25621d46a | ||
|
2054facb21 | ||
|
dae0dabb51 | ||
|
0fa5efa628 | ||
|
9a875a7515 | ||
|
e95d171163 | ||
|
aba832559f | ||
|
7ef0e00659 | ||
|
8497327b7c | ||
|
f4a76d5bcb | ||
|
74eadf4181 | ||
|
e0dc257bd3 | ||
|
2f58d65abd | ||
|
b76344bfac |
@@ -1,3 +1,4 @@
|
||||
subdir('gtk-demo')
|
||||
subdir('icon-browser')
|
||||
subdir('node-editor')
|
||||
subdir('widget-factory')
|
||||
|
28
demos/node-editor/main.c
Normal 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);
|
||||
}
|
17
demos/node-editor/meson.build
Normal 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)
|
114
demos/node-editor/node-editor-application.c
Normal 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);
|
||||
}
|
38
demos/node-editor/node-editor-application.h
Normal 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__ */
|
408
demos/node-editor/node-editor-window.c
Normal 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);
|
||||
}
|
42
demos/node-editor/node-editor-window.h
Normal 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__ */
|
118
demos/node-editor/node-editor-window.ui
Normal 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>
|
6
demos/node-editor/node-editor.gresource.xml
Normal 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>
|
@@ -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
@@ -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
@@ -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
153
gsk/gskcsstokenizerprivate.h
Normal 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__ */
|
@@ -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
10
gsk/gskrendernodeparserprivate.h
Normal 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
|
@@ -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;
|
||||
}
|
||||
|
@@ -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
|
||||
|
@@ -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__ */
|
||||
|
@@ -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',
|
||||
|
@@ -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
@@ -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;
|
||||
}
|
||||
|
43
gtk/gtkdiffpaintableprivate.h
Normal 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
@@ -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;
|
||||
}
|
||||
|
43
gtk/gtkrendererpaintableprivate.h
Normal 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__ */
|
@@ -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:
|
||||
|
@@ -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>
|
||||
|
@@ -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',
|
||||
|
@@ -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;
|
||||
}
|
||||
|
19
testsuite/gsk/compare/clip-coordinates-3d.node
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
BIN
testsuite/gsk/compare/clip-coordinates-3d.png
Normal file
After Width: | Height: | Size: 269 B |
41
testsuite/gsk/compare/clipped_rounded_clip.node
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
BIN
testsuite/gsk/compare/clipped_rounded_clip.png
Normal file
After Width: | Height: | Size: 3.1 KiB |
36
testsuite/gsk/compare/opacity_clip.node
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
BIN
testsuite/gsk/compare/opacity_clip.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
8
testsuite/gsk/compare/outset_shadow_offset_both.node
Normal file
@@ -0,0 +1,8 @@
|
||||
outset-shadow {
|
||||
outline: 100 100 100 100;
|
||||
color: black;
|
||||
dx: -100;
|
||||
dy: 100;
|
||||
spread: 10;
|
||||
blur: 0;
|
||||
}
|
Before Width: | Height: | Size: 527 B After Width: | Height: | Size: 527 B |
8
testsuite/gsk/compare/outset_shadow_offset_x.node
Normal file
@@ -0,0 +1,8 @@
|
||||
outset-shadow {
|
||||
outline: 100 100 100 100;
|
||||
color: black;
|
||||
dx: 10;
|
||||
dy: 0;
|
||||
spread: 10;
|
||||
blur: 0;
|
||||
}
|
Before Width: | Height: | Size: 391 B After Width: | Height: | Size: 391 B |
8
testsuite/gsk/compare/outset_shadow_offset_y.node
Normal file
@@ -0,0 +1,8 @@
|
||||
outset-shadow {
|
||||
outline: 100 100 100 100;
|
||||
color: black;
|
||||
dx: 0;
|
||||
dy: 10;
|
||||
spread: 10;
|
||||
blur: 0;
|
||||
}
|
Before Width: | Height: | Size: 391 B After Width: | Height: | Size: 391 B |
8
testsuite/gsk/compare/outset_shadow_rounded_top.node
Normal 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;
|
||||
}
|
Before Width: | Height: | Size: 761 B After Width: | Height: | Size: 761 B |
8
testsuite/gsk/compare/outset_shadow_simple.node
Normal file
@@ -0,0 +1,8 @@
|
||||
outset-shadow {
|
||||
outline: 100 100 100 100;
|
||||
color: black;
|
||||
dx: 0;
|
||||
dy: 0;
|
||||
spread: 10;
|
||||
blur: 0;
|
||||
}
|
Before Width: | Height: | Size: 396 B After Width: | Height: | Size: 396 B |
Before Width: | Height: | Size: 157 B |
Before Width: | Height: | Size: 613 B |
13
testsuite/gsk/gl/cross_fade.txt
Normal 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;
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 1.6 KiB |
9
testsuite/gsk/gl/gradient_clipped.txt
Normal 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;
|
||||
}
|
||||
}
|
14
testsuite/gsk/gl/gradient_simple.txt
Normal 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;
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 744 B |
Before Width: | Height: | Size: 543 B |
8
testsuite/gsk/gl/outset_shadow_blurred_offset.txt
Normal 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;
|
||||
}
|
8
testsuite/gsk/gl/outset_shadow_blurred_simple.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
outset-shadow {
|
||||
outline: 100 100 200 200;
|
||||
color: black;
|
||||
dx: 0;
|
||||
dy: 0;
|
||||
spread: 0;
|
||||
blur: 40;
|
||||
}
|
8
testsuite/gsk/gl/outset_shadow_blurred_small.txt
Normal 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;
|
||||
}
|
8
testsuite/gsk/gl/outset_shadow_rounded1.txt
Normal 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;
|
||||
}
|
8
testsuite/gsk/gl/outset_shadow_rounded2.txt
Normal 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;
|
||||
}
|
Before Width: | Height: | Size: 790 B |
Before Width: | Height: | Size: 1011 B |
Before Width: | Height: | Size: 613 B |
@@ -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' ],
|
||||
|
30
testsuite/gsk/node-parser.c
Normal 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;
|
||||
}
|
2
testsuite/gsk/nodeparser/crash1.node
Normal file
@@ -0,0 +1,2 @@
|
||||
opacity {
|
||||
child = color {
|
3
testsuite/gsk/nodeparser/crash2.node
Normal file
@@ -0,0 +1,3 @@
|
||||
cross-fade {
|
||||
from: color {
|
||||
co
|
18
testsuite/gsk/nodeparser/crash3.node
Normal 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;
|
||||
}
|
||||
|