Add custom parser for guides defined in GtkBuilder UI files

Like we describe constraints, we can also define guides.
This commit is contained in:
Emmanuele Bassi 2019-07-01 19:22:48 +01:00
parent 6bc156c237
commit 8ab609e4e7

View File

@ -103,6 +103,27 @@
* The "source" and "target" attributes can be set to "super" to indicate * The "source" and "target" attributes can be set to "super" to indicate
* that the constraint target is the widget using the GtkConstraintLayout. * that the constraint target is the widget using the GtkConstraintLayout.
* *
* Additionally, the "constraints" element can also contain a description
* of the #GtkConstraintGuides used by the layout:
*
* |[
* <constraints>
* <guide min-width="100" max-width="500" name="hspace"/>
* <guide min-height="64" nat-height="128" name="vspace" strength="strong"/>
* </constraints>
* ]|
*
* The "guide" element has the following optional attributes:
*
* - "min-width", "nat-width", and "max-width", describe the minimum,
* natural, and maximum width of the guide, respectively
* - "min-height", "nat-height", and "max-height", describe the minimum,
* natural, and maximum height of the guide, respectively
* - "strength" describes the strength of the constraint on the natural
* size of the guide; if not specified, the constraint is assumed to
* have a medium strength
* - "name" describes a name for the guide, useful when debugging
*
* # Using the Visual Format Language * # Using the Visual Format Language
* *
* Complex constraints can be described using a compact syntax called VFL, * Complex constraints can be described using a compact syntax called VFL,
@ -1187,6 +1208,7 @@ typedef struct {
GtkConstraintLayout *layout; GtkConstraintLayout *layout;
GtkBuilder *builder; GtkBuilder *builder;
GList *constraints; GList *constraints;
GList *guides;
} ConstraintsParserData; } ConstraintsParserData;
typedef struct { typedef struct {
@ -1200,6 +1222,14 @@ typedef struct {
double multiplier; double multiplier;
} ConstraintData; } ConstraintData;
typedef struct {
char *name;
char *strength;
struct {
int min, nat, max;
} sizes[2];
} GuideData;
static void static void
constraint_data_free (gpointer _data) constraint_data_free (gpointer _data)
{ {
@ -1215,6 +1245,17 @@ constraint_data_free (gpointer _data)
g_free (data); g_free (data);
} }
static void
guide_data_free (gpointer _data)
{
GuideData *data = _data;
g_free (data->name);
g_free (data->strength);
g_free (data);
}
static void static void
parse_double (const char *string, parse_double (const char *string,
double *value_p, double *value_p,
@ -1241,6 +1282,32 @@ parse_double (const char *string,
errno = saved_errno; errno = saved_errno;
} }
static void
parse_int (const char *string,
int *value_p,
int default_value)
{
gint64 value;
char *endptr;
int saved_errno;
if (string == NULL || string[0] == '\0')
{
*value_p = default_value;
return;
}
saved_errno = errno;
errno = 0;
value = g_ascii_strtoll (string, &endptr, 10);
if (errno == 0 && endptr != string)
*value_p = (int) value;
else
*value_p = default_value;
errno = saved_errno;
}
static GtkConstraint * static GtkConstraint *
constraint_data_to_constraint (const ConstraintData *data, constraint_data_to_constraint (const ConstraintData *data,
GtkBuilder *builder, GtkBuilder *builder,
@ -1322,6 +1389,8 @@ constraint_data_to_constraint (const ConstraintData *data,
data->strength, data->strength,
&strength, &strength,
error); error);
if (!res)
return NULL;
} }
else else
strength = GTK_CONSTRAINT_STRENGTH_REQUIRED; strength = GTK_CONSTRAINT_STRENGTH_REQUIRED;
@ -1340,6 +1409,38 @@ constraint_data_to_constraint (const ConstraintData *data,
strength); strength);
} }
static GtkConstraintGuide *
guide_data_to_guide (const GuideData *data,
GtkBuilder *builder,
GError **error)
{
int strength;
gboolean res;
if (data->strength != NULL)
{
res = _gtk_builder_enum_from_string (GTK_TYPE_CONSTRAINT_STRENGTH,
data->strength,
&strength,
error);
if (!res)
return NULL;
}
else
strength = GTK_CONSTRAINT_STRENGTH_MEDIUM;
return g_object_new (GTK_TYPE_CONSTRAINT_GUIDE,
"min-width", data->sizes[GTK_ORIENTATION_HORIZONTAL].min,
"nat-width", data->sizes[GTK_ORIENTATION_HORIZONTAL].nat,
"max-width", data->sizes[GTK_ORIENTATION_HORIZONTAL].max,
"min-height", data->sizes[GTK_ORIENTATION_VERTICAL].min,
"nat-height", data->sizes[GTK_ORIENTATION_VERTICAL].nat,
"max-height", data->sizes[GTK_ORIENTATION_VERTICAL].max,
"strength", strength,
"name", data->name,
NULL);
}
static void static void
constraints_start_element (GMarkupParseContext *context, constraints_start_element (GMarkupParseContext *context,
const char *element_name, const char *element_name,
@ -1399,6 +1500,44 @@ constraints_start_element (GMarkupParseContext *context,
data->constraints = g_list_prepend (data->constraints, cdata); data->constraints = g_list_prepend (data->constraints, cdata);
} }
else if (strcmp (element_name, "guide") == 0)
{
const char *min_width, *nat_width, *max_width;
const char *min_height, *nat_height, *max_height;
const char *strength_str;
const char *name;
GuideData *gdata;
if (!_gtk_builder_check_parent (data->builder, context, "constraints", error))
return;
if (!g_markup_collect_attributes (element_name, attr_names, attr_values, error,
G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL, "min-width", &min_width,
G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL, "nat-width", &nat_width,
G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL, "max-width", &max_width,
G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL, "min-height", &min_height,
G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL, "nat-height", &nat_height,
G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL, "max-height", &max_height,
G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL, "strength", &strength_str,
G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL, "name", &name,
G_MARKUP_COLLECT_INVALID))
{
_gtk_builder_prefix_error (data->builder, context, error);
return;
}
gdata = g_new0 (GuideData, 1);
parse_int (min_width, &(gdata->sizes[GTK_ORIENTATION_HORIZONTAL].min), 0);
parse_int (nat_width, &(gdata->sizes[GTK_ORIENTATION_HORIZONTAL].nat), 0);
parse_int (max_width, &(gdata->sizes[GTK_ORIENTATION_HORIZONTAL].max), G_MAXINT);
parse_int (min_height, &(gdata->sizes[GTK_ORIENTATION_VERTICAL].min), 0);
parse_int (nat_height, &(gdata->sizes[GTK_ORIENTATION_VERTICAL].nat), 0);
parse_int (max_height, &(gdata->sizes[GTK_ORIENTATION_VERTICAL].max), G_MAXINT);
gdata->name = g_strdup (name);
gdata->strength = g_strdup (strength_str);
data->guides = g_list_prepend (data->guides, gdata);
}
else else
{ {
_gtk_builder_error_unhandled_tag (data->builder, context, _gtk_builder_error_unhandled_tag (data->builder, context,
@ -1436,6 +1575,7 @@ gtk_constraint_layout_custom_tag_start (GtkBuildable *buildable,
data->layout = g_object_ref (GTK_CONSTRAINT_LAYOUT (buildable)); data->layout = g_object_ref (GTK_CONSTRAINT_LAYOUT (buildable));
data->builder = builder; data->builder = builder;
data->constraints = NULL; data->constraints = NULL;
data->guides = NULL;
*parser = constraints_parser; *parser = constraints_parser;
*parser_data = data; *parser_data = data;
@ -1468,6 +1608,25 @@ gtk_constraint_layout_custom_finished (GtkBuildable *buildable,
{ {
GList *l; GList *l;
data->guides = g_list_reverse (data->guides);
for (l = data->guides; l != NULL; l = l->next)
{
const GuideData *gdata = l->data;
GtkConstraintGuide *g;
GError *error = NULL;
g = guide_data_to_guide (gdata, builder, &error);
if (error != NULL)
{
g_critical ("Unable to parse guide definition: %s",
error->message);
g_error_free (error);
continue;
}
gtk_constraint_layout_add_guide (data->layout, g);
}
data->constraints = g_list_reverse (data->constraints); data->constraints = g_list_reverse (data->constraints);
for (l = data->constraints; l != NULL; l = l->next) for (l = data->constraints; l != NULL; l = l->next)
{ {
@ -1493,6 +1652,7 @@ gtk_constraint_layout_custom_finished (GtkBuildable *buildable,
} }
g_list_free_full (data->constraints, constraint_data_free); g_list_free_full (data->constraints, constraint_data_free);
g_list_free_full (data->guides, guide_data_free);
g_object_unref (data->layout); g_object_unref (data->layout);
g_free (data); g_free (data);
} }