Implement support for loading system fonts on Linux, macOS / iOS and Windows.

This commit is contained in:
bruvzg 2022-07-08 15:38:30 +03:00
parent 3e0e84a54c
commit 36ef8f29dc
No known key found for this signature in database
GPG Key ID: 7960FCF39844EC38
27 changed files with 1089 additions and 54 deletions

View File

@ -115,7 +115,7 @@ jobs:
sudo apt-get install build-essential pkg-config libx11-dev libxcursor-dev \
libxinerama-dev libgl1-mesa-dev libglu-dev libasound2-dev libpulse-dev \
libdbus-1-dev libudev-dev libxi-dev libxrandr-dev yasm xvfb wget unzip \
llvm libspeechd-dev speech-dispatcher
llvm libspeechd-dev speech-dispatcher fontconfig libfontconfig-dev
- name: Setup Godot build cache
uses: ./.github/actions/godot-cache

View File

@ -231,6 +231,14 @@ void OS::crash(const String &p_message) {
CRASH_NOW_MSG(p_message);
}
Vector<String> OS::get_system_fonts() const {
return ::OS::get_singleton()->get_system_fonts();
}
String OS::get_system_font_path(const String &p_font_name, bool p_bold, bool p_italic) const {
return ::OS::get_singleton()->get_system_font_path(p_font_name, p_bold, p_italic);
}
String OS::get_executable_path() const {
return ::OS::get_singleton()->get_executable_path();
}
@ -589,6 +597,8 @@ void OS::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_processor_count"), &OS::get_processor_count);
ClassDB::bind_method(D_METHOD("get_processor_name"), &OS::get_processor_name);
ClassDB::bind_method(D_METHOD("get_system_fonts"), &OS::get_system_fonts);
ClassDB::bind_method(D_METHOD("get_system_font_path", "font_name", "bold", "italic"), &OS::get_system_font_path, DEFVAL(false), DEFVAL(false));
ClassDB::bind_method(D_METHOD("get_executable_path"), &OS::get_executable_path);
ClassDB::bind_method(D_METHOD("execute", "path", "arguments", "output", "read_stderr", "open_console"), &OS::execute, DEFVAL(Array()), DEFVAL(false), DEFVAL(false));
ClassDB::bind_method(D_METHOD("create_process", "path", "arguments", "open_console"), &OS::create_process, DEFVAL(false));

View File

@ -170,6 +170,8 @@ public:
void alert(const String &p_alert, const String &p_title = "ALERT!");
void crash(const String &p_message);
Vector<String> get_system_fonts() const;
String get_system_font_path(const String &p_font_name, bool p_bold = false, bool p_italic = false) const;
String get_executable_path() const;
int execute(const String &p_path, const Vector<String> &p_arguments, Array r_output = Array(), bool p_read_stderr = false, bool p_open_console = false);
int create_process(const String &p_path, const Vector<String> &p_arguments, bool p_open_console = false);

View File

@ -142,6 +142,8 @@ public:
virtual void set_low_processor_usage_mode_sleep_usec(int p_usec);
virtual int get_low_processor_usage_mode_sleep_usec() const;
virtual Vector<String> get_system_fonts() const { return Vector<String>(); };
virtual String get_system_font_path(const String &p_font_name, bool p_bold = false, bool p_italic = false) const { return String(); };
virtual String get_executable_path() const;
virtual Error execute(const String &p_path, const List<String> &p_arguments, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr, bool p_open_console = false) = 0;
virtual Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr, bool p_open_console = false) = 0;

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="FontFile" inherits="Font" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
FontFile source data and prerendered glyph cache, imported from dynamic or bitmap font.
Font source data and prerendered glyph cache, imported from dynamic or bitmap font.
</brief_description>
<description>
[FontFile] contains a set of glyphs to represent Unicode characters imported from a font file, as well as a cache of rasterized glyphs, and a set of fallback [Font]s to use.

View File

@ -385,6 +385,23 @@
[b]Note:[/b] Shared storage is implemented on Android and allows to differentiate between app specific and shared directories. Shared directories have additional restrictions on Android.
</description>
</method>
<method name="get_system_font_path" qualifiers="const">
<return type="String" />
<argument index="0" name="font_name" type="String" />
<argument index="1" name="bold" type="bool" default="false" />
<argument index="2" name="italic" type="bool" default="false" />
<description>
Returns path to the system font file with [code]font_name[/code] and style. Return empty string if no matching fonts found.
[b]Note:[/b] This method is implemented on iOS, Linux, macOS and Windows.
</description>
</method>
<method name="get_system_fonts" qualifiers="const">
<return type="PackedStringArray" />
<description>
Returns list of font family names available.
[b]Note:[/b] This method is implemented on iOS, Linux, macOS and Windows.
</description>
</method>
<method name="get_thread_caller_id" qualifiers="const">
<return type="int" />
<description>

View File

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="SystemFont" inherits="Font" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
Font loaded from a system font.
[b]Note:[/b] This class is implemented on iOS, Linux, macOS and Windows, on other platforms it will fallback to default theme font.
</brief_description>
<description>
[SystemFont] loads a font from a system font with the first matching name from [member font_names].
It will attempt to match font style, but it's not guaranteed.
The returned font might be part of a font collection or be a variable font with OpenType "weight" and/or "italic" features set.
You can create [FontVariation] of the system font for fine control over its features.
</description>
<tutorials>
</tutorials>
<members>
<member name="antialiased" type="bool" setter="set_antialiased" getter="is_antialiased" default="true">
If set to [code]true[/code], font 8-bit anitialiased glyph rendering is supported and enabled.
</member>
<member name="fallbacks" type="Font[]" setter="set_fallbacks" getter="get_fallbacks" default="[]">
Array of fallback [Font]s.
</member>
<member name="font_names" type="PackedStringArray" setter="set_font_names" getter="get_font_names" default="PackedStringArray()">
Array of font family names to search, first matching font found is used.
</member>
<member name="font_style" type="int" setter="set_font_style" getter="get_font_style" enum="TextServer.FontStyle" default="0">
Font style flags, see [enum TextServer.FontStyle].
</member>
<member name="force_autohinter" type="bool" setter="set_force_autohinter" getter="is_force_autohinter" default="false">
If set to [code]true[/code], auto-hinting is supported and preferred over font built-in hinting.
</member>
<member name="generate_mipmaps" type="bool" setter="set_generate_mipmaps" getter="get_generate_mipmaps" default="false">
If set to [code]true[/code], generate mipmaps for the font textures.
</member>
<member name="hinting" type="int" setter="set_hinting" getter="get_hinting" enum="TextServer.Hinting" default="1">
Font hinting mode.
</member>
<member name="oversampling" type="float" setter="set_oversampling" getter="get_oversampling" default="0.0">
Font oversampling factor, if set to [code]0.0[/code] global oversampling factor is used instead.
</member>
<member name="subpixel_positioning" type="int" setter="set_subpixel_positioning" getter="get_subpixel_positioning" enum="TextServer.SubpixelPositioning" default="1">
Font glyph sub-pixel positioning mode. Subpixel positioning provides shaper text and better kerning for smaller font sizes, at the cost of memory usage and font rasterization speed. Use [constant TextServer.SUBPIXEL_POSITIONING_AUTO] to automatically enable it based on the font size.
</member>
</members>
</class>

