diff --git a/Source/Engine/UI/GUI/Common/RichTextBox.Parsing.cs b/Source/Engine/UI/GUI/Common/RichTextBox.Parsing.cs index f6f7e0115..e51ca7da4 100644 --- a/Source/Engine/UI/GUI/Common/RichTextBox.Parsing.cs +++ b/Source/Engine/UI/GUI/Common/RichTextBox.Parsing.cs @@ -41,6 +41,19 @@ namespace FlaxEngine.GUI /// Text styles stack (new tags push modified style and pop on tag end). /// public Stack StyleStack; + + /// + /// Adds the text block to the control + /// + /// The text block to add. + public void AddTextBlock(ref TextBlock textBlock) + { + // Post-processing + Control.PostProcessBlock?.Invoke(ref this, ref textBlock); + + // Add to the text blocks + Control._textBlocks.Add(textBlock); + } } /// @@ -76,6 +89,7 @@ namespace FlaxEngine.GUI { "b", ProcessBold }, { "i", ProcessItalic }, { "size", ProcessSize }, + { "img", ProcessImage }, }; private HtmlParser _parser = new HtmlParser(); @@ -184,11 +198,7 @@ namespace FlaxEngine.GUI textBlock.Bounds = new Rectangle(context.Caret, line.Size); textBlock.Bounds.X += line.Location.X; - // Post-processing - PostProcessBlock?.Invoke(ref context, ref textBlock); - - // Add to the text blocks - _textBlocks.Add(textBlock); + context.AddTextBlock(ref textBlock); } // Update the caret location diff --git a/Source/Engine/UI/GUI/Common/RichTextBox.Tags.cs b/Source/Engine/UI/GUI/Common/RichTextBox.Tags.cs index 8985cc781..69d150637 100644 --- a/Source/Engine/UI/GUI/Common/RichTextBox.Tags.cs +++ b/Source/Engine/UI/GUI/Common/RichTextBox.Tags.cs @@ -47,13 +47,9 @@ namespace FlaxEngine.GUI if (tag.Attributes.TryGetValue(string.Empty, out var alphaText)) { if (alphaText.Length == 3 && alphaText[0] == '#') - { style.Color.A = ((StringUtils.HexDigit(alphaText[1]) << 4) + StringUtils.HexDigit(alphaText[2])) / 255.0f; - } else if (alphaText.Length > 1 && alphaText[alphaText.Length - 1] == '%') - { style.Color.A = float.Parse(alphaText.Substring(0, alphaText.Length - 1)) / 100.0f; - } } context.StyleStack.Push(style); } @@ -93,16 +89,9 @@ namespace FlaxEngine.GUI style.Font = new FontReference(style.Font); if (tag.Attributes.TryGetValue(string.Empty, out var fontName)) { - var ids = Content.GetAllAssetsByType(typeof(FontAsset)); - foreach (var id in ids) - { - if (Content.GetAssetInfo(id, out var info) && string.Equals(fontName, System.IO.Path.GetFileNameWithoutExtension(info.Path), System.StringComparison.OrdinalIgnoreCase)) - { - var font = Content.LoadAsync(id); - if (font != null) - style.Font.Font = font; - } - } + var font = (FontAsset)FindAsset(fontName, typeof(FontAsset)); + if (font) + style.Font.Font = font; } if (tag.Attributes.TryGetValue("size", out var sizeText) && int.TryParse(sizeText, out var size)) style.Font.Size = size; @@ -148,15 +137,80 @@ namespace FlaxEngine.GUI { var style = context.StyleStack.Peek(); style.Font = new FontReference(style.Font); - if (tag.Attributes.TryGetValue(string.Empty, out var sizeText)) - { - if (int.TryParse(sizeText, out var sizeInt)) - style.Font.Size = sizeInt; - if (sizeText.Length > 1 && sizeText[sizeText.Length - 1] == '%') - style.Font.Size = (int)(style.Font.Size * float.Parse(sizeText.Substring(0, sizeText.Length - 1)) / 100.0f); - } + TryParseNumberTag(ref tag, string.Empty, style.Font.Size, out var size); + style.Font.Size = (int)size; context.StyleStack.Push(style); } } + + private static void ProcessImage(ref ParsingContext context, ref HtmlTag tag) + { + // Get image brush + IBrush image = null; + if (tag.Attributes.TryGetValue(string.Empty, out var srcText) || tag.Attributes.TryGetValue("src", out srcText)) + { + if (!context.Control.Images.TryGetValue(srcText, out image)) + { + var tex = (Texture)FindAsset(srcText, typeof(Texture)); + if (tex) + image = new TextureBrush(tex); + } + } + if (image == null) + return; + + // Create image block + var imageBlock = new TextBlock + { + Range = new TextRange + { + StartIndex = tag.StartPosition, + EndIndex = tag.StartPosition, + }, + Style = context.StyleStack.Peek(), + Bounds = new Rectangle(context.Caret, new Float2(64.0f)), + }; + imageBlock.Style.BackgroundBrush = image; + + // Setup size + var font = imageBlock.Style.Font.GetFont(); + if (font) + imageBlock.Bounds.Size = new Float2(font.Height); + imageBlock.Bounds.Size.X *= image.Size.X / image.Size.Y; // Keep aspect ration + TryParseNumberTag(ref tag, "width", imageBlock.Bounds.Width, out var width); + imageBlock.Bounds.Width = width; + TryParseNumberTag(ref tag, "height", imageBlock.Bounds.Height, out var height); + imageBlock.Bounds.Height = height; + TryParseNumberTag(ref tag, "scale", 1.0f, out var scale); + imageBlock.Bounds.Size *= scale; + + context.AddTextBlock(ref imageBlock); + context.Caret.X += imageBlock.Bounds.Size.X; + } + + private static Asset FindAsset(string name, System.Type type) + { + var ids = Content.GetAllAssetsByType(type); + foreach (var id in ids) + { + if (Content.GetAssetInfo(id, out var info) && string.Equals(name, System.IO.Path.GetFileNameWithoutExtension(info.Path), System.StringComparison.OrdinalIgnoreCase)) + { + return Content.LoadAsync(id, type); + } + } + return null; + } + + private static void TryParseNumberTag(ref HtmlTag tag, string name, float input, out float output) + { + output = input; + if (tag.Attributes.TryGetValue(name, out var text)) + { + if (float.TryParse(text, out var width)) + output = width; + if (text.Length > 1 && text[text.Length - 1] == '%') + output = input * float.Parse(text.Substring(0, text.Length - 1)) / 100.0f; + } + } } } diff --git a/Source/Engine/UI/GUI/Common/RichTextBox.cs b/Source/Engine/UI/GUI/Common/RichTextBox.cs index 6d066b0f7..af0a45eec 100644 --- a/Source/Engine/UI/GUI/Common/RichTextBox.cs +++ b/Source/Engine/UI/GUI/Common/RichTextBox.cs @@ -31,6 +31,12 @@ namespace FlaxEngine.GUI [EditorOrder(30)] public Dictionary Styles = new Dictionary(); + /// + /// The collection of custom images/sprites that can be inlined in text (named). + /// + [EditorOrder(40)] + public Dictionary Images = new Dictionary(); + /// /// Initializes a new instance of the class. /// diff --git a/Source/Engine/UI/GUI/Common/RichTextBoxBase.cs b/Source/Engine/UI/GUI/Common/RichTextBoxBase.cs index c56f0be30..eb83d2a55 100644 --- a/Source/Engine/UI/GUI/Common/RichTextBoxBase.cs +++ b/Source/Engine/UI/GUI/Common/RichTextBoxBase.cs @@ -267,11 +267,17 @@ namespace FlaxEngine.GUI } } - // Draw selection background + // Draw background for (int i = firstTextBlock; i < endTextBlock; i++) { ref TextBlock textBlock = ref textBlocks[i]; + // Background + if (textBlock.Style.BackgroundBrush != null) + { + textBlock.Style.BackgroundBrush.Draw(textBlock.Bounds, textBlock.Style.Color); + } + // Pick font var font = textBlock.Style.Font.GetFont(); if (!font) diff --git a/Source/Engine/UI/GUI/TextBlockStyle.cs b/Source/Engine/UI/GUI/TextBlockStyle.cs index e2fa7df0c..659af37bf 100644 --- a/Source/Engine/UI/GUI/TextBlockStyle.cs +++ b/Source/Engine/UI/GUI/TextBlockStyle.cs @@ -37,6 +37,12 @@ namespace FlaxEngine.GUI [EditorOrder(40), Tooltip("The text shadow offset from the text location. Set to zero to disable shadow drawing.")] public Float2 ShadowOffset; + /// + /// The background brush for the text range. + /// + [EditorOrder(45), Tooltip("The background brush for the text range.")] + public IBrush BackgroundBrush; + /// /// The background brush for the selected text range. ///