Merge branch 'builder-tool-scope' into 'main'

docs: Update gtk4-builder-tool docs

See merge request GNOME/gtk!5145
This commit is contained in:
Matthias Clasen 2022-10-19 18:34:25 +00:00
commit 8a3f1a1fa1
6 changed files with 367 additions and 108 deletions

View File

@ -13,7 +13,7 @@ SYNOPSIS
| **gtk4-builder-tool** <COMMAND> [OPTIONS...] <FILE>
|
| **gtk4-builder-tool** validate [OPTIONS...] <FILE>
| **gtk4-builder-tool** enumerate <FILE>
| **gtk4-builder-tool** enumerate [OPTIONS...] <FILE>
| **gtk4-builder-tool** simplify [OPTIONS...] <FILE>
| **gtk4-builder-tool** preview [OPTIONS...] <FILE>
| **gtk4-builder-tool** screenshot [OPTIONS...] <FILE>
@ -40,9 +40,13 @@ errors to ``stderr``.
Enumeration
^^^^^^^^^^^
The ``enumerate`` command lists all the named objects that are present in the UI
The ``enumerate`` command prints all the named objects that are present in the UI
definition file.
``--callbacks``
Print the names of callbacks as well.
Preview
^^^^^^^

213
tools/fake-scope.c Normal file
View File

@ -0,0 +1,213 @@
/* Copyright 2015 Red Hat, Inc.
*
* GTK+ 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.
*
* GLib 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 GTK+; see the file COPYING. If not,
* see <http://www.gnu.org/licenses/>.
*
* Author: Matthias Clasen
*/
#include "config.h"
#include "fake-scope.h"
#include "gtk-builder-tool.h"
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <glib/gi18n.h>
#include <glib/gprintf.h>
#include <glib/gstdio.h>
/* {{{ Scope implementation */
struct _FakeScope
{
GtkBuilderCScope parent;
GPtrArray *types;
GPtrArray *callbacks;
};
static GtkBuilderScopeInterface *parent_scope_iface;
static void
dummy_cb (void)
{
}
static GClosure *
fake_scope_create_closure (GtkBuilderScope *scope,
GtkBuilder *builder,
const char *function_name,
GtkBuilderClosureFlags flags,
GObject *object,
GError **error)
{
FakeScope *self = FAKE_SCOPE (scope);
GClosure *closure;
gboolean swapped = flags & GTK_BUILDER_CLOSURE_SWAPPED;
g_ptr_array_add (self->callbacks, g_strdup (function_name));
if (object == NULL)
object = gtk_builder_get_current_object (builder);
if (object)
{
if (swapped)
closure = g_cclosure_new_object_swap (dummy_cb, object);
else
closure = g_cclosure_new_object (dummy_cb, object);
}
else
{
if (swapped)
closure = g_cclosure_new_swap (dummy_cb, NULL, NULL);
else
closure = g_cclosure_new (dummy_cb, NULL, NULL);
}
return closure;
}
static GType
fake_scope_get_type_from_name (GtkBuilderScope *scope,
GtkBuilder *builder,
const char *type_name)
{
FakeScope *self = FAKE_SCOPE (scope);
GType type;
type = parent_scope_iface->get_type_from_name (scope, builder, type_name);
g_ptr_array_add (self->types, g_strdup (type_name));
return type;
}
static GType
fake_scope_get_type_from_function (GtkBuilderScope *scope,
GtkBuilder *builder,
const char *function_name)
{
FakeScope *self = FAKE_SCOPE (scope);
GType type;
type = parent_scope_iface->get_type_from_function (scope, builder, function_name);
if (type != G_TYPE_INVALID)
g_ptr_array_add (self->types, g_strdup (g_type_name (type)));
return type;
}
static void
fake_scope_scope_init (GtkBuilderScopeInterface *iface)
{
parent_scope_iface = g_type_interface_peek_parent (iface);
iface->get_type_from_name = fake_scope_get_type_from_name;
iface->get_type_from_function = fake_scope_get_type_from_function;
iface->create_closure = fake_scope_create_closure;
}
G_DEFINE_TYPE_WITH_CODE (FakeScope, fake_scope, GTK_TYPE_BUILDER_CSCOPE,
G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDER_SCOPE,
fake_scope_scope_init))
static void
fake_scope_init (FakeScope *scope)
{
scope->types = g_ptr_array_new_with_free_func (g_free);
scope->callbacks = g_ptr_array_new_with_free_func (g_free);
}
static void
fake_scope_finalize (GObject *object)
{
FakeScope *self = FAKE_SCOPE (object);
g_ptr_array_unref (self->types);
g_ptr_array_unref (self->callbacks);
G_OBJECT_CLASS (fake_scope_parent_class)->finalize (object);
}
static void
fake_scope_class_init (FakeScopeClass *class)
{
G_OBJECT_CLASS (class)->finalize = fake_scope_finalize;
}
/* }}} */
/* {{{ API */
FakeScope *
fake_scope_new (void)
{
return g_object_new (fake_scope_get_type (), NULL);
}
static int
cmp_strings (gconstpointer a,
gconstpointer b)
{
const char **aa = (const char **)a;
const char **bb = (const char **)b;
return strcmp (*aa, *bb);
}
static void
g_ptr_array_unique (GPtrArray *array,
GCompareFunc cmp)
{
int i;
i = 1;
while (i < array->len)
{
gconstpointer *one = g_ptr_array_index (array, i - 1);
gconstpointer *two = g_ptr_array_index (array, i);
if (cmp (&one, &two) == 0)
g_ptr_array_remove_index (array, i);
else
i++;
}
}
GPtrArray *
fake_scope_get_types (FakeScope *self)
{
g_ptr_array_sort (self->types, cmp_strings);
g_ptr_array_unique (self->types, cmp_strings);
return self->types;
}
GPtrArray *
fake_scope_get_callbacks (FakeScope *self)
{
g_ptr_array_sort (self->callbacks, cmp_strings);
g_ptr_array_unique (self->callbacks, cmp_strings);
return self->callbacks;
}
/* }}} */
/* vim:set foldmethod=marker expandtab: */

