Compare commits

...

19 Commits

Author SHA1 Message Date
Matthias Clasen
d5ea7aa3a0 glyphpaintable: fix an off-by-one 2023-01-25 07:57:59 -05:00
Matthias Clasen
8bfdbb8eb8 wip: reshuffling 2023-01-25 07:28:32 -05:00
Matthias Clasen
7b03ce983e glyph paintable: Be safe against NULL 2023-01-24 13:44:50 -05:00
Matthias Clasen
be3435590c glyph demo: small refactoring 2023-01-24 13:44:33 -05:00
Matthias Clasen
7a84de8918 Add a glyph grid 2023-01-24 07:55:31 -05:00
Matthias Clasen
ce23f588d0 glyphpaintable: Improve palette handling
Parse the custom colors when they are set,
not every time we snapshot.
2023-01-23 22:31:21 -05:00
Matthias Clasen
1869909a7d demo: Some fixes 2023-01-23 22:30:58 -05:00
Matthias Clasen
c8b1229d83 glyphpaintable: Avoid some redraws
Determine if the glyph uses foreground or
palette colors, and use that information to
avoid invalidating if not necessary.
2023-01-23 21:34:51 -05:00
Matthias Clasen
5b9ece807d glyph paintable: simplify 2023-01-23 20:26:23 -05:00
Matthias Clasen
1412b4c797 glyphpaintable: Use a GskGlyphNode 2023-01-23 20:03:56 -05:00
Matthias Clasen
540ef17131 Add a GskGlyphNode
This is experimental, starting to push
the rendering from the GtkGlyphPaintable
downwards. Only a cairo fallback implementation
so far.
2023-01-23 20:03:56 -05:00
Matthias Clasen
dbe07159a4 Support icon fonts in GtkImage/GtkPicture
When creating paintables from a file or resource,
check if we have a font, and create a glyph paintable.
2023-01-22 17:33:58 -05:00
Matthias Clasen
5e6b52fbfc Add GtkGlyphPaintable
This is a paintable that wraps a font file,
and renders a single glyph. The glyph can be
selected by the "glyph" property, but the
paintable looks for a glyph with the name
"icon0" first, so single-icon fonts can be
made to work out of the box.

The paintable has properties to set font
variations, as well as colors.

It implements GtkSymbolicPaintable, and will
override the foreground, error, warning and
success colors with theme colors when used
symbolically.
2023-01-22 17:29:57 -05:00
Matthias Clasen
9e19450504 iconhelper: Handle GtkSymbolicPaintable
If we are given a GtkSymbolicPaintable,
treat it as symbolic.
2023-01-22 13:17:11 -05:00
Matthias Clasen
a73abd0b78 Revert "Add a GtkImage for comparison"
This reverts commit 6736888fb9.
2023-01-22 13:16:55 -05:00
Matthias Clasen
6736888fb9 Add a GtkImage for comparison
This is just a temporary hack, we don't
really want an image here.
2023-01-22 13:16:31 -05:00
Matthias Clasen
a84616167b glyphpaintable: Implement GtkSymbolicPaintable 2023-01-22 13:16:03 -05:00
Matthias Clasen
3ec1ca13e0 glyphpaintable: Smart defaults
Default to showing the glyph named 'icon0'
or 'A'. For single-glyph icon fonts, this
should avoid the need to specify a glyph
explicitly.
2023-01-22 11:37:14 -05:00
Matthias Clasen
5c270a7b0e wip: Add a glyph paintable demo
This is using hb-cairo to draw a single
glyph from a hb_face_t, applying variations
and colors.
2023-01-21 20:05:12 -05:00
33 changed files with 3489 additions and 5 deletions

View File

@@ -0,0 +1,207 @@
#include "colorpicker.h"
#include <gtk/gtk.h>
enum {
PROP_FOREGROUND= 1,
PROP_BACKGROUND,
NUM_PROPERTIES
};
static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
struct _ColorPicker
{
GtkWidget parent;
GtkWidget *grid;
GtkWidget *fg;
GtkWidget *bg;
GtkWidget *swap;
GSimpleAction *reset_action;
};
struct _ColorPickerClass
{
GtkWidgetClass parent_class;
};
G_DEFINE_TYPE (ColorPicker, color_picker, GTK_TYPE_WIDGET);
static void
reset (GSimpleAction *action,
GVariant *parameter,
ColorPicker *self)
{
GdkRGBA fg, bg;
fg = (GdkRGBA) { 0, 0, 0, 1 };
bg = (GdkRGBA) { 1, 1, 1, 1 };
g_object_freeze_notify (G_OBJECT (self));
g_object_set (self, "foreground", &fg, NULL);
g_object_set (self, "background", &bg, NULL);
g_object_thaw_notify (G_OBJECT (self));
g_simple_action_set_enabled (self->reset_action, FALSE);
}
static void
fg_changed (GObject *obj,
GParamSpec *pspec,
ColorPicker *self)
{
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FOREGROUND]);
g_simple_action_set_enabled (self->reset_action, TRUE);
}
static void
bg_changed (GObject *obj,
GParamSpec *pspec,
ColorPicker *self)
{
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_BACKGROUND]);
g_simple_action_set_enabled (self->reset_action, TRUE);
}
static void
swap_colors (GtkButton *button,
ColorPicker *self)
{
const GdkRGBA *fg, *bg;
g_object_freeze_notify (G_OBJECT (self));
g_object_get (self, "foreground", &fg, "background", &bg, NULL);
g_object_set (self, "foreground", bg, "background", fg, NULL);
g_object_thaw_notify (G_OBJECT (self));
}
static void
color_picker_init (ColorPicker *self)
{
gtk_widget_init_template (GTK_WIDGET (self));
g_signal_connect (self->fg, "notify::rgba", G_CALLBACK (fg_changed), self);
g_signal_connect (self->bg, "notify::rgba", G_CALLBACK (bg_changed), self);
g_signal_connect (self->swap, "clicked", G_CALLBACK (swap_colors), self);
self->reset_action = g_simple_action_new ("reset", NULL);
g_simple_action_set_enabled (self->reset_action, FALSE);
g_signal_connect (self->reset_action, "activate", G_CALLBACK (reset), self);
reset (NULL, NULL, self);
}
static void
color_picker_dispose (GObject *object)
{
//ColorPicker *self = COLOR_PICKER (object);
gtk_widget_dispose_template (GTK_WIDGET (object), COLOR_PICKER_TYPE);
G_OBJECT_CLASS (color_picker_parent_class)->dispose (object);
}
static void
color_picker_finalize (GObject *object)
{
//ColorPicker *self = COLOR_PICKER (object);
G_OBJECT_CLASS (color_picker_parent_class)->finalize (object);
}
static void
color_picker_set_property (GObject *object,
unsigned int prop_id,
const GValue *value,
GParamSpec *pspec)
{
ColorPicker *self = COLOR_PICKER (object);
const GdkRGBA *color;
switch (prop_id)
{
case PROP_FOREGROUND:
color = g_value_get_boxed (value);
g_object_set (self->fg, "rgba", color, NULL);
break;
case PROP_BACKGROUND:
color = g_value_get_boxed (value);
g_object_set (self->bg, "rgba", color, NULL);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
color_picker_get_property (GObject *object,
unsigned int prop_id,
GValue *value,
GParamSpec *pspec)
{
ColorPicker *self = COLOR_PICKER (object);
const GdkRGBA *color;
switch (prop_id)
{
case PROP_FOREGROUND:
g_object_get (self->fg, "rgba", &color, NULL);
g_value_set_boxed (value, color);
break;
case PROP_BACKGROUND:
g_object_get (self->bg, "rgba", &color, NULL);
g_value_set_boxed (value, color);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
color_picker_class_init (ColorPickerClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
object_class->dispose = color_picker_dispose;
object_class->finalize = color_picker_finalize;
object_class->get_property = color_picker_get_property;
object_class->set_property = color_picker_set_property;
properties[PROP_FOREGROUND] =
g_param_spec_boxed ("foreground", "", "",
GDK_TYPE_RGBA,
G_PARAM_READWRITE);
properties[PROP_BACKGROUND] =
g_param_spec_boxed ("background", "", "",
GDK_TYPE_RGBA,
G_PARAM_READWRITE);
g_object_class_install_properties (G_OBJECT_CLASS (class), NUM_PROPERTIES, properties);
gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (class),
"/paintable_glyph/colorpicker.ui");
gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), ColorPicker, grid);
gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), ColorPicker, fg);
gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), ColorPicker, bg);
gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), ColorPicker, swap);
gtk_widget_class_set_layout_manager_type (GTK_WIDGET_CLASS (class), GTK_TYPE_BIN_LAYOUT);
gtk_widget_class_set_css_name (GTK_WIDGET_CLASS (class), "colorpicker");
}
GAction *
color_picker_get_reset_action (ColorPicker *self)
{
return G_ACTION (self->reset_action);
}

View File

@@ -0,0 +1,15 @@
#pragma once
#include <gtk/gtk.h>
#define COLOR_PICKER_TYPE (color_picker_get_type ())
#define COLOR_PICKER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), COLOR_PICKER_TYPE, ColorPicker))
typedef struct _ColorPicker ColorPicker;
typedef struct _ColorPickerClass ColorPickerClass;
GType color_picker_get_type (void);
GAction * color_picker_get_reset_action (ColorPicker *self);

View File

@@ -0,0 +1,64 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="ColorPicker" parent="GtkWidget">
<child>
<object class="GtkGrid" id="grid">
<child>
<object class="GtkLabel" id="label">
<property name="label" translatable="yes">Foreground</property>
<property name="xalign">0</property>
<layout>
<property name="row">0</property>
<property name="column">0</property>
</layout>
</object>
</child>
<child>
<object class="GtkColorButton" id="fg">
<property name="hexpand">1</property>
<property name="use-alpha">1</property>
<layout>
<property name="row">0</property>
<property name="column">1</property>
</layout>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="label" translatable="yes">Background</property>
<property name="xalign">0</property>
<layout>
<property name="row">1</property>
<property name="column">0</property>
</layout>
</object>
</child>
<child>
<object class="GtkColorButton" id="bg">
<property name="hexpand">1</property>
<property name="use-alpha">1</property>
<layout>
<property name="row">1</property>
<property name="column">1</property>
</layout>
</object>
</child>
<child>
<object class="GtkButton" id="swap">
<property name="icon-name">object-flip-vertical-symbolic</property>
<property name="halign">start</property>
<property name="valign">center</property>
<style>
<class name="circular"/>
</style>
<layout>
<property name="row">0</property>
<property name="column">2</property>
<property name="row-span">2</property>
</layout>
</object>
</child>
</object>
</child>
</template>
</interface>

View File

@@ -220,6 +220,26 @@
<file>svgpaintable.c</file>
<file>org.gtk.gtk4.NodeEditor.Devel.svg</file>
</gresource>
<gresource prefix="/paintable_glyph">
<file>paintable_glyph.ui</file>
<file>paintable_glyph.css</file>
<file>fontvariations.h</file>
<file>fontvariations.c</file>
<file>fontvariations.ui</file>
<file>rangeedit.h</file>
<file>rangeedit.c</file>
<file>rangeedit.ui</file>
<file>fontcolors.h</file>
<file>fontcolors.c</file>
<file>fontcolors.ui</file>
<file>glyphpicker.h</file>
<file>glyphpicker.c</file>
<file>colorpicker.h</file>
<file>colorpicker.c</file>
<file>colorpicker.ui</file>
<file>fontpicker.h</file>
<file>fontpicker.c</file>
</gresource>
<gresource prefix="/shortcuts">
<file>shortcuts.ui</file>
<file>shortcuts-builder.ui</file>
@@ -319,6 +339,7 @@
<file>paintable.c</file>
<file>paintable_animated.c</file>
<file>paintable_emblem.c</file>
<file>paintable_glyph.c</file>
<file>paintable_mediastream.c</file>
<file>paintable_svg.c</file>
<file>paintable_symbolic.c</file>

452
demos/gtk-demo/fontcolors.c Normal file
View File

