diff --git a/Source/Engine/UI/GUI/Common/RichTextBox.Parsing.cs b/Source/Engine/UI/GUI/Common/RichTextBox.Parsing.cs index 53f1a9491..113e4a943 100644 --- a/Source/Engine/UI/GUI/Common/RichTextBox.Parsing.cs +++ b/Source/Engine/UI/GUI/Common/RichTextBox.Parsing.cs @@ -27,6 +27,16 @@ namespace FlaxEngine.GUI /// public Float2 Caret; + /// + /// Index of the current line start character. + /// + public int LineStartCharacterIndex; + + /// + /// Index of the current line start text block. + /// + public int LineStartTextBlockIndex; + /// /// Text styles stack (new tags push modified style and pop on tag end). /// @@ -86,6 +96,9 @@ namespace FlaxEngine.GUI { Control = this, Parser = _parser, + Caret = Float2.Zero, + LineStartCharacterIndex = 0, + LineStartTextBlockIndex = 0, StyleStack = _styleStack, }; @@ -155,15 +168,19 @@ namespace FlaxEngine.GUI return; for (int i = 0; i < lines.Length; i++) { - if (i != 0) - context.Caret.X = 0; ref var line = ref lines[i]; textBlock.Range = new TextRange { StartIndex = start + line.FirstCharIndex, EndIndex = start + line.LastCharIndex, }; - textBlock.Bounds = new Rectangle(context.Caret + line.Location, line.Size); + if (i != 0) + { + context.Caret.X = 0; + OnLineAdded(ref context, textBlock.Range.StartIndex - 1); + } + textBlock.Bounds = new Rectangle(context.Caret, line.Size); + textBlock.Bounds.X += line.Location.X; // Post-processing PostProcessBlock?.Invoke(ref context, ref textBlock); @@ -181,8 +198,41 @@ namespace FlaxEngine.GUI else { context.Caret.X = lastLine.Size.X; - context.Caret.Y += lastLine.Location.Y; } } + + private void OnLineAdded(ref ParsingContext context, int lineEnd) + { + // Calculate size of the line + var textBlocks = Utils.ExtractArrayFromList(_textBlocks); + var lineOrigin = textBlocks[context.LineStartTextBlockIndex].Bounds.Location; + var lineSize = Float2.Zero; + var lineAscender = 0.0f; + for(int i = context.LineStartTextBlockIndex; i < _textBlocks.Count; i++) + { + ref TextBlock textBlock = ref textBlocks[i]; + var textBlockSize = textBlock.Bounds.BottomRight - lineOrigin; + var textBlockFont = textBlock.Style.Font.GetFont(); + if (textBlockFont) + lineAscender = Mathf.Max(lineAscender, textBlockFont.Ascender); + lineSize = Float2.Max(lineSize, textBlockSize); + } + + // Organize text blocks to match the baseline of the line (use ascender) + for(int i = context.LineStartTextBlockIndex; i < _textBlocks.Count; i++) + { + ref TextBlock textBlock = ref textBlocks[i]; + var offset = lineSize.Y - textBlock.Bounds.Height; + var textBlockFont = textBlock.Style.Font.GetFont(); + if (textBlockFont) + offset = lineAscender - textBlockFont.Ascender; + textBlock.Bounds.Location.Y += offset; + } + + // Move to the next line + context.LineStartCharacterIndex = lineEnd + 1; + context.LineStartTextBlockIndex = _textBlocks.Count; + context.Caret.Y += lineSize.Y; + } } }