9
tools/fake-scope.h Normal file
View File

@ -0,0 +1,9 @@
#pragma once
#include <gtk.h>
G_DECLARE_FINAL_TYPE (FakeScope, fake_scope, FAKE, SCOPE, GtkBuilderCScope)
FakeScope * fake_scope_new (void);
GPtrArray * fake_scope_get_types (FakeScope *self);
GPtrArray * fake_scope_get_callbacks (FakeScope *self);

View File

@ -29,6 +29,7 @@
#include <gtk/gtk.h>
#include "gtkbuilderprivate.h"
#include "gtk-builder-tool.h"
#include "fake-scope.h"
static const char *
object_get_id (GObject *object)
@ -42,15 +43,16 @@ object_get_id (GObject *object)
void
do_enumerate (int *argc, const char ***argv)
{
FakeScope *scope;
GtkBuilder *builder;
GError *error = NULL;
int ret;
GSList *list, *l;
GObject *object;
const char *name;
gboolean callbacks = FALSE;
char **filenames = NULL;
GOptionContext *context;
const GOptionEntry entries[] = {
{ "callbacks", 0, 0, G_OPTION_ARG_NONE, &callbacks, "Also print callbacks", NULL },
{ G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &filenames, NULL, N_("FILE") },
{ NULL, }
};
@ -59,7 +61,7 @@ do_enumerate (int *argc, const char ***argv)
context = g_option_context_new (NULL);
g_option_context_set_translation_domain (context, GETTEXT_PACKAGE);
g_option_context_add_main_entries (context, entries, NULL);
g_option_context_set_summary (context, _("List all named objects."));
g_option_context_set_summary (context, _("Print all named objects."));
if (!g_option_context_parse (context, argc, (char ***)argv, &error))
{
@ -83,6 +85,9 @@ do_enumerate (int *argc, const char ***argv)
}
builder = gtk_builder_new ();
scope = fake_scope_new ();
gtk_builder_set_scope (builder, GTK_BUILDER_SCOPE (scope));
ret = gtk_builder_add_from_file (builder, filenames[0], &error);
if (ret == 0)
@ -91,11 +96,14 @@ do_enumerate (int *argc, const char ***argv)
exit (1);
}
if (callbacks)
g_print ("Objects:\n");
list = gtk_builder_get_objects (builder);
for (l = list; l; l = l->next)
{
object = l->data;
name = object_get_id (object);
GObject *object = l->data;
const char *name = object_get_id (object);
if (g_str_has_prefix (name, "___") && g_str_has_suffix (name, "___"))
continue;
@ -103,6 +111,27 @@ do_enumerate (int *argc, const char ***argv)
}
g_slist_free (list);
if (callbacks)
{
GPtrArray *names;
gboolean need_prefix = TRUE;
names = fake_scope_get_callbacks (scope);
for (int i = 0; i < names->len; i++)
{
const char *name = g_ptr_array_index (names, i);
if (need_prefix)
{
need_prefix = FALSE;
g_print ("\nCallbacks:\n");
}
g_print ("%s\n", name);
}
}
g_object_unref (scope);
g_object_unref (builder);
g_strfreev (filenames);