View File

@ -81,10 +81,7 @@ class EditorPropertyArray : public EditorProperty {
GDCLASS(EditorPropertyArray, EditorProperty);
PopupMenu *change_type = nullptr;
bool updating = false;
bool dropping = false;
Ref<EditorPropertyArrayObject> object;
int page_length = 20;
int page_index = 0;
int changing_type_index;
@ -106,29 +103,35 @@ class EditorPropertyArray : public EditorProperty {
Button *reorder_selected_button = nullptr;
void _page_changed(int p_page);
void _length_changed(double p_page);
void _add_element();
void _edit_pressed();
void _property_changed(const String &p_property, Variant p_value, const String &p_name = "", bool p_changing = false);
void _change_type(Object *p_button, int p_index);
void _change_type_menu(int p_index);
void _object_id_selected(const StringName &p_property, ObjectID p_id);
void _remove_pressed(int p_index);
void _button_draw();
bool _is_drop_valid(const Dictionary &p_drag_data) const;
bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
void _reorder_button_gui_input(const Ref<InputEvent> &p_event);
void _reorder_button_down(int p_index);
void _reorder_button_up();
protected:
Ref<EditorPropertyArrayObject> object;
bool updating = false;
bool dropping = false;
static void _bind_methods();
void _notification(int p_what);
virtual void _add_element();
virtual void _length_changed(double p_page);
virtual void _edit_pressed();
virtual void _property_changed(const String &p_property, Variant p_value, const String &p_name = "", bool p_changing = false);
virtual void _change_type(Object *p_button, int p_index);
virtual void _change_type_menu(int p_index);
virtual void _object_id_selected(const StringName &p_property, ObjectID p_id);
virtual void _remove_pressed(int p_index);
virtual void _button_draw();
virtual bool _is_drop_valid(const Dictionary &p_drag_data) const;
virtual bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
virtual void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
public:
void setup(Variant::Type p_array_type, const String &p_hint_string = "");
virtual void update_property() override;

View File

@ -1 +1 @@
<svg height="16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M1.5 1v3h1a1 1 0 0 1 1-1h2v6a1 1 0 0 1-1 1v1h4v-1a1 1 0 0 1-1-1V3h2a1 1 0 0 1 1 1h1V1h-6Z" fill="#e0e0e0"/><path d="M4.5 5v3h1a1 1 0 0 1 1-1h2v6a1 1 0 0 1-1 1v1h4v-1a1 1 0 0 1-1-1V7h2a1 1 0 0 1 1 1h1V5h-6Z" fill="#ff5f5f"/></svg>
<svg height="16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M7.5 7.5h-1a.519.519 0 0 0-.281.084A.491.491 0 0 0 6 8v.5h-.5V9a1 1 0 0 1-1 1v1H8V9.854A1 1 0 0 1 7.5 9V7.5zM1.5 1v3h1a1 1 0 0 1 1-1h2v1.5h2V3h2a1 1 0 0 1 1 1h1V1h-10z" style="fill:#e0e0e0;fill-opacity:1"/><path d="M4.5 5v3h1a1 1 0 0 1 1-1h2v6a1 1 0 0 1-1 1v1h4v-1a1 1 0 0 1-1-1V7h2a1 1 0 0 1 1 1h1V5h-6z" fill="#ff5f5f"/></svg>

Before

Width:  |  Height:  |  Size: 302 B

After

Width:  |  Height:  |  Size: 401 B

View File

@ -1 +1 @@
<svg height="16" width="16" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><path d="M2.437 1 1.379 4h1A.84 1.192 50 0 1 3.73 3h2L3.615 9a.84 1.192 50 0 1-1.352 1l-.353 1h4l.353-1a.84 1.192 50 0 1-.648-1l2.116-6h2a.84 1.192 50 0 1 .648 1h1l1.058-3h-6Z" fill="#e0e0e0"/><path d="m4.621 5-.705 2-.353 1h1a.84 1.192 49.998 0 1 1.353-1h2L5.8 13a.84 1.192 49.998 0 1-1.353 1l-.353 1h4l.353-1a.84 1.192 49.998 0 1-.647-1l2.116-6h2a.84 1.192 49.998 0 1 .647 1h1l.353-1 .705-2h-6Z" fill="#ff5f5f"/></svg>
<svg height="16" width="16" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><path d="M5.975 11 7.21 7.5H5.916a.478.478 0 0 0-.113.016.837.837 0 0 0-.127.043c-.044.018-.089.04-.133.066l-.043.027V9a1 1 0 0 1-1 1v1h1.475zM1.5 1v3h1a1 1 0 0 1 1-1h2v1.5h2V3h2a1 1 0 0 1 1 1h1V1h-10z" style="fill:#e0e0e0;fill-opacity:1"/><path d="m4.621 5-.705 2-.353 1h1a.84 1.192 49.998 0 1 1.353-1h2L5.8 13a.84 1.192 49.998 0 1-1.353 1l-.353 1h4l.353-1a.84 1.192 49.998 0 1-.647-1l2.116-6h2a.84 1.192 49.998 0 1 .647 1h1l.353-1 .705-2h-6z" fill="#ff5f5f"/></svg>

Before

Width:  |  Height:  |  Size: 505 B

After

Width:  |  Height:  |  Size: 552 B

View File

@ -0,0 +1 @@
<svg height="16" width="16" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><path style="fill:#e0e0e0;fill-opacity:1;stroke-width:.714755" d="m5.787 1-.402 1.613c-.352.265-.71.122-1.012-.111l-.904-.541L2.46 2.973l.853 1.425c-.058.438-.412.586-.79.635-.343.065-.674.216-1.024.213V6.72c.367 0 .715.157 1.074.224.371.032.716.243.727.65l-.84 1.4 1.008 1.01c.443-.266.895-.53 1.33-.802.349-.044.675.139.674.506l.314 1.258c.459-.059 1.099.115 1.45-.082.117-.475.242-.954.35-1.428A.67.67 0 0 1 8 9.195V7.5H6.5a.519.519 0 0 0-.281.084A.491.491 0 0 0 6 8v.5H4v-4h5.75c-.005-.22.107-.434.254-.625l.543-.902L9.535 1.96l-1.426.853c-.437-.058-.588-.412-.636-.79L7.217 1h-1.43z"/><path d="M4.5 5v3h1a1 1 0 0 1 1-1h2v6a1 1 0 0 1-1 1v1h4v-1a1 1 0 0 1-1-1V7h2a1 1 0 0 1 1 1h1V5h-6z" fill="#ff5f5f"/></svg>

After

Width:  |  Height:  |  Size: 797 B

View File

@ -895,17 +895,45 @@ void FontPreview::_notification(int p_what) {
Ref<Font> font = get_theme_font(SNAME("font"), SNAME("Label"));
int font_size = get_theme_font_size(SNAME("font_size"), SNAME("Label"));
Color text_color = get_theme_color(SNAME("font_color"), SNAME("Label"));
font->draw_string(get_canvas_item(), Point2(0, font->get_height(font_size) + 2 * EDSCALE), name, HORIZONTAL_ALIGNMENT_CENTER, get_size().x, font_size, text_color);
// Draw font preview.
Vector2 pos = Vector2(0, font->get_height(font_size)) + (get_size() - Vector2(0, font->get_height(font_size)) - line->get_size()) / 2;
line->draw(get_canvas_item(), pos, text_color);
bool prev_ok = true;
if (prev_font.is_valid()) {
if (prev_font->get_font_name().is_empty()) {
prev_ok = false;
} else {
String name;
if (prev_font->get_font_style_name().is_empty()) {
name = prev_font->get_font_name();
} else {
name = vformat("%s (%s)", prev_font->get_font_name(), prev_font->get_font_style_name());
}
if (prev_font->is_class("FontVariation")) {
name += " " + TTR(" - Variation");
}
font->draw_string(get_canvas_item(), Point2(0, font->get_height(font_size) + 2 * EDSCALE), name, HORIZONTAL_ALIGNMENT_CENTER, get_size().x, font_size, text_color);
// Draw font baseline.
Color line_color = text_color;
line_color.a *= 0.6;
draw_line(Vector2(0, pos.y + line->get_line_ascent()), Vector2(pos.x - 5, pos.y + line->get_line_ascent()), line_color);
draw_line(Vector2(pos.x + line->get_size().x + 5, pos.y + line->get_line_ascent()), Vector2(get_size().x, pos.y + line->get_line_ascent()), line_color);
String sample;
static const String sample_base = U"12漢字ԱբΑαАбΑαאבابܐܒހށआআਆઆଆஆఆಆആආกิກິༀကႠა한글ᎣᐁᚁᚠᜀᜠᝀᝠកᠠᤁᥐAb😀";
for (int i = 0; i < sample_base.length(); i++) {
if (prev_font->has_char(sample_base[i])) {
sample += sample_base[i];
}
}
if (sample.is_empty()) {
sample = prev_font->get_supported_chars().substr(0, 6);
}
if (sample.is_empty()) {
prev_ok = false;
} else {
prev_font->draw_string(get_canvas_item(), Point2(0, font->get_height(font_size) + prev_font->get_height(50)), sample, HORIZONTAL_ALIGNMENT_CENTER, get_size().x, 50, text_color);
}
}
}
if (!prev_ok) {
text_color.a *= 0.5;
font->draw_string(get_canvas_item(), Point2(0, font->get_height(font_size) + 2 * EDSCALE), TTR("Unable to preview font"), HORIZONTAL_ALIGNMENT_CENTER, get_size().x, font_size, text_color);
}
} break;
}
}
@ -917,30 +945,11 @@ Size2 FontPreview::get_minimum_size() const {
}
void FontPreview::set_data(const Ref<Font> &p_f) {
line->clear();
if (p_f.is_valid()) {
name = vformat("%s (%s)", p_f->get_font_name(), p_f->get_font_style_name());
if (p_f->is_class("FontVariation")) {
name += " " + TTR(" - Variation");
}
String sample;
static const String sample_base = U"12漢字ԱբΑαАбΑαאבابܐܒހށआআਆઆଆஆఆಆആආกิກິༀကႠა한글ᎣᐁᚁᚠᜀᜠᝀᝠកᠠᤁᥐAb😀";
for (int i = 0; i < sample_base.length(); i++) {
if (p_f->has_char(sample_base[i])) {
sample += sample_base[i];
}
}
if (sample.is_empty()) {
sample = p_f->get_supported_chars().substr(0, 6);
}
line->add_string(sample, p_f, 50);
}
prev_font = p_f;
update();
}
FontPreview::FontPreview() {
line.instantiate();
}
/*************************************************************************/
@ -964,6 +973,71 @@ bool EditorInspectorPluginFontPreview::parse_property(Object *p_object, const Va
return false;
}
/*************************************************************************/
/* EditorPropertyFontNamesArray */
/*************************************************************************/
void EditorPropertyFontNamesArray::_add_element() {
Size2 size = get_size();
menu->set_position(get_screen_position() + Size2(0, size.height * get_global_transform().get_scale().y));
menu->reset_size();
menu->popup();
}
void EditorPropertyFontNamesArray::_add_font(int p_option) {
if (updating) {
return;
}
Variant array = object->get_array();
int previous_size = array.call("size");
array.call("resize", previous_size + 1);
array.set(previous_size, menu->get_item_text(p_option));
emit_changed(get_edited_property(), array, "", false);
object->set_array(array);
update_property();
}
EditorPropertyFontNamesArray::EditorPropertyFontNamesArray() {
menu = memnew(PopupMenu);
menu->add_item("Sans-Serif", 0);
menu->add_item("Serif", 1);
menu->add_item("Monospace", 2);
menu->add_item("Fantasy", 3);
menu->add_item("Cursive", 4);
menu->add_separator();
if (OS::get_singleton()) {
Vector<String> fonts = OS::get_singleton()->get_system_fonts();
for (int i = 0; i < fonts.size(); i++) {
menu->add_item(fonts[i], i + 6);
}
}
add_child(menu);
menu->connect("id_pressed", callable_mp(this, &EditorPropertyFontNamesArray::_add_font));
}
/*************************************************************************/
/* EditorInspectorPluginSystemFont */
/*************************************************************************/
bool EditorInspectorPluginSystemFont::can_handle(Object *p_object) {
return Object::cast_to<SystemFont>(p_object) != nullptr;
}
bool EditorInspectorPluginSystemFont::parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const uint32_t p_usage, const bool p_wide) {
if (p_path == "font_names") {
EditorPropertyFontNamesArray *editor = memnew(EditorPropertyFontNamesArray);
editor->setup(p_type, p_hint_text);
add_property_editor(p_path, editor);
return true;
}
return false;
}
/*************************************************************************/
/* FontEditorPlugin */
/*************************************************************************/
@ -973,6 +1047,10 @@ FontEditorPlugin::FontEditorPlugin() {
fc_plugin.instantiate();
EditorInspector::add_inspector_plugin(fc_plugin);
Ref<EditorInspectorPluginSystemFont> fs_plugin;
fs_plugin.instantiate();
EditorInspector::add_inspector_plugin(fs_plugin);
Ref<EditorInspectorPluginFontPreview> fp_plugin;
fp_plugin.instantiate();
EditorInspector::add_inspector_plugin(fp_plugin);

View File

@ -34,6 +34,7 @@
#include "core/io/marshalls.h"
#include "editor/editor_plugin.h"
#include "editor/editor_properties.h"
#include "editor/editor_properties_array_dict.h"
/*************************************************************************/
@ -225,8 +226,7 @@ protected:
void _notification(int p_what);
static void _bind_methods();
String name;
Ref<TextLine> line;
Ref<Font> prev_font;
public:
virtual Size2 get_minimum_size() const override;
@ -249,6 +249,33 @@ public:
/*************************************************************************/
class EditorPropertyFontNamesArray : public EditorPropertyArray {
GDCLASS(EditorPropertyFontNamesArray, EditorPropertyArray);
PopupMenu *menu = nullptr;
protected:
virtual void _add_element() override;
void _add_font(int p_option);
static void _bind_methods(){};
public:
EditorPropertyFontNamesArray();
};
/*************************************************************************/
class EditorInspectorPluginSystemFont : public EditorInspectorPlugin {
GDCLASS(EditorInspectorPluginSystemFont, EditorInspectorPlugin);
public:
virtual bool can_handle(Object *p_object) override;
virtual bool parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const uint32_t p_usage, const bool p_wide = false) override;
};
/*************************************************************************/
class FontEditorPlugin : public EditorPlugin {
GDCLASS(FontEditorPlugin, EditorPlugin);

View File

@ -92,6 +92,9 @@ public:
virtual void alert(const String &p_alert, const String &p_title = "ALERT!") override;
virtual Vector<String> get_system_fonts() const override;
virtual String get_system_font_path(const String &p_font_name, bool p_bold = false, bool p_italic = false) const override;
virtual Error open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path = false, String *r_resolved_path = nullptr) override;
virtual Error close_dynamic_library(void *p_library_handle) override;
virtual Error get_dynamic_library_symbol_handle(void *p_library_handle, const String p_name, void *&p_symbol_handle, bool p_optional = false) override;

