generated from mstar/godot-template
240 lines
7.3 KiB
GDScript
240 lines
7.3 KiB
GDScript
extends Object
|
|
|
|
const FONT_FILE_PATTERN = "\\.ttf$"
|
|
const FONT_WEIGHT_PATTERNS = {
|
|
"thin": "(?i)(-|_)thin",
|
|
"extra_light": "(?i)(-|_)extralight",
|
|
"light": "(?i)(-|_)light",
|
|
"regular": "(?i)(-|_)regular",
|
|
"medium": "(?i)(-|_)medium",
|
|
"semi_bold": "(?i)(-|_)semibold",
|
|
"bold": "(?i)(-|_)bold",
|
|
"extra_bold": "(?i)(-|_)extrabold",
|
|
"black": "(?i)(-|_)black",
|
|
"extra_black": "(?i)(-|_)extrablack"
|
|
}
|
|
const FONT_ITALIC_PATTERN = "(?i)italic"
|
|
const FONT_ITALIC_ONLY_PATTERN = "(?i)(-|_)italic"
|
|
const FONT_VARIABLE_PATTERN = "(?i)(-|_)variable"
|
|
var FONT_FORMATTINGS = {
|
|
"Heading 1": FontFormatting.new("light", 96, -3),
|
|
"Heading 2": FontFormatting.new("light", 60, -2),
|
|
"Heading 3": FontFormatting.new("regular", 48),
|
|
"Heading 4": FontFormatting.new("regular", 34, 1),
|
|
"Heading 5": FontFormatting.new("regular", 24),
|
|
"Heading 6": FontFormatting.new("medium", 20, 1),
|
|
"Subtitle 1": FontFormatting.new("regular", 16),
|
|
"Subtitle 2": FontFormatting.new("medium", 14, 1),
|
|
"Body 1": FontFormatting.new("regular", 16, 1),
|
|
"Body 2": FontFormatting.new("regular", 14, 1),
|
|
"Button": FontFormatting.new("medium", 14, 1),
|
|
"Caption": FontFormatting.new("regular", 12, 1),
|
|
"Overline": FontFormatting.new("regular", 10)
|
|
} # Typography hierarchy presets, see https://material.io/design/typography/the-type-system.html#type-scale
|
|
const DIR_FOLDER_PATTERN = "\\w+(?!.*\\w)"
|
|
|
|
var font_families = {}
|
|
|
|
var _font_file_regex = RegEx.new()
|
|
var _font_weight_regexes = {
|
|
"thin": RegEx.new(),
|
|
"extra_light": RegEx.new(),
|
|
"light": RegEx.new(),
|
|
"regular": RegEx.new(),
|
|
"medium": RegEx.new(),
|
|
"semi_bold": RegEx.new(),
|
|
"bold": RegEx.new(),
|
|
"extra_bold": RegEx.new(),
|
|
"black": RegEx.new(),
|
|
"extra_black": RegEx.new()
|
|
}
|
|
var _font_italic_regex = RegEx.new()
|
|
var _font_italic_only_regex = RegEx.new()
|
|
var _font_variable_regex = RegEx.new()
|
|
var _dir_folder_regex = RegEx.new()
|
|
|
|
|
|
func _init():
|
|
if _font_file_regex.compile(FONT_FILE_PATTERN):
|
|
print("Failed to compile ", FONT_FILE_PATTERN)
|
|
|
|
for font_weight in _font_weight_regexes.keys():
|
|
if _font_weight_regexes[font_weight].compile(FONT_WEIGHT_PATTERNS[font_weight]):
|
|
print("Failed to compile ", FONT_WEIGHT_PATTERNS[font_weight])
|
|
|
|
if _font_italic_regex.compile(FONT_ITALIC_PATTERN):
|
|
print("Failed to compile ", FONT_ITALIC_PATTERN)
|
|
|
|
if _font_italic_only_regex.compile(FONT_ITALIC_ONLY_PATTERN):
|
|
print("Failed to compile ", FONT_ITALIC_ONLY_PATTERN)
|
|
|
|
if _font_variable_regex.compile(FONT_VARIABLE_PATTERN):
|
|
print("Failed to compile ", FONT_VARIABLE_PATTERN)
|
|
|
|
if _dir_folder_regex.compile(DIR_FOLDER_PATTERN):
|
|
print("Failed to compile ", DIR_FOLDER_PATTERN)
|
|
|
|
# Load root dir of font resources, check Readme for directory structure
|
|
func load_root_dir(root_dir):
|
|
var directory = DirAccess.open(root_dir)
|
|
var result = DirAccess.get_open_error()
|
|
if result == OK:
|
|
font_families.clear()
|
|
directory.list_dir_begin() # Skip . and .. directory and hidden# TODOGODOT4 fill missing arguments https://github.com/godotengine/godot/pull/40547
|
|
var dir = directory.get_next()
|
|
while dir != "":
|
|
if not directory.current_is_dir():
|
|
dir = directory.get_next()
|
|
continue
|
|
|
|
load_fonts(directory.get_current_dir() + "/" + dir)
|
|
dir = directory.get_next()
|
|
directory.list_dir_end()
|
|
else:
|
|
push_warning("UI Design Tool: An error occurred when trying to access %s, ERROR: %d" % [root_dir, result])
|
|
return false
|
|
|
|
return true
|
|
|
|
# Load fonts data from directory, check Readme for filename pattern
|
|
func load_fonts(dir):
|
|
var directory = DirAccess.open(dir)
|
|
var result = DirAccess.get_open_error()
|
|
if result == OK:
|
|
var font_family_name = _dir_folder_regex.search(dir).get_string()
|
|
var font_family = FontFamily.new(font_family_name)
|
|
directory.list_dir_begin()
|
|
var filename = directory.get_next()
|
|
while filename != "":
|
|
if directory.current_is_dir():
|
|
filename = directory.get_next()
|
|
continue
|
|
|
|
if _font_file_regex.search(filename):
|
|
for font_weight in _font_weight_regexes.keys():
|
|
if _font_variable_regex.search(filename): # Godot doesn't support variable font
|
|
continue
|
|
|
|
var abs_dir = directory.get_current_dir() + "/" + filename
|
|
if _font_weight_regexes[font_weight].search(filename):
|
|
var font_data = load(abs_dir)
|
|
|
|
if _font_italic_regex.search(filename):
|
|
font_family.set_font_face(FontFace.new(font_family.name, font_weight, font_data, FONT_STYLE.ITALIC))
|
|
else:
|
|
font_family.set_font_face(FontFace.new(font_family.name, font_weight, font_data))
|
|
break
|
|
else:
|
|
# Capture regular italic from {font-name}-italic.ttf
|
|
if _font_italic_only_regex.search(filename):
|
|
var font_data = load(abs_dir)
|
|
font_family.set_font_face(FontFace.new(font_family.name, "regular", font_data, FONT_STYLE.ITALIC))
|
|
break
|
|
filename = directory.get_next()
|
|
directory.list_dir_end()
|
|
|
|
if not font_family.is_empty():
|
|
font_families[font_family.name] = font_family
|
|
else:
|
|
push_warning("UI Design Tool: Unable to locate usable .ttf files from %s, check README.md for proper directory/filename structure" % dir)
|
|
else:
|
|
push_warning("UI Design Tool: An error occurred when trying to access %s, ERROR: %d" % [dir, result])
|
|
return false
|
|
|
|
return true
|
|
|
|
func get_font_face(font_data):
|
|
for res in font_families.values():
|
|
for font_weight in FONT_WEIGHT.keys():
|
|
var font_faces = res.get(font_weight)
|
|
for font_face in font_faces.values():
|
|
if font_face.data and font_data:
|
|
if font_face.data.resource_path == font_data.resource_path:
|
|
return font_face
|
|
return null
|
|
|
|
# Find font resource with font name
|
|
func get_font_family(font_family_name):
|
|
return font_families.get(font_family_name)
|
|
|
|
static func get_font_style_str(font_style):
|
|
return FONT_STYLE.keys()[font_style].to_lower()
|
|
|
|
# Declaration of font type with font_faces
|
|
class FontFamily:
|
|
var name = ""
|
|
var thin = {}
|
|
var extra_light = {}
|
|
var light = {}
|
|
var regular = {}
|
|
var medium = {}
|
|
var semi_bold = {}
|
|
var bold = {}
|
|
var extra_bold = {}
|
|
var black = {}
|
|
var extra_black = {}
|
|
|
|
func _init(n):
|
|
name = n
|
|
|
|
func set_font_face(font_face):
|
|
var font_faces = get(font_face.font_weight.replace('-', '_'))
|
|
font_faces[FONT_STYLE.keys()[font_face.font_style].to_lower()] = font_face
|
|
|
|
func is_empty():
|
|
for font_weight in FONT_WEIGHT.keys():
|
|
var font_faces = get(font_weight)
|
|
if not font_faces.values().is_empty():
|
|
return false
|
|
return true
|
|
|
|
func get_class():
|
|
return "FontFamily"
|
|
|
|
# Font face data, see (https://developer.mozilla.org/my/docs/Web/CSS/@font-face)
|
|
class FontFace:
|
|
var font_family = ""
|
|
var font_weight = ""
|
|
var font_style = FONT_STYLE.NORMAL
|
|
var data
|
|
|
|
func _init(ff, fw, d, fs=FONT_STYLE.NORMAL):
|
|
font_family = ff
|
|
font_weight = fw
|
|
font_style = fs
|
|
data = d
|
|
|
|
func get_class():
|
|
return "FontFace"
|
|
|
|
# Declaration of font style TODO: Custom resource to define font style
|
|
class FontFormatting:
|
|
var font_weight = "regular"
|
|
var font_style = 0 # FONT_STYLE.NORMAL
|
|
var size = 16
|
|
var letter_spacing = 0
|
|
|
|
func _init(fw, s, ls=0):
|
|
font_weight = fw
|
|
size = s
|
|
letter_spacing = ls
|
|
|
|
# List of font style, see (https://developer.mozilla.org/my/docs/Web/CSS/font-style)
|
|
enum FONT_STYLE {
|
|
NORMAL,
|
|
ITALIC,
|
|
OBLIQUE
|
|
}
|
|
|
|
# List of font weights, see (https://docs.microsoft.com/en-us/typography/opentype/spec/os2#usweightclass)
|
|
const FONT_WEIGHT = {
|
|
"thin": 100,
|
|
"extra_light": 200,
|
|
"light": 300,
|
|
"regular": 400,
|
|
"medium": 500,
|
|
"semi_bold": 600,
|
|
"bold": 700,
|
|
"extra_bold": 800,
|
|
"black": 900
|
|
}
|