View File

@ -29,6 +29,8 @@
#include <gtk/gtk.h>
#include "gtkbuilderprivate.h"
#include "gtk-builder-tool.h"
#include "fake-scope.h"
static GType
make_fake_type (const char *type_name,
@ -54,73 +56,10 @@ make_fake_type (const char *type_name,
0);
}
static void
do_validate_template (const char *filename,
const char *type_name,
const char *parent_name)
{
GType template_type;
GObject *object;
GtkBuilder *builder;
GError *error = NULL;
int ret;
/* Only make a fake type if it doesn't exist yet.
* This lets us e.g. validate the GtkFileChooserWidget template.
*/
template_type = g_type_from_name (type_name);
if (template_type == G_TYPE_INVALID)
template_type = make_fake_type (type_name, parent_name);
object = g_object_new (template_type, NULL);
if (!object)
{
g_printerr ("Failed to create an instance of the template type %s\n", type_name);
exit (1);
}
builder = gtk_builder_new ();
ret = gtk_builder_extend_with_template (builder, object , template_type, " ", 1, &error);
if (ret)
ret = gtk_builder_add_from_file (builder, filename, &error);
g_object_unref (builder);
if (ret == 0)
{
g_printerr ("%s\n", error->message);
exit (1);
}
}
/* {{{ Deprecations */
static gboolean
parse_template_error (const char *message,
char **class_name,
char **parent_name)
{
char *p;
p = strstr (message, "(class '");
if (p)
{
*class_name = g_strdup (p + strlen ("(class '"));
p = strstr (*class_name, "'");
if (p)
*p = '\0';
}
p = strstr (message, ", parent '");
if (p)
{
*parent_name = g_strdup (p + strlen (", parent '"));
p = strstr (*parent_name, "'");
if (p)
*p = '\0';
}
return *class_name && *parent_name;
}
static gboolean
is_deprecated (GObject *object)
is_deprecated (const char *name)
{
const char *names[] = {
"GtkAppChooser",
@ -160,52 +99,33 @@ is_deprecated (GObject *object)
NULL
};
return g_strv_contains (names, G_OBJECT_TYPE_NAME (object));
}
static const char *
object_get_id (GObject *object)
{
const char *name;
if (GTK_IS_BUILDABLE (object))
name = gtk_buildable_get_buildable_id (GTK_BUILDABLE (object));
else
name = g_object_get_data (object, "gtk-builder-id");
if (g_str_has_prefix (name, "___"))
return NULL;
return name;
return g_strv_contains (names, name);
}
static gboolean
check_deprecations (GtkBuilder *builder,
GError **error)
fake_scope_check_deprecations (FakeScope *self,
GError **error)
{
GSList *objects;
GPtrArray *types;
GString *s;
types = fake_scope_get_types (self);
s = g_string_new ("");
objects = gtk_builder_get_objects (builder);
for (GSList *l = objects; l; l = l->next)
for (int i = 0; i < types->len; i++)
{
GObject *obj = l->data;
const char *name = g_ptr_array_index (types, i);
if (is_deprecated (obj))
if (is_deprecated (name))
{
if (s->len == 0)
g_string_append (s, "Deprecated types:\n");
g_string_append_printf (s, "%s", G_OBJECT_TYPE_NAME (obj));
if (object_get_id (obj))
g_string_append_printf (s, " (named '%s')", object_get_id (obj));
g_string_append_printf (s, "%s", name);
g_string_append (s, "\n");
}
}
g_slist_free (objects);
if (s->len > 0)
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, s->str);
@ -214,37 +134,118 @@ check_deprecations (GtkBuilder *builder,
return *error == NULL;
}
/* }}} */
static gboolean
validate_template (const char *filename,
const char *type_name,
const char *parent_name,
gboolean deprecations)
{
GType template_type;
GObject *object;
FakeScope *scope;
GtkBuilder *builder;
GError *error = NULL;
gboolean ret;
/* Only make a fake type if it doesn't exist yet.
* This lets us e.g. validate the GtkFileChooserWidget template.
*/
template_type = g_type_from_name (type_name);
if (template_type == G_TYPE_INVALID)
template_type = make_fake_type (type_name, parent_name);
object = g_object_new (template_type, NULL);
if (!object)
{
g_printerr ("Failed to create an instance of the template type %s\n", type_name);
return FALSE;
}
builder = gtk_builder_new ();
scope = fake_scope_new ();
gtk_builder_set_scope (builder, GTK_BUILDER_SCOPE (scope));
ret = gtk_builder_extend_with_template (builder, object, template_type, " ", 1, &error);
if (ret)
ret = gtk_builder_add_from_file (builder, filename, &error);
if (ret && deprecations)
ret = fake_scope_check_deprecations (scope, &error);
g_object_unref (scope);
g_object_unref (builder);
if (!ret)
{
g_printerr ("%s\n", error->message);
g_error_free (error);
}
return ret;
}
static gboolean
parse_template_error (const char *message,
char **class_name,
char **parent_name)
{
char *p;
p = strstr (message, "(class '");
if (p)
{
*class_name = g_strdup (p + strlen ("(class '"));
p = strstr (*class_name, "'");
if (p)
*p = '\0';
}
p = strstr (message, ", parent '");
if (p)
{
*parent_name = g_strdup (p + strlen (", parent '"));
p = strstr (*parent_name, "'");
if (p)
*p = '\0';
}
return *class_name && *parent_name;
}
static gboolean
validate_file (const char *filename,
gboolean deprecations)
{
FakeScope *scope;
GtkBuilder *builder;
GError *error = NULL;
int ret;
gboolean ret;
char *class_name = NULL;
char *parent_name = NULL;
builder = gtk_builder_new ();
scope = fake_scope_new ();
gtk_builder_set_scope (builder, GTK_BUILDER_SCOPE (scope));
ret = gtk_builder_add_from_file (builder, filename, &error);
if (ret && deprecations)
ret = check_deprecations (builder, &error);
ret = fake_scope_check_deprecations (scope, &error);
g_object_unref (scope);
g_object_unref (builder);
if (ret == 0)
if (!ret)
{
if (g_error_matches (error, GTK_BUILDER_ERROR, GTK_BUILDER_ERROR_UNHANDLED_TAG) &&
parse_template_error (error->message, &class_name, &parent_name))
{
do_validate_template (filename, class_name, parent_name);
ret = validate_template (filename, class_name, parent_name, deprecations);
}
else
{
g_printerr ("%s\n", error->message);
return FALSE;
}
g_error_free (error);
}
return TRUE;
return ret;
}
void
@ -284,3 +285,5 @@ do_validate (int *argc, const char ***argv)
g_strfreev (filenames);
}
/* vim:set foldmethod=marker expandtab: */

View File

@ -29,7 +29,8 @@ gtk_tools = [
'gtk-builder-tool-validate.c',
'gtk-builder-tool-enumerate.c',
'gtk-builder-tool-screenshot.c',
'gtk-builder-tool-preview.c'], [libgtk_dep] ],
'gtk-builder-tool-preview.c',
'fake-scope.c'], [libgtk_dep] ],
['gtk4-update-icon-cache', ['updateiconcache.c'] + extra_update_icon_cache_objs, [ libgtk_static_dep ] ],
['gtk4-encode-symbolic-svg', ['encodesymbolic.c'], [ libgtk_static_dep ] ],
]