View File

@ -44,6 +44,7 @@
#import "view_controller.h"
#import <AudioToolbox/AudioServices.h>
#import <CoreText/CoreText.h>
#import <UIKit/UIKit.h>
#import <dlfcn.h>
#include <sys/sysctl.h>
@ -302,6 +303,80 @@ String OS_IOS::get_processor_name() const {
ERR_FAIL_V_MSG("", String("Couldn't get the CPU model name. Returning an empty string."));
}
Vector<String> OS_IOS::get_system_fonts() const {
HashSet<String> font_names;
CFArrayRef fonts = CTFontManagerCopyAvailableFontFamilyNames();
if (fonts) {
for (CFIndex i = 0; i < CFArrayGetCount(fonts); i++) {
CFStringRef cf_name = (CFStringRef)CFArrayGetValueAtIndex(fonts, i);
if (cf_name && (CFStringGetLength(cf_name) > 0) && (CFStringCompare(cf_name, CFSTR("LastResort"), kCFCompareCaseInsensitive) != kCFCompareEqualTo) && (CFStringGetCharacterAtIndex(cf_name, 0) != '.')) {
NSString *ns_name = (__bridge NSString *)cf_name;
font_names.insert(String::utf8([ns_name UTF8String]));
}
}
CFRelease(fonts);
}
Vector<String> ret;
for (const String &E : font_names) {
ret.push_back(E);
}
return ret;
}
String OS_IOS::get_system_font_path(const String &p_font_name, bool p_bold, bool p_italic) const {
String ret;
String font_name = p_font_name;
if (font_name.to_lower() == "sans-serif") {
font_name = "Helvetica";
} else if (font_name.to_lower() == "serif") {
font_name = "Times";
} else if (font_name.to_lower() == "monospace") {
font_name = "Courier";
} else if (font_name.to_lower() == "fantasy") {
font_name = "Papyrus";
} else if (font_name.to_lower() == "cursive") {
font_name = "Apple Chancery";
};
CFStringRef name = CFStringCreateWithCString(kCFAllocatorDefault, font_name.utf8().get_data(), kCFStringEncodingUTF8);
CTFontSymbolicTraits traits = 0;
if (p_bold) {
traits |= kCTFontBoldTrait;
}
if (p_italic) {
traits |= kCTFontItalicTrait;
}
CFNumberRef sym_traits = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &traits);
CFMutableDictionaryRef traits_dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, nullptr, nullptr);
CFDictionaryAddValue(traits_dict, kCTFontSymbolicTrait, sym_traits);
CFMutableDictionaryRef attributes = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, nullptr, nullptr);
CFDictionaryAddValue(attributes, kCTFontFamilyNameAttribute, name);
CFDictionaryAddValue(attributes, kCTFontTraitsAttribute, traits_dict);
CTFontDescriptorRef font = CTFontDescriptorCreateWithAttributes(attributes);
if (font) {
CFURLRef url = (CFURLRef)CTFontDescriptorCopyAttribute(font, kCTFontURLAttribute);
if (url) {
NSString *font_path = [NSString stringWithString:[(__bridge NSURL *)url path]];
ret = String::utf8([font_path UTF8String]);
CFRelease(url);
}
CFRelease(font);
}
CFRelease(attributes);
CFRelease(traits_dict);
CFRelease(sym_traits);
CFRelease(name);
return ret;
}
void OS_IOS::vibrate_handheld(int p_duration_ms) {
if (ios->supports_haptic_engine()) {
ios->vibrate_haptic_engine((float)p_duration_ms / 1000.f);

View File

@ -298,6 +298,12 @@ def configure(env):
## Flags
if os.system("pkg-config --exists fontconfig") == 0: # 0 means found
env.Append(CPPDEFINES=["FONTCONFIG_ENABLED"])
env.ParseConfig("pkg-config fontconfig --cflags --libs")
else:
print("Warning: fontconfig libraries not found. Disabling the system fonts support.")
if os.system("pkg-config --exists alsa") == 0: # 0 means found
env["alsa"] = True
env.Append(CPPDEFINES=["ALSA_ENABLED", "ALSAMIDI_ENABLED"])

View File

@ -52,6 +52,10 @@
#include <sys/types.h>
#include <unistd.h>
#ifdef FONTCONFIG_ENABLED
#include <fontconfig/fontconfig.h>
#endif
void OS_LinuxBSD::alert(const String &p_alert, const String &p_title) {
const char *message_programs[] = { "zenity", "kdialog", "Xdialog", "xmessage" };
@ -327,6 +331,91 @@ uint64_t OS_LinuxBSD::get_embedded_pck_offset() const {
return off;
}
Vector<String> OS_LinuxBSD::get_system_fonts() const {
#ifdef FONTCONFIG_ENABLED
HashSet<String> font_names;
Vector<String> ret;
FcConfig *config = FcInitLoadConfigAndFonts();
ERR_FAIL_COND_V(!config, ret);
FcObjectSet *object_set = FcObjectSetBuild(FC_FAMILY, nullptr);
ERR_FAIL_COND_V(!object_set, ret);
static const char *allowed_formats[] = { "TrueType", "CFF" };
for (size_t i = 0; i < sizeof(allowed_formats) / sizeof(const char *); i++) {
FcPattern *pattern = FcPatternCreate();
ERR_CONTINUE(!pattern);
FcPatternAddBool(pattern, FC_SCALABLE, FcTrue);
FcPatternAddString(pattern, FC_FONTFORMAT, reinterpret_cast<const FcChar8 *>(allowed_formats[i]));
FcFontSet *font_set = FcFontList(config, pattern, object_set);
if (font_set) {
for (int j = 0; j < font_set->nfont; j++) {
char *family_name = nullptr;
if (FcPatternGetString(font_set->fonts[j], FC_FAMILY, 0, reinterpret_cast<FcChar8 **>(&family_name)) == FcResultMatch) {
if (family_name) {
font_names.insert(String::utf8(family_name));
}
}
}
FcFontSetDestroy(font_set);
}
FcPatternDestroy(pattern);
}
FcObjectSetDestroy(object_set);
for (const String &E : font_names) {
ret.push_back(E);
}
return ret;
#else
ERR_FAIL_COND_V_MSG(Vector<String>(), "Godot was compiled without fontconfig, system font support is disabled.")
#endif
}
String OS_LinuxBSD::get_system_font_path(const String &p_font_name, bool p_bold, bool p_italic) const {
#ifdef FONTCONFIG_ENABLED
String ret;
FcConfig *config = FcInitLoadConfigAndFonts();
ERR_FAIL_COND_V(!config, ret);
FcObjectSet *object_set = FcObjectSetBuild(FC_FAMILY, FC_FILE, nullptr);
ERR_FAIL_COND_V(!object_set, ret);
FcPattern *pattern = FcPatternCreate();
if (pattern) {
FcPatternAddBool(pattern, FC_SCALABLE, FcTrue);
FcPatternAddString(pattern, FC_FAMILY, reinterpret_cast<const FcChar8 *>(p_font_name.utf8().get_data()));
FcPatternAddInteger(pattern, FC_WEIGHT, p_bold ? FC_WEIGHT_BOLD : FC_WEIGHT_NORMAL);
FcPatternAddInteger(pattern, FC_SLANT, p_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN);
FcConfigSubstitute(0, pattern, FcMatchPattern);
FcDefaultSubstitute(pattern);
FcResult result;
FcPattern *match = FcFontMatch(0, pattern, &result);
if (match) {
char *file_name = nullptr;
if (FcPatternGetString(match, FC_FILE, 0, reinterpret_cast<FcChar8 **>(&file_name)) == FcResultMatch) {
if (file_name) {
ret = String::utf8(file_name);
}
}
FcPatternDestroy(match);
}
FcPatternDestroy(pattern);
}
FcObjectSetDestroy(object_set);
#else
ERR_FAIL_COND_V_MSG(Vector<String>(), "Godot was compiled without fontconfig, system font support is disabled.")
#endif
return ret;
}
String OS_LinuxBSD::get_config_path() const {
if (has_environment("XDG_CONFIG_HOME")) {
if (get_environment("XDG_CONFIG_HOME").is_absolute_path()) {

View File

@ -80,6 +80,9 @@ public:
virtual uint64_t get_embedded_pck_offset() const override;
virtual Vector<String> get_system_fonts() const override;
virtual String get_system_font_path(const String &p_font_name, bool p_bold = false, bool p_italic = false) const override;
virtual String get_config_path() const override;
virtual String get_data_path() const override;
virtual String get_cache_path() const override;

View File

@ -97,6 +97,8 @@ public:
virtual String get_locale() const override;
virtual Vector<String> get_system_fonts() const override;
virtual String get_system_font_path(const String &p_font_name, bool p_bold = false, bool p_italic = false) const override;
virtual String get_executable_path() const override;
virtual Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr, bool p_open_console = false) override;
virtual Error create_instance(const List<String> &p_arguments, ProcessID *r_child_id = nullptr) override;

View File

@ -303,6 +303,80 @@ String OS_MacOS::get_locale() const {
return String([locale_code UTF8String]).replace("-", "_");
}
Vector<String> OS_MacOS::get_system_fonts() const {
HashSet<String> font_names;
CFArrayRef fonts = CTFontManagerCopyAvailableFontFamilyNames();
if (fonts) {
for (CFIndex i = 0; i < CFArrayGetCount(fonts); i++) {
CFStringRef cf_name = (CFStringRef)CFArrayGetValueAtIndex(fonts, i);
if (cf_name && (CFStringGetLength(cf_name) > 0) && (CFStringCompare(cf_name, CFSTR("LastResort"), kCFCompareCaseInsensitive) != kCFCompareEqualTo) && (CFStringGetCharacterAtIndex(cf_name, 0) != '.')) {
NSString *ns_name = (__bridge NSString *)cf_name;
font_names.insert(String::utf8([ns_name UTF8String]));
}
}
CFRelease(fonts);
}
Vector<String> ret;
for (const String &E : font_names) {
ret.push_back(E);
}
return ret;
}
String OS_MacOS::get_system_font_path(const String &p_font_name, bool p_bold, bool p_italic) const {
String ret;
String font_name = p_font_name;
if (font_name.to_lower() == "sans-serif") {
font_name = "Helvetica";
} else if (font_name.to_lower() == "serif") {
font_name = "Times";
} else if (font_name.to_lower() == "monospace") {
font_name = "Courier";
} else if (font_name.to_lower() == "fantasy") {
font_name = "Papyrus";
} else if (font_name.to_lower() == "cursive") {
font_name = "Apple Chancery";
};
CFStringRef name = CFStringCreateWithCString(kCFAllocatorDefault, font_name.utf8().get_data(), kCFStringEncodingUTF8);
CTFontSymbolicTraits traits = 0;
if (p_bold) {
traits |= kCTFontBoldTrait;
}
if (p_italic) {
traits |= kCTFontItalicTrait;
}
CFNumberRef sym_traits = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &traits);
CFMutableDictionaryRef traits_dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, nullptr, nullptr);
CFDictionaryAddValue(traits_dict, kCTFontSymbolicTrait, sym_traits);
CFMutableDictionaryRef attributes = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, nullptr, nullptr);
CFDictionaryAddValue(attributes, kCTFontFamilyNameAttribute, name);
CFDictionaryAddValue(attributes, kCTFontTraitsAttribute, traits_dict);
CTFontDescriptorRef font = CTFontDescriptorCreateWithAttributes(attributes);
if (font) {
CFURLRef url = (CFURLRef)CTFontDescriptorCopyAttribute(font, kCTFontURLAttribute);
if (url) {
NSString *font_path = [NSString stringWithString:[(__bridge NSURL *)url path]];
ret = String::utf8([font_path UTF8String]);
CFRelease(url);
}
CFRelease(font);
}
CFRelease(attributes);
CFRelease(traits_dict);
CFRelease(sym_traits);
CFRelease(name);
return ret;
}
String OS_MacOS::get_executable_path() const {
char pathbuf[PROC_PIDPATHINFO_MAXSIZE];
int pid = getpid();

View File

@ -270,6 +270,7 @@ def configure_msvc(env, manual_msvc_config):
"bcrypt",
"Avrt",
"dwmapi",
"dwrite",
]
if env["vulkan"]:
@ -441,6 +442,7 @@ def configure_mingw(env):
"avrt",
"uuid",
"dwmapi",
"dwrite",
]
)

