2018-01-03 19:23:33 +00:00
/* Pango/Font Explorer
2015-06-19 13:28:09 +00:00
*
2016-01-25 05:40:49 +00:00
* This example demonstrates support for OpenType font features with
* Pango attributes . The attributes can be used manually or via Pango
* markup .
*
* It can also be used to explore available features in OpenType fonts
* and their effect .
2018-01-03 19:23:33 +00:00
*
* If the selected font supports OpenType font variations , then the
* axes are also offered for customization .
2015-06-19 13:28:09 +00:00
*/
# include <gtk/gtk.h>
2016-01-25 05:40:49 +00:00
# include <hb.h>
# include <hb-ot.h>
2018-01-03 19:23:33 +00:00
# include <glib/gi18n.h>
# include "open-type-layout.h"
# include "fontplane.h"
# include "script-names.h"
# include "language-names.h"
2022-10-27 01:52:06 +00:00
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
2022-10-22 14:11:28 +00:00
/* {{{ ScriptLang object */
G_DECLARE_FINAL_TYPE ( ScriptLang , script_lang , SCRIPT , LANG , GObject )
struct _ScriptLang
{
GObject parent ;
char * langname ;
unsigned int script_index ;
unsigned int lang_index ;
hb_tag_t lang_tag ;
} ;
struct _ScriptLangClass
{
GObjectClass parent_class ;
} ;
G_DEFINE_TYPE ( ScriptLang , script_lang , G_TYPE_OBJECT )
static void
script_lang_init ( ScriptLang * self )
{
}
static void
script_lang_finalize ( GObject * object )
{
ScriptLang * self = SCRIPT_LANG ( object ) ;
g_free ( self - > langname ) ;
G_OBJECT_CLASS ( script_lang_parent_class ) - > finalize ( object ) ;
}
static void
script_lang_class_init ( ScriptLangClass * class )
{
GObjectClass * object_class = G_OBJECT_CLASS ( class ) ;
object_class - > finalize = script_lang_finalize ;
}
static ScriptLang *
script_lang_new ( const char * langname ,
unsigned int script_index ,
unsigned int lang_index ,
hb_tag_t lang_tag )
{
ScriptLang * self ;
self = g_object_new ( script_lang_get_type ( ) , NULL ) ;
self - > langname = g_strdup ( langname ) ;
self - > script_index = script_index ;
self - > lang_index = lang_index ;
self - > lang_tag = lang_tag ;
return self ;
}
static char *
script_lang_get_langname ( ScriptLang * self )
{
return g_strdup ( self - > langname ) ;
}
/* }}} */
2018-01-03 19:23:33 +00:00
# define MAKE_TAG(a,b,c,d) (unsigned int)(((a) << 24) | ((b) << 16) | ((c) << 8) | (d))
2015-06-19 13:28:09 +00:00
2022-06-30 21:02:50 +00:00
typedef struct {
unsigned int tag ;
const char * name ;
GtkWidget * icon ;
GtkWidget * dflt ;
GtkWidget * feat ;
} FeatureItem ;
typedef struct {
unsigned int start ;
unsigned int end ;
PangoFontDescription * desc ;
char * features ;
PangoLanguage * language ;
} Range ;
typedef struct {
guint32 tag ;
GtkAdjustment * adjustment ;
2022-06-30 21:51:41 +00:00
double default_value ;
2022-06-30 22:53:10 +00:00
guint tick_cb ;
guint64 start_time ;
gboolean increasing ;
2022-07-01 03:52:37 +00:00
GtkWidget * button ;
2022-06-30 21:02:50 +00:00
} Axis ;
typedef struct {
GtkWidget * the_label ;
GtkWidget * settings ;
GtkWidget * description ;
GtkWidget * font ;
GtkWidget * script_lang ;
GtkWidget * feature_list ;
GtkWidget * variations_grid ;
GtkWidget * instance_combo ;
GtkWidget * stack ;
2022-07-02 15:34:57 +00:00
GtkWidget * entry ;
2022-07-01 03:52:37 +00:00
GtkWidget * plain_toggle ;
GtkWidget * waterfall_toggle ;
2022-06-30 21:02:50 +00:00
GtkWidget * edit_toggle ;
GtkAdjustment * size_adjustment ;
GtkAdjustment * letterspacing_adjustment ;
GtkAdjustment * line_height_adjustment ;
2022-07-02 15:34:57 +00:00
GtkWidget * foreground ;
GtkWidget * background ;
2022-07-01 03:52:37 +00:00
GtkWidget * size_scale ;
2022-06-30 21:02:50 +00:00
GtkWidget * size_entry ;
GtkWidget * letterspacing_entry ;
GtkWidget * line_height_entry ;
GList * feature_items ;
GList * ranges ;
GHashTable * instances ;
GHashTable * axes ;
2022-07-01 03:52:37 +00:00
char * text ;
GtkWidget * swin ;
2022-07-02 15:34:57 +00:00
GtkCssProvider * provider ;
2022-07-03 03:37:34 +00:00
int sample ;
2022-06-30 21:02:50 +00:00
} FontFeaturesDemo ;
static void
demo_free ( gpointer data )
{
FontFeaturesDemo * demo = data ;
g_list_free_full ( demo - > feature_items , g_free ) ;
g_list_free_full ( demo - > ranges , g_free ) ;
g_clear_pointer ( & demo - > instances , g_hash_table_unref ) ;
g_clear_pointer ( & demo - > axes , g_hash_table_unref ) ;
2022-07-01 03:52:37 +00:00
g_clear_pointer ( & demo - > text , g_free ) ;
2022-06-30 21:02:50 +00:00
2022-10-22 15:38:46 +00:00
gtk_style_context_remove_provider_for_display ( gdk_display_get_default ( ) ,
GTK_STYLE_PROVIDER ( demo - > provider ) ) ;
g_object_unref ( demo - > provider ) ;
2022-06-30 21:02:50 +00:00
g_free ( demo ) ;
}
static FontFeaturesDemo * demo ;
2022-06-30 13:42:32 +00:00
static void update_display ( void ) ;
2022-07-01 03:52:37 +00:00
static void
font_features_toggle_plain ( void )
{
if ( gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON ( demo - > plain_toggle ) ) | |
gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON ( demo - > waterfall_toggle ) ) )
{
gtk_stack_set_visible_child_name ( GTK_STACK ( demo - > stack ) , " label " ) ;
update_display ( ) ;
}
}
static void
font_features_notify_waterfall ( void )
{
gboolean can_change_size ;
can_change_size = ! gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON ( demo - > waterfall_toggle ) ) ;
gtk_widget_set_sensitive ( demo - > size_scale , can_change_size ) ;
gtk_widget_set_sensitive ( demo - > size_entry , can_change_size ) ;
}
2022-06-30 13:42:32 +00:00
typedef struct {
GtkAdjustment * adjustment ;
GtkEntry * entry ;
} BasicData ;
static gboolean
update_in_idle ( gpointer data )
{
BasicData * bd = data ;
char * str ;
str = g_strdup_printf ( " %g " , gtk_adjustment_get_value ( bd - > adjustment ) ) ;
gtk_editable_set_text ( GTK_EDITABLE ( bd - > entry ) , str ) ;
g_free ( str ) ;
update_display ( ) ;
g_free ( bd ) ;
return G_SOURCE_REMOVE ;
}
static void
basic_value_changed ( GtkAdjustment * adjustment ,
gpointer data )
{
BasicData * bd ;
bd = g_new ( BasicData , 1 ) ;
bd - > adjustment = adjustment ;
bd - > entry = GTK_ENTRY ( data ) ;
g_idle_add ( update_in_idle , bd ) ;
}
static void
basic_entry_activated ( GtkEntry * entry ,
gpointer data )
{
GtkAdjustment * adjustment = data ;
double value ;
char * err = NULL ;
value = g_strtod ( gtk_editable_get_text ( GTK_EDITABLE ( entry ) ) , & err ) ;
if ( err ! = NULL )
gtk_adjustment_set_value ( adjustment , value ) ;
}
2015-06-19 13:28:09 +00:00
2022-07-02 15:34:57 +00:00
static void
color_set_cb ( void )
{
update_display ( ) ;
}
static void
swap_colors ( void )
{
GdkRGBA fg ;
GdkRGBA bg ;
2022-10-28 15:38:57 +00:00
fg = * gtk_color_dialog_button_get_rgba ( GTK_COLOR_DIALOG_BUTTON ( demo - > foreground ) ) ;
bg = * gtk_color_dialog_button_get_rgba ( GTK_COLOR_DIALOG_BUTTON ( demo - > background ) ) ;
gtk_color_dialog_button_set_rgba ( GTK_COLOR_DIALOG_BUTTON ( demo - > foreground ) , & bg ) ;
gtk_color_dialog_button_set_rgba ( GTK_COLOR_DIALOG_BUTTON ( demo - > background ) , & fg ) ;
2022-07-02 15:34:57 +00:00
}
2022-06-30 21:51:41 +00:00
static void
font_features_reset_basic ( void )
{
gtk_adjustment_set_value ( demo - > size_adjustment , 20 ) ;
gtk_adjustment_set_value ( demo - > letterspacing_adjustment , 0 ) ;
gtk_adjustment_set_value ( demo - > line_height_adjustment , 1 ) ;
2022-10-28 15:38:57 +00:00
gtk_color_dialog_button_set_rgba ( GTK_COLOR_DIALOG_BUTTON ( demo - > foreground ) , & ( GdkRGBA ) { 0. , 0. , 0. , 1. } ) ;
gtk_color_dialog_button_set_rgba ( GTK_COLOR_DIALOG_BUTTON ( demo - > background ) , & ( GdkRGBA ) { 1. , 1. , 1. , 1. } ) ;
2022-06-30 21:51:41 +00:00
}
static void
update_basic ( void )
{
PangoFontDescription * desc ;
2022-10-28 15:38:57 +00:00
desc = gtk_font_dialog_button_get_font_desc ( GTK_FONT_DIALOG_BUTTON ( demo - > font ) ) ;
2022-06-30 21:51:41 +00:00
gtk_adjustment_set_value ( demo - > size_adjustment ,
pango_font_description_get_size ( desc ) / ( double ) PANGO_SCALE ) ;
}
2018-01-03 19:23:33 +00:00
static void add_font_variations ( GString * s ) ;
static void
free_range ( gpointer data )
{
Range * range = data ;
if ( range - > desc )
pango_font_description_free ( range - > desc ) ;
g_free ( range - > features ) ;
g_free ( range ) ;
}
static int
compare_range ( gconstpointer a , gconstpointer b )
{
const Range * ra = a ;
const Range * rb = b ;
if ( ra - > start < rb - > start )
return - 1 ;
else if ( ra - > start > rb - > start )
return 1 ;
else if ( ra - > end < rb - > end )
return 1 ;
else if ( ra - > end > rb - > end )
return - 1 ;
return 0 ;
}
static void
ensure_range ( unsigned int start ,
unsigned int end ,
PangoFontDescription * desc ,
const char * features ,
PangoLanguage * language )
{
GList * l ;
Range * range ;
2022-06-30 21:02:50 +00:00
for ( l = demo - > ranges ; l ; l = l - > next )
2018-01-03 19:23:33 +00:00
{
Range * r = l - > data ;
if ( r - > start = = start & & r - > end = = end )
{
range = r ;
goto set ;
}
}
range = g_new0 ( Range , 1 ) ;
range - > start = start ;
range - > end = end ;
2022-06-30 21:02:50 +00:00
demo - > ranges = g_list_insert_sorted ( demo - > ranges , range , compare_range ) ;
2018-01-03 19:23:33 +00:00
set :
if ( range - > desc )
pango_font_description_free ( range - > desc ) ;
if ( desc )
range - > desc = pango_font_description_copy ( desc ) ;
g_free ( range - > features ) ;
range - > features = g_strdup ( features ) ;
range - > language = language ;
}
2022-07-03 05:19:19 +00:00
static char *
2018-01-03 19:23:33 +00:00
get_feature_display_name ( unsigned int tag )
{
int i ;
2020-08-03 01:11:30 +00:00
static char buf [ 5 ] = { 0 , } ;
if ( tag = = MAKE_TAG ( ' x ' , ' x ' , ' x ' , ' x ' ) )
2022-07-03 05:19:19 +00:00
return g_strdup ( _ ( " Default " ) ) ;
hb_tag_to_string ( tag , buf ) ;
if ( g_str_has_prefix ( buf , " ss " ) & & g_ascii_isdigit ( buf [ 2 ] ) & & g_ascii_isdigit ( buf [ 3 ] ) )
{
int num = ( buf [ 2 ] - ' 0 ' ) * 10 + ( buf [ 3 ] - ' 0 ' ) ;
return g_strdup_printf ( g_dpgettext2 ( NULL , " OpenType layout " , " Stylistic Set %d " ) , num ) ;
}
else if ( g_str_has_prefix ( buf , " cv " ) & & g_ascii_isdigit ( buf [ 2 ] ) & & g_ascii_isdigit ( buf [ 3 ] ) )
{
int num = ( buf [ 2 ] - ' 0 ' ) * 10 + ( buf [ 3 ] - ' 0 ' ) ;
return g_strdup_printf ( g_dpgettext2 ( NULL , " OpenType layout " , " Character Variant %d " ) , num ) ;
}
2018-01-03 19:23:33 +00:00
for ( i = 0 ; i < G_N_ELEMENTS ( open_type_layout_features ) ; i + + )
{
if ( tag = = open_type_layout_features [ i ] . tag )
2022-07-03 05:19:19 +00:00
return g_strdup ( g_dpgettext2 ( NULL , " OpenType layout " , open_type_layout_features [ i ] . name ) ) ;
2018-01-03 19:23:33 +00:00
}
2020-08-03 01:11:30 +00:00
g_warning ( " unknown OpenType layout feature tag: %s " , buf ) ;
2022-07-03 05:19:19 +00:00
return g_strdup ( buf ) ;
2018-01-03 19:23:33 +00:00
}
static void
set_inconsistent ( GtkCheckButton * button ,
gboolean inconsistent )
{
gtk_check_button_set_inconsistent ( GTK_CHECK_BUTTON ( button ) , inconsistent ) ;
gtk_widget_set_opacity ( gtk_widget_get_first_child ( GTK_WIDGET ( button ) ) , inconsistent ? 0.0 : 1.0 ) ;
}
static void
2020-08-30 21:35:53 +00:00
feat_pressed ( GtkGestureClick * gesture ,
int n_press ,
double x ,
double y ,
GtkWidget * feat )
2018-01-03 19:23:33 +00:00
{
2020-08-30 21:35:53 +00:00
const guint button = gtk_gesture_single_get_current_button ( GTK_GESTURE_SINGLE ( gesture ) ) ;
2018-01-03 19:23:33 +00:00
2020-08-30 21:35:53 +00:00
if ( button = = GDK_BUTTON_PRIMARY )
2018-01-03 19:23:33 +00:00
{
2020-08-30 21:35:53 +00:00
g_signal_handlers_block_by_func ( feat , feat_pressed , NULL ) ;
if ( gtk_check_button_get_inconsistent ( GTK_CHECK_BUTTON ( feat ) ) )
{
set_inconsistent ( GTK_CHECK_BUTTON ( feat ) , FALSE ) ;
gtk_check_button_set_active ( GTK_CHECK_BUTTON ( feat ) , TRUE ) ;
}
g_signal_handlers_unblock_by_func ( feat , feat_pressed , NULL ) ;
2018-01-03 19:23:33 +00:00
}
2020-08-30 21:35:53 +00:00
else if ( button = = GDK_BUTTON_SECONDARY )
2018-01-03 19:23:33 +00:00
{
2020-08-30 21:35:53 +00:00
gboolean inconsistent = gtk_check_button_get_inconsistent ( GTK_CHECK_BUTTON ( feat ) ) ;
set_inconsistent ( GTK_CHECK_BUTTON ( feat ) , ! inconsistent ) ;
2018-01-03 19:23:33 +00:00
}
2020-08-30 21:35:53 +00:00
}
2018-01-03 19:23:33 +00:00
2020-08-30 21:35:53 +00:00
static void
feat_toggled_cb ( GtkCheckButton * check_button ,
gpointer data )
{
set_inconsistent ( check_button , FALSE ) ;
2018-01-03 19:23:33 +00:00
}
static void
add_check_group ( GtkWidget * box ,
const char * title ,
const char * * tags )
{
GtkWidget * label ;
GtkWidget * group ;
int i ;
group = gtk_box_new ( GTK_ORIENTATION_VERTICAL , 0 ) ;
gtk_widget_set_halign ( group , GTK_ALIGN_START ) ;
label = gtk_label_new ( title ) ;
gtk_label_set_xalign ( GTK_LABEL ( label ) , 0.0 ) ;
gtk_widget_set_halign ( label , GTK_ALIGN_START ) ;
g_object_set ( label , " margin-top " , 10 , " margin-bottom " , 10 , NULL ) ;
2022-07-01 04:10:16 +00:00
gtk_widget_add_css_class ( label , " heading " ) ;
2020-05-09 12:26:52 +00:00
gtk_box_append ( GTK_BOX ( group ) , label ) ;
2018-01-03 19:23:33 +00:00
for ( i = 0 ; tags [ i ] ; i + + )
{
unsigned int tag ;
GtkWidget * feat ;
FeatureItem * item ;
2020-08-30 21:35:53 +00:00
GtkGesture * gesture ;
2022-07-03 05:19:19 +00:00
char * name ;
2018-01-03 19:23:33 +00:00
tag = hb_tag_from_string ( tags [ i ] , - 1 ) ;
2022-07-03 05:19:19 +00:00
name = get_feature_display_name ( tag ) ;
feat = gtk_check_button_new_with_label ( name ) ;
g_free ( name ) ;
2018-01-03 19:23:33 +00:00
set_inconsistent ( GTK_CHECK_BUTTON ( feat ) , TRUE ) ;
g_signal_connect ( feat , " notify::active " , G_CALLBACK ( update_display ) , NULL ) ;
g_signal_connect ( feat , " notify::inconsistent " , G_CALLBACK ( update_display ) , NULL ) ;
2020-08-30 21:35:53 +00:00
g_signal_connect ( feat , " toggled " , G_CALLBACK ( feat_toggled_cb ) , NULL ) ;
gesture = gtk_gesture_click_new ( ) ;
gtk_gesture_single_set_button ( GTK_GESTURE_SINGLE ( gesture ) , GDK_BUTTON_SECONDARY ) ;
g_signal_connect ( gesture , " pressed " , G_CALLBACK ( feat_pressed ) , feat ) ;
gtk_widget_add_controller ( feat , GTK_EVENT_CONTROLLER ( gesture ) ) ;
2018-01-03 19:23:33 +00:00
2020-05-09 12:26:52 +00:00
gtk_box_append ( GTK_BOX ( group ) , feat ) ;
2018-01-03 19:23:33 +00:00
item = g_new ( FeatureItem , 1 ) ;
item - > name = tags [ i ] ;
item - > tag = tag ;
item - > icon = NULL ;
item - > dflt = NULL ;
item - > feat = feat ;
2022-06-30 21:02:50 +00:00
demo - > feature_items = g_list_prepend ( demo - > feature_items , item ) ;
2018-01-03 19:23:33 +00:00
}
2020-05-09 12:26:52 +00:00
gtk_box_append ( GTK_BOX ( box ) , group ) ;
2018-01-03 19:23:33 +00:00
}
static void
add_radio_group ( GtkWidget * box ,
const char * title ,
const char * * tags )
{
GtkWidget * label ;
GtkWidget * group ;
int i ;
GtkWidget * group_button = NULL ;
group = gtk_box_new ( GTK_ORIENTATION_VERTICAL , 0 ) ;
gtk_widget_set_halign ( group , GTK_ALIGN_START ) ;
label = gtk_label_new ( title ) ;
gtk_label_set_xalign ( GTK_LABEL ( label ) , 0.0 ) ;
gtk_widget_set_halign ( label , GTK_ALIGN_START ) ;
g_object_set ( label , " margin-top " , 10 , " margin-bottom " , 10 , NULL ) ;
2022-07-01 04:10:16 +00:00
gtk_widget_add_css_class ( label , " heading " ) ;
2020-05-09 12:26:52 +00:00
gtk_box_append ( GTK_BOX ( group ) , label ) ;
2018-01-03 19:23:33 +00:00
for ( i = 0 ; tags [ i ] ; i + + )
{
unsigned int tag ;
GtkWidget * feat ;
FeatureItem * item ;
2022-07-03 05:19:19 +00:00
char * name ;
2018-01-03 19:23:33 +00:00
tag = hb_tag_from_string ( tags [ i ] , - 1 ) ;
name = get_feature_display_name ( tag ) ;
2020-08-30 21:35:53 +00:00
feat = gtk_check_button_new_with_label ( name ? name : _ ( " Default " ) ) ;
2022-07-03 05:19:19 +00:00
g_free ( name ) ;
2018-01-03 19:23:33 +00:00
if ( group_button = = NULL )
group_button = feat ;
2020-08-30 21:35:53 +00:00
else
gtk_check_button_set_group ( GTK_CHECK_BUTTON ( feat ) , GTK_CHECK_BUTTON ( group_button ) ) ;
2018-01-03 19:23:33 +00:00
g_signal_connect ( feat , " notify::active " , G_CALLBACK ( update_display ) , NULL ) ;
g_object_set_data ( G_OBJECT ( feat ) , " default " , group_button ) ;
2020-05-09 12:26:52 +00:00
gtk_box_append ( GTK_BOX ( group ) , feat ) ;
2018-01-03 19:23:33 +00:00
item = g_new ( FeatureItem , 1 ) ;
item - > name = tags [ i ] ;
item - > tag = tag ;
item - > icon = NULL ;
item - > dflt = NULL ;
item - > feat = feat ;
2022-06-30 21:02:50 +00:00
demo - > feature_items = g_list_prepend ( demo - > feature_items , item ) ;
2018-01-03 19:23:33 +00:00
}
2020-05-09 12:26:52 +00:00
gtk_box_append ( GTK_BOX ( box ) , group ) ;
2018-01-03 19:23:33 +00:00
}
2015-06-19 13:28:09 +00:00
static void
2016-01-25 05:40:49 +00:00
update_display ( void )
2015-06-19 13:28:09 +00:00
{
GString * s ;
2022-07-02 15:34:57 +00:00
char * text ;
2015-06-19 13:28:09 +00:00
gboolean has_feature ;
2018-01-03 19:23:33 +00:00
PangoFontDescription * desc ;
GList * l ;
PangoAttrList * attrs ;
PangoAttribute * attr ;
2020-07-24 13:54:49 +00:00
int ins , bound ;
2018-01-03 19:23:33 +00:00
guint start , end ;
PangoLanguage * lang ;
char * font_desc ;
char * features ;
2022-06-30 13:42:32 +00:00
double value ;
2022-07-01 03:52:37 +00:00
int text_len ;
gboolean do_waterfall ;
GString * waterfall ;
2015-06-19 13:28:09 +00:00
2022-07-02 15:34:57 +00:00
{
GtkTextBuffer * buffer ;
GtkTextIter start_iter , end_iter ;
buffer = gtk_text_view_get_buffer ( GTK_TEXT_VIEW ( demo - > entry ) ) ;
gtk_text_buffer_get_bounds ( buffer , & start_iter , & end_iter ) ;
text = gtk_text_buffer_get_text ( buffer , & start_iter , & end_iter , FALSE ) ;
text_len = strlen ( text ) ;
}
2022-07-01 03:52:37 +00:00
do_waterfall = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON ( demo - > waterfall_toggle ) ) ;
2015-06-19 13:28:09 +00:00
2022-07-01 03:52:37 +00:00
gtk_label_set_wrap ( GTK_LABEL ( demo - > the_label ) , ! do_waterfall ) ;
if ( do_waterfall )
{
start = PANGO_ATTR_INDEX_FROM_TEXT_BEGINNING ;
end = PANGO_ATTR_INDEX_TO_TEXT_END ;
}
else if ( gtk_label_get_selection_bounds ( GTK_LABEL ( demo - > the_label ) , & ins , & bound ) )
2018-01-03 19:23:33 +00:00
{
start = g_utf8_offset_to_pointer ( text , ins ) - text ;
end = g_utf8_offset_to_pointer ( text , bound ) - text ;
}
else
{
start = PANGO_ATTR_INDEX_FROM_TEXT_BEGINNING ;
end = PANGO_ATTR_INDEX_TO_TEXT_END ;
}
2022-10-28 15:38:57 +00:00
desc = gtk_font_dialog_button_get_font_desc ( GTK_FONT_DIALOG_BUTTON ( demo - > font ) ) ;
2018-01-03 19:23:33 +00:00
2022-06-30 21:02:50 +00:00
value = gtk_adjustment_get_value ( demo - > size_adjustment ) ;
2022-06-30 13:42:32 +00:00
pango_font_description_set_size ( desc , value * PANGO_SCALE ) ;
2018-01-03 19:23:33 +00:00
s = g_string_new ( " " ) ;
add_font_variations ( s ) ;
if ( s - > len > 0 )
{
pango_font_description_set_variations ( desc , s - > str ) ;
g_string_free ( s , TRUE ) ;
}
font_desc = pango_font_description_to_string ( desc ) ;
2015-06-19 13:28:09 +00:00
s = g_string_new ( " " ) ;
has_feature = FALSE ;
2022-06-30 21:02:50 +00:00
for ( l = demo - > feature_items ; l ; l = l - > next )
2015-06-19 13:28:09 +00:00
{
2018-01-03 19:23:33 +00:00
FeatureItem * item = l - > data ;
if ( ! gtk_widget_is_sensitive ( item - > feat ) )
2015-06-19 13:28:09 +00:00
continue ;
2020-08-30 21:35:53 +00:00
if ( GTK_IS_CHECK_BUTTON ( item - > feat ) )
2015-06-19 13:28:09 +00:00
{
2020-08-30 21:35:53 +00:00
if ( g_object_get_data ( G_OBJECT ( item - > feat ) , " default " ) )
{
if ( gtk_check_button_get_active ( GTK_CHECK_BUTTON ( item - > feat ) ) & &
strcmp ( item - > name , " xxxx " ) ! = 0 )
{
if ( has_feature )
g_string_append ( s , " , " ) ;
g_string_append ( s , item - > name ) ;
g_string_append ( s , " 1 " ) ;
has_feature = TRUE ;
}
}
else
2015-06-19 13:28:09 +00:00
{
2020-08-30 21:35:53 +00:00
if ( gtk_check_button_get_inconsistent ( GTK_CHECK_BUTTON ( item - > feat ) ) )
continue ;
2015-06-19 13:28:09 +00:00
if ( has_feature )
g_string_append ( s , " , " ) ;
2018-01-03 19:23:33 +00:00
g_string_append ( s , item - > name ) ;
2020-08-30 21:35:53 +00:00
if ( gtk_check_button_get_active ( GTK_CHECK_BUTTON ( item - > feat ) ) )
g_string_append ( s , " 1 " ) ;
else
g_string_append ( s , " 0 " ) ;
2015-06-19 13:28:09 +00:00
has_feature = TRUE ;
}
}
}
2018-01-03 19:23:33 +00:00
features = g_string_free ( s , FALSE ) ;
2016-01-25 05:40:49 +00:00
2022-10-22 14:11:28 +00:00
if ( gtk_drop_down_get_selected ( GTK_DROP_DOWN ( demo - > script_lang ) ) ! = 0 )
2016-01-25 05:40:49 +00:00
{
2022-10-22 14:11:28 +00:00
ScriptLang * selected ;
2018-01-03 19:23:33 +00:00
2022-10-22 14:11:28 +00:00
selected = gtk_drop_down_get_selected_item ( GTK_DROP_DOWN ( demo - > script_lang ) ) ;
2016-01-25 05:40:49 +00:00
2022-10-22 14:11:28 +00:00
lang = pango_language_from_string ( hb_language_to_string ( hb_ot_tag_to_language ( selected - > lang_tag ) ) ) ;
2016-01-25 05:40:49 +00:00
}
else
lang = NULL ;
2018-01-03 19:23:33 +00:00
attrs = pango_attr_list_new ( ) ;
2015-06-19 13:28:09 +00:00
2022-06-30 21:02:50 +00:00
if ( gtk_adjustment_get_value ( demo - > letterspacing_adjustment ) ! = 0. )
2022-06-30 13:42:32 +00:00
{
2022-06-30 21:02:50 +00:00
attr = pango_attr_letter_spacing_new ( gtk_adjustment_get_value ( demo - > letterspacing_adjustment ) ) ;
2022-06-30 13:42:32 +00:00
attr - > start_index = start ;
attr - > end_index = end ;
pango_attr_list_insert ( attrs , attr ) ;
}
2022-06-30 21:02:50 +00:00
if ( gtk_adjustment_get_value ( demo - > line_height_adjustment ) ! = 1. )
2022-06-30 13:42:32 +00:00
{
2022-06-30 21:02:50 +00:00
attr = pango_attr_line_height_new ( gtk_adjustment_get_value ( demo - > line_height_adjustment ) ) ;
2022-06-30 13:42:32 +00:00
attr - > start_index = start ;
attr - > end_index = end ;
pango_attr_list_insert ( attrs , attr ) ;
}
2022-07-02 15:34:57 +00:00
{
GdkRGBA rgba ;
char * fg , * bg , * css ;
2022-10-28 15:38:57 +00:00
rgba = * gtk_color_dialog_button_get_rgba ( GTK_COLOR_DIALOG_BUTTON ( demo - > foreground ) ) ;
2022-07-02 15:34:57 +00:00
attr = pango_attr_foreground_new ( 65535 * rgba . red ,
65535 * rgba . green ,
65535 * rgba . blue ) ;
attr - > start_index = start ;
attr - > end_index = end ;
pango_attr_list_insert ( attrs , attr ) ;
attr = pango_attr_foreground_alpha_new ( 65535 * rgba . alpha ) ;
attr - > start_index = start ;
attr - > end_index = end ;
pango_attr_list_insert ( attrs , attr ) ;
fg = gdk_rgba_to_string ( & rgba ) ;
2022-10-28 15:38:57 +00:00
rgba = * gtk_color_dialog_button_get_rgba ( GTK_COLOR_DIALOG_BUTTON ( demo - > background ) ) ;
2022-07-02 15:34:57 +00:00
bg = gdk_rgba_to_string ( & rgba ) ;
css = g_strdup_printf ( " .font_features_background { caret-color: %s; background-color: %s; } " , fg , bg ) ;
gtk_css_provider_load_from_data ( demo - > provider , css , strlen ( css ) ) ;
g_free ( css ) ;
g_free ( fg ) ;
g_free ( bg ) ;
}
2022-07-01 03:52:37 +00:00
if ( do_waterfall )
2018-01-03 19:23:33 +00:00
{
2022-07-01 03:52:37 +00:00
attr = pango_attr_font_desc_new ( desc ) ;
2018-01-03 19:23:33 +00:00
pango_attr_list_insert ( attrs , attr ) ;
2022-07-01 03:52:37 +00:00
attr = pango_attr_font_features_new ( features ) ;
pango_attr_list_insert ( attrs , attr ) ;
attr = pango_attr_language_new ( lang ) ;
2018-01-03 19:23:33 +00:00
pango_attr_list_insert ( attrs , attr ) ;
2022-07-01 03:52:37 +00:00
}
else
{
ensure_range ( start , end , desc , features , lang ) ;
2018-01-03 19:23:33 +00:00
2022-07-01 03:52:37 +00:00
for ( l = demo - > ranges ; l ; l = l - > next )
2018-01-03 19:23:33 +00:00
{
2022-07-01 03:52:37 +00:00
Range * range = l - > data ;
attr = pango_attr_font_desc_new ( range - > desc ) ;
attr - > start_index = range - > start ;
attr - > end_index = range - > end ;
pango_attr_list_insert ( attrs , attr ) ;
attr = pango_attr_font_features_new ( range - > features ) ;
2018-01-03 19:23:33 +00:00
attr - > start_index = range - > start ;
attr - > end_index = range - > end ;
pango_attr_list_insert ( attrs , attr ) ;
2022-07-01 03:52:37 +00:00
if ( range - > language )
{
attr = pango_attr_language_new ( range - > language ) ;
attr - > start_index = range - > start ;
attr - > end_index = range - > end ;
pango_attr_list_insert ( attrs , attr ) ;
}
2018-01-03 19:23:33 +00:00
}
}
2022-06-30 21:02:50 +00:00
gtk_label_set_text ( GTK_LABEL ( demo - > description ) , font_desc ) ;
gtk_label_set_text ( GTK_LABEL ( demo - > settings ) , features ) ;
2022-07-01 03:52:37 +00:00
if ( do_waterfall )
{
waterfall = g_string_new ( " " ) ;
int sizes [ ] = { 7 , 8 , 9 , 10 , 12 , 14 , 16 , 20 , 24 , 30 , 40 , 50 , 60 , 70 , 90 } ;
start = 0 ;
for ( int i = 0 ; i < G_N_ELEMENTS ( sizes ) ; i + + )
{
g_string_append ( waterfall , text ) ;
g_string_append_c ( waterfall , ' \n ' ) ;
attr = pango_attr_size_new ( sizes [ i ] * PANGO_SCALE ) ;
attr - > start_index = start ;
attr - > end_index = start + text_len ;
pango_attr_list_insert ( attrs , attr ) ;
start + = text_len + 1 ;
}
gtk_label_set_text ( GTK_LABEL ( demo - > the_label ) , waterfall - > str ) ;
g_string_free ( waterfall , TRUE ) ;
}
else
gtk_label_set_text ( GTK_LABEL ( demo - > the_label ) , text ) ;
2022-06-30 21:02:50 +00:00
gtk_label_set_attributes ( GTK_LABEL ( demo - > the_label ) , attrs ) ;
2015-06-19 13:28:09 +00:00
g_free ( font_desc ) ;
2018-01-03 19:23:33 +00:00
g_free ( features ) ;
pango_attr_list_unref ( attrs ) ;
2022-07-02 15:34:57 +00:00
g_free ( text ) ;
2015-06-19 13:28:09 +00:00
}
2016-01-25 05:40:49 +00:00
static PangoFont *
get_pango_font ( void )
{
PangoFontDescription * desc ;
PangoContext * context ;
2022-10-28 15:38:57 +00:00
desc = gtk_font_dialog_button_get_font_desc ( GTK_FONT_DIALOG_BUTTON ( demo - > font ) ) ;
2022-06-30 21:02:50 +00:00
context = gtk_widget_get_pango_context ( demo - > font ) ;
2016-01-25 05:40:49 +00:00
2018-01-03 19:23:33 +00:00
return pango_context_load_font ( context , desc ) ;
}
2016-01-25 05:40:49 +00:00
typedef struct {
hb_tag_t script_tag ;
hb_tag_t lang_tag ;
unsigned int script_index ;
unsigned int lang_index ;
} TagPair ;
static guint
tag_pair_hash ( gconstpointer data )
{
const TagPair * pair = data ;
return pair - > script_tag + pair - > lang_tag ;
}
static gboolean
tag_pair_equal ( gconstpointer a , gconstpointer b )
{
const TagPair * pair_a = a ;
const TagPair * pair_b = b ;
return pair_a - > script_tag = = pair_b - > script_tag & & pair_a - > lang_tag = = pair_b - > lang_tag ;
}
2018-01-03 19:23:33 +00:00
2022-10-22 14:11:28 +00:00
static GtkOrdering
script_sort ( const void * item1 ,
const void * item2 ,
void * data )
{
ScriptLang * a = ( ScriptLang * ) item1 ;
ScriptLang * b = ( ScriptLang * ) item2 ;
2018-01-03 19:23:33 +00:00
2022-10-22 14:11:28 +00:00
return strcmp ( a - > langname , b - > langname ) ;
2018-01-03 19:23:33 +00:00
}
2016-01-25 05:40:49 +00:00
static void
update_script_combo ( void )
{
2022-10-22 14:11:28 +00:00
GListStore * store ;
GtkSortListModel * sortmodel ;
2016-01-25 05:40:49 +00:00
hb_font_t * hb_font ;
PangoFont * pango_font ;
GHashTable * tags ;
GHashTableIter iter ;
TagPair * pair ;
2022-10-28 15:38:57 +00:00
PangoLanguage * language ;
const char * lang ;
2018-01-03 19:23:33 +00:00
hb_tag_t active ;
2022-10-28 15:38:57 +00:00
language = gtk_font_dialog_button_get_language ( GTK_FONT_DIALOG_BUTTON ( demo - > font ) ) ;
lang = pango_language_to_string ( language ) ;
2019-04-12 17:42:38 +00:00
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
2018-01-03 19:23:33 +00:00
active = hb_ot_tag_from_language ( hb_language_from_string ( lang , - 1 ) ) ;
2019-04-12 17:42:38 +00:00
G_GNUC_END_IGNORE_DEPRECATIONS
2022-10-22 14:11:28 +00:00
store = g_list_store_new ( script_lang_get_type ( ) ) ;
2016-01-25 05:40:49 +00:00
pango_font = get_pango_font ( ) ;
2019-07-30 20:32:34 +00:00
hb_font = pango_font_get_hb_font ( pango_font ) ;
2016-01-25 05:40:49 +00:00
tags = g_hash_table_new_full ( tag_pair_hash , tag_pair_equal , g_free , NULL ) ;
2022-06-30 13:42:32 +00:00
pair = g_new ( TagPair , 1 ) ;
pair - > script_tag = 0 ;
pair - > lang_tag = 0 ;
g_hash_table_add ( tags , pair ) ;
2016-01-25 05:40:49 +00:00
pair = g_new ( TagPair , 1 ) ;
pair - > script_tag = HB_OT_TAG_DEFAULT_SCRIPT ;
pair - > lang_tag = HB_OT_TAG_DEFAULT_LANGUAGE ;
2018-01-03 19:23:33 +00:00
g_hash_table_add ( tags , pair ) ;
2016-01-25 05:40:49 +00:00
if ( hb_font )
{
hb_tag_t tables [ 2 ] = { HB_OT_TAG_GSUB , HB_OT_TAG_GPOS } ;
hb_face_t * hb_face ;
hb_face = hb_font_get_face ( hb_font ) ;
2022-10-22 14:11:28 +00:00
for ( guint i = 0 ; i < 2 ; i + + )
2016-01-25 05:40:49 +00:00
{
hb_tag_t scripts [ 80 ] ;
unsigned int script_count = G_N_ELEMENTS ( scripts ) ;
hb_ot_layout_table_get_script_tags ( hb_face , tables [ i ] , 0 , & script_count , scripts ) ;
2022-10-22 14:11:28 +00:00
for ( guint j = 0 ; j < script_count ; j + + )
2016-01-25 05:40:49 +00:00
{
hb_tag_t languages [ 80 ] ;
unsigned int language_count = G_N_ELEMENTS ( languages ) ;
hb_ot_layout_script_get_language_tags ( hb_face , tables [ i ] , j , 0 , & language_count , languages ) ;
2022-10-22 14:11:28 +00:00
for ( guint k = 0 ; k < language_count ; k + + )
2016-01-25 05:40:49 +00:00
{
pair = g_new ( TagPair , 1 ) ;
pair - > script_tag = scripts [ j ] ;
pair - > lang_tag = languages [ k ] ;
pair - > script_index = j ;
pair - > lang_index = k ;
g_hash_table_add ( tags , pair ) ;
}
}
}
}
g_object_unref ( pango_font ) ;
g_hash_table_iter_init ( & iter , tags ) ;
while ( g_hash_table_iter_next ( & iter , ( gpointer * ) & pair , NULL ) )
{
const char * langname ;
char langbuf [ 5 ] ;
2022-06-30 13:42:32 +00:00
if ( pair - > lang_tag = = 0 & & pair - > script_tag = = 0 )
langname = NC_ ( " Language " , " None " ) ;
else if ( pair - > lang_tag = = HB_OT_TAG_DEFAULT_LANGUAGE )
2018-01-03 19:23:33 +00:00
langname = NC_ ( " Language " , " Default " ) ;
2016-01-25 05:40:49 +00:00
else
{
2018-01-03 19:23:33 +00:00
langname = get_language_name_for_tag ( pair - > lang_tag ) ;
if ( ! langname )
2016-01-25 05:40:49 +00:00
{
2018-01-03 19:23:33 +00:00
hb_tag_to_string ( pair - > lang_tag , langbuf ) ;
langbuf [ 4 ] = 0 ;
langname = langbuf ;
2016-01-25 05:40:49 +00:00
}
}
2022-10-22 14:11:28 +00:00
g_list_store_append ( store , script_lang_new ( langname ,
pair - > script_index ,
pair - > lang_index ,
pair - > lang_tag ) ) ;
2016-01-25 05:40:49 +00:00
}
g_hash_table_destroy ( tags ) ;
2022-10-22 14:11:28 +00:00
sortmodel = gtk_sort_list_model_new ( G_LIST_MODEL ( store ) ,
GTK_SORTER ( gtk_custom_sorter_new ( script_sort , NULL , NULL ) ) ) ;
gtk_drop_down_set_model ( GTK_DROP_DOWN ( demo - > script_lang ) , G_LIST_MODEL ( sortmodel ) ) ;
for ( guint i = 0 ; i < g_list_model_get_n_items ( G_LIST_MODEL ( sortmodel ) ) ; i + + )
{
ScriptLang * item = g_list_model_get_item ( G_LIST_MODEL ( sortmodel ) , i ) ;
g_object_unref ( item ) ;
if ( item - > lang_tag = = active )
{
gtk_drop_down_set_selected ( GTK_DROP_DOWN ( demo - > script_lang ) , i ) ;
break ;
}
}
g_object_unref ( sortmodel ) ;
2016-01-25 05:40:49 +00:00
}
2022-07-03 05:19:19 +00:00
static char *
get_name ( hb_face_t * hbface ,
hb_ot_name_id_t id )
{
unsigned int len ;
char * text ;
if ( id = = HB_OT_NAME_ID_INVALID )
return NULL ;
len = hb_ot_name_get_utf8 ( hbface , id , HB_LANGUAGE_INVALID , NULL , NULL ) ;
len + + ;
text = g_new ( char , len ) ;
hb_ot_name_get_utf8 ( hbface , id , HB_LANGUAGE_INVALID , & len , text ) ;
return text ;
}
2016-01-25 05:40:49 +00:00
static void
update_features ( void )
{
2020-07-24 13:54:49 +00:00
int i , j ;
2016-01-25 05:40:49 +00:00
PangoFont * pango_font ;
hb_font_t * hb_font ;
2018-01-03 19:23:33 +00:00
GList * l ;
2022-10-22 14:11:28 +00:00
ScriptLang * selected ;
2016-01-25 05:40:49 +00:00
/* set feature presence checks from the font features */
2022-10-22 14:11:28 +00:00
if ( gtk_drop_down_get_selected ( GTK_DROP_DOWN ( demo - > script_lang ) ) = = 0 )
2016-01-25 05:40:49 +00:00
return ;
2022-10-22 14:11:28 +00:00
selected = gtk_drop_down_get_selected_item ( GTK_DROP_DOWN ( demo - > script_lang ) ) ;
2016-01-25 05:40:49 +00:00
2022-10-22 14:11:28 +00:00
if ( selected - > lang_tag = = 0 ) /* None is selected */
2022-06-30 13:42:32 +00:00
{
2022-06-30 21:02:50 +00:00
for ( l = demo - > feature_items ; l ; l = l - > next )
2022-06-30 13:42:32 +00:00
{
FeatureItem * item = l - > data ;
gtk_widget_show ( item - > feat ) ;
gtk_widget_show ( gtk_widget_get_parent ( item - > feat ) ) ;
if ( strcmp ( item - > name , " xxxx " ) = = 0 )
gtk_check_button_set_active ( GTK_CHECK_BUTTON ( item - > feat ) , TRUE ) ;
}
return ;
}
2022-06-30 21:02:50 +00:00
for ( l = demo - > feature_items ; l ; l = l - > next )
2022-06-30 13:42:32 +00:00
{
FeatureItem * item = l - > data ;
gtk_widget_hide ( item - > feat ) ;
gtk_widget_hide ( gtk_widget_get_parent ( item - > feat ) ) ;
if ( strcmp ( item - > name , " xxxx " ) = = 0 )
gtk_check_button_set_active ( GTK_CHECK_BUTTON ( item - > feat ) , TRUE ) ;
}
2016-01-25 05:40:49 +00:00
pango_font = get_pango_font ( ) ;
2019-07-30 20:32:34 +00:00
hb_font = pango_font_get_hb_font ( pango_font ) ;
2016-01-25 05:40:49 +00:00
if ( hb_font )
{
hb_tag_t tables [ 2 ] = { HB_OT_TAG_GSUB , HB_OT_TAG_GPOS } ;
hb_face_t * hb_face ;
2022-10-28 15:38:57 +00:00
const char * feat ;
2016-01-25 05:40:49 +00:00
hb_face = hb_font_get_face ( hb_font ) ;
for ( i = 0 ; i < 2 ; i + + )
{
hb_tag_t features [ 80 ] ;
unsigned int count = G_N_ELEMENTS ( features ) ;
hb_ot_layout_language_get_feature_tags ( hb_face ,
tables [ i ] ,
2022-10-22 14:11:28 +00:00
selected - > script_index ,
selected - > lang_index ,
2016-01-25 05:40:49 +00:00
0 ,
& count ,
features ) ;
for ( j = 0 ; j < count ; j + + )
{
2018-01-03 19:23:33 +00:00
char buf [ 5 ] ;
hb_tag_to_string ( features [ j ] , buf ) ;
buf [ 4 ] = 0 ;
2022-07-03 05:19:19 +00:00
#if 0
2018-01-03 19:23:33 +00:00
g_print ( " %s present in %s \n " , buf , i = = 0 ? " GSUB " : " GPOS " ) ;
# endif
2022-07-03 05:19:19 +00:00
if ( g_str_has_prefix ( buf , " ss " ) | | g_str_has_prefix ( buf , " cv " ) )
{
unsigned int feature_index ;
hb_ot_name_id_t label_id , tooltip_id , sample_id , first_param_id ;
unsigned int num_params ;
hb_ot_layout_language_find_feature ( hb_face ,
tables [ i ] ,
2022-10-22 14:11:28 +00:00
selected - > script_index ,
selected - > lang_index ,
2022-07-03 05:19:19 +00:00
features [ j ] ,
& feature_index ) ;
if ( hb_ot_layout_feature_get_name_ids ( hb_face ,
tables [ i ] ,
feature_index ,
& label_id ,
& tooltip_id ,
& sample_id ,
& num_params ,
& first_param_id ) )
{
char * label = get_name ( hb_face , label_id ) ;
if ( label )
{
for ( l = demo - > feature_items ; l ; l = l - > next )
{
FeatureItem * item = l - > data ;
if ( item - > tag = = features [ j ] )
{
gtk_check_button_set_label ( GTK_CHECK_BUTTON ( item - > feat ) , label ) ;
break ;
}
}
}
g_free ( label ) ;
}
}
2022-06-30 21:02:50 +00:00
for ( l = demo - > feature_items ; l ; l = l - > next )
2016-01-25 05:40:49 +00:00
{
2018-01-03 19:23:33 +00:00
FeatureItem * item = l - > data ;
if ( item - > tag = = features [ j ] )
{
gtk_widget_show ( item - > feat ) ;
gtk_widget_show ( gtk_widget_get_parent ( item - > feat ) ) ;
2020-08-30 21:35:53 +00:00
if ( GTK_IS_CHECK_BUTTON ( item - > feat ) )
2018-01-03 19:23:33 +00:00
{
GtkWidget * def = GTK_WIDGET ( g_object_get_data ( G_OBJECT ( item - > feat ) , " default " ) ) ;
2020-08-30 21:35:53 +00:00
if ( def )
{
gtk_widget_show ( def ) ;
gtk_widget_show ( gtk_widget_get_parent ( def ) ) ;
gtk_check_button_set_active ( GTK_CHECK_BUTTON ( def ) , TRUE ) ;
}
else
set_inconsistent ( GTK_CHECK_BUTTON ( item - > feat ) , TRUE ) ;
2018-01-03 19:23:33 +00:00
}
}
2016-01-25 05:40:49 +00:00
}
}
}
2022-10-28 15:38:57 +00:00
feat = gtk_font_dialog_button_get_font_features ( GTK_FONT_DIALOG_BUTTON ( demo - > font ) ) ;
2018-01-03 19:23:33 +00:00
if ( feat )
{
2022-06-30 21:02:50 +00:00
for ( l = demo - > feature_items ; l ; l = l - > next )
2018-01-03 19:23:33 +00:00
{
FeatureItem * item = l - > data ;
char buf [ 5 ] ;
char * p ;
hb_tag_to_string ( item - > tag , buf ) ;
buf [ 4 ] = 0 ;
p = strstr ( feat , buf ) ;
if ( p )
{
2020-08-30 21:35:53 +00:00
if ( GTK_IS_CHECK_BUTTON ( item - > feat ) & & g_object_get_data ( G_OBJECT ( item - > feat ) , " default " ) )
2018-01-03 19:23:33 +00:00
{
2020-08-30 21:35:53 +00:00
gtk_check_button_set_active ( GTK_CHECK_BUTTON ( item - > feat ) , p [ 6 ] = = ' 1 ' ) ;
2018-01-03 19:23:33 +00:00
}
else if ( GTK_IS_CHECK_BUTTON ( item - > feat ) )
{
set_inconsistent ( GTK_CHECK_BUTTON ( item - > feat ) , FALSE ) ;
2020-08-30 21:35:53 +00:00
gtk_check_button_set_active ( GTK_CHECK_BUTTON ( item - > feat ) , p [ 6 ] = = ' 1 ' ) ;
2018-01-03 19:23:33 +00:00
}
}
}
}
2016-01-25 05:40:49 +00:00
}
g_object_unref ( pango_font ) ;
}
2018-01-03 19:23:33 +00:00
# define FixedToFloat(f) (((float)(f)) / 65536.0)
static void
adjustment_changed ( GtkAdjustment * adjustment ,
GtkEntry * entry )
{
char * str ;
str = g_strdup_printf ( " %g " , gtk_adjustment_get_value ( adjustment ) ) ;
2019-02-28 19:31:36 +00:00
gtk_editable_set_text ( GTK_EDITABLE ( entry ) , str ) ;
2018-01-03 19:23:33 +00:00
g_free ( str ) ;
update_display ( ) ;
}
static void
entry_activated ( GtkEntry * entry ,
GtkAdjustment * adjustment )
{
2020-07-24 20:32:16 +00:00
double value ;
2020-07-24 18:40:36 +00:00
char * err = NULL ;
2018-01-03 19:23:33 +00:00
2019-02-28 19:31:36 +00:00
value = g_strtod ( gtk_editable_get_text ( GTK_EDITABLE ( entry ) ) , & err ) ;
2018-01-03 19:23:33 +00:00
if ( err ! = NULL )
gtk_adjustment_set_value ( adjustment , value ) ;
}
static void unset_instance ( GtkAdjustment * adjustment ) ;
2022-07-01 03:52:37 +00:00
static void start_or_stop_axis_animation ( GtkButton * button ,
gpointer data ) ;
2022-06-30 21:51:41 +00:00
static void
font_features_reset_variations ( void )
{
GHashTableIter iter ;
Axis * axis ;
g_hash_table_iter_init ( & iter , demo - > axes ) ;
while ( g_hash_table_iter_next ( & iter , ( gpointer * ) NULL , ( gpointer * ) & axis ) )
{
2022-07-01 03:52:37 +00:00
if ( axis - > tick_cb )
start_or_stop_axis_animation ( GTK_BUTTON ( axis - > button ) , axis ) ;
2022-06-30 21:51:41 +00:00
gtk_adjustment_set_value ( axis - > adjustment , axis - > default_value ) ;
}
}
2018-01-03 19:23:33 +00:00
static void
add_font_variations ( GString * s )
{
GHashTableIter iter ;
Axis * axis ;
char buf [ G_ASCII_DTOSTR_BUF_SIZE ] ;
2020-03-07 14:01:21 +00:00
const char * sep = " " ;
2018-01-03 19:23:33 +00:00
2022-06-30 21:02:50 +00:00
g_hash_table_iter_init ( & iter , demo - > axes ) ;
2018-01-03 19:23:33 +00:00
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 = " , " ;
}
}
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 ;
}
2022-06-30 22:53:10 +00:00
static double
ease_out_cubic ( double t )
{
double p = t - 1 ;
return p * p * p + 1 ;
}
static gboolean
animate_axis ( GtkWidget * widget ,
GdkFrameClock * frame_clock ,
gpointer data )
{
Axis * axis = data ;
guint64 now ;
double upper , lower , value ;
now = g_get_monotonic_time ( ) ;
if ( now > = axis - > start_time + G_TIME_SPAN_SECOND )
{
axis - > start_time + = G_TIME_SPAN_SECOND ;
axis - > increasing = ! axis - > increasing ;
}
value = ( now - axis - > start_time ) / ( double ) G_TIME_SPAN_SECOND ;
value = ease_out_cubic ( value ) ;
lower = gtk_adjustment_get_lower ( axis - > adjustment ) ;
upper = gtk_adjustment_get_upper ( axis - > adjustment ) ;
if ( axis - > increasing )
gtk_adjustment_set_value ( axis - > adjustment , lower + ( upper - lower ) * value ) ;
else
gtk_adjustment_set_value ( axis - > adjustment , upper - ( upper - lower ) * value ) ;
return G_SOURCE_CONTINUE ;
}
static void
2022-07-01 03:52:37 +00:00
start_or_stop_axis_animation ( GtkButton * button ,
gpointer data )
2022-06-30 22:53:10 +00:00
{
Axis * axis = data ;
if ( axis - > tick_cb )
{
gtk_widget_remove_tick_callback ( GTK_WIDGET ( button ) , axis - > tick_cb ) ;
axis - > tick_cb = 0 ;
gtk_button_set_icon_name ( button , " media-playback-start " ) ;
}
else
{
double value , upper , lower ;
gtk_button_set_icon_name ( button , " media-playback-stop " ) ;
axis - > tick_cb = gtk_widget_add_tick_callback ( GTK_WIDGET ( button ) , animate_axis , axis , NULL ) ;
value = gtk_adjustment_get_value ( axis - > adjustment ) ;
lower = gtk_adjustment_get_lower ( axis - > adjustment ) ;
upper = gtk_adjustment_get_upper ( axis - > adjustment ) ;
value = value / ( upper - lower ) ;
axis - > start_time = g_get_monotonic_time ( ) - value * G_TIME_SPAN_SECOND ;
axis - > increasing = TRUE ;
}
}
2018-01-03 19:23:33 +00:00
static void
2019-07-30 20:32:34 +00:00
add_axis ( hb_face_t * hb_face ,
hb_ot_var_axis_info_t * ax ,
float value ,
int i )
2018-01-03 19:23:33 +00:00
{
GtkWidget * axis_label ;
GtkWidget * axis_entry ;
GtkWidget * axis_scale ;
GtkAdjustment * adjustment ;
Axis * axis ;
2019-07-30 20:32:34 +00:00
char name [ 20 ] ;
unsigned int name_len = 20 ;
hb_ot_name_get_utf8 ( hb_face , ax - > name_id , HB_LANGUAGE_INVALID , & name_len , name ) ;
2018-01-03 19:23:33 +00:00
2019-07-30 20:32:34 +00:00
axis_label = gtk_label_new ( name ) ;
2018-01-03 19:23:33 +00:00
gtk_widget_set_halign ( axis_label , GTK_ALIGN_START ) ;
gtk_widget_set_valign ( axis_label , GTK_ALIGN_BASELINE ) ;
2022-06-30 21:02:50 +00:00
gtk_grid_attach ( GTK_GRID ( demo - > variations_grid ) , axis_label , 0 , i , 1 , 1 ) ;
2019-07-30 20:32:34 +00:00
adjustment = gtk_adjustment_new ( value , ax - > min_value , ax - > max_value ,
2018-01-03 19:23:33 +00:00
1.0 , 10.0 , 0.0 ) ;
axis_scale = gtk_scale_new ( GTK_ORIENTATION_HORIZONTAL , adjustment ) ;
2019-07-30 20:32:34 +00:00
gtk_scale_add_mark ( GTK_SCALE ( axis_scale ) , ax - > default_value , GTK_POS_TOP , NULL ) ;
2018-01-03 19:23:33 +00:00
gtk_widget_set_valign ( axis_scale , GTK_ALIGN_BASELINE ) ;
gtk_widget_set_hexpand ( axis_scale , TRUE ) ;
gtk_widget_set_size_request ( axis_scale , 100 , - 1 ) ;
2022-06-30 21:02:50 +00:00
gtk_grid_attach ( GTK_GRID ( demo - > variations_grid ) , axis_scale , 1 , i , 1 , 1 ) ;
2018-01-03 19:23:33 +00:00
axis_entry = gtk_entry_new ( ) ;
gtk_widget_set_valign ( axis_entry , GTK_ALIGN_BASELINE ) ;
2019-02-28 19:31:36 +00:00
gtk_editable_set_width_chars ( GTK_EDITABLE ( axis_entry ) , 4 ) ;
2022-06-30 13:42:32 +00:00
gtk_editable_set_max_width_chars ( GTK_EDITABLE ( axis_entry ) , 4 ) ;
gtk_widget_set_hexpand ( axis_entry , FALSE ) ;
2022-06-30 21:02:50 +00:00
gtk_grid_attach ( GTK_GRID ( demo - > variations_grid ) , axis_entry , 2 , i , 1 , 1 ) ;
2018-01-03 19:23:33 +00:00
2022-07-01 01:11:49 +00:00
axis = g_new0 ( Axis , 1 ) ;
2018-01-03 19:23:33 +00:00
axis - > tag = ax - > tag ;
axis - > adjustment = adjustment ;
2022-06-30 21:51:41 +00:00
axis - > default_value = ax - > default_value ;
2022-06-30 21:02:50 +00:00
g_hash_table_add ( demo - > axes , axis ) ;
2018-01-03 19:23:33 +00:00
2022-07-01 03:52:37 +00:00
axis - > button = gtk_button_new_from_icon_name ( " media-playback-start " ) ;
gtk_widget_add_css_class ( GTK_WIDGET ( axis - > button ) , " circular " ) ;
gtk_widget_set_valign ( GTK_WIDGET ( axis - > button ) , GTK_ALIGN_CENTER ) ;
g_signal_connect ( axis - > button , " clicked " , G_CALLBACK ( start_or_stop_axis_animation ) , axis ) ;
gtk_grid_attach ( GTK_GRID ( demo - > variations_grid ) , axis - > button , 3 , i , 1 , 1 ) ;
2022-06-30 22:53:10 +00:00
2018-01-03 19:23:33 +00:00
adjustment_changed ( adjustment , GTK_ENTRY ( axis_entry ) ) ;
g_signal_connect ( adjustment , " value-changed " , G_CALLBACK ( adjustment_changed ) , axis_entry ) ;
g_signal_connect ( adjustment , " value-changed " , G_CALLBACK ( unset_instance ) , NULL ) ;
g_signal_connect ( axis_entry , " activate " , G_CALLBACK ( entry_activated ) , adjustment ) ;
}
typedef struct {
char * name ;
2019-07-30 20:32:34 +00:00
unsigned int index ;
2018-01-03 19:23:33 +00:00
} 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
2022-10-22 14:11:28 +00:00
add_instance ( hb_face_t * face ,
unsigned int index ,
GtkStringList * strings )
2018-01-03 19:23:33 +00:00
{
Instance * instance ;
2019-07-30 20:32:34 +00:00
hb_ot_name_id_t name_id ;
char name [ 20 ] ;
unsigned int name_len = 20 ;
2018-01-03 19:23:33 +00:00
instance = g_new0 ( Instance , 1 ) ;
2019-07-30 20:32:34 +00:00
name_id = hb_ot_var_named_instance_get_subfamily_name_id ( face , index ) ;
hb_ot_name_get_utf8 ( face , name_id , HB_LANGUAGE_INVALID , & name_len , name ) ;
2019-11-25 21:11:28 +00:00
instance - > name = g_strdup ( name ) ;
2019-07-30 20:32:34 +00:00
instance - > index = index ;
2018-01-03 19:23:33 +00:00
2022-06-30 21:02:50 +00:00
g_hash_table_add ( demo - > instances , instance ) ;
2022-10-22 14:11:28 +00:00
gtk_string_list_append ( GTK_STRING_LIST ( strings ) , instance - > name ) ;
2018-01-03 19:23:33 +00:00
}
static void
unset_instance ( GtkAdjustment * adjustment )
{
2022-06-30 21:02:50 +00:00
if ( demo - > instance_combo )
2022-10-22 14:11:28 +00:00
gtk_drop_down_set_selected ( GTK_DROP_DOWN ( demo - > instance_combo ) , 0 ) ;
2018-01-03 19:23:33 +00:00
}
static void
2022-10-22 14:11:28 +00:00
instance_changed ( GtkDropDown * combo )
2018-01-03 19:23:33 +00:00
{
2022-10-22 14:11:28 +00:00
const char * text ;
2018-01-03 19:23:33 +00:00
Instance * instance ;
Instance ikey ;
int i ;
2019-07-30 20:32:34 +00:00
unsigned int coords_length ;
float * coords = NULL ;
hb_ot_var_axis_info_t * ai = NULL ;
unsigned int n_axes ;
PangoFont * pango_font = NULL ;
hb_font_t * hb_font ;
hb_face_t * hb_face ;
2018-01-03 19:23:33 +00:00
2022-10-22 14:11:28 +00:00
text = gtk_string_list_get_string ( GTK_STRING_LIST ( gtk_drop_down_get_model ( combo ) ) ,
gtk_drop_down_get_selected ( combo ) ) ;
2018-01-03 19:23:33 +00:00
if ( text [ 0 ] = = ' \0 ' )
goto out ;
2022-10-22 14:11:28 +00:00
ikey . name = ( char * ) text ;
2022-06-30 21:02:50 +00:00
instance = g_hash_table_lookup ( demo - > instances , & ikey ) ;
2018-01-03 19:23:33 +00:00
if ( ! instance )
{
g_print ( " did not find instance %s \n " , text ) ;
goto out ;
}
2019-07-30 20:32:34 +00:00
pango_font = get_pango_font ( ) ;
hb_font = pango_font_get_hb_font ( pango_font ) ;
hb_face = hb_font_get_face ( hb_font ) ;
n_axes = hb_ot_var_get_axis_infos ( hb_face , 0 , NULL , NULL ) ;
ai = g_new ( hb_ot_var_axis_info_t , n_axes ) ;
hb_ot_var_get_axis_infos ( hb_face , 0 , & n_axes , ai ) ;
coords = g_new ( float , n_axes ) ;
hb_ot_var_named_instance_get_design_coords ( hb_face ,
instance - > index ,
& coords_length ,
coords ) ;
for ( i = 0 ; i < n_axes ; i + + )
2018-01-03 19:23:33 +00:00
{
Axis * axis ;
Axis akey ;
2020-07-24 20:32:16 +00:00
double value ;
2018-01-03 19:23:33 +00:00
2019-07-30 20:32:34 +00:00
value = coords [ ai [ i ] . axis_index ] ;
2018-01-03 19:23:33 +00:00
2019-07-30 20:32:34 +00:00
akey . tag = ai [ i ] . tag ;
2022-06-30 21:02:50 +00:00
axis = g_hash_table_lookup ( demo - > axes , & akey ) ;
2018-01-03 19:23:33 +00:00
if ( axis )
{
g_signal_handlers_block_by_func ( axis - > adjustment , unset_instance , NULL ) ;
gtk_adjustment_set_value ( axis - > adjustment , value ) ;
g_signal_handlers_unblock_by_func ( axis - > adjustment , unset_instance , NULL ) ;
}
}
out :
2019-07-30 20:32:34 +00:00
g_clear_object ( & pango_font ) ;
g_free ( ai ) ;
g_free ( coords ) ;
2018-01-03 19:23:33 +00:00
}
static gboolean
2019-07-30 20:32:34 +00:00
matches_instance ( hb_face_t * hb_face ,
unsigned int index ,
unsigned int n_axes ,
float * coords )
2018-01-03 19:23:33 +00:00
{
2019-07-30 20:32:34 +00:00
float * instance_coords ;
unsigned int coords_length ;
int i ;
instance_coords = g_new ( float , n_axes ) ;
coords_length = n_axes ;
2018-01-03 19:23:33 +00:00
2019-07-30 20:32:34 +00:00
hb_ot_var_named_instance_get_design_coords ( hb_face ,
index ,
& coords_length ,
instance_coords ) ;
for ( i = 0 ; i < n_axes ; i + + )
if ( instance_coords [ i ] ! = coords [ i ] )
2018-01-03 19:23:33 +00:00
return FALSE ;
return TRUE ;
}
static void
add_font_plane ( int i )
{
GtkWidget * plane ;
Axis * weight_axis ;
Axis * width_axis ;
Axis key ;
key . tag = MAKE_TAG ( ' w ' , ' g ' , ' h ' , ' t ' ) ;
2022-06-30 21:02:50 +00:00
weight_axis = g_hash_table_lookup ( demo - > axes , & key ) ;
2018-01-03 19:23:33 +00:00
key . tag = MAKE_TAG ( ' w ' , ' d ' , ' t ' , ' h ' ) ;
2022-06-30 21:02:50 +00:00
width_axis = g_hash_table_lookup ( demo - > axes , & key ) ;
2018-01-03 19:23:33 +00:00
if ( weight_axis & & width_axis )
{
plane = gtk_font_plane_new ( weight_axis - > adjustment ,
width_axis - > adjustment ) ;
gtk_widget_set_size_request ( plane , 300 , 300 ) ;
gtk_widget_set_halign ( plane , GTK_ALIGN_CENTER ) ;
2022-06-30 21:02:50 +00:00
gtk_grid_attach ( GTK_GRID ( demo - > variations_grid ) , plane , 0 , i , 3 , 1 ) ;
2018-01-03 19:23:33 +00:00
}
}
2019-07-30 20:32:34 +00:00
/* FIXME: This doesn't work if the font has an avar table */
static float
denorm_coord ( hb_ot_var_axis_info_t * axis , int coord )
{
float r = coord / 16384.0 ;
if ( coord < 0 )
return axis - > default_value + r * ( axis - > default_value - axis - > min_value ) ;
else
return axis - > default_value + r * ( axis - > max_value - axis - > default_value ) ;
}
2018-01-03 19:23:33 +00:00
static void
update_font_variations ( void )
{
2020-05-09 14:33:02 +00:00
GtkWidget * child ;
2019-07-30 20:32:34 +00:00
PangoFont * pango_font = NULL ;
hb_font_t * hb_font ;
hb_face_t * hb_face ;
unsigned int n_axes ;
hb_ot_var_axis_info_t * ai = NULL ;
float * design_coords = NULL ;
const int * coords ;
unsigned int length ;
int i ;
2018-01-03 19:23:33 +00:00
2022-06-30 21:02:50 +00:00
while ( ( child = gtk_widget_get_first_child ( demo - > variations_grid ) ) )
gtk_grid_remove ( GTK_GRID ( demo - > variations_grid ) , child ) ;
2018-01-03 19:23:33 +00:00
2022-06-30 21:02:50 +00:00
demo - > instance_combo = NULL ;
2018-01-03 19:23:33 +00:00
2022-06-30 21:02:50 +00:00
g_hash_table_remove_all ( demo - > axes ) ;
g_hash_table_remove_all ( demo - > instances ) ;
2018-01-03 19:23:33 +00:00
pango_font = get_pango_font ( ) ;
2019-07-30 20:32:34 +00:00
hb_font = pango_font_get_hb_font ( pango_font ) ;
hb_face = hb_font_get_face ( hb_font ) ;
n_axes = hb_ot_var_get_axis_infos ( hb_face , 0 , NULL , NULL ) ;
if ( n_axes = = 0 )
goto done ;
ai = g_new0 ( hb_ot_var_axis_info_t , n_axes ) ;
design_coords = g_new ( float , n_axes ) ;
2018-01-03 19:23:33 +00:00
2019-07-30 20:32:34 +00:00
hb_ot_var_get_axis_infos ( hb_face , 0 , & n_axes , ai ) ;
coords = hb_font_get_var_coords_normalized ( hb_font , & length ) ;
for ( i = 0 ; i < length ; i + + )
design_coords [ i ] = denorm_coord ( & ai [ i ] , coords [ i ] ) ;
if ( hb_ot_var_get_named_instance_count ( hb_face ) > 0 )
2018-01-03 19:23:33 +00:00
{
2019-07-30 20:32:34 +00:00
GtkWidget * label ;
GtkWidget * combo ;
2022-10-22 14:11:28 +00:00
GtkStringList * strings ;
2019-07-30 20:32:34 +00:00
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 ) ;
2022-07-01 04:10:16 +00:00
gtk_grid_attach ( GTK_GRID ( demo - > variations_grid ) , label , 0 , - 1 , 1 , 1 ) ;
2019-07-30 20:32:34 +00:00
2022-10-22 14:11:28 +00:00
strings = gtk_string_list_new ( NULL ) ;
combo = gtk_drop_down_new ( G_LIST_MODEL ( strings ) , NULL ) ;
2022-07-01 04:10:16 +00:00
gtk_widget_set_halign ( combo , GTK_ALIGN_START ) ;
2019-07-30 20:32:34 +00:00
gtk_widget_set_valign ( combo , GTK_ALIGN_BASELINE ) ;
2018-01-03 19:23:33 +00:00
2022-10-22 14:11:28 +00:00
gtk_string_list_append ( strings , " " ) ;
2018-01-03 19:23:33 +00:00
2019-07-30 20:32:34 +00:00
for ( i = 0 ; i < hb_ot_var_get_named_instance_count ( hb_face ) ; i + + )
2022-10-22 14:11:28 +00:00
add_instance ( hb_face , i , strings ) ;
2019-07-30 20:32:34 +00:00
for ( i = 0 ; i < hb_ot_var_get_named_instance_count ( hb_face ) ; i + + )
2018-01-03 19:23:33 +00:00
{
2019-07-30 20:32:34 +00:00
if ( matches_instance ( hb_face , i , n_axes , design_coords ) )
{
2022-10-22 14:11:28 +00:00
gtk_drop_down_set_selected ( GTK_DROP_DOWN ( combo ) , i + 1 ) ;
2019-07-30 20:32:34 +00:00
break ;
}
2018-01-03 19:23:33 +00:00
}
2022-07-01 04:10:16 +00:00
gtk_grid_attach ( GTK_GRID ( demo - > variations_grid ) , combo , 1 , - 1 , 3 , 1 ) ;
2022-10-22 14:11:28 +00:00
g_signal_connect ( combo , " notify::selecte " , G_CALLBACK ( instance_changed ) , NULL ) ;
2022-06-30 21:02:50 +00:00
demo - > instance_combo = combo ;
2019-07-30 20:32:34 +00:00
}
2018-01-03 19:23:33 +00:00
2019-07-30 20:32:34 +00:00
for ( i = 0 ; i < n_axes ; i + + )
add_axis ( hb_face , & ai [ i ] , design_coords [ i ] , i ) ;
2018-01-03 19:23:33 +00:00
2019-07-30 20:32:34 +00:00
add_font_plane ( n_axes ) ;
done :
g_clear_object ( & pango_font ) ;
g_free ( ai ) ;
g_free ( design_coords ) ;
2018-01-03 19:23:33 +00:00
}
2022-07-01 03:52:37 +00:00
static void
2019-11-29 07:48:48 +00:00
font_features_font_changed ( void )
2016-01-25 05:40:49 +00:00
{
2022-06-30 21:51:41 +00:00
update_basic ( ) ;
2016-01-25 05:40:49 +00:00
update_script_combo ( ) ;
2018-01-03 19:23:33 +00:00
update_features ( ) ;
update_font_variations ( ) ;
2016-01-25 05:40:49 +00:00
}
2022-07-01 03:52:37 +00:00
static void
2019-11-29 07:48:48 +00:00
font_features_script_changed ( void )
2016-01-25 05:40:49 +00:00
{
update_features ( ) ;
update_display ( ) ;
}
2022-06-30 21:51:41 +00:00
static void
2019-11-29 07:48:48 +00:00
font_features_reset_features ( void )
2015-06-19 13:28:09 +00:00
{
2018-01-03 19:23:33 +00:00
GList * l ;
2015-06-19 13:28:09 +00:00
2022-06-30 21:02:50 +00:00
gtk_label_select_region ( GTK_LABEL ( demo - > the_label ) , 0 , 0 ) ;
2018-01-03 19:23:33 +00:00
2022-06-30 21:02:50 +00:00
g_list_free_full ( demo - > ranges , free_range ) ;
demo - > ranges = NULL ;
2018-01-03 19:23:33 +00:00
2022-06-30 21:02:50 +00:00
for ( l = demo - > feature_items ; l ; l = l - > next )
2015-06-19 13:28:09 +00:00
{
2018-01-03 19:23:33 +00:00
FeatureItem * item = l - > data ;
2020-08-30 23:36:27 +00:00
if ( GTK_IS_CHECK_BUTTON ( item - > feat ) )
2015-06-19 13:28:09 +00:00
{
2018-01-03 19:23:33 +00:00
if ( strcmp ( item - > name , " xxxx " ) = = 0 )
2020-08-30 23:36:27 +00:00
gtk_check_button_set_active ( GTK_CHECK_BUTTON ( item - > feat ) , TRUE ) ;
else
{
gtk_check_button_set_active ( GTK_CHECK_BUTTON ( item - > feat ) , FALSE ) ;
set_inconsistent ( GTK_CHECK_BUTTON ( item - > feat ) , TRUE ) ;
}
2015-06-19 13:28:09 +00:00
}
}
}
static void
2019-11-29 07:48:48 +00:00
font_features_toggle_edit ( void )
2018-01-03 19:23:33 +00:00
{
2022-07-01 03:52:37 +00:00
if ( strcmp ( gtk_stack_get_visible_child_name ( GTK_STACK ( demo - > stack ) ) , " entry " ) ! = 0 )
{
2022-07-03 02:46:27 +00:00
GtkTextBuffer * buffer ;
GtkTextIter start , end ;
buffer = gtk_text_view_get_buffer ( GTK_TEXT_VIEW ( demo - > entry ) ) ;
gtk_text_buffer_get_bounds ( buffer , & start , & end ) ;
2022-07-01 03:52:37 +00:00
g_free ( demo - > text ) ;
2022-07-03 02:46:27 +00:00
demo - > text = gtk_text_buffer_get_text ( buffer , & start , & end , FALSE ) ;
2022-07-01 03:52:37 +00:00
gtk_stack_set_visible_child_name ( GTK_STACK ( demo - > stack ) , " entry " ) ;
2022-07-02 15:34:57 +00:00
gtk_widget_grab_focus ( demo - > entry ) ;
2022-07-01 03:52:37 +00:00
gtk_adjustment_set_value ( gtk_scrolled_window_get_vadjustment ( GTK_SCROLLED_WINDOW ( demo - > swin ) ) , 0 ) ;
}
2018-01-03 19:23:33 +00:00
else
2022-07-01 03:52:37 +00:00
{
g_clear_pointer ( & demo - > text , g_free ) ;
gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON ( demo - > plain_toggle ) , TRUE ) ;
update_display ( ) ;
}
2018-01-03 19:23:33 +00:00
}
2022-07-01 03:52:37 +00:00
static void
2019-11-29 07:48:48 +00:00
font_features_stop_edit ( void )
2018-01-03 19:23:33 +00:00
{
2022-06-30 21:02:50 +00:00
g_signal_emit_by_name ( demo - > edit_toggle , " clicked " ) ;
2018-01-03 19:23:33 +00:00
}
2015-06-19 13:28:09 +00:00
static gboolean
2018-01-31 11:42:43 +00:00
entry_key_press ( GtkEventController * controller ,
guint keyval ,
guint keycode ,
GdkModifierType modifiers ,
2022-07-02 15:34:57 +00:00
GtkTextView * entry )
2015-06-19 13:28:09 +00:00
{
2017-08-28 22:47:03 +00:00
if ( keyval = = GDK_KEY_Escape )
2015-06-19 13:28:09 +00:00
{
2022-07-02 15:34:57 +00:00
gtk_text_buffer_set_text ( gtk_text_view_get_buffer ( entry ) , demo - > text , - 1 ) ;
2015-06-19 13:28:09 +00:00
return GDK_EVENT_STOP ;
}
return GDK_EVENT_PROPAGATE ;
}
2022-07-03 03:37:34 +00:00
static const char * paragraphs [ ] = {
" Grumpy wizards make toxic brew for the evil Queen and Jack. A quick movement of the enemy will jeopardize six gunboats. The job of waxing linoleum frequently peeves chintzy kids. My girl wove six dozen plaid jackets before she quit. Twelve ziggurats quickly jumped a finch box. " ,
" Разъяренный чтец эгоистично бьёт пятью жердями шустрого фехтовальщика. Наш банк вчера же выплатил Ф.Я. Эйхгольду комиссию за ценные вещи. Эх, чужак, общий съём цен шляп (юфть) – вдрызг! В чащах юга жил бы цитрус? Да, но фальшивый экземпляр! " ,
" Τάχιστη αλώπηξ βαφής ψημένη γη, δρασκελίζει υπέρ νωθρού κυνός " ,
} ;
static const char * alphabets [ ] = {
" abcdefghijklmnopqrstuvwxzy " ,
" ABCDEFGHIJKLMNOPQRSTUVWXYZ " ,
" 0123456789 " ,
" !@#$%^&*/?; " ,
} ;
static void
set_text_alphabet ( void )
{
demo - > sample + + ;
gtk_text_buffer_set_text ( gtk_text_view_get_buffer ( GTK_TEXT_VIEW ( demo - > entry ) ) ,
alphabets [ demo - > sample % G_N_ELEMENTS ( alphabets ) ] ,
- 1 ) ;
update_display ( ) ;
}
static void
set_text_paragraph ( void )
{
demo - > sample + + ;
gtk_text_buffer_set_text ( gtk_text_view_get_buffer ( GTK_TEXT_VIEW ( demo - > entry ) ) ,
paragraphs [ demo - > sample % G_N_ELEMENTS ( paragraphs ) ] ,
- 1 ) ;
update_display ( ) ;
}
2015-06-19 13:28:09 +00:00
GtkWidget *
do_font_features ( GtkWidget * do_widget )
{
static GtkWidget * window = NULL ;
if ( ! window )
{
GtkBuilder * builder ;
2022-06-30 13:42:32 +00:00
GtkBuilderScope * scope ;
2018-01-31 11:42:43 +00:00
GtkEventController * controller ;
2022-10-22 14:11:28 +00:00
GtkExpression * expression ;
2015-06-19 13:28:09 +00:00
2022-06-30 13:42:32 +00:00
builder = gtk_builder_new ( ) ;
scope = gtk_builder_cscope_new ( ) ;
gtk_builder_cscope_add_callback ( scope , basic_value_changed ) ;
gtk_builder_cscope_add_callback ( scope , basic_entry_activated ) ;
2022-07-02 15:34:57 +00:00
gtk_builder_cscope_add_callback ( scope , color_set_cb ) ;
gtk_builder_cscope_add_callback ( scope , swap_colors ) ;
2022-06-30 21:51:41 +00:00
gtk_builder_cscope_add_callback ( scope , font_features_reset_basic ) ;
gtk_builder_cscope_add_callback ( scope , font_features_reset_features ) ;
gtk_builder_cscope_add_callback ( scope , font_features_reset_variations ) ;
2022-07-01 03:52:37 +00:00
gtk_builder_cscope_add_callback ( scope , font_features_toggle_plain ) ;
gtk_builder_cscope_add_callback ( scope , font_features_toggle_edit ) ;
gtk_builder_cscope_add_callback ( scope , font_features_stop_edit ) ;
gtk_builder_cscope_add_callback ( scope , font_features_font_changed ) ;
gtk_builder_cscope_add_callback ( scope , font_features_script_changed ) ;
gtk_builder_cscope_add_callback ( scope , font_features_notify_waterfall ) ;
2022-07-03 03:37:34 +00:00
gtk_builder_cscope_add_callback ( scope , set_text_alphabet ) ;
gtk_builder_cscope_add_callback ( scope , set_text_paragraph ) ;
2022-06-30 13:42:32 +00:00
gtk_builder_set_scope ( builder , scope ) ;
2022-06-30 21:02:50 +00:00
demo = g_new0 ( FontFeaturesDemo , 1 ) ;
2022-06-30 20:08:12 +00:00
gtk_builder_add_from_resource ( builder , " /font_features/font_features.ui " , NULL ) ;
2015-06-19 13:28:09 +00:00
window = GTK_WIDGET ( gtk_builder_get_object ( builder , " window " ) ) ;
2022-06-30 21:02:50 +00:00
g_object_set_data_full ( G_OBJECT ( window ) , " demo " , demo , demo_free ) ;
demo - > the_label = GTK_WIDGET ( gtk_builder_get_object ( builder , " label " ) ) ;
demo - > settings = GTK_WIDGET ( gtk_builder_get_object ( builder , " settings " ) ) ;
demo - > description = GTK_WIDGET ( gtk_builder_get_object ( builder , " description " ) ) ;
demo - > font = GTK_WIDGET ( gtk_builder_get_object ( builder , " font " ) ) ;
demo - > script_lang = GTK_WIDGET ( gtk_builder_get_object ( builder , " script_lang " ) ) ;
2022-10-28 15:38:57 +00:00
g_assert ( GTK_IS_DROP_DOWN ( demo - > script_lang ) ) ;
2022-10-22 14:11:28 +00:00
expression = gtk_cclosure_expression_new ( G_TYPE_STRING , NULL , 0 , NULL , G_CALLBACK ( script_lang_get_langname ) , NULL , NULL ) ;
gtk_drop_down_set_expression ( GTK_DROP_DOWN ( demo - > script_lang ) , expression ) ;
gtk_expression_unref ( expression ) ;
2022-06-30 21:02:50 +00:00
demo - > feature_list = GTK_WIDGET ( gtk_builder_get_object ( builder , " feature_list " ) ) ;
demo - > stack = GTK_WIDGET ( gtk_builder_get_object ( builder , " stack " ) ) ;
2022-07-02 15:34:57 +00:00
demo - > entry = GTK_WIDGET ( gtk_builder_get_object ( builder , " entry " ) ) ;
2022-07-01 03:52:37 +00:00
demo - > plain_toggle = GTK_WIDGET ( gtk_builder_get_object ( builder , " plain_toggle " ) ) ;
demo - > waterfall_toggle = GTK_WIDGET ( gtk_builder_get_object ( builder , " waterfall_toggle " ) ) ;
2022-06-30 21:02:50 +00:00
demo - > edit_toggle = GTK_WIDGET ( gtk_builder_get_object ( builder , " edit_toggle " ) ) ;
2022-07-01 03:52:37 +00:00
demo - > size_scale = GTK_WIDGET ( gtk_builder_get_object ( builder , " size_scale " ) ) ;
2022-06-30 21:02:50 +00:00
demo - > size_entry = GTK_WIDGET ( gtk_builder_get_object ( builder , " size_entry " ) ) ;
demo - > size_adjustment = GTK_ADJUSTMENT ( gtk_builder_get_object ( builder , " size_adjustment " ) ) ;
demo - > letterspacing_entry = GTK_WIDGET ( gtk_builder_get_object ( builder , " letterspacing_entry " ) ) ;
demo - > letterspacing_adjustment = GTK_ADJUSTMENT ( gtk_builder_get_object ( builder , " letterspacing_adjustment " ) ) ;
demo - > line_height_entry = GTK_WIDGET ( gtk_builder_get_object ( builder , " line_height_entry " ) ) ;
demo - > line_height_adjustment = GTK_ADJUSTMENT ( gtk_builder_get_object ( builder , " line_height_adjustment " ) ) ;
2022-07-02 15:34:57 +00:00
demo - > foreground = GTK_WIDGET ( gtk_builder_get_object ( builder , " foreground " ) ) ;
demo - > background = GTK_WIDGET ( gtk_builder_get_object ( builder , " background " ) ) ;
2022-07-01 03:52:37 +00:00
demo - > swin = GTK_WIDGET ( gtk_builder_get_object ( builder , " swin " ) ) ;
2022-06-30 21:02:50 +00:00
2022-07-02 15:34:57 +00:00
demo - > provider = gtk_css_provider_new ( ) ;
2022-10-22 15:38:46 +00:00
gtk_style_context_add_provider_for_display ( gdk_display_get_default ( ) ,
GTK_STYLE_PROVIDER ( demo - > provider ) , 800 ) ;
2022-07-02 15:34:57 +00:00
2022-06-30 21:02:50 +00:00
basic_value_changed ( demo - > size_adjustment , demo - > size_entry ) ;
basic_value_changed ( demo - > letterspacing_adjustment , demo - > letterspacing_entry ) ;
basic_value_changed ( demo - > line_height_adjustment , demo - > line_height_entry ) ;
2018-01-03 19:23:33 +00:00
2018-04-20 17:58:06 +00:00
controller = gtk_event_controller_key_new ( ) ;
2022-07-02 15:34:57 +00:00
g_signal_connect ( controller , " key-pressed " , G_CALLBACK ( entry_key_press ) , demo - > entry ) ;
gtk_widget_add_controller ( demo - > entry , controller ) ;
2022-06-30 21:02:50 +00:00
add_check_group ( demo - > feature_list , _ ( " Kerning " ) ,
( const char * [ ] ) { " kern " , NULL } ) ;
add_check_group ( demo - > feature_list , _ ( " Ligatures " ) ,
( const char * [ ] ) { " liga " , " dlig " , " hlig " , " clig " , " rlig " , NULL } ) ;
add_check_group ( demo - > feature_list , _ ( " Letter Case " ) ,
( const char * [ ] ) { " smcp " , " c2sc " , " pcap " , " c2pc " , " unic " , " cpsp " ,
" case " , NULL } ) ;
add_radio_group ( demo - > feature_list , _ ( " Number Case " ) ,
( const char * [ ] ) { " xxxx " , " lnum " , " onum " , NULL } ) ;
add_radio_group ( demo - > feature_list , _ ( " Number Spacing " ) ,
( const char * [ ] ) { " xxxx " , " pnum " , " tnum " , NULL } ) ;
add_radio_group ( demo - > feature_list , _ ( " Fractions " ) ,
( const char * [ ] ) { " xxxx " , " frac " , " afrc " , NULL } ) ;
add_check_group ( demo - > feature_list , _ ( " Numeric Extras " ) ,
( const char * [ ] ) { " zero " , " nalt " , " sinf " , NULL } ) ;
add_check_group ( demo - > feature_list , _ ( " Character Alternatives " ) ,
( const char * [ ] ) { " swsh " , " cswh " , " locl " , " calt " , " falt " , " hist " ,
" salt " , " jalt " , " titl " , " rand " , " subs " , " sups " ,
" ordn " , " ltra " , " ltrm " , " rtla " , " rtlm " , " rclt " , NULL } ) ;
add_check_group ( demo - > feature_list , _ ( " Positional Alternatives " ) ,
( const char * [ ] ) { " init " , " medi " , " med2 " , " fina " , " fin2 " , " fin3 " ,
" isol " , NULL } ) ;
add_check_group ( demo - > feature_list , _ ( " Width Variants " ) ,
( const char * [ ] ) { " fwid " , " hwid " , " halt " , " pwid " , " palt " , " twid " ,
" qwid " , NULL } ) ;
add_check_group ( demo - > feature_list , _ ( " Alternative Stylistic Sets " ) ,
( const char * [ ] ) { " ss01 " , " ss02 " , " ss03 " , " ss04 " , " ss05 " , " ss06 " ,
" ss07 " , " ss08 " , " ss09 " , " ss10 " , " ss11 " , " ss12 " ,
" ss13 " , " ss14 " , " ss15 " , " ss16 " , " ss17 " , " ss18 " ,
" ss19 " , " ss20 " , NULL } ) ;
2022-07-03 05:19:19 +00:00
add_check_group ( demo - > feature_list , _ ( " Character Variants " ) ,
( const char * [ ] ) { " cv01 " , " cv02 " , " cv03 " , " cv04 " , " cv05 " , " cv06 " ,
" cv07 " , " cv08 " , " cv09 " , " cv10 " , " cv11 " , " cv12 " ,
" cv13 " , " cv14 " , " cv15 " , " cv16 " , " cv17 " , " cv18 " ,
" cv19 " , " cv20 " , NULL } ) ;
2022-06-30 21:02:50 +00:00
add_check_group ( demo - > feature_list , _ ( " Mathematical " ) ,
( const char * [ ] ) { " dtls " , " flac " , " mgrk " , " ssty " , NULL } ) ;
add_check_group ( demo - > feature_list , _ ( " Optical Bounds " ) ,
( const char * [ ] ) { " opbd " , " lfbd " , " rtbd " , NULL } ) ;
demo - > feature_items = g_list_reverse ( demo - > feature_items ) ;
demo - > variations_grid = GTK_WIDGET ( gtk_builder_get_object ( builder , " variations_grid " ) ) ;
if ( demo - > instances = = NULL )
demo - > instances = g_hash_table_new_full ( instance_hash , instance_equal , NULL , free_instance ) ;
2018-01-03 19:23:33 +00:00
else
2022-06-30 21:02:50 +00:00
g_hash_table_remove_all ( demo - > instances ) ;
2015-06-19 13:28:09 +00:00
2022-06-30 21:02:50 +00:00
if ( demo - > axes = = NULL )
demo - > axes = g_hash_table_new_full ( axes_hash , axes_equal , NULL , g_free ) ;
2018-01-03 19:23:33 +00:00
else
2022-06-30 21:02:50 +00:00
g_hash_table_remove_all ( demo - > axes ) ;
2016-01-25 05:40:49 +00:00
2019-11-29 07:48:48 +00:00
font_features_font_changed ( ) ;
2015-06-20 01:59:15 +00:00
2020-05-09 16:03:11 +00:00
g_object_add_weak_pointer ( G_OBJECT ( window ) , ( gpointer * ) & window ) ;
2015-06-28 13:23:28 +00:00
g_object_unref ( builder ) ;
2019-12-24 03:35:44 +00:00
update_display ( ) ;
2015-06-19 13:28:09 +00:00
}
if ( ! gtk_widget_get_visible ( window ) )
2015-06-28 13:23:28 +00:00
gtk_window_present ( GTK_WINDOW ( window ) ) ;
2015-06-19 13:28:09 +00:00
else
2020-05-09 14:26:22 +00:00
gtk_window_destroy ( GTK_WINDOW ( window ) ) ;
2015-06-19 13:28:09 +00:00
return window ;
}
2022-10-22 14:11:28 +00:00
/* vim:set foldmethod=marker expandtab: */