diff --git a/TestGame/TestGame.cs b/TestGame/TestGame.cs index e5f1e8b..fbda807 100644 --- a/TestGame/TestGame.cs +++ b/TestGame/TestGame.cs @@ -56,10 +56,10 @@ public class TestGame : Game Input.AddInputMapping("reload", new IInputAction[] { new KeyInputAction(KeyboardKey.R) }); _emitterId = _particleSystem.CreateEmitter(Renderer.WindowSize / 2, _fireEffect); - _fillContainer.AddChild(_marginContainer); - _marginContainer.AddChild(_container); + // _fillContainer.AddChild(_container); + // _marginContainer.AddChild(_container); - _uiSystem.AddElement(_fillContainer); + _uiSystem.AddElement(_container); } @@ -73,7 +73,7 @@ public class TestGame : Game if (Input.IsActionPressed("accept")) { - _container.AddChild(new RectangleWidget(new Rect(32.0f, 32.0f), MathUtils.RandomColor())); + _container.AddChild(new Label("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", _font)); } if (Input.IsActionPressed("cancel") && _container.Children.Count != 0) @@ -123,7 +123,7 @@ public class TestGame : Game private ResourceRef _sound; private ResourceRef _icon; - private FlexContainer _container = new(minimumSize: new Rect(64.0f, 64.0f), new()) + private FlexContainer _container = new(minimumSize: new Rect(256.0f, 256.0f), new()) { Anchor = Anchor.Center, Direction = FlexDirection.Column, @@ -133,6 +133,8 @@ public class TestGame : Game Gap = 8.0f }; + [NotNull] private Label _label; + private FillContainer _fillContainer = new(); private MarginContainer _marginContainer = new(new Margin(32.0f)) { diff --git a/Voile/Source/Resources/Font.cs b/Voile/Source/Resources/Font.cs index 70421bf..bd7253e 100644 --- a/Voile/Source/Resources/Font.cs +++ b/Voile/Source/Resources/Font.cs @@ -34,6 +34,8 @@ public class Font : Resource, IDisposable internal nint FacePtr; internal nint LibraryPtr; + internal List Codepoints => _glyphs.Keys.ToList(); + public Font(string path, byte[] buffer) : base(path) { Buffer = buffer; @@ -62,12 +64,38 @@ public class Font : Resource, IDisposable } } - public void Measure(string text) + /// + /// Measures a given string using the font metrics. + /// + /// Text to measure. + /// A with the sizes of a given text using this font. + public Rect Measure(string text) { + if (string.IsNullOrEmpty(text)) + return Rect.Zero; + + float totalWidth = 0; + float maxAscent = 0; + float maxDescent = 0; + foreach (char c in text) { + var glyph = GetGlyph(c); + totalWidth += glyph.Advance; + + float ascent = glyph.Bearing.Y; + float descent = glyph.Height - glyph.Bearing.Y; + + if (ascent > maxAscent) + maxAscent = ascent; + + if (descent > maxDescent) + maxDescent = descent; } + + float totalHeight = maxAscent + maxDescent; + return new Rect(totalWidth, totalHeight); } public Glyph GetGlyph(char character) @@ -80,31 +108,29 @@ public class Font : Resource, IDisposable return loaded; } - internal void GetGlyphBoundingBox(Glyph glyph) - { - - } - private unsafe Glyph LoadGlyph(char character) { var face = (FT_FaceRec_*)FacePtr; // TODO: for now, we're loading glyphs for metrics, but when implementing WebGPU rendering, we want to somehow render them to SDF or bitmap. - FT_Error error = FT_Load_Char(face, character, FT_LOAD_NO_BITMAP); + FT_Error error = FT_Set_Pixel_Sizes(face, 0, (uint)Size); + error = FT_Load_Char(face, character, FT_LOAD_NO_BITMAP); + if (error != 0) throw new Exception($"Failed to load glyph for '{character}'"); var glyph = face->glyph; var bitmap = glyph->bitmap; + var metrics = glyph->metrics; return new Glyph { - Width = bitmap.width, - Height = bitmap.rows, - Bearing = new Vector2(glyph->bitmap_left, glyph->bitmap_top), + Width = metrics.width >> 6, + Height = metrics.height >> 6, + Bearing = new Vector2(metrics.horiBearingX >> 6, metrics.horiBearingY >> 6), Advance = (int)glyph->advance.x >> 6, }; } - private Dictionary _glyphs = new(); + private Dictionary _glyphs = new(); } \ No newline at end of file diff --git a/Voile/Source/Resources/Loaders/ResourceLoader.cs b/Voile/Source/Resources/Loaders/ResourceLoader.cs index 833300f..baf2608 100644 --- a/Voile/Source/Resources/Loaders/ResourceLoader.cs +++ b/Voile/Source/Resources/Loaders/ResourceLoader.cs @@ -65,7 +65,12 @@ namespace Voile.Resources var resource = ResourceManager.LoadedResources[resourceGuid]; ResourceManager.RemoveResource(resourceGuid); - resource.Dispose(); + + if (resource is IDisposable disposable) + { + disposable.Dispose(); + } + return true; } diff --git a/Voile/Source/UI/Widgets/Label.cs b/Voile/Source/UI/Widgets/Label.cs index 0f2605a..ec65091 100644 --- a/Voile/Source/UI/Widgets/Label.cs +++ b/Voile/Source/UI/Widgets/Label.cs @@ -6,24 +6,29 @@ namespace Voile.UI.Widgets; public class Label : Widget { - public override Rect MinimumSize => throw new NotImplementedException(); + public override Rect MinimumSize => _textSize; - public string Text { get; set; } = "Hello World!"; - - public Label(string text) + public string Text { - Text = text; + get => _text; set + { + _text = value; + MarkDirty(); + } } public Label(string text, ResourceRef fontOverride) { - Text = text; + _text = text; _fontOverride = fontOverride; + + MarkDirty(); + Update(); } protected override void OnInput(UIInputContext action) { - throw new NotImplementedException(); + } public override void Render(RenderSystem renderer, Style style) @@ -31,13 +36,19 @@ public class Label : Widget // TODO: use style here. if (!_fontOverride.HasValue) return; renderer.SetTransform(GlobalPosition, Vector2.Zero); - renderer.DrawText(_fontOverride, Text, Color.White); + renderer.DrawText(_fontOverride, _text, Color.White); } protected override void OnUpdate() { - throw new NotImplementedException(); + var font = _fontOverride.Value; + _textSize = font.Measure(_text); + + Size = _textSize; } private ResourceRef _fontOverride = ResourceRef.Empty(); + + private string _text = "Hello, World!"; + private Rect _textSize = Rect.Zero; } \ No newline at end of file