View File

@ -48,6 +48,7 @@
#include <avrt.h>
#include <bcrypt.h>
#include <direct.h>
#include <dwrite.h>
#include <knownfolders.h>
#include <process.h>
#include <regstr.h>
@ -621,6 +622,135 @@ Error OS_Windows::set_cwd(const String &p_cwd) {
return OK;
}
Vector<String> OS_Windows::get_system_fonts() const {
Vector<String> ret;
HashSet<String> font_names;
ComAutoreleaseRef<IDWriteFactory> dwrite_factory;
HRESULT hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), reinterpret_cast<IUnknown **>(&dwrite_factory.reference));
ERR_FAIL_COND_V(FAILED(hr) || dwrite_factory.is_null(), ret);
ComAutoreleaseRef<IDWriteFontCollection> font_collection;
hr = dwrite_factory->GetSystemFontCollection(&font_collection.reference, false);
ERR_FAIL_COND_V(FAILED(hr) || font_collection.is_null(), ret);
UINT32 family_count = font_collection->GetFontFamilyCount();
for (UINT32 i = 0; i < family_count; i++) {
ComAutoreleaseRef<IDWriteFontFamily> family;
hr = font_collection->GetFontFamily(i, &family.reference);
ERR_CONTINUE(FAILED(hr) || family.is_null());
ComAutoreleaseRef<IDWriteLocalizedStrings> family_names;
hr = family->GetFamilyNames(&family_names.reference);
ERR_CONTINUE(FAILED(hr) || family_names.is_null());
UINT32 index = 0;
BOOL exists = false;
UINT32 length = 0;
Char16String name;
hr = family_names->FindLocaleName(L"en-us", &index, &exists);
ERR_CONTINUE(FAILED(hr));
hr = family_names->GetStringLength(index, &length);
ERR_CONTINUE(FAILED(hr));
name.resize(length + 1);
hr = family_names->GetString(index, (WCHAR *)name.ptrw(), length + 1);
ERR_CONTINUE(FAILED(hr));
font_names.insert(String::utf16(name.ptr(), length));
}
for (const String &E : font_names) {
ret.push_back(E);
}
return ret;
}
String OS_Windows::get_system_font_path(const String &p_font_name, bool p_bold, bool p_italic) const {
String font_name = p_font_name;
if (font_name.to_lower() == "sans-serif") {
font_name = "Arial";
} else if (font_name.to_lower() == "serif") {
font_name = "Times New Roman";
} else if (font_name.to_lower() == "monospace") {
font_name = "Courier New";
} else if (font_name.to_lower() == "cursive") {
font_name = "Comic Sans MS";
} else if (font_name.to_lower() == "fantasy") {
font_name = "Gabriola";
}
ComAutoreleaseRef<IDWriteFactory> dwrite_factory;
HRESULT hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), reinterpret_cast<IUnknown **>(&dwrite_factory.reference));
ERR_FAIL_COND_V(FAILED(hr) || dwrite_factory.is_null(), String());
ComAutoreleaseRef<IDWriteFontCollection> font_collection;
hr = dwrite_factory->GetSystemFontCollection(&font_collection.reference, false);
ERR_FAIL_COND_V(FAILED(hr) || font_collection.is_null(), String());
UINT32 index = 0;
BOOL exists = false;
font_collection->FindFamilyName((const WCHAR *)font_name.utf16().get_data(), &index, &exists);
if (FAILED(hr)) {
return String();
}
ComAutoreleaseRef<IDWriteFontFamily> family;
hr = font_collection->GetFontFamily(index, &family.reference);
if (FAILED(hr) || family.is_null()) {
return String();
}
ComAutoreleaseRef<IDWriteFont> dwrite_font;
hr = family->GetFirstMatchingFont(p_bold ? DWRITE_FONT_WEIGHT_BOLD : DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STRETCH_NORMAL, p_italic ? DWRITE_FONT_STYLE_ITALIC : DWRITE_FONT_STYLE_NORMAL, &dwrite_font.reference);
if (FAILED(hr) || dwrite_font.is_null()) {
return String();
}
ComAutoreleaseRef<IDWriteFontFace> dwrite_face;
hr = dwrite_font->CreateFontFace(&dwrite_face.reference);
if (FAILED(hr) || dwrite_face.is_null()) {
return String();
}
UINT32 number_of_files = 0;
hr = dwrite_face->GetFiles(&number_of_files, nullptr);
if (FAILED(hr)) {
return String();
}
Vector<ComAutoreleaseRef<IDWriteFontFile>> files;
files.resize(number_of_files);
hr = dwrite_face->GetFiles(&number_of_files, (IDWriteFontFile **)files.ptrw());
if (FAILED(hr)) {
return String();
}
for (UINT32 i = 0; i < number_of_files; i++) {
void const *reference_key = nullptr;
UINT32 reference_key_size = 0;
ComAutoreleaseRef<IDWriteLocalFontFileLoader> loader;
hr = files.write[i]->GetLoader((IDWriteFontFileLoader **)&loader.reference);
if (FAILED(hr) || loader.is_null()) {
continue;
}
hr = files.write[i]->GetReferenceKey(&reference_key, &reference_key_size);
if (FAILED(hr)) {
continue;
}
WCHAR file_path[MAX_PATH];
hr = loader->GetFilePathFromKey(reference_key, reference_key_size, &file_path[0], MAX_PATH);
if (FAILED(hr)) {
continue;
}
return String::utf16((const char16_t *)&file_path[0]);
}
return String();
}
String OS_Windows::get_executable_path() const {
WCHAR bufname[4096];
GetModuleFileNameW(nullptr, bufname, 4096);

View File

@ -62,6 +62,26 @@
#define WINDOWS_DEBUG_OUTPUT_ENABLED
#endif
template <class T>
class ComAutoreleaseRef {
public:
T *reference = nullptr;
_FORCE_INLINE_ T *operator->() { return reference; }
_FORCE_INLINE_ const T *operator->() const { return reference; }
_FORCE_INLINE_ T *operator*() { return reference; }
_FORCE_INLINE_ const T *operator*() const { return reference; }
_FORCE_INLINE_ bool is_valid() const { return reference != nullptr; }
_FORCE_INLINE_ bool is_null() const { return reference == nullptr; }
ComAutoreleaseRef() {}
~ComAutoreleaseRef() {
if (reference != nullptr) {
reference->Release();
reference = nullptr;
}
}
};
class JoypadWindows;
class OS_Windows : public OS {
#ifdef STDOUT_FILE
@ -147,6 +167,9 @@ public:
virtual String get_environment(const String &p_var) const override;
virtual bool set_environment(const String &p_var, const String &p_value) const override;
virtual Vector<String> get_system_fonts() const override;
virtual String get_system_font_path(const String &p_font_name, bool p_bold = false, bool p_italic = false) const override;
virtual String get_executable_path() const override;
virtual String get_locale() const override;

View File

@ -443,8 +443,10 @@ void TextEdit::_notification(int p_what) {
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
case NOTIFICATION_TRANSLATION_CHANGED:
case NOTIFICATION_THEME_CHANGED: {
_update_caches();
_update_wrap_at_column(true);
if (is_inside_tree()) {
_update_caches();
_update_wrap_at_column(true);
}
} break;
case NOTIFICATION_WM_WINDOW_FOCUS_IN: {

View File

@ -870,6 +870,7 @@ void register_scene_types() {
GDREGISTER_ABSTRACT_CLASS(Font);
GDREGISTER_CLASS(FontFile);
GDREGISTER_CLASS(FontVariation);
GDREGISTER_CLASS(SystemFont);
GDREGISTER_CLASS(Curve);

View File

@ -2693,3 +2693,374 @@ FontVariation::FontVariation() {
FontVariation::~FontVariation() {
reset_state();
}
/*************************************************************************/
/* SystemFont */
/*************************************************************************/
void SystemFont::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_antialiased", "antialiased"), &SystemFont::set_antialiased);
ClassDB::bind_method(D_METHOD("is_antialiased"), &SystemFont::is_antialiased);
ClassDB::bind_method(D_METHOD("set_generate_mipmaps", "generate_mipmaps"), &SystemFont::set_generate_mipmaps);
ClassDB::bind_method(D_METHOD("get_generate_mipmaps"), &SystemFont::get_generate_mipmaps);
ClassDB::bind_method(D_METHOD("set_force_autohinter", "force_autohinter"), &SystemFont::set_force_autohinter);
ClassDB::bind_method(D_METHOD("is_force_autohinter"), &SystemFont::is_force_autohinter);
ClassDB::bind_method(D_METHOD("set_hinting", "hinting"), &SystemFont::set_hinting);
ClassDB::bind_method(D_METHOD("get_hinting"), &SystemFont::get_hinting);
ClassDB::bind_method(D_METHOD("set_subpixel_positioning", "subpixel_positioning"), &SystemFont::set_subpixel_positioning);
ClassDB::bind_method(D_METHOD("get_subpixel_positioning"), &SystemFont::get_subpixel_positioning);
ClassDB::bind_method(D_METHOD("set_oversampling", "oversampling"), &SystemFont::set_oversampling);
ClassDB::bind_method(D_METHOD("get_oversampling"), &SystemFont::get_oversampling);
ClassDB::bind_method(D_METHOD("get_font_names"), &SystemFont::get_font_names);
ClassDB::bind_method(D_METHOD("set_font_names", "names"), &SystemFont::set_font_names);
ClassDB::bind_method(D_METHOD("set_font_style", "style"), &SystemFont::set_font_style);
ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "font_names"), "set_font_names", "get_font_names");
ADD_PROPERTY(PropertyInfo(Variant::INT, "font_style", PROPERTY_HINT_FLAGS, "Bold,Italic"), "set_font_style", "get_font_style");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "antialiased"), "set_antialiased", "is_antialiased");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "generate_mipmaps"), "set_generate_mipmaps", "get_generate_mipmaps");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "force_autohinter"), "set_force_autohinter", "is_force_autohinter");
ADD_PROPERTY(PropertyInfo(Variant::INT, "hinting", PROPERTY_HINT_ENUM, "None,Light,Normal"), "set_hinting", "get_hinting");
ADD_PROPERTY(PropertyInfo(Variant::INT, "subpixel_positioning", PROPERTY_HINT_ENUM, "Disabled,Auto,One half of a pixel,One quarter of a pixel"), "set_subpixel_positioning", "get_subpixel_positioning");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "oversampling", PROPERTY_HINT_RANGE, "0,10,0.1"), "set_oversampling", "get_oversampling");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "fallbacks", PROPERTY_HINT_ARRAY_TYPE, vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "Font")), "set_fallbacks", "get_fallbacks");
}
void SystemFont::_update_rids() const {
Ref<Font> f = _get_base_font_or_default();
rids.clear();
if (fallbacks.is_empty() && f.is_valid()) {
RID rid = _get_rid();
if (rid.is_valid()) {
rids.push_back(rid);
}
const TypedArray<Font> &base_fallbacks = f->get_fallbacks();
for (int i = 0; i < base_fallbacks.size(); i++) {
_update_rids_fb(base_fallbacks[i], 0);
}
} else {
_update_rids_fb(const_cast<SystemFont *>(this), 0);
}
dirty_rids = false;
}
void SystemFont::_update_base_font() {
if (base_font.is_valid()) {
base_font->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids));
base_font.unref();
}
face_indeces.clear();
ftr_weight = 0;
ftr_italic = 0;
for (const String &E : names) {
if (E.is_empty()) {
continue;
}
String path = OS::get_singleton()->get_system_font_path(E, style & TextServer::FONT_BOLD, style & TextServer::FONT_ITALIC);
if (path.is_empty()) {
continue;
}
Ref<FontFile> file;
file.instantiate();
Error err = file->load_dynamic_font(path);
if (err != OK) {
continue;
}
// If it's a font collection check all faces to match requested style.
for (int i = 0; i < file->get_face_count(); i++) {
file->set_face_index(0, i);
if (((file->get_font_style() & TextServer::FONT_BOLD) == (style & TextServer::FONT_BOLD)) && ((file->get_font_style() & TextServer::FONT_ITALIC) == (style & TextServer::FONT_ITALIC))) {
face_indeces.push_back(i);
}
}
if (face_indeces.is_empty()) {
face_indeces.push_back(0);
}
file->set_face_index(0, face_indeces[0]);
// If it's a variable font, apply weight and italic coordinates to match requested style.
Dictionary ftr = file->get_supported_variation_list();
if ((style & TextServer::FONT_BOLD) && ftr.has(TS->name_to_tag("weight"))) {
ftr_weight = 700;
}
if ((style & TextServer::FONT_ITALIC) && ftr.has(TS->name_to_tag("italic"))) {
ftr_italic = 1;
}
// Apply font rendering settings.
file->set_antialiased(antialiased);
file->set_generate_mipmaps(mipmaps);
file->set_force_autohinter(force_autohinter);
file->set_hinting(hinting);
file->set_subpixel_positioning(subpixel_positioning);
file->set_oversampling(oversampling);
base_font = file;
}
if (base_font.is_valid()) {
base_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids), varray(), CONNECT_REFERENCE_COUNTED);
}
_invalidate_rids();
notify_property_list_changed();
}
void SystemFont::reset_state() {
if (base_font.is_valid()) {
base_font->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids));
base_font.unref();
}
if (theme_font.is_valid()) {
theme_font->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids));
theme_font.unref();
}
names.clear();
face_indeces.clear();
ftr_weight = 0;
ftr_italic = 0;
style = 0;
antialiased = true;
mipmaps = false;
force_autohinter = false;
hinting = TextServer::HINTING_LIGHT;
subpixel_positioning = TextServer::SUBPIXEL_POSITIONING_DISABLED;
oversampling = 0.f;
Font::reset_state();
}
Ref<Font> SystemFont::_get_base_font_or_default() const {
if (theme_font.is_valid()) {
theme_font->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<SystemFont *>(this)), &Font::_invalidate_rids));
theme_font.unref();
}
if (base_font.is_valid()) {
return base_font;
}
// Check the project-defined Theme resource.
if (Theme::get_project_default().is_valid()) {
List<StringName> theme_types;
Theme::get_project_default()->get_type_dependencies(get_class_name(), StringName(), &theme_types);
for (const StringName &E : theme_types) {
if (Theme::get_project_default()->has_theme_item(Theme::DATA_TYPE_FONT, "font", E)) {
Ref<Font> f = Theme::get_project_default()->get_theme_item(Theme::DATA_TYPE_FONT, "font", E);
if (f.is_valid()) {
theme_font = f;
theme_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<SystemFont *>(this)), &Font::_invalidate_rids), varray(), CONNECT_REFERENCE_COUNTED);
}
return f;
}
}
}
// Lastly, fall back on the items defined in the default Theme, if they exist.
if (Theme::get_default().is_valid()) {
List<StringName> theme_types;
Theme::get_default()->get_type_dependencies(get_class_name(), StringName(), &theme_types);
for (const StringName &E : theme_types) {
if (Theme::get_default()->has_theme_item(Theme::DATA_TYPE_FONT, "font", E)) {
Ref<Font> f = Theme::get_default()->get_theme_item(Theme::DATA_TYPE_FONT, "font", E);
if (f.is_valid()) {
theme_font = f;
theme_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<SystemFont *>(this)), &Font::_invalidate_rids), varray(), CONNECT_REFERENCE_COUNTED);
}
return f;
}
}
// If they don't exist, use any type to return the default/empty value.
Ref<Font> f = Theme::get_default()->get_theme_item(Theme::DATA_TYPE_FONT, "font", StringName());
if (f.is_valid()) {
theme_font = f;
theme_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<SystemFont *>(this)), &Font::_invalidate_rids), varray(), CONNECT_REFERENCE_COUNTED);
}
return f;
}
return Ref<Font>();
}
void SystemFont::set_antialiased(bool p_antialiased) {
if (antialiased != p_antialiased) {
antialiased = p_antialiased;
if (base_font.is_valid()) {
base_font->set_antialiased(antialiased);
}
emit_changed();
}
}
bool SystemFont::is_antialiased() const {
return antialiased;
}
void SystemFont::set_generate_mipmaps(bool p_generate_mipmaps) {
if (mipmaps != p_generate_mipmaps) {
mipmaps = p_generate_mipmaps;
if (base_font.is_valid()) {
base_font->set_generate_mipmaps(mipmaps);
}
emit_changed();
}
}
bool SystemFont::get_generate_mipmaps() const {
return mipmaps;
}
void SystemFont::set_force_autohinter(bool p_force_autohinter) {
if (force_autohinter != p_force_autohinter) {
force_autohinter = p_force_autohinter;
if (base_font.is_valid()) {
base_font->set_force_autohinter(force_autohinter);
}
emit_changed();
}
}
bool SystemFont::is_force_autohinter() const {
return force_autohinter;
}
void SystemFont::set_hinting(TextServer::Hinting p_hinting) {
if (hinting != p_hinting) {
hinting = p_hinting;
if (base_font.is_valid()) {
base_font->set_hinting(hinting);
}
emit_changed();
}
}
TextServer::Hinting SystemFont::get_hinting() const {
return hinting;
}
void SystemFont::set_subpixel_positioning(TextServer::SubpixelPositioning p_subpixel) {
if (subpixel_positioning != p_subpixel) {
subpixel_positioning = p_subpixel;
if (base_font.is_valid()) {
base_font->set_subpixel_positioning(subpixel_positioning);
}
emit_changed();
}
}
TextServer::SubpixelPositioning SystemFont::get_subpixel_positioning() const {
return subpixel_positioning;
}
void SystemFont::set_oversampling(real_t p_oversampling) {
if (oversampling != p_oversampling) {
oversampling = p_oversampling;
if (base_font.is_valid()) {
base_font->set_oversampling(oversampling);
}
emit_changed();
}
}
real_t SystemFont::get_oversampling() const {
return oversampling;
}
void SystemFont::set_font_names(const PackedStringArray &p_names) {
if (names != p_names) {
names = p_names;
_update_base_font();
}
}
PackedStringArray SystemFont::get_font_names() const {
return names;
}
void SystemFont::set_font_style(BitField<TextServer::FontStyle> p_style) {
if (style != p_style) {
style = p_style;
_update_base_font();
}
}
BitField<TextServer::FontStyle> SystemFont::get_font_style() const {
return style;
}
int SystemFont::get_spacing(TextServer::SpacingType p_spacing) const {
if (base_font.is_valid()) {
return base_font->get_spacing(p_spacing);
} else {
return 0;
}
}
RID SystemFont::find_variation(const Dictionary &p_variation_coordinates, int p_face_index, float p_strength, Transform2D p_transform) const {
Ref<Font> f = _get_base_font_or_default();
if (f.is_valid()) {
Dictionary var = p_variation_coordinates;
if (ftr_weight > 0 && !var.has(TS->name_to_tag("weight"))) {
var[TS->name_to_tag("weight")] = ftr_weight;
}
if (ftr_italic > 0 && !var.has(TS->name_to_tag("italic"))) {
var[TS->name_to_tag("italic")] = ftr_italic;
}
if (!face_indeces.is_empty()) {
int face_index = CLAMP(p_face_index, 0, face_indeces.size() - 1);
return f->find_variation(var, face_indeces[face_index], p_strength, p_transform);
} else {
return f->find_variation(var, 0, p_strength, p_transform);
}
}
return RID();
}
RID SystemFont::_get_rid() const {
Ref<Font> f = _get_base_font_or_default();
if (f.is_valid()) {
if (!face_indeces.is_empty()) {
Dictionary var;
if (ftr_weight > 0) {
var[TS->name_to_tag("weight")] = ftr_weight;
}
if (ftr_italic > 0) {
var[TS->name_to_tag("italic")] = ftr_italic;
}
return f->find_variation(var, face_indeces[0]);
} else {
return f->_get_rid();
}
}
return RID();
}
int64_t SystemFont::get_face_count() const {
return face_indeces.size();
}
SystemFont::SystemFont() {
/* NOP */
}
SystemFont::~SystemFont() {
reset_state();
}