@@ -0,0 +1,452 @@
#include "config.h"
#include "fontcolors.h"
#include <gtk/gtk.h>
#include <hb-ot.h>
#include <hb-gobject.h>
enum {
PROP_FACE = 1,
PROP_PALETTE_INDEX,
PROP_CUSTOM_COLORS,
NUM_PROPERTIES
};
static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
struct _FontColors
{
GtkWidget parent;
GtkGrid *label;
GtkGrid *grid;
GSimpleAction *reset_action;
gboolean has_colors;
unsigned int palette_index;
GtkCheckButton *default_check;
GList *custom_colors;
gboolean has_custom_colors;
hb_face_t *face;
};
struct _FontColorsClass
{
GtkWidgetClass parent_class;
};
G_DEFINE_TYPE (FontColors, font_colors, GTK_TYPE_WIDGET);
static void
palette_changed (GtkCheckButton *button,
FontColors *self)
{
self->palette_index = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (button), "palette"));
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_PALETTE_INDEX]);
g_simple_action_set_enabled (self->reset_action,
(self->palette_index != 0) || self->has_custom_colors);
}
static char *
get_name (hb_face_t *face,
hb_ot_name_id_t name_id)
{
char *info;
unsigned int len;
len = hb_ot_name_get_utf8 (face, name_id, HB_LANGUAGE_INVALID, NULL, NULL);
len++;
info = g_new (char, len);
hb_ot_name_get_utf8 (face, name_id, HB_LANGUAGE_INVALID, &len, info);
return info;
}
static void
custom_color_changed (GtkColorButton *button,
GParamSpec *pspec,
GtkWidget *swatch)
{
GdkRGBA rgba;
FontColors *self;
gtk_color_chooser_get_rgba (GTK_COLOR_CHOOSER (button), &rgba);
g_object_set (swatch, "rgba", &rgba, NULL);
self = FONT_COLORS (gtk_widget_get_ancestor (swatch, FONT_COLORS_TYPE));
self->has_custom_colors = TRUE;
g_simple_action_set_enabled (self->reset_action, TRUE);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_CUSTOM_COLORS]);
}
static char *
get_custom_colors (FontColors *self)
{
GString *s;
GList *l;
if (!self->has_custom_colors)
return NULL;
s = g_string_new ("");
for (l = self->custom_colors; l; l = l->next)
{
GtkWidget *button = l->data;
const GdkRGBA rgba;
if (l != self->custom_colors)
g_string_append (s, ",");
gtk_color_chooser_get_rgba (GTK_COLOR_CHOOSER (button), &rgba);
if (rgba.red != 0 || rgba.green != 0 || rgba.blue != 0 || rgba.alpha != 0)
g_string_append_printf (s, "%.2x%.2x%.2x%.2x",
(unsigned int)(rgba.red * 255),
(unsigned int)(rgba.green * 255),
(unsigned int)(rgba.blue * 255),
(unsigned int)(rgba.alpha * 255));
}
g_print ("%s\n", s->str);
return g_string_free (s, FALSE);
}
static GtkWidget *
make_palette (unsigned int n_colors,
hb_color_t *colors,
const char **names)
{
GtkWidget *palette;
palette = gtk_grid_new ();
/* HACK - defeat first-child/last-child theming */
gtk_grid_attach (GTK_GRID (palette), gtk_picture_new (), -1, 0, 1, 1);
for (int k = 0; k < n_colors; k++)
{
GtkWidget *swatch;
swatch = g_object_new (g_type_from_name ("GtkColorSwatch"),
"rgba", &(GdkRGBA){ hb_color_get_red (colors[k])/255.,
hb_color_get_green (colors[k])/255.,
hb_color_get_blue (colors[k])/255.,
hb_color_get_alpha (colors[k])/255.},
"selectable", FALSE,
"has-menu", FALSE,
"can-drag", FALSE,
"width-request", 16,
"height-request", 16,
"can-focus", FALSE,
NULL);
if (names[k])
gtk_widget_set_tooltip_text (swatch, names[k]);
gtk_grid_attach (GTK_GRID (palette), swatch, k % 6, k / 6, 1, 1);
}
/* HACK - defeat first-child/last-child theming */
gtk_grid_attach (GTK_GRID (palette), gtk_picture_new (), 6, 0, 1, 1);
return palette;
}
static void
reset_one_color (GtkWidget *button)
{
gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (button), &(GdkRGBA) { 0,0,0,0 });
}
static void
update_colors (FontColors *self)
{
GtkWidget *child;
GtkWidget *check;
unsigned int n_palettes;
unsigned int n_colors;
hb_color_t *colors;
char **color_names;
GtkWidget *palette;
GList *custom_colors;
hb_ot_name_id_t name_id;
char *name;
self->has_custom_colors = FALSE;
g_object_ref (self->label);
while ((child = gtk_widget_get_first_child (GTK_WIDGET (self->grid))))
gtk_grid_remove (self->grid, child);
gtk_grid_attach (self->grid, GTK_WIDGET (self->label), 0, -4, 2, 1);
g_object_unref (self->label);
self->default_check = NULL;
self->has_colors = hb_ot_color_has_palettes (self->face);
gtk_widget_set_visible (GTK_WIDGET (self), self->has_colors);
if (!self->has_colors)
{
g_simple_action_set_enabled (self->reset_action, FALSE);
return;
}
n_palettes = hb_ot_color_palette_get_count (self->face);
n_colors = hb_ot_color_palette_get_colors (self->face, 0, 0, NULL, NULL);
colors = g_new (hb_color_t, n_colors);
color_names = g_new0 (char *, n_colors);
for (int k = 0; k < n_colors; k++)
{
name_id = hb_ot_color_palette_color_get_name_id (self->face, k);
if (name_id != HB_OT_NAME_ID_INVALID)
color_names[k] = get_name (self->face, name_id);
}
for (int i = 0; i < n_palettes; i++)
{
name_id = hb_ot_color_palette_get_name_id (self->face, i);
if (name_id != HB_OT_NAME_ID_INVALID)
name = get_name (self->face, name_id);
else if (i != 0)
name = g_strdup_printf ("Palette %d", i);
else
name = g_strdup ("Default");
check = gtk_check_button_new_with_label (name);
g_free (name);
g_object_set_data (G_OBJECT (check), "palette", GUINT_TO_POINTER (i));
if (self->palette_index == i)
gtk_check_button_set_active (GTK_CHECK_BUTTON (check), TRUE);
g_signal_connect (check, "toggled", G_CALLBACK (palette_changed), self);
if (i == 0)
self->default_check = GTK_CHECK_BUTTON (check);
else
gtk_check_button_set_group (GTK_CHECK_BUTTON (check), self->default_check);
gtk_grid_attach (self->grid, check, 0, i, 1, 1);
hb_ot_color_palette_get_colors (self->face, i, 0, &n_colors, colors);
palette = make_palette (n_colors, colors, (const char **)color_names);
gtk_widget_set_valign (palette, GTK_ALIGN_CENTER);
gtk_grid_attach (self->grid, palette, 1, i, 1, 1);
}
#ifdef HAVE_CAIRO_FONT_OPTIONS_SET_CUSTOM_PALETTE_COLOR
{
GtkWidget *expander;
GtkWidget *custom_grid;
GtkWidget *swatch;
expander = gtk_expander_new ("Overrides");
gtk_grid_attach (self->grid, expander, 0, n_palettes, 1, 1);
for (int k = 0; k < n_colors; k++)
colors[k] = HB_COLOR (0, 0, 0, 0);
palette = make_palette (n_colors, colors, (const char **)color_names);
gtk_grid_attach (self->grid, palette, 1, n_palettes, 1, 1);
g_list_free (self->custom_colors);
custom_colors = NULL;
custom_grid = gtk_grid_new ();
gtk_widget_add_css_class (custom_grid, "custom-colors");
gtk_widget_set_hexpand (custom_grid, FALSE);
gtk_grid_attach (GTK_GRID (self->grid), custom_grid, 0, n_palettes + 1, 2, 1);
g_object_bind_property (expander, "expanded", custom_grid, "visible", G_BINDING_SYNC_CREATE);
swatch = gtk_widget_get_first_child (palette);
swatch = gtk_widget_get_next_sibling (swatch);
for (int k = 0; k < n_colors; k++)
{
GtkWidget *label;
GtkWidget *color_button;
GtkWidget *reset_button;
label = gtk_label_new (color_names[k] ? color_names[k] : "");
gtk_label_set_xalign (label, 0);
gtk_widget_set_hexpand (label, TRUE);
gtk_grid_attach (GTK_GRID (custom_grid), label, 0, k, 1, 1);
color_button = gtk_color_button_new ();
gtk_color_chooser_set_use_alpha (GTK_COLOR_CHOOSER (color_button), TRUE);
gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (color_button), &(GdkRGBA){ 0,0,0,0 });
g_signal_connect (color_button, "notify::rgba",
G_CALLBACK (custom_color_changed), swatch);
custom_colors = g_list_prepend (custom_colors, color_button);
gtk_grid_attach (GTK_GRID (custom_grid), color_button, 1, k, 1, 1);
reset_button = gtk_button_new_from_icon_name ("view-refresh-symbolic");
gtk_widget_add_css_class (reset_button, "circular");
gtk_widget_add_css_class (reset_button, "flat");
g_signal_connect_swapped (reset_button, "clicked",
G_CALLBACK (reset_one_color), color_button);
gtk_grid_attach (GTK_GRID (custom_grid), reset_button, 2, k, 1, 1);
swatch = gtk_widget_get_next_sibling (swatch);
}
self->custom_colors = g_list_reverse (custom_colors);
}
#endif
g_free (colors);
g_free (color_names);
}
static void
reset (GSimpleAction *action,
GVariant *parameter,
FontColors *self)
{
if (self->has_colors)
{
self->palette_index = 0;
self->has_custom_colors = FALSE;
gtk_check_button_set_active (self->default_check, TRUE);
for (GList *l = self->custom_colors; l; l = l->next)
reset_one_color (GTK_WIDGET (l->data));
}
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_PALETTE_INDEX]);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_CUSTOM_COLORS]);
g_simple_action_set_enabled (self->reset_action, FALSE);
}
static void
font_colors_init (FontColors *self)
{
gtk_widget_init_template (GTK_WIDGET (self));
self->reset_action = g_simple_action_new ("reset", NULL);
g_simple_action_set_enabled (self->reset_action, FALSE);
g_signal_connect (self->reset_action, "activate", G_CALLBACK (reset), self);
}
static void
font_colors_dispose (GObject *object)
{
FontColors *self = FONT_COLORS (object);
g_list_free (self->custom_colors);
gtk_widget_dispose_template (GTK_WIDGET (object), FONT_COLORS_TYPE);
G_OBJECT_CLASS (font_colors_parent_class)->dispose (object);
}
static void
font_colors_finalize (GObject *object)
{
FontColors *self = FONT_COLORS (object);
g_clear_pointer (&self->face, hb_face_destroy);
G_OBJECT_CLASS (font_colors_parent_class)->finalize (object);
}
static void
font_colors_set_property (GObject *object,
unsigned int prop_id,
const GValue *value,
GParamSpec *pspec)
{
FontColors *self = FONT_COLORS (object);
switch (prop_id)
{
case PROP_FACE:
{
hb_face_t *face = g_value_get_boxed (value);
if (self->face == face)
return;
if (self->face)
hb_face_destroy (self->face);
self->face = face;
if (self->face)
hb_face_reference (self->face);
update_colors (self);
}
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
font_colors_get_property (GObject *object,
unsigned int prop_id,
GValue *value,
GParamSpec *pspec)
{
FontColors *self = FONT_COLORS (object);
switch (prop_id)
{
case PROP_FACE:
g_value_set_boxed (value, self->face);
break;
case PROP_PALETTE_INDEX:
g_value_set_uint (value, self->palette_index);
break;
case PROP_CUSTOM_COLORS:
g_value_take_string (value, get_custom_colors (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
font_colors_class_init (FontColorsClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
object_class->dispose = font_colors_dispose;
object_class->finalize = font_colors_finalize;
object_class->get_property = font_colors_get_property;
object_class->set_property = font_colors_set_property;
properties[PROP_FACE] = g_param_spec_boxed ("face", "", "",
HB_GOBJECT_TYPE_FACE,
G_PARAM_READWRITE);
properties[PROP_PALETTE_INDEX] = g_param_spec_uint ("palette-index", "", "",
0, G_MAXUINT, 0,
G_PARAM_READABLE);
properties[PROP_CUSTOM_COLORS] = g_param_spec_string ("custom-colors", "", "",
NULL,
G_PARAM_READABLE);
g_object_class_install_properties (G_OBJECT_CLASS (class), NUM_PROPERTIES, properties);
gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (class),
"/paintable_glyph/fontcolors.ui");
gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), FontColors, grid);
gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), FontColors, label);
gtk_widget_class_set_css_name (GTK_WIDGET_CLASS (class), "fontcolors");
}
GAction *
font_colors_get_reset_action (FontColors *self)
{
return G_ACTION (self->reset_action);
}

View File

@@ -0,0 +1,15 @@
#pragma once
#include <gtk/gtk.h>
#define FONT_COLORS_TYPE (font_colors_get_type ())
#define FONT_COLORS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FONT_COLORS_TYPE, FontColors))
typedef struct _FontColors FontColors;
typedef struct _FontColorsClass FontColorsClass;
GType font_colors_get_type (void);
GAction * font_colors_get_reset_action (FontColors *self);

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="FontColors" parent="GtkWidget">
<property name="layout-manager"><object class="GtkBinLayout"/></property>
<child>
<object class="GtkGrid" id="grid">
<child>
<object class="GtkLabel" id="label">
<property name="label" translatable="yes">Colors</property>
<property name="margin-bottom">10</property>
<property name="xalign">0</property>
<style>
<class name="heading"/>
</style>
<layout>
<property name="row">-2</property>
<property name="column">0</property>
<property name="column-span">2</property>
</layout>
</object>
</child>
</object>
</child>
</template>
</interface>

220
demos/gtk-demo/fontpicker.c Normal file
View File

@@ -0,0 +1,220 @@
#include "fontpicker.h"
#include <gtk/gtk.h>
#include <hb-ot.h>
#include <hb-gobject.h>
enum {
PROP_FACE = 1,
PROP_FAMILY_NAME,
NUM_PROPERTIES
};
static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
struct _FontPicker
{
GtkWidget parent;
GtkWidget *button;
hb_face_t *face;
};
struct _FontPickerClass
{
GtkWidgetClass parent_class;
};
G_DEFINE_TYPE (FontPicker, font_picker, GTK_TYPE_WIDGET);
static char *
get_family_name (FontPicker *self)
{
char *name;
unsigned int len;
len = hb_ot_name_get_utf8 (self->face, HB_OT_NAME_ID_FONT_FAMILY, HB_LANGUAGE_INVALID, NULL, NULL);
len++;
name = g_new (char, len);
hb_ot_name_get_utf8 (self->face, HB_OT_NAME_ID_FONT_FAMILY, HB_LANGUAGE_INVALID, &len, name);
return name;
}
static void
open_response_cb (GObject *source,
GAsyncResult *result,
void *data)
{
GtkFileDialog *dialog = GTK_FILE_DIALOG (source);
FontPicker *self = FONT_PICKER (data);
GFile *file;
file = gtk_file_dialog_open_finish (dialog, result, NULL);
if (file)
{
font_picker_set_from_file (self, g_file_peek_path (file));
g_object_unref (file);
}
}
static void
show_file_open (FontPicker *self)
{
GtkFileFilter *filter;
GtkFileDialog *dialog;
GListStore *filters;
GFile *folder;
dialog = gtk_file_dialog_new ();
gtk_file_dialog_set_title (dialog, "Open Font");
filter = gtk_file_filter_new ();
gtk_file_filter_set_name (filter, "Font Files");
gtk_file_filter_add_suffix (filter, "ttf");
gtk_file_filter_add_suffix (filter, "otf");
filters = g_list_store_new (GTK_TYPE_FILE_FILTER);
g_list_store_append (filters, filter);
g_object_unref (filter);
gtk_file_dialog_set_filters (dialog, G_LIST_MODEL (filters));
g_object_unref (filters);
folder = g_file_new_for_path ("/usr/share/fonts");
gtk_file_dialog_set_initial_folder (dialog, folder);
g_object_unref (folder);
gtk_file_dialog_open (dialog,
GTK_WINDOW (gtk_widget_get_root (GTK_WIDGET (self))),
NULL,
open_response_cb, self);
}
static void
font_picker_init (FontPicker *self)
{
self->button = gtk_button_new_with_label ("Font");
gtk_widget_set_hexpand (self->button, TRUE);
gtk_widget_set_parent (GTK_WIDGET (self->button), GTK_WIDGET (self));
g_signal_connect_swapped (self->button, "clicked", G_CALLBACK (show_file_open), self);
}
static void
font_picker_dispose (GObject *object)
{
FontPicker *self = FONT_PICKER (object);
g_clear_pointer ((GtkWidget **)&self->button, gtk_widget_unparent);
G_OBJECT_CLASS (font_picker_parent_class)->dispose (object);
}
static void
font_picker_finalize (GObject *object)
{
FontPicker *self = FONT_PICKER (object);
g_clear_pointer (&self->face, hb_face_destroy);
G_OBJECT_CLASS (font_picker_parent_class)->finalize (object);
}
static void
font_picker_set_property (GObject *object,
unsigned int prop_id,
const GValue *value,
GParamSpec *pspec)
{
FontPicker *self = FONT_PICKER (object);
switch (prop_id)
{
case PROP_FACE:
font_picker_set_face (self, (hb_face_t *) g_value_get_boxed (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
font_picker_get_property (GObject *object,
unsigned int prop_id,
GValue *value,
GParamSpec *pspec)
{
FontPicker *self = FONT_PICKER (object);
switch (prop_id)
{
case PROP_FACE:
g_value_set_boxed (value, self->face);
break;
case PROP_FAMILY_NAME:
g_value_take_string (value, get_family_name (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
font_picker_class_init (FontPickerClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
object_class->dispose = font_picker_dispose;
object_class->finalize = font_picker_finalize;
object_class->get_property = font_picker_get_property;
object_class->set_property = font_picker_set_property;
properties[PROP_FACE] =
g_param_spec_boxed ("face", "", "",
HB_GOBJECT_TYPE_FACE,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
properties[PROP_FAMILY_NAME] =
g_param_spec_string ("family-name", "", "",
NULL,
G_PARAM_READABLE);
g_object_class_install_properties (G_OBJECT_CLASS (class), NUM_PROPERTIES, properties);
gtk_widget_class_set_layout_manager_type (GTK_WIDGET_CLASS (class), GTK_TYPE_BIN_LAYOUT);
gtk_widget_class_set_css_name (GTK_WIDGET_CLASS (class), "fontpicker");
}
void
font_picker_set_face (FontPicker *self,
hb_face_t *face)
{
if (self->face == face)
return;
if (self->face)
hb_face_destroy (self->face);
self->face = face;
if (self->face)
hb_face_reference (self->face);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FACE]);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FAMILY_NAME]);
}
void
font_picker_set_from_file (FontPicker *self,
const char *path)
{
hb_blob_t *blob;
hb_face_t *face;
blob = hb_blob_create_from_file (path);
face = hb_face_create (blob, 0);
hb_blob_destroy (blob);
font_picker_set_face (self, face);
hb_face_destroy (face);
}

View File

@@ -0,0 +1,18 @@
#pragma once
#include <gtk/gtk.h>
#define FONT_PICKER_TYPE (font_picker_get_type ())
#define FONT_PICKER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FONT_PICKER_TYPE, FontPicker))
typedef struct _FontPicker FontPicker;
typedef struct _FontPickerClass FontPickerClass;
GType font_picker_get_type (void);
void font_picker_set_face (FontPicker *self,
hb_face_t *face);
void font_picker_set_from_file (FontPicker *self,
const char *path);

View File

@@ -0,0 +1,507 @@
#include "fontvariations.h"
#include <gtk/gtk.h>
#include <hb-ot.h>
#include <hb-gobject.h>
#include "rangeedit.h"
enum {
PROP_FACE = 1,
PROP_VARIATIONS,
NUM_PROPERTIES
};
static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
struct _FontVariations
{
GtkWidget parent;
hb_face_t *face;
GtkGrid *label;
GtkGrid *grid;
GSimpleAction *reset_action;
gboolean has_variations;
GtkWidget *instance_combo;
GHashTable *axes;
GHashTable *instances;
};
struct _FontVariationsClass
{
GtkWidgetClass parent_class;
};
G_DEFINE_TYPE(FontVariations, font_variations, GTK_TYPE_WIDGET);
typedef struct {
guint32 tag;
GtkAdjustment *adjustment;
double default_value;
} Axis;
static guint
axes_hash (gconstpointer v)
{
const Axis *p = v;
return p->tag;
}
static gboolean
axes_equal (gconstpointer v1, gconstpointer v2)
{
const Axis *p1 = v1;
const Axis *p2 = v2;
return p1->tag == p2->tag;
}
static void
unset_instance (GtkAdjustment *adjustment,
FontVariations *self)
{
if (self->instance_combo)
gtk_drop_down_set_selected (GTK_DROP_DOWN (self->instance_combo), 0);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_VARIATIONS]);
g_simple_action_set_enabled (self->reset_action, TRUE);
}
static char *
get_name (hb_face_t *face,
hb_ot_name_id_t name_id)
{
char *info;
unsigned int len;
len = hb_ot_name_get_utf8 (face, name_id, HB_LANGUAGE_INVALID, NULL, NULL);
len++;
info = g_new (char, len);
hb_ot_name_get_utf8 (face, name_id, HB_LANGUAGE_INVALID, &len, info);
return info;
}
static void
add_axis (FontVariations *self,
hb_face_t *hb_face,
hb_ot_var_axis_info_t *ax,
int i)
{
GtkWidget *axis_label;
GtkWidget *axis_scale;
GtkAdjustment *adjustment;
Axis *axis;
char *name;
name = get_name (hb_face, ax->name_id);
axis_label = gtk_label_new (name);
g_free (name);
gtk_widget_set_halign (axis_label, GTK_ALIGN_START);
gtk_widget_set_valign (axis_label, GTK_ALIGN_BASELINE);
gtk_grid_attach (self->grid, axis_label, 0, i, 1, 1);
adjustment = gtk_adjustment_new (ax->default_value, ax->min_value, ax->max_value,
1.0, 10.0, 0.0);
axis_scale = g_object_new (RANGE_EDIT_TYPE,
"adjustment", adjustment,
"default-value", ax->default_value,
"n-chars", 5,
"hexpand", TRUE,
"halign", GTK_ALIGN_FILL,
"valign", GTK_ALIGN_BASELINE,
NULL);
gtk_grid_attach (self->grid, axis_scale, 1, i, 1, 1);
axis = g_new0 (Axis, 1);
axis->tag = ax->tag;
axis->adjustment = adjustment;
axis->default_value = ax->default_value;
g_hash_table_add (self->axes, axis);
g_signal_connect (adjustment, "value-changed", G_CALLBACK (unset_instance), self);
}
typedef struct {
char *name;
unsigned int index;
} Instance;
static guint
instance_hash (gconstpointer v)
{
const Instance *p = v;
return g_str_hash (p->name);
}
static gboolean
instance_equal (gconstpointer v1, gconstpointer v2)
{
const Instance *p1 = v1;
const Instance *p2 = v2;
return g_str_equal (p1->name, p2->name);
}
static void
free_instance (gpointer data)
{
Instance *instance = data;
g_free (instance->name);
g_free (instance);
}
static void
add_instance (FontVariations *self,
hb_face_t *face,
unsigned int index,
GtkStringList *strings,
int pos)
{
Instance *instance;
hb_ot_name_id_t name_id;
instance = g_new0 (Instance, 1);
name_id = hb_ot_var_named_instance_get_subfamily_name_id (face, index);
instance->name = get_name (face, name_id);
instance->index = index;
g_hash_table_add (self->instances, instance);
gtk_string_list_append (strings, instance->name);
}
static void
instance_changed (GtkDropDown *combo,
GParamSpec *pspec,
FontVariations *self)
{
const char *text;
unsigned int selected;
int i;
unsigned int coords_length;
float *coords = NULL;
hb_ot_var_axis_info_t *ai = NULL;
unsigned int n_axes;
GtkStringList *strings;
strings = GTK_STRING_LIST (gtk_drop_down_get_model (combo));
selected = gtk_drop_down_get_selected (combo);
text = gtk_string_list_get_string (strings, selected);
n_axes = hb_ot_var_get_axis_infos (self->face, 0, NULL, NULL);
ai = g_new (hb_ot_var_axis_info_t, n_axes);
hb_ot_var_get_axis_infos (self->face, 0, &n_axes, ai);
coords = g_new (float, n_axes);
if (selected == 0)
goto out;
if (selected == 1)
{
for (i = 0; i < n_axes; i++)
coords[ai[i].axis_index] = ai[i].default_value;
}
else
{
Instance ikey;
Instance *instance;
ikey.name = (char *)text;
instance = g_hash_table_lookup (self->instances, &ikey);
if (!instance)
{
g_print ("did not find instance %s\n", text);
goto out;
}
hb_ot_var_named_instance_get_design_coords (self->face,
instance->index,
&coords_length,
coords);
}
for (i = 0; i < n_axes; i++)
{
Axis *axis;
Axis akey;
double value;
value = coords[ai[i].axis_index];
akey.tag = ai[i].tag;
axis = g_hash_table_lookup (self->axes, &akey);
if (axis)
{
g_signal_handlers_block_by_func (axis->adjustment, unset_instance, self);
gtk_adjustment_set_value (axis->adjustment, value);
g_signal_handlers_unblock_by_func (axis->adjustment, unset_instance, self);
}
}
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_VARIATIONS]);
g_simple_action_set_enabled (self->reset_action, TRUE);
out:
g_free (ai);
g_free (coords);
}
static void
update_variations (FontVariations *self)
{
GtkWidget *child;
hb_face_t *hb_face;
unsigned int n_axes;
hb_ot_var_axis_info_t *ai = NULL;
int i;
hb_face = self->face;
g_object_ref (self->label);
while ((child = gtk_widget_get_first_child (GTK_WIDGET (self->grid))))
gtk_grid_remove (self->grid, child);
gtk_grid_attach (self->grid, GTK_WIDGET (self->label), 0, -2, 2, 1);
g_object_unref (self->label);
self->instance_combo = NULL;
g_hash_table_remove_all (self->axes);
g_hash_table_remove_all (self->instances);
n_axes = hb_ot_var_get_axis_infos (hb_face, 0, NULL, NULL);
self->has_variations = n_axes > 0;
gtk_widget_set_visible (GTK_WIDGET (self), self->has_variations);
if (!self->has_variations)
{
g_simple_action_set_enabled (self->reset_action, FALSE);
return;
}
if (hb_ot_var_get_named_instance_count (hb_face) > 0)
{
GtkWidget *label;
GtkWidget *combo;
GtkStringList *strings;
label = gtk_label_new ("Instance");
gtk_label_set_xalign (GTK_LABEL (label), 0);
gtk_widget_set_halign (label, GTK_ALIGN_START);
gtk_widget_set_valign (label, GTK_ALIGN_BASELINE);
gtk_grid_attach (self->grid, label, 0, -1, 1, 1);
strings = gtk_string_list_new (NULL);
combo = gtk_drop_down_new (G_LIST_MODEL (strings), NULL);
gtk_widget_set_halign (combo, GTK_ALIGN_START);
gtk_widget_set_valign (combo, GTK_ALIGN_BASELINE);
gtk_widget_set_hexpand (combo, TRUE);
gtk_string_list_append (strings, "None");
gtk_string_list_append (strings, "Default");
for (i = 0; i < hb_ot_var_get_named_instance_count (hb_face); i++)
add_instance (self, hb_face, i, strings, i);
gtk_drop_down_set_selected (GTK_DROP_DOWN (combo), 1);
gtk_grid_attach (GTK_GRID (self->grid), combo, 1, -1, 1, 1);
g_signal_connect (combo, "notify::selected", G_CALLBACK (instance_changed), self);
self->instance_combo = combo;
}
ai = g_new (hb_ot_var_axis_info_t, n_axes);
hb_ot_var_get_axis_infos (hb_face, 0, &n_axes, ai);
for (i = 0; i < n_axes; i++)
add_axis (self, hb_face, &ai[i], i);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_VARIATIONS]);
g_free (ai);
}
static char *
get_variations (FontVariations *self)
{
GHashTableIter iter;
Axis *axis;
char buf[G_ASCII_DTOSTR_BUF_SIZE];
const char *sep = "";
GString *s;
if (!self->has_variations)
return g_strdup ("");
s = g_string_new ("");
g_hash_table_iter_init (&iter, self->axes);
while (g_hash_table_iter_next (&iter, (gpointer *)NULL, (gpointer *)&axis))
{
char tag[5];
double value;
hb_tag_to_string (axis->tag, tag);
tag[4] = '\0';
value = gtk_adjustment_get_value (axis->adjustment);
g_string_append_printf (s, "%s%s=%s", sep, tag, g_ascii_dtostr (buf, sizeof (buf), value));
sep = ",";
}
return g_string_free (s, FALSE);
}
static void
reset (GSimpleAction *action,
GVariant *parameter,
FontVariations *self)
{
GHashTableIter iter;
Axis *axis;
if (self->instance_combo)
gtk_drop_down_set_selected (GTK_DROP_DOWN (self->instance_combo), 1);
g_hash_table_iter_init (&iter, self->axes);
while (g_hash_table_iter_next (&iter, (gpointer *)NULL, (gpointer *)&axis))
{
g_signal_handlers_block_by_func (axis->adjustment, unset_instance, self);
gtk_adjustment_set_value (axis->adjustment, axis->default_value);
g_signal_handlers_unblock_by_func (axis->adjustment, unset_instance, self);
}
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_VARIATIONS]);
g_simple_action_set_enabled (self->reset_action, FALSE);
}
static void
font_variations_init (FontVariations *self)
{
gtk_widget_init_template (GTK_WIDGET (self));
self->reset_action = g_simple_action_new ("reset", NULL);
g_simple_action_set_enabled (self->reset_action, FALSE);
g_signal_connect (self->reset_action, "activate", G_CALLBACK (reset), self);
self->instances = g_hash_table_new_full (instance_hash, instance_equal,
NULL, free_instance);
self->axes = g_hash_table_new_full (axes_hash, axes_equal,
NULL, g_free);
}
static void
font_variations_dispose (GObject *object)
{
FontVariations *self = FONT_VARIATIONS (object);
gtk_widget_dispose_template (GTK_WIDGET (object), FONT_VARIATIONS_TYPE);
g_clear_pointer (&self->face, hb_face_destroy);
g_hash_table_unref (self->instances);
g_hash_table_unref (self->axes);
G_OBJECT_CLASS (font_variations_parent_class)->dispose (object);
}
static void
font_variations_finalize (GObject *object)
{
//FontVariations *self = FONT_VARIATIONS (object);
G_OBJECT_CLASS (font_variations_parent_class)->finalize (object);
}
static void
font_variations_set_property (GObject *object,
unsigned int prop_id,
const GValue *value,
GParamSpec *pspec)
{
FontVariations *self = FONT_VARIATIONS (object);
switch (prop_id)
{
case PROP_FACE:
{
hb_face_t *face = g_value_get_boxed (value);
if (self->face)
hb_face_destroy (self->face);
self->face = face;
if (self->face)
hb_face_reference (self->face);
}
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
update_variations (self);
}
static void
font_variations_get_property (GObject *object,
unsigned int prop_id,
GValue *value,
GParamSpec *pspec)
{
FontVariations *self = FONT_VARIATIONS (object);
switch (prop_id)
{
case PROP_FACE:
g_value_set_boxed (value, self->face);
break;
case PROP_VARIATIONS:
g_value_take_string (value, get_variations (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
font_variations_class_init (FontVariationsClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
object_class->dispose = font_variations_dispose;
object_class->finalize = font_variations_finalize;
object_class->get_property = font_variations_get_property;
object_class->set_property = font_variations_set_property;
properties[PROP_FACE] =
g_param_spec_boxed ("face", "", "",
HB_GOBJECT_TYPE_FACE,
G_PARAM_READWRITE);
properties[PROP_VARIATIONS] =
g_param_spec_string ("variations", "", "",
"",
G_PARAM_READABLE);
g_object_class_install_properties (G_OBJECT_CLASS (class), NUM_PROPERTIES, properties);
gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (class),
"/paintable_glyph/fontvariations.ui");
gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), FontVariations, grid);
gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), FontVariations, label);
gtk_widget_class_set_css_name (GTK_WIDGET_CLASS (class), "fontvariations");
}
GAction *
font_variations_get_reset_action (FontVariations *self)
{
return G_ACTION (self->reset_action);
}

