Compare commits
19 Commits
main
...
glyph-pain
Author | SHA1 | Date | |
---|---|---|---|
|
d5ea7aa3a0 | ||
|
8bfdbb8eb8 | ||
|
7b03ce983e | ||
|
be3435590c | ||
|
7a84de8918 | ||
|
ce23f588d0 | ||
|
1869909a7d | ||
|
c8b1229d83 | ||
|
5b9ece807d | ||
|
1412b4c797 | ||
|
540ef17131 | ||
|
dbe07159a4 | ||
|
5e6b52fbfc | ||
|
9e19450504 | ||
|
a73abd0b78 | ||
|
6736888fb9 | ||
|
a84616167b | ||
|
3ec1ca13e0 | ||
|
5c270a7b0e |
207
demos/gtk-demo/colorpicker.c
Normal file
207
demos/gtk-demo/colorpicker.c
Normal 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);
|
||||
}
|
15
demos/gtk-demo/colorpicker.h
Normal file
15
demos/gtk-demo/colorpicker.h
Normal 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);
|
64
demos/gtk-demo/colorpicker.ui
Normal file
64
demos/gtk-demo/colorpicker.ui
Normal 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>
|
@@ -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
452
demos/gtk-demo/fontcolors.c
Normal 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);
|
||||
}
|
15
demos/gtk-demo/fontcolors.h
Normal file
15
demos/gtk-demo/fontcolors.h
Normal 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);
|
25
demos/gtk-demo/fontcolors.ui
Normal file
25
demos/gtk-demo/fontcolors.ui
Normal 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
220
demos/gtk-demo/fontpicker.c
Normal 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);
|
||||
}
|
18
demos/gtk-demo/fontpicker.h
Normal file
18
demos/gtk-demo/fontpicker.h
Normal 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);
|
507
demos/gtk-demo/fontvariations.c
Normal file
507
demos/gtk-demo/fontvariations.c
Normal 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);
|
||||
}
|
15
demos/gtk-demo/fontvariations.h
Normal file
15
demos/gtk-demo/fontvariations.h
Normal 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);
|
25
demos/gtk-demo/fontvariations.ui
Normal file
25
demos/gtk-demo/fontvariations.ui
Normal 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
174
demos/gtk-demo/glyphmodel.c
Normal 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);
|
||||
}
|
17
demos/gtk-demo/glyphmodel.h
Normal file
17
demos/gtk-demo/glyphmodel.h
Normal 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);
|
249
demos/gtk-demo/glyphpicker.c
Normal file
249
demos/gtk-demo/glyphpicker.c
Normal 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]);
|
||||
}
|
14
demos/gtk-demo/glyphpicker.h
Normal file
14
demos/gtk-demo/glyphpicker.h
Normal 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);
|
@@ -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
|
||||
|
215
demos/gtk-demo/paintable_glyph.c
Normal file
215
demos/gtk-demo/paintable_glyph.c
Normal 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;
|
||||
}
|
21
demos/gtk-demo/paintable_glyph.css
Normal file
21
demos/gtk-demo/paintable_glyph.css
Normal 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;
|
||||
}
|
162
demos/gtk-demo/paintable_glyph.ui
Normal file
162
demos/gtk-demo/paintable_glyph.ui
Normal 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">Paintable — Glyph</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>
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
||||
/**
|
||||
|
@@ -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
|
||||
|
@@ -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 =
|
||||
{
|
||||
|
@@ -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;
|
||||
|
@@ -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[];
|
||||
|
||||
|
@@ -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;
|
||||
|
@@ -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
695
gtk/gtkglyphpaintable.c
Normal 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
58
gtk/gtkglyphpaintable.h
Normal 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
|
@@ -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:
|
||||
|
@@ -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,
|
||||
|
@@ -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',
|
||||
|
Reference in New Issue
Block a user