View File

@ -41,7 +41,7 @@ class TextLine;
class TextParagraph;
/*************************************************************************/
/* Font */
/* Font */
/*************************************************************************/
class Font : public Resource {
@ -381,4 +381,74 @@ public:
~FontVariation();
};
/*************************************************************************/
/* SystemFont */
/*************************************************************************/
class SystemFont : public Font {
GDCLASS(SystemFont, Font);
PackedStringArray names;
BitField<TextServer::FontStyle> style = 0;
mutable Ref<Font> theme_font;
Ref<FontFile> base_font;
Vector<int> face_indeces;
int ftr_weight = 0;
int ftr_italic = 0;
bool antialiased = true;
bool mipmaps = false;
bool force_autohinter = false;
TextServer::Hinting hinting = TextServer::HINTING_LIGHT;
TextServer::SubpixelPositioning subpixel_positioning = TextServer::SUBPIXEL_POSITIONING_AUTO;
real_t oversampling = 0.f;
protected:
static void _bind_methods();
virtual void _update_base_font();
virtual void _update_rids() const override;
virtual void reset_state() override;
public:
virtual Ref<Font> _get_base_font_or_default() const;
virtual void set_antialiased(bool p_antialiased);
virtual bool is_antialiased() const;
virtual void set_generate_mipmaps(bool p_generate_mipmaps);
virtual bool get_generate_mipmaps() const;
virtual void set_force_autohinter(bool p_force_autohinter);
virtual bool is_force_autohinter() const;
virtual void set_hinting(TextServer::Hinting p_hinting);
virtual TextServer::Hinting get_hinting() const;
virtual void set_subpixel_positioning(TextServer::SubpixelPositioning p_subpixel);
virtual TextServer::SubpixelPositioning get_subpixel_positioning() const;
virtual void set_oversampling(real_t p_oversampling);
virtual real_t get_oversampling() const;
virtual void set_font_names(const PackedStringArray &p_names);
virtual PackedStringArray get_font_names() const;
virtual void set_font_style(BitField<TextServer::FontStyle> p_style);
virtual BitField<TextServer::FontStyle> get_font_style() const override;
virtual int get_spacing(TextServer::SpacingType p_spacing) const override;
virtual RID find_variation(const Dictionary &p_variation_coordinates, int p_face_index = 0, float p_strength = 0.0, Transform2D p_transform = Transform2D()) const override;
virtual RID _get_rid() const override;
int64_t get_face_count() const override;
SystemFont();
~SystemFont();
};
#endif // FONT_H