View File

@@ -0,0 +1,15 @@
#pragma once
#include <gtk/gtk.h>
#define FONT_VARIATIONS_TYPE (font_variations_get_type ())
#define FONT_VARIATIONS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FONT_VARIATIONS_TYPE, FontVariations))
typedef struct _FontVariations FontVariations;
typedef struct _FontVariationsClass FontVariationsClass;
GType font_variations_get_type (void);
GAction * font_variations_get_reset_action (FontVariations *self);

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="FontVariations" parent="GtkWidget">
<property name="layout-manager"><object class="GtkBinLayout"/></property>
<child>
<object class="GtkGrid" id="grid">
<child>
<object class="GtkLabel" id="label">
<property name="label" translatable="yes">Variations</property>
<property name="margin-bottom">10</property>
<property name="xalign">0</property>
<style>
<class name="heading"/>
</style>
<layout>
<property name="row">-2</property>
<property name="column">0</property>
<property name="column-span">2</property>
</layout>
</object>
</child>
</object>
</child>
</template>
</interface>

174
demos/gtk-demo/glyphmodel.c Normal file
View File

@@ -0,0 +1,174 @@
#include "config.h"
#include "glyphmodel.h"
#include <gtk/gtk.h>
#include <hb-ot.h>
#include <hb-gobject.h>
enum {
PROP_FACE = 1,
NUM_PROPERTIES
};
static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
struct _GlyphModel
{
GObject parent;
hb_face_t *face;
GPtrArray *items;
unsigned int n_items;
};
struct _GlyphModelClass
{
GObjectClass parent_class;
};
static void
update_n_items (GlyphModel *self)
{
unsigned int n_items = 0;
unsigned int n_removed;
if (self->face)
n_items = hb_face_get_glyph_count (self->face);
g_clear_pointer (&self->items, g_ptr_array_unref);
self->items = g_ptr_array_new_with_free_func (g_object_unref);
for (unsigned int i = 0; i < n_items; i++)
{
GdkPaintable *item = gtk_glyph_paintable_new (self->face);
gtk_glyph_paintable_set_glyph (GTK_GLYPH_PAINTABLE (item), i);
g_ptr_array_add (self->items, item);
}
n_removed = self->n_items;
self->n_items = n_items;
g_list_model_items_changed (G_LIST_MODEL (self), 0, n_removed, n_items);
}
void
glyph_model_set_face (GlyphModel *self,
hb_face_t *face)
{
if (self->face)
hb_face_destroy (self->face);
self->face = face;
if (self->face)
hb_face_reference (self->face);
update_n_items (self);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FACE]);
}
static GType
glyph_list_model_get_item_type (GListModel *model)
{
return GTK_TYPE_GLYPH_PAINTABLE;
}
static guint
glyph_list_model_get_n_items (GListModel *model)
{
GlyphModel *self = GLYPH_MODEL (model);
return self->n_items;
}
static gpointer
glyph_list_model_get_item (GListModel *model,
guint position)
{
GlyphModel *self = GLYPH_MODEL (model);
if (position >= self->n_items)
return NULL;
return g_object_ref (g_ptr_array_index (self->items, position));
}
static void
glyph_list_model_init (GListModelInterface *iface)
{
iface->get_item_type = glyph_list_model_get_item_type;
iface->get_n_items = glyph_list_model_get_n_items;
iface->get_item = glyph_list_model_get_item;
}
G_DEFINE_TYPE_WITH_CODE (GlyphModel, glyph_model, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, glyph_list_model_init))
static void
glyph_model_init (GlyphModel *self)
{
}
static void
glyph_model_finalize (GObject *object)
{
GlyphModel *self = GLYPH_MODEL (object);
g_clear_pointer (&self->items, g_ptr_array_unref);
g_clear_pointer (&self->face, hb_face_destroy);
G_OBJECT_CLASS (glyph_model_parent_class)->finalize (object);
}
static void
glyph_model_set_property (GObject *object,
unsigned int prop_id,
const GValue *value,
GParamSpec *pspec)
{
GlyphModel *self = GLYPH_MODEL (object);
switch (prop_id)
{
case PROP_FACE:
glyph_model_set_face (self, g_value_get_boxed (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
glyph_model_get_property (GObject *object,
unsigned int prop_id,
GValue *value,
GParamSpec *pspec)
{
GlyphModel *self = GLYPH_MODEL (object);
switch (prop_id)
{
case PROP_FACE:
g_value_set_object (value, self->face);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
glyph_model_class_init (GlyphModelClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
object_class->finalize = glyph_model_finalize;
object_class->get_property = glyph_model_get_property;
object_class->set_property = glyph_model_set_property;
properties[PROP_FACE] = g_param_spec_boxed ("face", "", "",
HB_GOBJECT_TYPE_FACE,
G_PARAM_READWRITE);
g_object_class_install_properties (G_OBJECT_CLASS (class), NUM_PROPERTIES, properties);
}

View File

@@ -0,0 +1,17 @@
#pragma once
#include <gtk/gtk.h>
#include <hb.h>
#define GLYPH_MODEL_TYPE (glyph_model_get_type ())
#define GLYPH_MODEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GLYPH_MODEL_TYPE, GlyphModel))
typedef struct _GlyphModel GlyphModel;
typedef struct _GlyphModelClass GlyphModelClass;
GType glyph_model_get_type (void);
void glyph_model_set_face (GlyphModel *self,
hb_face_t *face);

View File

@@ -0,0 +1,249 @@
#include "glyphpicker.h"
#include <gtk/gtk.h>
#include <hb-ot.h>
#include <hb-gobject.h>
static void glyph_picker_set_face (GlyphPicker *self,
hb_face_t *face);
static void glyph_picker_set_glyph (GlyphPicker *self,
unsigned int glyph);
enum {
PROP_FACE = 1,
PROP_GLYPH,
PROP_GLYPH_NAME,
NUM_PROPERTIES
};
static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
struct _GlyphPicker
{
GtkWidget parent;
GtkLabel *label;
GtkSpinButton *spin;
GtkLabel *name;
hb_face_t *face;
hb_font_t *font;
};
struct _GlyphPickerClass
{
GtkWidgetClass parent_class;
};
G_DEFINE_TYPE (GlyphPicker, glyph_picker, GTK_TYPE_WIDGET);
static unsigned int
get_glyph (GlyphPicker *self)
{
return (unsigned int) gtk_spin_button_get_value_as_int (self->spin);
}
static char *
get_glyph_name (GlyphPicker *self)
{
char name[64];
hb_font_get_glyph_name (self->font, get_glyph (self), name, sizeof (name));
return g_strdup (name);
}
static void
value_changed (GlyphPicker *self)
{
char *name;
name = get_glyph_name (self);
gtk_label_set_label (self->name, name);
g_free (name);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_GLYPH]);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_GLYPH_NAME]);
}
static void
glyph_picker_init (GlyphPicker *self)
{
self->label = GTK_LABEL (gtk_label_new ("Glyph"));
gtk_widget_set_parent (GTK_WIDGET (self->label), GTK_WIDGET (self));
self->spin = GTK_SPIN_BUTTON (gtk_spin_button_new_with_range (0, 0, 1));
gtk_widget_set_parent (GTK_WIDGET (self->spin), GTK_WIDGET (self));
self->name = GTK_LABEL (gtk_label_new (""));
gtk_widget_set_parent (GTK_WIDGET (self->name), GTK_WIDGET (self));
g_signal_connect_swapped (self->spin, "value-changed", G_CALLBACK (value_changed), self);
}
static void
glyph_picker_dispose (GObject *object)
{
GlyphPicker *self = GLYPH_PICKER (object);
g_clear_pointer ((GtkWidget **)&self->spin, gtk_widget_unparent);
g_clear_pointer ((GtkWidget **)&self->label, gtk_widget_unparent);
G_OBJECT_CLASS (glyph_picker_parent_class)->dispose (object);
}
static void
glyph_picker_finalize (GObject *object)
{
GlyphPicker *self = GLYPH_PICKER (object);
g_clear_pointer (&self->face, hb_face_destroy);
G_OBJECT_CLASS (glyph_picker_parent_class)->finalize (object);
}
static void
update_bounds (GlyphPicker *self)
{
unsigned int glyph_count = 0;
GtkAdjustment *adjustment;
if (self->face)
glyph_count = hb_face_get_glyph_count (self->face);
adjustment = gtk_spin_button_get_adjustment (self->spin);
gtk_adjustment_set_upper (adjustment, glyph_count - 1);
}
static void
update_font (GlyphPicker *self)
{
g_clear_pointer (&self->font, hb_font_destroy);
if (self->face)
self->font = hb_font_create (self->face);
if (self->font)
value_changed (self);
}
static void
update_glyph (GlyphPicker *self)
{
hb_codepoint_t glyph;
if (hb_font_get_glyph_from_name (self->font, "icon0", -1, &glyph) ||
hb_font_get_glyph_from_name (self->font, "A", -1, &glyph))
{
glyph_picker_set_glyph (self, glyph);
}
}
static void
glyph_picker_set_property (GObject *object,
unsigned int prop_id,
const GValue *value,
GParamSpec *pspec)
{
GlyphPicker *self = GLYPH_PICKER (object);
switch (prop_id)
{
case PROP_FACE:
glyph_picker_set_face (self, (hb_face_t *) g_value_get_boxed (value));
break;
case PROP_GLYPH:
glyph_picker_set_glyph (self, g_value_get_uint (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
glyph_picker_get_property (GObject *object,
unsigned int prop_id,
GValue *value,
GParamSpec *pspec)
{
GlyphPicker *self = GLYPH_PICKER (object);
switch (prop_id)
{
case PROP_FACE:
g_value_set_boxed (value, self->face);
break;
case PROP_GLYPH:
g_value_set_uint (value, get_glyph (self));
break;
case PROP_GLYPH_NAME:
g_value_take_string (value, get_glyph_name (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
glyph_picker_class_init (GlyphPickerClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
object_class->dispose = glyph_picker_dispose;
object_class->finalize = glyph_picker_finalize;
object_class->get_property = glyph_picker_get_property;
object_class->set_property = glyph_picker_set_property;
properties[PROP_FACE] =
g_param_spec_boxed ("face", "", "",
HB_GOBJECT_TYPE_FACE,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
properties[PROP_GLYPH] =
g_param_spec_uint ("glyph", "", "",
0, G_MAXUINT, 0,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
properties[PROP_GLYPH_NAME] =
g_param_spec_string ("glyph-name", "", "",
NULL,
G_PARAM_READABLE);
g_object_class_install_properties (G_OBJECT_CLASS (class), NUM_PROPERTIES, properties);
gtk_widget_class_set_layout_manager_type (GTK_WIDGET_CLASS (class), GTK_TYPE_BOX_LAYOUT);
gtk_widget_class_set_css_name (GTK_WIDGET_CLASS (class), "glyphpicker");
}
static void
glyph_picker_set_face (GlyphPicker *self,
hb_face_t *face)
{
if (self->face == face)
return;
if (self->face)
hb_face_destroy (self->face);
self->face = face;
if (self->face)
hb_face_reference (self->face);
update_bounds (self);
update_font (self);
update_glyph (self);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FACE]);
}
static void
glyph_picker_set_glyph (GlyphPicker *self,
unsigned int glyph)
{
if (get_glyph (self) == glyph)
return;
gtk_spin_button_set_value (self->spin, glyph);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_GLYPH]);
}

View File

@@ -0,0 +1,14 @@
#pragma once
#include <gtk/gtk.h>
#define GLYPH_PICKER_TYPE (glyph_picker_get_type ())
#define GLYPH_PICKER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GLYPH_PICKER_TYPE, GlyphPicker))
typedef struct _GlyphPicker GlyphPicker;
typedef struct _GlyphPickerClass GlyphPickerClass;
GType glyph_picker_get_type (void);

View File

@@ -100,9 +100,10 @@ demos = files([
'tree_store.c',
'video_player.c',
'font_features.c',
'paintable_glyph.c',
])
gtkdemo_deps = [ libgtk_dep, ]
gtkdemo_deps = [ libgtk_dep, hbgobj_dep, hbcairo_dep ]
extra_demo_sources = files([
'main.c',
@@ -131,6 +132,13 @@ extra_demo_sources = files([
'unicode-names.c',
'suggestionentry.c',
'language-names.c',
'fontvariations.c',
'rangeedit.c',
'fontcolors.c',
'glyphpicker.c',
'colorpicker.c',
'glyphmodel.c',
'fontpicker.c',
])
if os_unix

View File

@@ -0,0 +1,215 @@
/* Paintable/Glyph
*
* This demo shows how to wrap a font in a GdkPaintable to display
* a single glyph that can be scaled by resizing the window.
*
* The demo also has controls for font variations and colors.
*/
#include <gtk/gtk.h>
#include <hb-ot.h>
#include "fontvariations.h"
#include "fontcolors.h"
#include "fontpicker.h"
#include "glyphpicker.h"
#include "colorpicker.h"
#include "glyphmodel.h"
static GtkWidget *window;
static GtkWidget *color_picker;
static GtkWidget *font_variations;
static GtkWidget *font_colors;
static GtkWidget *toggle;
static void
reset (GSimpleAction *action,
GVariant *parameter,
gpointer data)
{
g_action_activate (font_variations_get_reset_action (FONT_VARIATIONS (font_variations)), NULL);
g_action_activate (font_colors_get_reset_action (FONT_COLORS (font_colors)), NULL);
g_action_activate (color_picker_get_reset_action (COLOR_PICKER (color_picker)), NULL);
}
static void
update_reset (GSimpleAction *action,
GParamSpec *pspec,
GSimpleAction *reset_action)
{
gboolean enabled;
enabled = g_action_get_enabled (font_variations_get_reset_action (FONT_VARIATIONS (font_variations))) ||
g_action_get_enabled (font_colors_get_reset_action (FONT_COLORS (font_colors))) ||
g_action_get_enabled (color_picker_get_reset_action (COLOR_PICKER (color_picker)));
g_simple_action_set_enabled (G_SIMPLE_ACTION (reset_action), enabled);
}
static void
create_reset_action (void)
{
GSimpleAction *reset_action;
GSimpleActionGroup *group;
reset_action = g_simple_action_new ("reset", NULL);
g_signal_connect (reset_action, "activate", G_CALLBACK (reset), NULL);
g_signal_connect (font_variations_get_reset_action (FONT_VARIATIONS (font_variations)),
"notify::enabled", G_CALLBACK (update_reset), reset_action);
g_signal_connect (font_colors_get_reset_action (FONT_COLORS (font_colors)),
"notify::enabled", G_CALLBACK (update_reset), reset_action);
g_signal_connect (color_picker_get_reset_action (COLOR_PICKER (color_picker)),
"notify::enabled", G_CALLBACK (update_reset), reset_action);
update_reset (NULL, NULL, reset_action);
group = g_simple_action_group_new ();
g_simple_action_group_insert (group, G_ACTION (reset_action));
gtk_widget_insert_action_group (window, "win", G_ACTION_GROUP (group));
g_object_unref (group);
g_object_unref (reset_action);
}
static void
clear_provider (gpointer data)
{
GtkStyleProvider *provider = data;
gtk_style_context_remove_provider_for_display (gdk_display_get_default (), provider);
}
static void
color_changed (GtkWidget *picker,
GParamSpec *pspec,
gpointer data)
{
const GdkRGBA *fg, *bg;
char *css;
GtkCssProvider *provider;
provider = GTK_CSS_PROVIDER (g_object_get_data (G_OBJECT (picker), "bg-provider"));
if (!provider)
{
provider = gtk_css_provider_new ();
g_object_set_data_full (G_OBJECT (picker), "bg-provider", provider, clear_provider);
gtk_style_context_add_provider_for_display (gdk_display_get_default (),
GTK_STYLE_PROVIDER (provider),
800);
}
g_object_get (picker, "foreground", &fg, "background", &bg, NULL);
css = g_strdup_printf (".picture-parent-box { "
" color: rgba(%f,%f,%f,%f); "
" background-color: rgba(%f,%f,%f,%f); "
"} ",
255 * fg->red, 255 * fg->green, 255 * fg->blue, 255 * fg->alpha,
255 * bg->red, 255 * bg->green, 255 * bg->blue, 255 * bg->alpha);
gtk_css_provider_load_from_data (provider, css, -1);
g_free (css);
if (data)
gtk_widget_queue_draw (GTK_WIDGET (data));
}
static void
setup_grid_item (GtkSignalListItemFactory *factory,
GObject *listitem)
{
gtk_list_item_set_child (GTK_LIST_ITEM (listitem), gtk_image_new ());
}
static void
bind_grid_item (GtkSignalListItemFactory *factory,
GObject *listitem)
{
GtkWidget *image;
GdkPaintable *paintable;
image = gtk_list_item_get_child (GTK_LIST_ITEM (listitem));
paintable = gtk_list_item_get_item (GTK_LIST_ITEM (listitem));
gtk_image_set_from_paintable (GTK_IMAGE (image), paintable);
}
static void
grid_toggled (GtkToggleButton *grid_toggle,
GParamSpec *pspec,
GtkStack *stack)
{
if (gtk_toggle_button_get_active (grid_toggle))
{
gtk_stack_set_visible_child_name (stack, "grid");
gtk_widget_set_visible (toggle, FALSE);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), FALSE);
}
else
{
gtk_stack_set_visible_child_name (stack, "glyph");
gtk_widget_set_visible (toggle, TRUE);
}
}
GtkWidget *
do_paintable_glyph (GtkWidget *do_widget)
{
if (!window)
{
GtkBuilderScope *scope;
GtkBuilder *builder;
GtkCssProvider *provider;
GtkWidget *font_picker;
provider = gtk_css_provider_new ();
gtk_css_provider_load_from_resource (provider, "/paintable_glyph/paintable_glyph.css");
gtk_style_context_add_provider_for_display (gdk_display_get_default (),
GTK_STYLE_PROVIDER (provider),
800);
g_object_unref (provider);
g_type_ensure (FONT_VARIATIONS_TYPE);
g_type_ensure (FONT_COLORS_TYPE);
g_type_ensure (FONT_PICKER_TYPE);
g_type_ensure (GLYPH_PICKER_TYPE);
g_type_ensure (COLOR_PICKER_TYPE);
g_type_ensure (GLYPH_MODEL_TYPE);
scope = gtk_builder_cscope_new ();
gtk_builder_cscope_add_callback (scope, color_changed);
gtk_builder_cscope_add_callback (scope, setup_grid_item);
gtk_builder_cscope_add_callback (scope, bind_grid_item);
gtk_builder_cscope_add_callback (scope, grid_toggled);
builder = gtk_builder_new ();
gtk_builder_set_scope (builder, scope);
gtk_builder_add_from_resource (builder, "/paintable_glyph/paintable_glyph.ui", NULL);
window = GTK_WIDGET (gtk_builder_get_object (builder, "window"));
g_object_add_weak_pointer (G_OBJECT (window), (gpointer *)&window);
gtk_window_set_display (GTK_WINDOW (window), gtk_widget_get_display (do_widget));
font_picker = GTK_WIDGET (gtk_builder_get_object (builder, "font_picker"));
color_picker = GTK_WIDGET (gtk_builder_get_object (builder, "color_picker"));
font_variations = GTK_WIDGET (gtk_builder_get_object (builder, "font_variations"));
font_colors = GTK_WIDGET (gtk_builder_get_object (builder, "font_colors"));
toggle = GTK_WIDGET (gtk_builder_get_object (builder, "toggle"));
create_reset_action ();
font_picker_set_from_file (FONT_PICKER (font_picker), "/usr/share/fonts/abattis-cantarell-vf-fonts/Cantarell-VF.otf");
color_changed (color_picker, NULL, NULL);
g_object_unref (builder);
g_object_unref (scope);
}
if (!gtk_widget_get_visible (window))
gtk_window_present (GTK_WINDOW (window));
else
gtk_window_destroy (GTK_WINDOW (window));
return window;
}

View File

@@ -0,0 +1,21 @@
.toolbar {
padding: 10px;
}
box.sidebar {
padding: 20px;
border-spacing: 20px;
}
glyphpicker,
colorpicker > grid,
fontcolors .custom-colors,
fontvariations > grid {
border-spacing: 10px;
}
.sidebar box.row,
fontcolors > grid {
border-spacing: 10px;
}
.picture-parent-box gridview > child {
-gtk-icon-size: 32px;
margin: 8px;
}

View File

@@ -0,0 +1,162 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<object class="GtkGlyphPaintable" id="paintable">
<property name="face" bind-source="font_picker" bind-property="face"/>
<property name="glyph" bind-source="glyph_picker" bind-property="glyph" bind-flags="bidirectional | sync-create"/>
<property name="variations" bind-source="font_variations" bind-property="variations"/>
<property name="palette-index" bind-source="font_colors" bind-property="palette-index"/>
<property name="custom-colors" bind-source="font_colors" bind-property="custom-colors"/>
<property name="color" bind-source="color_picker" bind-property="foreground"/>
</object>
<object class="GtkWindow" id="window">
<property name="title">PaintableGlyph</property>
<property name="titlebar">
<object class="GtkHeaderBar">
<child type="start">
<object class="GtkButton">
<property name="tooltip-text">Reset</property>
<property name="icon-name">view-refresh-symbolic</property>
<property name="action-name">win.reset</property>
</object>
</child>
</object>
</property>
<property name="child">
<object class="GtkBox">
<property name="orientation">vertical</property>
<child>
<object class="GtkCenterBox">
<style>
<class name="toolbar"/>
</style>
<property name="start-widget">
<object class="GtkToggleButton" id="toggle">
<property name="icon-name">open-menu-symbolic</property>
</object>
</property>
<property name="center-widget">
<object class="GtkLabel">
<property name="label" bind-source="font_picker" bind-property="family-name"/>
</object>
</property>
<property name="end-widget">
<object class="GtkToggleButton">
<property name="icon-name">view-list-symbolic</property>
<signal name="notify::active" handler="grid_toggled" object="stack" swapped="0"/>
</object>
</property>
</object>
</child>
<child>
<object class="GtkSeparator"/>
</child>
<child>
<object class="GtkBox" id="box">
<child>
<object class="GtkRevealer">
<property name="reveal-child" bind-source="toggle" bind-property="active"/>
<property name="transition-type">slide-right</property>
<property name="child">
<object class="GtkScrolledWindow">
<property name="hscrollbar-policy">never</property>
<property name="child">
<object class="GtkBox">
<style>
<class name="sidebar"/>
</style>
<property name="orientation">vertical</property>
<child>
<object class="FontPicker" id="font_picker"/>
</child>
<child>
<object class="GlyphPicker" id="glyph_picker">
<property name="face" bind-source="font_picker" bind-property="face"/>
</object>
</child>
<child>
<object class="ColorPicker" id="color_picker">
<signal name="notify" handler="color_changed" object="stack" swapped="false"/>
</object>
</child>
<child>
<object class="FontVariations" id="font_variations">
<property name="face" bind-source="font_picker" bind-property="face"/>
</object>
</child>
<child>
<object class="FontColors" id="font_colors">
<property name="face" bind-source="font_picker" bind-property="face"/>
</object>
</child>
</object>
</property>
</object>
</property>
</object>
</child>
<child>
<object class="GtkStack" id="stack">
<style>
<class name="picture-parent-box"/>
</style>
<child>
<object class="GtkStackPage">
<property name="name">glyph</property>
<property name="child">
<object class="GtkPicture" id="picture">
<property name="paintable">paintable</property>
<property name="hexpand">1</property>
<property name="vexpand">1</property>
<property name="halign">center</property>
<property name="valign">center</property>
<property name="margin-start">20</property>
<property name="margin-end">20</property>
<property name="margin-top">20</property>
<property name="margin-bottom">20</property>
</object>
</property>
</object>
</child>
<child>
<object class="GtkStackPage">
<property name="name">grid</property>
<property name="child">
<object class="GtkScrolledWindow">
<property name="hscrollbar-policy">never</property>
<property name="vscrollbar-policy">automatic</property>
<property name="child">
<object class="GtkGridView" id="grid">
<property name="min-columns">5</property>
<property name="max-columns">25</property>
<property name="model">
<object class="GtkNoSelection">
<property name="model">
<object class="GlyphModel">
<property name="face" bind-source="font_picker" bind-property="face"/>
</object>
</property>
</object>
</property>
<property name="factory">
<object class="GtkSignalListItemFactory">
<signal name="setup" handler="setup_grid_item" swapped="0"/>
<signal name="bind" handler="bind_grid_item" swapped="0"/>
</object>
</property>
</object>
</property>
</object>
</property>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</property>
</object>
</interface>

View File

@@ -3766,6 +3766,7 @@ gsk_gl_render_job_visit_node (GskGLRenderJob *job,
gsk_gl_render_job_visit_transform_node (job, node);
break;
case GSK_GLYPH_NODE:
case GSK_CAIRO_NODE:
gsk_gl_render_job_visit_as_fallback (job, node);
break;

View File

@@ -50,7 +50,7 @@
* @GSK_BLUR_NODE: A node that applies a blur
* @GSK_DEBUG_NODE: Debug information that does not affect the rendering
* @GSK_GL_SHADER_NODE: A node that uses OpenGL fragment shaders to render
*
* The type of a node determines what the node is rendering.
*/
typedef enum {
@@ -79,7 +79,8 @@ typedef enum {
GSK_TEXT_NODE,
GSK_BLUR_NODE,
GSK_DEBUG_NODE,
GSK_GL_SHADER_NODE
GSK_GL_SHADER_NODE,
GSK_GLYPH_NODE,
} GskRenderNodeType;
/**

View File

@@ -164,6 +164,7 @@ GskRenderNode * gsk_render_node_deserialize (GBytes
#define GSK_TYPE_TEXT_NODE (gsk_text_node_get_type())
#define GSK_TYPE_BLUR_NODE (gsk_blur_node_get_type())
#define GSK_TYPE_GL_SHADER_NODE (gsk_gl_shader_node_get_type())
#define GSK_TYPE_GLYPH_NODE (gsk_glyph_node_get_type())
typedef struct _GskDebugNode GskDebugNode;
typedef struct _GskColorNode GskColorNode;
@@ -190,6 +191,7 @@ typedef struct _GskCrossFadeNode GskCrossFadeNode;
typedef struct _GskTextNode GskTextNode;
typedef struct _GskBlurNode GskBlurNode;
typedef struct _GskGLShaderNode GskGLShaderNode;
typedef struct _GskGlyphNode GskGlyphNode;
GDK_AVAILABLE_IN_ALL
GType gsk_debug_node_get_type (void) G_GNUC_CONST;
@@ -506,6 +508,29 @@ const GdkRGBA * gsk_text_node_get_color (const GskRender
GDK_AVAILABLE_IN_ALL
const graphene_point_t *gsk_text_node_get_offset (const GskRenderNode *node) G_GNUC_PURE;
GDK_AVAILABLE_IN_4_10
GType gsk_glyph_node_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_4_10
GskRenderNode * gsk_glyph_node_new (const graphene_rect_t *bounds,
hb_font_t *font,
hb_codepoint_t glyph,
unsigned int palette_index,
const GdkRGBA *foreground_color,
unsigned int n_colors,
const GdkRGBA *colors);
GDK_AVAILABLE_IN_4_10
hb_font_t * gsk_glyph_node_get_font (const GskRenderNode *node);
GDK_AVAILABLE_IN_4_10
hb_codepoint_t gsk_glyph_node_get_glyph (const GskRenderNode *node);
GDK_AVAILABLE_IN_4_10
unsigned int gsk_glyph_node_get_palette_index (const GskRenderNode *node);
GDK_AVAILABLE_IN_4_10
const GdkRGBA * gsk_glyph_node_get_foreground_color (const GskRenderNode *node);
GDK_AVAILABLE_IN_4_10
unsigned int gsk_glyph_node_get_n_colors (const GskRenderNode *node);
GDK_AVAILABLE_IN_4_10
const GdkRGBA * gsk_glyph_node_get_colors (const GskRenderNode *node);
GDK_AVAILABLE_IN_ALL
GType gsk_blur_node_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_ALL

View File

@@ -32,6 +32,7 @@
#include "gdk/gdkprivate.h"
#include <hb-ot.h>
#include <hb-cairo.h>
/* maximal number of rectangles we keep in a diff region before we throw
* the towel and just use the bounding box of the parent node.
@@ -4660,6 +4661,208 @@ gsk_text_node_get_offset (const GskRenderNode *node)
return &self->offset;
}
/*** GSK_GLYPH_NODE ***/
struct _GskGlyphNode
{
GskRenderNode render_node;
hb_font_t *font;
hb_codepoint_t glyph;
unsigned int palette_index;
GdkRGBA foreground_color;
unsigned int n_colors;
GdkRGBA *colors;
};
static void
gsk_glyph_node_finalize (GskRenderNode *node)
{
GskGlyphNode *self = (GskGlyphNode *) node;
GskRenderNodeClass *parent_class = g_type_class_peek (g_type_parent (GSK_TYPE_GLYPH_NODE));
hb_font_destroy (self->font);
g_free (self->colors);
parent_class->finalize (node);
}
static void
gsk_glyph_node_draw (GskRenderNode *node,
cairo_t *cr)
{
GskGlyphNode *self = (GskGlyphNode *) node;
unsigned int upem;
cairo_font_face_t *cairo_face;
cairo_matrix_t font_matrix, ctm;
cairo_font_options_t *font_options;
cairo_scaled_font_t *scaled_font;
hb_glyph_extents_t extents;
double scale;
cairo_glyph_t glyph;
if (!hb_font_get_glyph_extents (self->font, self->glyph, &extents))
return;
if (extents.width <= 0 || extents.height == 0)
return;
cairo_save (cr);
upem = hb_face_get_upem (hb_font_get_face (self->font));
cairo_face = hb_cairo_font_face_create_for_font (self->font);
hb_cairo_font_face_set_scale_factor (cairo_face, 1 << 6);
cairo_matrix_init_identity (&ctm);
cairo_matrix_init_scale (&font_matrix, (double)upem, (double)upem);
font_options = cairo_font_options_create ();
cairo_font_options_set_hint_style (font_options, CAIRO_HINT_STYLE_NONE);
cairo_font_options_set_hint_metrics (font_options, CAIRO_HINT_METRICS_OFF);
#ifdef CAIRO_COLOR_PALETTE_DEFAULT
cairo_font_options_set_color_palette (font_options, self->palette_index);
#endif
#ifdef HAVE_CAIRO_FONT_OPTIONS_SET_CUSTOM_PALETTE_COLOR
for (int i = 0; i < self->n_colors; i++)
cairo_font_options_set_custom_palette_color (font_options, i,
self->colors[i].red,
self->colors[i].green,
self->colors[i].blue,
self->colors[i].alpha);
#endif
scaled_font = cairo_scaled_font_create (cairo_face, &font_matrix, &ctm, font_options);
cairo_font_options_destroy (font_options);
cairo_font_face_destroy (cairo_face);
cairo_set_scaled_font (cr, scaled_font);
cairo_scaled_font_destroy (scaled_font);
scale = node->bounds.size.width * (1 << 6) / (double) extents.width;
cairo_scale (cr, scale, scale);
gdk_cairo_set_source_rgba (cr, &self->foreground_color);
glyph.index = self->glyph;
glyph.x = - extents.x_bearing / (1 << 6);
glyph.y = extents.y_bearing / (1 << 6);
cairo_show_glyphs (cr, &glyph, 1);
cairo_restore (cr);
}
static void
gsk_glyph_node_diff (GskRenderNode *node1,
GskRenderNode *node2,
cairo_region_t *region)
{
GskGlyphNode *self1 = (GskGlyphNode *) node1;
GskGlyphNode *self2 = (GskGlyphNode *) node2;
if (self1->font == self2->font &&
self1->glyph == self2->glyph &&
self1->palette_index == self2->palette_index &&
gdk_rgba_equal (&self1->foreground_color, &self2->foreground_color) &&
self1->n_colors == self2->n_colors)
{
for (unsigned int i = 0; i < self1->n_colors; i++)
{
if (!gdk_rgba_equal (&self1->colors[i], &self2->colors[i]))
{
gsk_render_node_diff_impossible (node1, node2, region);
return;
}
}
return;
}
gsk_render_node_diff_impossible (node1, node2, region);
}
GskRenderNode *
gsk_glyph_node_new (const graphene_rect_t *bounds,
hb_font_t *font,
hb_codepoint_t glyph,
unsigned int palette_index,
const GdkRGBA *foreground_color,
unsigned int n_colors,
const GdkRGBA *colors)
{
GskGlyphNode *self;
GskRenderNode *node;
self = gsk_render_node_alloc (GSK_GLYPH_NODE);
node = (GskRenderNode *) self;
node->offscreen_for_opacity = FALSE;
self->font = hb_font_reference (font);
self->glyph = glyph;
self->palette_index = palette_index;
self->foreground_color = *foreground_color;
self->n_colors = n_colors;
self->colors = g_new (GdkRGBA, n_colors);
for (unsigned int i = 0; i < n_colors; i++)
self->colors[i] = colors[i];
graphene_rect_init_from_rect (&node->bounds, bounds);
return node;
}
hb_font_t *
gsk_glyph_node_get_font (const GskRenderNode *node)
{
GskGlyphNode *self = (GskGlyphNode *) node;
return self->font;
}
hb_codepoint_t
gsk_glyph_node_get_glyph (const GskRenderNode *node)
{
GskGlyphNode *self = (GskGlyphNode *) node;
return self->glyph;
}
unsigned int
gsk_glyph_node_get_palette_index (const GskRenderNode *node)
{
GskGlyphNode *self = (GskGlyphNode *) node;
return self->palette_index;
}
const GdkRGBA *
gsk_glyph_node_get_foreground_color (const GskRenderNode *node)
{
GskGlyphNode *self = (GskGlyphNode *) node;
return &self->foreground_color;
}
unsigned int
gsk_glyph_node_get_n_colors (const GskRenderNode *node)
{
GskGlyphNode *self = (GskGlyphNode *) node;
return self->n_colors;
}
const GdkRGBA *
gsk_glyph_node_get_colors (const GskRenderNode *node)
{
GskGlyphNode *self = (GskGlyphNode *) node;
return self->colors;
}
/*** GSK_BLUR_NODE ***/
/**
@@ -5345,6 +5548,7 @@ GSK_DEFINE_RENDER_NODE_TYPE (gsk_shadow_node, GSK_SHADOW_NODE)
GSK_DEFINE_RENDER_NODE_TYPE (gsk_blend_node, GSK_BLEND_NODE)
GSK_DEFINE_RENDER_NODE_TYPE (gsk_cross_fade_node, GSK_CROSS_FADE_NODE)
GSK_DEFINE_RENDER_NODE_TYPE (gsk_text_node, GSK_TEXT_NODE)
GSK_DEFINE_RENDER_NODE_TYPE (gsk_glyph_node, GSK_GLYPH_NODE)
GSK_DEFINE_RENDER_NODE_TYPE (gsk_blur_node, GSK_BLUR_NODE)
GSK_DEFINE_RENDER_NODE_TYPE (gsk_gl_shader_node, GSK_GL_SHADER_NODE)
GSK_DEFINE_RENDER_NODE_TYPE (gsk_debug_node, GSK_DEBUG_NODE)
@@ -5704,6 +5908,22 @@ gsk_render_node_init_types_once (void)
gsk_render_node_types[GSK_TEXT_NODE] = node_type;
}
{
const GskRenderNodeTypeInfo node_info =
{
GSK_GLYPH_NODE,
sizeof (GskGlyphNode),
NULL,
gsk_glyph_node_finalize,
gsk_glyph_node_draw,
NULL,
gsk_glyph_node_diff,
};
GType node_type = gsk_render_node_type_register_static (I_("GskGlyphNode"), &node_info);
gsk_render_node_types[GSK_GLYPH_NODE] = node_type;
}
{
const GskRenderNodeTypeInfo node_info =
{

View File

@@ -3043,6 +3043,8 @@ render_node_print (Printer *p,
}
break;
case GSK_GLYPH_NODE:
default:
g_error ("Unhandled node: %s", g_type_name_from_instance ((GTypeInstance *) node));
break;

View File

@@ -13,7 +13,7 @@ typedef struct _GskRenderNodeClass GskRenderNodeClass;
* We don't add an "n-types" value to avoid having to handle
* it in every single switch.
*/
#define GSK_RENDER_NODE_TYPE_N_TYPES (GSK_GL_SHADER_NODE + 1)
#define GSK_RENDER_NODE_TYPE_N_TYPES (GSK_GLYPH_NODE + 1)
extern GType gsk_render_node_types[];

View File

@@ -19,9 +19,12 @@
#include <gdk/gdk.h>
#include "gdkpixbufutilsprivate.h"
#include "gtkscalerprivate.h"
#include "gtkglyphpaintable.h"
#include "gdk/gdktextureprivate.h"
#include <hb-glib.h>
static GdkPixbuf *
load_from_stream (GdkPixbufLoader *loader,
GInputStream *stream,
@@ -575,6 +578,24 @@ on_loader_size_prepared (GdkPixbufLoader *loader,
height * loader_data->scale_factor);
}
static gboolean
face_from_blob (GBytes *bytes, hb_face_t **face_out)
{
hb_blob_t *blob;
hb_face_t *face;
blob = hb_glib_blob_create (bytes);
face = hb_face_create (blob, 0);
hb_blob_destroy (blob);
if (face == hb_face_get_empty ())
face = NULL;
*face_out = face;
return face != NULL;
}
GdkPaintable *
gdk_paintable_new_from_bytes_scaled (GBytes *bytes,
int scale_factor)
@@ -582,6 +603,7 @@ gdk_paintable_new_from_bytes_scaled (GBytes *bytes,
LoaderData loader_data;
GdkTexture *texture;
GdkPaintable *paintable;
hb_face_t *face;
loader_data.scale_factor = scale_factor;
@@ -594,6 +616,10 @@ gdk_paintable_new_from_bytes_scaled (GBytes *bytes,
/* We know these formats can't be scaled */
paintable = GDK_PAINTABLE (texture);
}
else if (face_from_blob (bytes, &face))
{
paintable = gtk_glyph_paintable_new (face);
}
else
{
GdkPixbufLoader *loader;

View File

@@ -154,6 +154,7 @@
#include <gtk/gtkgestureswipe.h>
#include <gtk/gtkgesturezoom.h>
#include <gtk/gtkglarea.h>
#include <gtk/gtkglyphpaintable.h>
#include <gtk/gtkgrid.h>
#include <gtk/gtkgridlayout.h>
#include <gtk/gtkgridview.h>

695
gtk/gtkglyphpaintable.c Normal file
View File

@@ -0,0 +1,695 @@
#include "config.h"
#include "gtkglyphpaintable.h"
#include <hb-gobject.h>
#include <hb-cairo.h>
#include <hb-ot.h>
#define SUBPIXEL_BITS 6
struct _GtkGlyphPaintable
{
GObject parent_instance;
hb_face_t *face;
hb_font_t *font;
hb_codepoint_t glyph;
unsigned int palette_index;
char *variations;
GdkRGBA *custom_palette;
unsigned int num_palette_entries;
char *custom_colors;
GdkRGBA color;
unsigned int uses_foreground : 1;
unsigned int uses_palette : 1;
};
struct _GtkGlyphPaintableClass
{
GObjectClass parent_class;
};
enum {
PROP_FACE = 1,
PROP_GLYPH,
PROP_VARIATIONS,
PROP_COLOR,
PROP_PALETTE_INDEX,
PROP_CUSTOM_COLORS,
NUM_PROPERTIES
};
static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
static void
gtk_glyph_paintable_snapshot_symbolic (GtkSymbolicPaintable *paintable,
GdkSnapshot *snapshot,
double width,
double height,
const GdkRGBA *colors,
gsize n_colors)
{
GtkGlyphPaintable *self = GTK_GLYPH_PAINTABLE (paintable);
GskRenderNode *node;
GdkRGBA foreground_color;
unsigned int num_colors = 0;
GdkRGBA *custom_palette = NULL;
if (self->face == NULL)
return;
if (n_colors > 0)
foreground_color = colors[0];
else
foreground_color = self->color;
if (self->custom_palette && colors)
{
num_colors = self->num_palette_entries;
custom_palette = g_newa (GdkRGBA, num_colors);
memcpy (custom_palette, self->custom_palette, sizeof (GdkRGBA) * num_colors);
memcpy (custom_palette, &colors[1], sizeof (GdkRGBA) * MIN (n_colors - 1, num_colors));
}
else if (self->custom_palette)
{
num_colors = self->num_palette_entries;
custom_palette = self->custom_palette;
}
else if (n_colors > 1)
{
num_colors = n_colors - 1;
custom_palette = (GdkRGBA *)&colors[1];
}
node = gsk_glyph_node_new (&GRAPHENE_RECT_INIT (0, 0, width, height),
self->font,
self->glyph,
self->palette_index,
&foreground_color,
num_colors,
custom_palette);
gtk_snapshot_append_node (snapshot, node);
gsk_render_node_unref (node);
}
static void
gtk_glyph_paintable_snapshot (GdkPaintable *paintable,
GdkSnapshot *snapshot,
double width,
double height)
{
gtk_glyph_paintable_snapshot_symbolic (GTK_SYMBOLIC_PAINTABLE (paintable),
snapshot, width, height, NULL, 0);
}
static int
gtk_glyph_paintable_get_intrinsic_width (GdkPaintable *paintable)
{
GtkGlyphPaintable *self = GTK_GLYPH_PAINTABLE (paintable);
hb_glyph_extents_t extents;
if (!self->font)
return 0;
if (hb_font_get_glyph_extents (self->font, self->glyph, &extents))
return extents.width / (1 << SUBPIXEL_BITS);
return 0;
}
static int
gtk_glyph_paintable_get_intrinsic_height (GdkPaintable *paintable)
{
GtkGlyphPaintable *self = GTK_GLYPH_PAINTABLE (paintable);
hb_glyph_extents_t extents;
if (!self->font)
return 0;
if (hb_font_get_glyph_extents (self->font, self->glyph, &extents))
return (-extents.height) / (1 << SUBPIXEL_BITS);
return 0;
}
static void
gtk_glyph_paintable_init_interface (GdkPaintableInterface *iface)
{
iface->snapshot = gtk_glyph_paintable_snapshot;
iface->get_intrinsic_width = gtk_glyph_paintable_get_intrinsic_width;
iface->get_intrinsic_height = gtk_glyph_paintable_get_intrinsic_height;
}
static void
glyph_symbolic_paintable_init_interface (GtkSymbolicPaintableInterface *iface)
{
iface->snapshot_symbolic = gtk_glyph_paintable_snapshot_symbolic;
}
G_DEFINE_TYPE_WITH_CODE (GtkGlyphPaintable, gtk_glyph_paintable, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (GDK_TYPE_PAINTABLE,
gtk_glyph_paintable_init_interface)
G_IMPLEMENT_INTERFACE (GTK_TYPE_SYMBOLIC_PAINTABLE,
glyph_symbolic_paintable_init_interface))
static void
gtk_glyph_paintable_init (GtkGlyphPaintable *self)
{
self->color = (GdkRGBA) { 0, 0, 0, 1 };
}
static void
gtk_glyph_paintable_dispose (GObject *object)
{
GtkGlyphPaintable *self = GTK_GLYPH_PAINTABLE (object);
g_clear_pointer (&self->face, hb_face_destroy);
g_clear_pointer (&self->font, hb_font_destroy);
g_free (self->variations);
g_free (self->custom_colors);
g_free (self->custom_palette);
G_OBJECT_CLASS (gtk_glyph_paintable_parent_class)->dispose (object);
}
static void
gtk_glyph_paintable_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GtkGlyphPaintable *self = GTK_GLYPH_PAINTABLE (object);
switch (prop_id)
{
case PROP_FACE:
gtk_glyph_paintable_set_face (self, (hb_face_t *)g_value_get_boxed (value));
break;
case PROP_GLYPH:
gtk_glyph_paintable_set_glyph (self, g_value_get_uint (value));
break;
case PROP_VARIATIONS:
gtk_glyph_paintable_set_variations (self, g_value_get_string (value));
break;
case PROP_COLOR:
gtk_glyph_paintable_set_color (self, g_value_get_boxed (value));
break;
case PROP_PALETTE_INDEX:
gtk_glyph_paintable_set_palette_index (self, g_value_get_uint (value));
break;
case PROP_CUSTOM_COLORS:
gtk_glyph_paintable_set_custom_colors (self, g_value_get_string (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
gtk_glyph_paintable_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GtkGlyphPaintable *self = GTK_GLYPH_PAINTABLE (object);
switch (prop_id)
{
case PROP_FACE:
g_value_set_boxed (value, self->face);
break;
case PROP_GLYPH:
g_value_set_uint (value, self->glyph);
break;
case PROP_VARIATIONS:
g_value_set_string (value, self->variations);
break;
case PROP_COLOR:
g_value_set_boxed (value, &self->color);
break;
case PROP_PALETTE_INDEX:
g_value_set_uint (value, self->palette_index);
break;
case PROP_CUSTOM_COLORS:
g_value_set_string (value, self->custom_colors);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
gtk_glyph_paintable_class_init (GtkGlyphPaintableClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
object_class->dispose = gtk_glyph_paintable_dispose;
object_class->set_property = gtk_glyph_paintable_set_property;
object_class->get_property = gtk_glyph_paintable_get_property;
properties[PROP_FACE] = g_param_spec_boxed ("face", NULL, NULL,
HB_GOBJECT_TYPE_FACE,
G_PARAM_READWRITE);
properties[PROP_GLYPH] = g_param_spec_uint ("glyph", NULL, NULL,
0, G_MAXUINT, 0,
G_PARAM_READWRITE);
properties[PROP_VARIATIONS] = g_param_spec_string ("variations", NULL, NULL,
NULL,
G_PARAM_READWRITE);
properties[PROP_COLOR] = g_param_spec_boxed ("color", NULL, NULL,
GDK_TYPE_RGBA,
G_PARAM_READWRITE);
properties[PROP_PALETTE_INDEX] = g_param_spec_uint ("palette-index", NULL, NULL,
0, G_MAXUINT, 0,
G_PARAM_READWRITE);
properties[PROP_CUSTOM_COLORS] = g_param_spec_string ("custom-colors", NULL, NULL,
NULL,
G_PARAM_READWRITE);
g_object_class_install_properties (object_class, NUM_PROPERTIES, properties);
}
GdkPaintable *
gtk_glyph_paintable_new (hb_face_t *face)
{
return g_object_new (GTK_TYPE_GLYPH_PAINTABLE,
"face", face,
NULL);
}
static void
parse_variations (const char *str,
hb_variation_t *variations,
unsigned int length,
unsigned int *n_variations)
{
const char *p;
*n_variations = 0;
p = str;
while (p && *p && *n_variations < length)
{
const char *end = strchr (p, ',');
if (hb_variation_from_string (p, end ? end - p: -1, &variations[*n_variations]))
(*n_variations)++;
p = end ? end + 1 : NULL;
}
}
static unsigned int
count_variations (const char *string)
{
unsigned int n;
const char *p;
n = 1;
p = string;
while ((p = strchr (p + 1, ',')) != NULL)
n++;
return n;
}
static void
paint_color (hb_paint_funcs_t *funcs,
void *paint_data,
hb_bool_t use_foreground,
hb_color_t color,
void *user_data)
{
GtkGlyphPaintable *self = GTK_GLYPH_PAINTABLE (paint_data);
if (use_foreground)
self->uses_foreground = TRUE;
}
static void
handle_color_line (GtkGlyphPaintable *self,
hb_color_line_t *color_line)
{
unsigned int len;
hb_color_stop_t *stops;
len = hb_color_line_get_color_stops (color_line, 0, NULL, NULL);
stops = g_newa (hb_color_stop_t, len);
hb_color_line_get_color_stops (color_line, 0, &len, stops);
for (unsigned int i = 0; i < len; i++)
{
if (stops[i].is_foreground)
{
self->uses_foreground = TRUE;
break;
}
}
}
static void
linear_gradient (hb_paint_funcs_t *funcs,
void *paint_data,
hb_color_line_t *color_line,
float x0, float y0,
float x1, float y1,
float x2, float y2,
void *user_data)
{
GtkGlyphPaintable *self = GTK_GLYPH_PAINTABLE (paint_data);
handle_color_line (self, color_line);
}
static void
radial_gradient (hb_paint_funcs_t *funcs,
void *paint_data,
hb_color_line_t *color_line,
float x0, float y0, float r0,
float x1, float y1, float r1,
void *user_data)
{
GtkGlyphPaintable *self = GTK_GLYPH_PAINTABLE (paint_data);
handle_color_line (self, color_line);
}
static void
sweep_gradient (hb_paint_funcs_t *funcs,
void *paint_data,
hb_color_line_t *color_line,
float x0, float y0,
float start_angle, float end_angle,
void *user_data)
{
GtkGlyphPaintable *self = GTK_GLYPH_PAINTABLE (paint_data);
handle_color_line (self, color_line);
}
static hb_bool_t
custom_palette_color (hb_paint_funcs_t *funcs,
void *paint_data,
unsigned int color_index,
hb_color_t *color,
void *user_data)
{
GtkGlyphPaintable *self = GTK_GLYPH_PAINTABLE (paint_data);
self->uses_palette = TRUE;
return FALSE;
}
static hb_paint_funcs_t *
get_classify_funcs (void)
{
static hb_paint_funcs_t *funcs;
if (funcs)
return funcs;
funcs = hb_paint_funcs_create ();
hb_paint_funcs_set_color_func (funcs, paint_color, NULL, NULL);
hb_paint_funcs_set_linear_gradient_func (funcs, linear_gradient, NULL, NULL);
hb_paint_funcs_set_radial_gradient_func (funcs, radial_gradient, NULL, NULL);
hb_paint_funcs_set_sweep_gradient_func (funcs, sweep_gradient, NULL, NULL);
hb_paint_funcs_set_custom_palette_color_func (funcs, custom_palette_color, NULL, NULL);
hb_paint_funcs_make_immutable (funcs);
return funcs;
}
static void
classify_glyph (GtkGlyphPaintable *self)
{
self->uses_foreground = FALSE;
self->uses_palette = FALSE;
if (self->font)
hb_font_paint_glyph (self->font, self->glyph,
get_classify_funcs (), self,
0, HB_COLOR (0, 0, 0, 255));
}
static void
update_font (GtkGlyphPaintable *self)
{
unsigned int upem;
int scale;
g_clear_pointer (&self->font, hb_font_destroy);
if (!self->face)
return;
self->font = hb_font_create (self->face);
upem = hb_face_get_upem (self->face);
scale = (int) scalbnf ((double)upem, SUBPIXEL_BITS);
hb_font_set_scale (self->font, scale, scale);
if (self->variations)
{
unsigned int n_vars;
hb_variation_t *vars;
n_vars = count_variations (self->variations);
vars = g_newa (hb_variation_t, n_vars);
parse_variations (self->variations, vars, n_vars, &n_vars);
hb_font_set_variations (self->font, vars, n_vars);
}
}
static void
guess_default_glyph (GtkGlyphPaintable *self)
{
hb_codepoint_t glyph;
if (!self->font)
return;
self->glyph = 1;
if (hb_font_get_glyph_from_name (self->font, "icon0", -1, &glyph) ||
hb_font_get_glyph_from_name (self->font, "A", -1, &glyph))
self->glyph = glyph;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_GLYPH]);
}
void
gtk_glyph_paintable_set_face (GtkGlyphPaintable *self,
hb_face_t *face)
{
g_return_if_fail (GTK_IS_GLYPH_PAINTABLE (self));
g_clear_pointer (&self->face, hb_face_destroy);
if (face)
self->face = hb_face_reference (face);
update_font (self);
guess_default_glyph (self);
classify_glyph (self);
gdk_paintable_invalidate_contents (GDK_PAINTABLE (self));
gdk_paintable_invalidate_size (GDK_PAINTABLE (self));
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FACE]);
}
hb_face_t *
gtk_glyph_paintable_get_face (GtkGlyphPaintable *self)
{
g_return_val_if_fail (GTK_IS_GLYPH_PAINTABLE (self), NULL);
return self->face;
}
void
gtk_glyph_paintable_set_glyph (GtkGlyphPaintable *self,
hb_codepoint_t glyph)
{
g_return_if_fail (GTK_IS_GLYPH_PAINTABLE (self));
self->glyph = glyph;
classify_glyph (self);
gdk_paintable_invalidate_contents (GDK_PAINTABLE (self));
gdk_paintable_invalidate_size (GDK_PAINTABLE (self));
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_GLYPH]);
}
hb_codepoint_t
gtk_glyph_paintable_get_glyph (GtkGlyphPaintable *self)
{
g_return_val_if_fail (GTK_IS_GLYPH_PAINTABLE (self), 0);
return self->glyph;
}
static void
update_custom_palette (GtkGlyphPaintable *self)
{
unsigned int len;
hb_color_t *colors;
char **entries;
if (self->custom_colors == NULL)
{
g_clear_pointer (&self->custom_palette, g_free);
self->num_palette_entries = 0;
return;
}
len = hb_ot_color_palette_get_colors (self->face, self->palette_index, 0, NULL, NULL);
if (self->num_palette_entries != len)
{
self->custom_palette = g_realloc (self->custom_palette, sizeof (GdkRGBA) * len);
self->num_palette_entries = len;
}
colors = g_newa (hb_color_t, len);
hb_ot_color_palette_get_colors (self->face, self->palette_index, 0, &len, colors);
for (unsigned int i = 0; i < len; i++)
{
self->custom_palette[i].red = hb_color_get_red (colors[i]) / 255.;
self->custom_palette[i].green = hb_color_get_green (colors[i]) / 255.;
self->custom_palette[i].blue = hb_color_get_blue (colors[i]) / 255.;
self->custom_palette[i].alpha = hb_color_get_alpha (colors[i]) / 255.;
}
entries = g_strsplit (self->custom_colors, ",", -1);
for (int i = 0; entries[i] && i < len; i++)
{
unsigned int r, g, b, a;
if (sscanf (entries[i], "%2x%2x%2x%2x", &r, &g, &b, &a) == 4)
{
self->custom_palette[i].red = r / 255.;
self->custom_palette[i].green = g / 255.;
self->custom_palette[i].blue = b / 255.;
self->custom_palette[i].alpha = a / 255.;
}
}
g_strfreev (entries);
}
void
gtk_glyph_paintable_set_palette_index (GtkGlyphPaintable *self,
unsigned int palette_index)
{
g_return_if_fail (GTK_IS_GLYPH_PAINTABLE (self));
self->palette_index = palette_index;
update_custom_palette (self);
if (self->uses_palette)
gdk_paintable_invalidate_contents (GDK_PAINTABLE (self));
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_PALETTE_INDEX]);
}
unsigned int
gtk_glyph_paintable_get_palette_index (GtkGlyphPaintable *self)
{
g_return_val_if_fail (GTK_IS_GLYPH_PAINTABLE (self), 0);
return self->palette_index;
}
void
gtk_glyph_paintable_set_variations (GtkGlyphPaintable *self,
const char *variations)
{
g_return_if_fail (GTK_IS_GLYPH_PAINTABLE (self));
g_free (self->variations);
self->variations = g_strdup (variations);
update_font (self);
gdk_paintable_invalidate_contents (GDK_PAINTABLE (self));
gdk_paintable_invalidate_size (GDK_PAINTABLE (self));
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_VARIATIONS]);
}
const char *
gtk_glyph_paintable_get_variations (GtkGlyphPaintable *self)
{
g_return_val_if_fail (GTK_IS_GLYPH_PAINTABLE (self), NULL);
return self->variations;
}
void
gtk_glyph_paintable_set_custom_colors (GtkGlyphPaintable *self,
const char *custom_colors)
{
g_return_if_fail (GTK_IS_GLYPH_PAINTABLE (self));
g_free (self->custom_colors);
self->custom_colors = g_strdup (custom_colors);
update_custom_palette (self);
if (self->uses_palette)
gdk_paintable_invalidate_contents (GDK_PAINTABLE (self));
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_CUSTOM_COLORS]);
}
const char *
gtk_glyph_paintable_get_custom_colors (GtkGlyphPaintable *self)
{
g_return_val_if_fail (GTK_IS_GLYPH_PAINTABLE (self), NULL);
return self->custom_colors;
}
void
gtk_glyph_paintable_set_color (GtkGlyphPaintable *self,
const GdkRGBA *color)
{
g_return_if_fail (GTK_IS_GLYPH_PAINTABLE (self));
if (gdk_rgba_equal (&self->color, color))
return;
self->color = *color;
if (self->uses_foreground)
gdk_paintable_invalidate_contents (GDK_PAINTABLE (self));
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_COLOR]);
}
const GdkRGBA *
gtk_glyph_paintable_get_color (GtkGlyphPaintable *self)
{
g_return_val_if_fail (GTK_IS_GLYPH_PAINTABLE (self), NULL);
return &self->color;
}

58
gtk/gtkglyphpaintable.h Normal file
View File

@@ -0,0 +1,58 @@
#pragma once
#include <gtk/gtk.h>
#include <hb.h>
G_BEGIN_DECLS
#define GTK_TYPE_GLYPH_PAINTABLE (gtk_glyph_paintable_get_type ())
GDK_AVAILABLE_IN_4_10
G_DECLARE_FINAL_TYPE (GtkGlyphPaintable, gtk_glyph_paintable, GTK, GLYPH_PAINTABLE, GObject)
GDK_AVAILABLE_IN_4_10
GdkPaintable * gtk_glyph_paintable_new (hb_face_t *face);
GDK_AVAILABLE_IN_4_10
void gtk_glyph_paintable_set_face (GtkGlyphPaintable *self,
hb_face_t *face);
GDK_AVAILABLE_IN_4_10
hb_face_t * gtk_glyph_paintable_get_face (GtkGlyphPaintable *self);
GDK_AVAILABLE_IN_4_10
void gtk_glyph_paintable_set_glyph (GtkGlyphPaintable *self,
hb_codepoint_t glyph);
GDK_AVAILABLE_IN_4_10
hb_codepoint_t gtk_glyph_paintable_get_glyph (GtkGlyphPaintable *self);
GDK_AVAILABLE_IN_4_10
void gtk_glyph_paintable_set_variations (GtkGlyphPaintable *self,
const char *variations);
GDK_AVAILABLE_IN_4_10
const char * gtk_glyph_paintable_get_variations (GtkGlyphPaintable *self);
GDK_AVAILABLE_IN_4_10
void gtk_glyph_paintable_set_color (GtkGlyphPaintable *self,
const GdkRGBA *color);
GDK_AVAILABLE_IN_4_10
const GdkRGBA * gtk_glyph_paintable_get_color (GtkGlyphPaintable *self);
GDK_AVAILABLE_IN_4_10
void gtk_glyph_paintable_set_palette_index (GtkGlyphPaintable *self,
unsigned int palette_index);
GDK_AVAILABLE_IN_4_10
unsigned int gtk_glyph_paintable_get_palette_index (GtkGlyphPaintable *self);
GDK_AVAILABLE_IN_4_10
void gtk_glyph_paintable_set_custom_colors (GtkGlyphPaintable *self,
const char *palette);
GDK_AVAILABLE_IN_4_10
const char * gtk_glyph_paintable_get_custom_colors (GtkGlyphPaintable *self);
G_END_DECLS

View File

@@ -33,6 +33,7 @@
#include "gtksnapshot.h"
#include "gtkwidgetprivate.h"
#include "gdk/gdkprofilerprivate.h"
#include "gtksymbolicpaintable.h"
struct _GtkIconHelper
{
@@ -123,7 +124,7 @@ gtk_icon_helper_load_paintable (GtkIconHelper *self,
{
case GTK_IMAGE_PAINTABLE:
paintable = g_object_ref (gtk_image_definition_get_paintable (self->def));
symbolic = FALSE;
symbolic = GTK_IS_SYMBOLIC_PAINTABLE (paintable);
break;
case GTK_IMAGE_ICON_NAME:

View File

@@ -251,6 +251,7 @@ gtk_public_sources = files([
'gtkgestureswipe.c',
'gtkgesturezoom.c',
'gtkglarea.c',
'gtkglyphpaintable.c',
'gtkgrid.c',
'gtkgridlayout.c',
'gtkgridview.c',
@@ -500,6 +501,7 @@ gtk_public_headers = files([
'gtkgestureswipe.h',
'gtkgesturezoom.h',
'gtkglarea.h',
'gtkglyphpaintable.h',
'gtkgrid.h',
'gtkgridlayout.h',
'gtkgridview.h',
@@ -1050,6 +1052,8 @@ gtk_deps = [
platform_gio_dep,
pangocairo_dep,
harfbuzz_dep,
hbcairo_dep,
hbgobj_dep,
fribidi_dep,
cairogobj_dep,
fontconfig_dep,

View File

@@ -385,6 +385,8 @@ pango_dep = dependency('pango', version: pango_req)
fribidi_dep = dependency('fribidi', version: fribidi_req)
harfbuzz_dep = dependency('harfbuzz', version: harfbuzz_req,
default_options: ['coretext=enabled'])
hbgobj_dep = dependency('harfbuzz-gobject', version: harfbuzz_req)
hbcairo_dep = dependency('harfbuzz-cairo', version: harfbuzz_req)
# Require PangoFT2 if on X11 or wayland
pangoft_dep = dependency('pangoft2', version: pango_req,
@@ -464,6 +466,10 @@ if not cairo_csi_dep.found()
cairo_csi_dep = cc.find_library('cairo-script-interpreter', required: get_option('build-tests'))
endif
if cairo_dep.type_name() == 'internal' or cc.has_function('cairo_font_options_set_custom_palette_color', dependencies: cairo_dep)
cdata.set('HAVE_CAIRO_FONT_OPTIONS_SET_CUSTOM_PALETTE_COLOR', 1)
endif
have_egl = epoxy_dep.get_variable(
pkgconfig: 'epoxy_has_egl',
internal: 'epoxy_has_egl',