diff --git a/MLEM.Ui/Elements/Paragraph.cs b/MLEM.Ui/Elements/Paragraph.cs index c73fc33..fd9b10a 100644 --- a/MLEM.Ui/Elements/Paragraph.cs +++ b/MLEM.Ui/Elements/Paragraph.cs @@ -32,7 +32,13 @@ namespace MLEM.Ui.Elements { /// /// The tokenized version of the /// - public TokenizedString TokenizedText { get; private set; } + public TokenizedString TokenizedText { + get { + this.CheckTextChange(); + this.TokenizeIfNecessary(); + return this.tokenizedText; + } + } /// /// The color that the text will be rendered with /// @@ -53,14 +59,10 @@ namespace MLEM.Ui.Elements { /// public string Text { get { - var ret = this.GetTextCallback?.Invoke(this) ?? this.text; - this.CheckTextChange(ret); - return ret; - } - set { - this.text = value; - this.CheckTextChange(value); + this.CheckTextChange(); + return this.displayedText; } + set => this.explicitlySetText = value; } /// /// If this paragraph should automatically adjust its width based on the width of the text within it @@ -100,10 +102,11 @@ namespace MLEM.Ui.Elements { /// public override bool IsHidden => base.IsHidden || string.IsNullOrWhiteSpace(this.Text); - private string text; - private string lastText; + private string displayedText; + private string explicitlySetText; private StyleProp alignment; private StyleProp regularFont; + private TokenizedString tokenizedText; /// /// Creates a new paragraph with the given settings. @@ -127,16 +130,15 @@ namespace MLEM.Ui.Elements { /// protected override Vector2 CalcActualSize(RectangleF parentArea) { var size = base.CalcActualSize(parentArea); - this.ParseText(size); - var textSize = this.TokenizedText.GetArea(Vector2.Zero, this.TextScale * this.TextScaleMultiplier * this.Scale).Size; + this.AlignAndSplit(size); + var textSize = this.tokenizedText.GetArea(Vector2.Zero, this.TextScale * this.TextScaleMultiplier * this.Scale).Size; return new Vector2(this.AutoAdjustWidth ? textSize.X + this.ScaledPadding.Width : size.X, textSize.Y + this.ScaledPadding.Height); } /// public override void Update(GameTime time) { base.Update(time); - if (this.TokenizedText != null) - this.TokenizedText.Update(time); + this.TokenizedText?.Update(time); } /// @@ -157,47 +159,19 @@ namespace MLEM.Ui.Elements { this.Alignment = this.Alignment.OrStyle(style.TextAlignment); } - /// - /// Parses this paragraph's into . - /// Additionally, this method adds any elements for tokenized links in the text. - /// - /// The paragraph's default size - protected virtual void ParseText(Vector2 size) { - if (this.TokenizedText == null) { - // tokenize the text - this.TokenizedText = this.System.TextFormatter.Tokenize(this.RegularFont, this.Text, this.Alignment); - - // add links to the paragraph - this.RemoveChildren(c => c is Link); - foreach (var link in this.TokenizedText.Tokens.Where(t => t.AppliedCodes.Any(c => c is LinkCode))) - this.AddChild(new Link(Anchor.TopLeft, link, this.TextScale * this.TextScaleMultiplier)); - } - - var width = size.X - this.ScaledPadding.Width; - var scale = this.TextScale * this.TextScaleMultiplier * this.Scale; - if (this.TruncateIfLong) { - this.TokenizedText.Truncate(this.RegularFont, width, scale, this.Ellipsis, this.Alignment); - } else { - this.TokenizedText.Split(this.RegularFont, width, scale, this.Alignment); - } - } - - /// - /// A helper method that causes the to be reset. - /// Additionally, if this paragraph's area has changed enough to warrant it, or if it has any children. - /// - protected void SetTextDirty() { - this.TokenizedText = null; + private void SetTextDirty() { + this.tokenizedText = null; // only set our area dirty if our size changed as a result of this action if (!this.AreaDirty && !this.CalcActualSize(this.ParentArea).Equals(this.DisplayArea.Size, Element.Epsilon)) this.SetAreaDirty(); } - private void CheckTextChange(string newText) { - if (this.lastText == newText) + private void CheckTextChange() { + var newText = this.GetTextCallback?.Invoke(this) ?? this.explicitlySetText; + if (this.displayedText == newText) return; - var emptyChanged = string.IsNullOrWhiteSpace(this.lastText) != string.IsNullOrWhiteSpace(newText); - this.lastText = newText; + var emptyChanged = string.IsNullOrWhiteSpace(this.displayedText) != string.IsNullOrWhiteSpace(newText); + this.displayedText = newText; if (emptyChanged) this.SetAreaDirty(); this.SetTextDirty(); @@ -213,6 +187,30 @@ namespace MLEM.Ui.Elements { return 0; } + private void TokenizeIfNecessary() { + if (this.tokenizedText != null) + return; + + // tokenize the text + this.tokenizedText = this.System.TextFormatter.Tokenize(this.RegularFont, this.Text, this.Alignment); + + // add links to the paragraph + this.RemoveChildren(c => c is Link); + foreach (var link in this.tokenizedText.Tokens.Where(t => t.AppliedCodes.Any(c => c is LinkCode))) + this.AddChild(new Link(Anchor.TopLeft, link, this.TextScale * this.TextScaleMultiplier)); + } + + private void AlignAndSplit(Vector2 size) { + this.TokenizeIfNecessary(); + var width = size.X - this.ScaledPadding.Width; + var scale = this.TextScale * this.TextScaleMultiplier * this.Scale; + if (this.TruncateIfLong) { + this.tokenizedText.Truncate(this.RegularFont, width, scale, this.Ellipsis, this.Alignment); + } else { + this.tokenizedText.Split(this.RegularFont, width, scale, this.Alignment); + } + } + /// /// A delegate method used for ///