160 lines
6.5 KiB
Plaintext
160 lines
6.5 KiB
Plaintext
Font_File :: struct {
|
|
font_name: string;
|
|
font_data: string;
|
|
}
|
|
|
|
// The fonts are compiled into the executable. The paths are relative to the working directory of
|
|
// first.jai; assets/fonts holds a few more to swap in. #run,host reads the file on the build
|
|
// machine even when cross-compiling, and sees nothing declared outside its own block.
|
|
OpenSans_Bold :: Font_File.{"OpenSans-Bold", #run,host -> string { #import "File"; return read_entire_file("assets/fonts/OpenSans-Bold.ttf"); }};
|
|
OpenSans_Regular :: Font_File.{"OpenSans-Regular", #run,host -> string { #import "File"; return read_entire_file("assets/fonts/OpenSans-Regular.ttf"); }};
|
|
RobotoMono_Regular :: Font_File.{"RobotoMono-Regular", #run,host -> string { #import "File"; return read_entire_file("assets/fonts/RobotoMono-Regular.ttf"); }};
|
|
|
|
default_global_font_size :: 12.0;
|
|
default_header_font_size :: 0.5 * default_global_font_size + default_global_font_size;
|
|
|
|
font_data_default: *ImGui.ImFont;
|
|
alternate_font: *ImGui.ImFont;
|
|
header_font: *ImGui.ImFont;
|
|
|
|
push_imgui_font :: (font: *ImGui.ImFont, scale: float32) #expand {
|
|
style := ImGui.GetStyle();
|
|
ImGui.PushFont(font, style.FontSizeBase * scale);
|
|
`defer ImGui.PopFont();
|
|
}
|
|
|
|
app_imgui_load_fonts :: () {
|
|
io := ImGui.GetIO();
|
|
cjk_fonts_load();
|
|
|
|
{ font_config: ImGui.ImFontConfig;
|
|
font_config.Constructor(*font_config); // *sigh*
|
|
// By default, ImGui takes ownership of the font data and attempts to free it, resulting in a crash,
|
|
// so we have to explicitly tell it to not do this.
|
|
font_config.FontDataOwnedByAtlas = false;
|
|
memcpy(font_config.Name.data, RobotoMono_Regular.font_name.data, RobotoMono_Regular.font_name.count);
|
|
|
|
font_data_default = ImGui.IO.Fonts.AddFontFromMemoryTTF(io.Fonts, RobotoMono_Regular.font_data.data,
|
|
xx RobotoMono_Regular.font_data.count, default_global_font_size, *font_config);
|
|
merge_cjk_fallbacks(font_data_default);
|
|
}
|
|
{ // Header font for menu
|
|
font_config: ImGui.ImFontConfig;
|
|
font_config.Constructor(*font_config);
|
|
font_config.FontDataOwnedByAtlas = false;
|
|
memcpy(font_config.Name.data, OpenSans_Bold.font_name.data, OpenSans_Bold.font_name.count);
|
|
header_font = ImGui.IO.Fonts.AddFontFromMemoryTTF(io.Fonts, OpenSans_Bold.font_data.data,
|
|
xx OpenSans_Bold.font_data.count, default_header_font_size, *font_config);
|
|
merge_cjk_fallbacks(header_font);
|
|
}
|
|
// Add another font:
|
|
{ font_config: ImGui.ImFontConfig;
|
|
font_config.Constructor(*font_config);
|
|
font_config.FontDataOwnedByAtlas = false;
|
|
memcpy(font_config.Name.data, OpenSans_Regular.font_name.data, OpenSans_Regular.font_name.count);
|
|
alternate_font = ImGui.IO.Fonts.AddFontFromMemoryTTF(io.Fonts, OpenSans_Regular.font_data.data,
|
|
xx OpenSans_Regular.font_data.count, default_global_font_size, *font_config);
|
|
merge_cjk_fallbacks(alternate_font);
|
|
}
|
|
|
|
assert(font_data_default != null);
|
|
|
|
// Set global default font:
|
|
io.FontDefault = font_data_default;
|
|
}
|
|
|
|
Cjk_Script :: enum u8 { SIMPLIFIED; TRADITIONAL; JAPANESE; KOREAN; }
|
|
|
|
Cjk_Font :: struct {
|
|
script: Cjk_Script;
|
|
path: string;
|
|
name: string; // for the log and ImFontConfig.Name
|
|
face: u32; // index within a .ttc
|
|
data: string; // once loaded: mapped or read, kept for the life of the atlas
|
|
}
|
|
|
|
cjk_fonts: [..] Cjk_Font; // in fallback order
|
|
|
|
cjk_candidate :: (list: *[..] Cjk_Font, script: Cjk_Script, path: string, name: string, face: u32 = 0) {
|
|
array_add(list, .{script = script, path = path, name = name, face = face});
|
|
}
|
|
|
|
// The scripts share their Han characters and the first merged font that has a glyph draws it, so
|
|
// the user's language picks which comes first.
|
|
cjk_fallback_order :: (language: string) -> [4] Cjk_Script {
|
|
subtags := split(to_lower_copy(replace(language, "_", "-",, temp),, temp), "-",, temp);
|
|
primary := ifx subtags.count then subtags[0] else "";
|
|
|
|
if primary == "ja" return Cjk_Script.[.JAPANESE, .SIMPLIFIED, .TRADITIONAL, .KOREAN];
|
|
if primary == "ko" return Cjk_Script.[.KOREAN, .SIMPLIFIED, .TRADITIONAL, .JAPANESE];
|
|
if primary == "zh" {
|
|
for subtags if it == "hant" || it == "tw" || it == "hk" || it == "mo" return Cjk_Script.[.TRADITIONAL, .SIMPLIFIED, .JAPANESE, .KOREAN];
|
|
}
|
|
return Cjk_Script.[.SIMPLIFIED, .TRADITIONAL, .JAPANESE, .KOREAN];
|
|
}
|
|
|
|
// One font per script, the first candidate found. A file several scripts share is loaded once.
|
|
cjk_fonts_load :: () {
|
|
push_allocator(context.default_allocator);
|
|
language := platform_preferred_language();
|
|
candidates := platform_cjk_font_candidates();
|
|
|
|
for script: cjk_fallback_order(language) {
|
|
for candidate: candidates {
|
|
if candidate.script != script || !file_exists(candidate.path) continue;
|
|
|
|
font := candidate;
|
|
for cjk_fonts if it.path == font.path font.data = it.data;
|
|
if !font.data {
|
|
ok: bool;
|
|
font.data, ok = platform_map_file_read_only(font.path);
|
|
if !ok {
|
|
font.data, ok = read_entire_file(font.path);
|
|
if !ok continue;
|
|
log("[fonts] Could not map %; read it into memory instead.", font.path);
|
|
}
|
|
}
|
|
font.path = copy_string(font.path);
|
|
font.name = copy_string(font.name);
|
|
array_add(*cjk_fonts, font);
|
|
break;
|
|
}
|
|
}
|
|
|
|
names: [..] string;
|
|
names.allocator = temp;
|
|
for cjk_fonts array_add(*names, it.name);
|
|
log("[fonts] CJK fallback for %: %.", ifx language then language else "an unknown language",
|
|
ifx names.count then join(..names, ", ",, temp) else "none found");
|
|
}
|
|
|
|
// Since ImGui 1.92 a merged font is only asked for glyphs the ones before it lack, as they are drawn.
|
|
merge_cjk_fallbacks :: (dst: *ImGui.ImFont) {
|
|
io := ImGui.GetIO();
|
|
for cjk_fonts {
|
|
font_config: ImGui.ImFontConfig;
|
|
font_config.Constructor(*font_config);
|
|
font_config.FontDataOwnedByAtlas = false;
|
|
font_config.MergeMode = true;
|
|
font_config.DstFont = dst;
|
|
font_config.FontNo = it.face;
|
|
memcpy(font_config.Name.data, it.name.data, min(it.name.count, font_config.Name.count - 1));
|
|
ImGui.IO.Fonts.AddFontFromMemoryTTF(io.Fonts, it.data.data, xx it.data.count, 0, *font_config);
|
|
}
|
|
}
|
|
|
|
DEFAULT_FONT_SCALE_INCREMENT :: 0.05;
|
|
FONT_SCALE_MIN :: 0.5; FONT_SCALE_MAX :: 4.0;
|
|
|
|
imgui_decrease_font_scale :: (scale_increment:=DEFAULT_FONT_SCALE_INCREMENT) {
|
|
style := ImGui.GetStyle();
|
|
style.FontScaleMain = clamp(style.FontScaleMain - scale_increment,
|
|
FONT_SCALE_MIN, FONT_SCALE_MAX);
|
|
}
|
|
|
|
imgui_increase_font_scale :: (scale_increment:=DEFAULT_FONT_SCALE_INCREMENT) {
|
|
style := ImGui.GetStyle();
|
|
style.FontScaleMain = clamp(style.FontScaleMain + scale_increment,
|
|
FONT_SCALE_MIN, FONT_SCALE_MAX);
|
|
}
|