diff --git a/TestGame/Resources/fonts/default.fontset.toml b/TestGame/Resources/fonts/default.fontset.toml new file mode 100644 index 0000000..c082639 --- /dev/null +++ b/TestGame/Resources/fonts/default.fontset.toml @@ -0,0 +1,3 @@ +[FontSet] + +fonts = ["Inter-Regular.ttf", "NotoSansJP-Regular.ttf"] diff --git a/TestGame/TestGame.cs b/TestGame/TestGame.cs index c8a9e62..00dc823 100644 --- a/TestGame/TestGame.cs +++ b/TestGame/TestGame.cs @@ -54,6 +54,8 @@ public class TestGame : Game { throw new Exception("Failed to load emitter settings!"); } + + _defaultFontSet = new([_font, _jpFont]); } protected override void Ready() @@ -78,7 +80,7 @@ public class TestGame : Game if (Input.IsActionPressed("accept")) { - _container.AddChild(new Label("こんにちは世界!", _jpFont)); + _container.AddChild(new Label("こんにちは世界!", _defaultFontSet)); } if (Input.IsActionPressed("cancel") && _container.Children.Count != 0) @@ -126,6 +128,9 @@ public class TestGame : Game private ResourceRef _fireEffect; private ResourceRef _font; private ResourceRef _jpFont; + + private FontSet _defaultFontSet; + private ResourceRef _sound; private ResourceRef _icon; diff --git a/Voile/Source/Resources/FontSet.cs b/Voile/Source/Resources/FontSet.cs new file mode 100644 index 0000000..5e2e16a --- /dev/null +++ b/Voile/Source/Resources/FontSet.cs @@ -0,0 +1,45 @@ +using System.Diagnostics.CodeAnalysis; + +namespace Voile.Resources; + +/// +/// Contains a set of multiple fonts. Used to fetch fonts based on availability of glyphs. +/// +public class FontSet +{ + public FontSet() { } + + public FontSet(IEnumerable> fonts) + { + _fonts = fonts.ToList(); + } + + public void AddFont(ResourceRef font) => _fonts.Add(font); + + /// + /// Tries to get a suitable font that has a given character. + /// + /// Character to get a suitable font for. + /// Font that contains this character. + /// true if a font that contains this character exists, false otherwise. + public bool TryGetFontFor(char c, [NotNullWhen(true)] out ResourceRef? result) + { + result = ResourceRef.Empty(); + foreach (var fontRef in _fonts) + { + if (!fontRef.TryGetValue(out var font)) + { + return false; + } + + if (font.HasGlyph(c)) + { + result = fontRef; + return true; + } + } + return false; + } + + private List> _fonts = new(); +} \ No newline at end of file diff --git a/Voile/Source/Resources/Resource.cs b/Voile/Source/Resources/Resource.cs index 272615b..8b6b775 100644 --- a/Voile/Source/Resources/Resource.cs +++ b/Voile/Source/Resources/Resource.cs @@ -52,6 +52,28 @@ namespace Voile { Guid = guid; } + + public override bool Equals(object? obj) + { + return obj is ResourceRef other && Guid.Equals(other.Guid); + } + + public override int GetHashCode() + { + return Guid.GetHashCode(); + } + + public static bool operator ==(ResourceRef? left, ResourceRef? right) + { + if (ReferenceEquals(left, right)) return true; + if (left is null || right is null) return false; + return left.Guid == right.Guid; + } + + public static bool operator !=(ResourceRef? left, ResourceRef? right) + { + return !(left == right); + } } /// diff --git a/Voile/Source/Resources/ResourceManager.cs b/Voile/Source/Resources/ResourceManager.cs index 755aa3e..6055902 100644 --- a/Voile/Source/Resources/ResourceManager.cs +++ b/Voile/Source/Resources/ResourceManager.cs @@ -231,7 +231,7 @@ namespace Voile.Resources /// /// Path to the resource. /// True if a resource at the specified path is loaded, otherwise false. - public bool IsResourceLoaded(string path) => _resourcePathMap.ContainsKey(path); + public static bool IsResourceLoaded(string path) => _resourcePathMap.ContainsKey(path); /// /// Adds a resource loader associated with a resource type. diff --git a/Voile/Source/UI/Widgets/Label.cs b/Voile/Source/UI/Widgets/Label.cs index ec65091..449e79f 100644 --- a/Voile/Source/UI/Widgets/Label.cs +++ b/Voile/Source/UI/Widgets/Label.cs @@ -1,6 +1,7 @@ using System.Numerics; using Voile.Input; using Voile.Rendering; +using Voile.Resources; namespace Voile.UI.Widgets; @@ -17,10 +18,26 @@ public class Label : Widget } } + /// + /// to use with this label. + /// + public FontSet FontSet { get; set; } = new(); + public Label(string text, ResourceRef fontOverride) { _text = text; - _fontOverride = fontOverride; + + FontSet.AddFont(fontOverride); + + MarkDirty(); + Update(); + } + + public Label(string text, FontSet fontSet) + { + _text = text; + + FontSet = fontSet; MarkDirty(); Update(); @@ -34,20 +51,34 @@ public class Label : Widget public override void Render(RenderSystem renderer, Style style) { // TODO: use style here. - if (!_fontOverride.HasValue) return; + renderer.SetTransform(GlobalPosition, Vector2.Zero); - renderer.DrawText(_fontOverride, _text, Color.White); + renderer.DrawText(_suitableFont, _text, Color.White); } protected override void OnUpdate() { - var font = _fontOverride.Value; + ResourceRef fontRef = ResourceRef.Empty(); + foreach (var c in _text) + { + if (FontSet.TryGetFontFor(c, out var fallbackFont)) + { + if (fallbackFont != fontRef) + { + fontRef = fallbackFont; + } + } + } + + _suitableFont = fontRef; + + var font = _suitableFont.Value; _textSize = font.Measure(_text); Size = _textSize; } - private ResourceRef _fontOverride = ResourceRef.Empty(); + private ResourceRef _suitableFont = ResourceRef.Empty(); private string _text = "Hello, World!"; private Rect _textSize = Rect.Zero;