From c9d16e16cc3a21d5dd6e90ac417900d83fed862f Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 29 Jan 2026 10:55:10 +0100 Subject: [PATCH] Fix Rich Text Box vertical alignment of the inlined images and whole contents #3502 ae4ae7a638e26a33985836b33362942c6e65a692 7ee2e66881e732f1df98a2aa205ca99c8f36635d --- .../UI/GUI/Common/RichTextBox.Parsing.cs | 66 ++++++++++++------- .../Engine/UI/GUI/Common/RichTextBox.Tags.cs | 21 +++--- 2 files changed, 53 insertions(+), 34 deletions(-) diff --git a/Source/Engine/UI/GUI/Common/RichTextBox.Parsing.cs b/Source/Engine/UI/GUI/Common/RichTextBox.Parsing.cs index 20ef1c401..bb6ee22a5 100644 --- a/Source/Engine/UI/GUI/Common/RichTextBox.Parsing.cs +++ b/Source/Engine/UI/GUI/Common/RichTextBox.Parsing.cs @@ -143,6 +143,40 @@ namespace FlaxEngine.GUI context.Caret.X = 0; OnLineAdded(ref context, _text.Length - 1); } + + // Organize lines vertically + if (_textBlocks.Count != 0) + { + var lastBlock = _textBlocks[_textBlocks.Count - 1]; + + // Get style (global or leftover from style stack or the last lime) + var verticalAlignments = _textStyle.Alignment; + if (context.StyleStack.Count > 1) + verticalAlignments = context.StyleStack.Peek().Alignment; + else if ((lastBlock.Style.Alignment & TextBlockStyle.Alignments.VerticalMask) != TextBlockStyle.Alignments.Baseline) + verticalAlignments = lastBlock.Style.Alignment; + + var totalSize = lastBlock.Bounds.BottomRight; + var sizeOffset = Size - totalSize; + var textBlocks = CollectionsMarshal.AsSpan(_textBlocks); + if ((verticalAlignments & TextBlockStyle.Alignments.Middle) == TextBlockStyle.Alignments.Middle) + { + sizeOffset.Y *= 0.5f; + for (int i = 0; i < _textBlocks.Count; i++) + { + ref TextBlock textBlock = ref textBlocks[i]; + textBlock.Bounds.Location.Y += sizeOffset.Y; + } + } + else if ((verticalAlignments & TextBlockStyle.Alignments.Bottom) == TextBlockStyle.Alignments.Bottom) + { + for (int i = 0; i < _textBlocks.Count; i++) + { + ref TextBlock textBlock = ref textBlocks[i]; + textBlock.Bounds.Location.Y += sizeOffset.Y; + } + } + } } /// @@ -239,14 +273,15 @@ namespace FlaxEngine.GUI } // Organize text blocks within line - var horizontalAlignments = TextBlockStyle.Alignments.Baseline; - var verticalAlignments = TextBlockStyle.Alignments.Baseline; + var lineAlignments = TextBlockStyle.Alignments.Baseline; for (int i = context.LineStartTextBlockIndex; i < _textBlocks.Count; i++) { ref TextBlock textBlock = ref textBlocks[i]; var vOffset = lineSize.Y - textBlock.Bounds.Height; - horizontalAlignments |= textBlock.Style.Alignment & TextBlockStyle.Alignments.HorizontalMask; - verticalAlignments |= textBlock.Style.Alignment & TextBlockStyle.Alignments.VerticalMask; + if (i == context.LineStartTextBlockIndex) + lineAlignments = textBlock.Style.Alignment; + else + lineAlignments &= textBlock.Style.Alignment; switch (textBlock.Style.Alignment & TextBlockStyle.Alignments.VerticalMask) { case TextBlockStyle.Alignments.Baseline: @@ -275,9 +310,9 @@ namespace FlaxEngine.GUI } } - // Organize blocks within whole container + // Organize whole line horizontally var sizeOffset = Size - lineSize; - if ((horizontalAlignments & TextBlockStyle.Alignments.Center) == TextBlockStyle.Alignments.Center) + if ((lineAlignments & TextBlockStyle.Alignments.Center) == TextBlockStyle.Alignments.Center) { sizeOffset.X *= 0.5f; for (int i = context.LineStartTextBlockIndex; i < _textBlocks.Count; i++) @@ -286,7 +321,7 @@ namespace FlaxEngine.GUI textBlock.Bounds.Location.X += sizeOffset.X; } } - else if ((horizontalAlignments & TextBlockStyle.Alignments.Right) == TextBlockStyle.Alignments.Right) + else if ((lineAlignments & TextBlockStyle.Alignments.Right) == TextBlockStyle.Alignments.Right) { for (int i = context.LineStartTextBlockIndex; i < _textBlocks.Count; i++) { @@ -294,23 +329,6 @@ namespace FlaxEngine.GUI textBlock.Bounds.Location.X += sizeOffset.X; } } - if ((verticalAlignments & TextBlockStyle.Alignments.Middle) == TextBlockStyle.Alignments.Middle) - { - sizeOffset.Y *= 0.5f; - for (int i = context.LineStartTextBlockIndex; i < _textBlocks.Count; i++) - { - ref TextBlock textBlock = ref textBlocks[i]; - textBlock.Bounds.Location.Y += sizeOffset.Y; - } - } - else if ((verticalAlignments & TextBlockStyle.Alignments.Bottom) == TextBlockStyle.Alignments.Bottom) - { - for (int i = context.LineStartTextBlockIndex; i < _textBlocks.Count; i++) - { - ref TextBlock textBlock = ref textBlocks[i]; - textBlock.Bounds.Location.Y += sizeOffset.Y; - } - } // Move to the next line context.LineStartCharacterIndex = lineEnd + 1; diff --git a/Source/Engine/UI/GUI/Common/RichTextBox.Tags.cs b/Source/Engine/UI/GUI/Common/RichTextBox.Tags.cs index b57fac47d..3bb99762f 100644 --- a/Source/Engine/UI/GUI/Common/RichTextBox.Tags.cs +++ b/Source/Engine/UI/GUI/Common/RichTextBox.Tags.cs @@ -175,7 +175,7 @@ namespace FlaxEngine.GUI // Setup size var font = imageBlock.Style.Font.GetFont(); if (font) - imageBlock.Bounds.Size = new Float2(font.Height); + imageBlock.Bounds.Size = new Float2(font.Ascender); var imageSize = image.Size; imageBlock.Bounds.Size.X *= imageSize.X / imageSize.Y; // Keep original aspect ratio bool hasWidth = TryParseNumberTag(ref tag, "width", imageBlock.Bounds.Width, out var width); @@ -215,16 +215,16 @@ namespace FlaxEngine.GUI switch (valign) { case "top": - style.Alignment = TextBlockStyle.Alignments.Top; + style.Alignment |= TextBlockStyle.Alignments.Top; break; case "bottom": - style.Alignment = TextBlockStyle.Alignments.Bottom; + style.Alignment |= TextBlockStyle.Alignments.Bottom; break; case "middle": - style.Alignment = TextBlockStyle.Alignments.Middle; + style.Alignment |= TextBlockStyle.Alignments.Middle; break; case "baseline": - style.Alignment = TextBlockStyle.Alignments.Baseline; + style.Alignment |= TextBlockStyle.Alignments.Baseline; break; } } @@ -243,17 +243,17 @@ namespace FlaxEngine.GUI var style = context.StyleStack.Peek(); if (tag.Attributes.TryGetValue(string.Empty, out var valign)) { - style.Alignment &= ~TextBlockStyle.Alignments.VerticalMask; + style.Alignment &= ~TextBlockStyle.Alignments.HorizontalMask; switch (valign) { case "left": - style.Alignment = TextBlockStyle.Alignments.Left; + style.Alignment |= TextBlockStyle.Alignments.Left; break; case "right": - style.Alignment = TextBlockStyle.Alignments.Right; + style.Alignment |= TextBlockStyle.Alignments.Right; break; case "center": - style.Alignment = TextBlockStyle.Alignments.Center; + style.Alignment |= TextBlockStyle.Alignments.Center; break; } } @@ -270,7 +270,8 @@ namespace FlaxEngine.GUI else { var style = context.StyleStack.Peek(); - style.Alignment = TextBlockStyle.Alignments.Center; + style.Alignment &= ~TextBlockStyle.Alignments.HorizontalMask; + style.Alignment |= TextBlockStyle.Alignments.Center; context.StyleStack.Push(style); } }