mirror of
https://gitlab.acidiclight.dev/sociallydistant/sociallydistant.git
synced 2025-01-22 09:31:47 -05:00
Add text alignment to TextWidget
This commit is contained in:
parent
d24ae2f680
commit
cfad59d0e0
11 changed files with 448 additions and 29 deletions
|
@ -13,6 +13,7 @@ public struct LayoutRect
|
|||
public float Bottom => Top + Height;
|
||||
|
||||
public Vector2 TopLeft => new Vector2(Left, Top);
|
||||
public Vector2 Size => new Vector2(Width, Height);
|
||||
|
||||
public LayoutRect(float left, float top, float width, float height)
|
||||
{
|
||||
|
|
8
src/AcidicGUI/Layout/TextAlignment.cs
Normal file
8
src/AcidicGUI/Layout/TextAlignment.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
namespace AcidicGUI.Layout;
|
||||
|
||||
public enum TextAlignment
|
||||
{
|
||||
Left,
|
||||
Center,
|
||||
Right
|
||||
}
|
|
@ -7,13 +7,19 @@ namespace AcidicGUI.Rendering;
|
|||
|
||||
public class GeometryHelper : IFontStashRenderer2
|
||||
{
|
||||
private readonly GuiMeshBuilder whiteMesh = new(null);
|
||||
private readonly GuiMeshBuilder whiteMesh;
|
||||
private readonly Dictionary<Texture2D, GuiMeshBuilder> meshes = new();
|
||||
private readonly GuiRenderer guiRenderer;
|
||||
private readonly bool desaturate;
|
||||
private readonly float opacity;
|
||||
|
||||
internal GeometryHelper(GuiRenderer guiRenderer)
|
||||
internal GeometryHelper(GuiRenderer guiRenderer, float opacity, bool desaturate)
|
||||
{
|
||||
this.opacity = opacity;
|
||||
this.desaturate = desaturate;
|
||||
this.guiRenderer = guiRenderer;
|
||||
|
||||
whiteMesh = new GuiMeshBuilder(null, opacity, desaturate);
|
||||
}
|
||||
|
||||
public GuiMesh ExportMesh()
|
||||
|
@ -39,7 +45,7 @@ public class GeometryHelper : IFontStashRenderer2
|
|||
|
||||
if (!meshes.TryGetValue(texture, out GuiMeshBuilder? builder))
|
||||
{
|
||||
builder = new GuiMeshBuilder(texture);
|
||||
builder = new GuiMeshBuilder(texture, opacity, desaturate);
|
||||
meshes.Add(texture, builder);
|
||||
}
|
||||
|
||||
|
@ -51,7 +57,7 @@ public class GeometryHelper : IFontStashRenderer2
|
|||
AddRoundedRectangle(rectangle, uniformRadius, uniformRadius, uniformRadius, uniformRadius, color, texture);
|
||||
}
|
||||
|
||||
private void AddQuad(LayoutRect rectangle, Color color, Texture2D? texture = null)
|
||||
public void AddQuad(LayoutRect rectangle, Color color, Texture2D? texture = null)
|
||||
{
|
||||
var mesh = GetMeshBuilder(texture);
|
||||
|
||||
|
|
|
@ -8,16 +8,19 @@ public sealed class GuiMeshBuilder
|
|||
private readonly List<VertexPositionColorTexture> vertices = new();
|
||||
private readonly List<int> indices = new();
|
||||
private readonly Texture2D? texture;
|
||||
private readonly float opacity;
|
||||
private readonly bool desaturate;
|
||||
|
||||
public GuiMeshBuilder(Texture2D? texture)
|
||||
public GuiMeshBuilder(Texture2D? texture, float opacity, bool desaturate)
|
||||
{
|
||||
this.texture = texture;
|
||||
this.opacity = opacity;
|
||||
this.desaturate = desaturate;
|
||||
}
|
||||
|
||||
public VertexPositionColorTexture this[int index]
|
||||
{
|
||||
get => vertices[index];
|
||||
set => vertices[index] = value;
|
||||
}
|
||||
|
||||
public GuiSubMesh ExportSubMesh()
|
||||
|
@ -28,6 +31,12 @@ public sealed class GuiMeshBuilder
|
|||
public int AddVertex(VertexPositionColorTexture vertex)
|
||||
{
|
||||
int index = vertices.Count;
|
||||
|
||||
vertex.Color.A = (byte)(vertex.Color.A * opacity);
|
||||
|
||||
if (desaturate)
|
||||
vertex.Color.A = (byte) (vertex.Color.A / 2);
|
||||
|
||||
vertices.Add(vertex);
|
||||
return index;
|
||||
}
|
||||
|
|
|
@ -11,11 +11,11 @@ public sealed class CanvasPanel : ContainerWidget
|
|||
LayoutRoot = this;
|
||||
}
|
||||
|
||||
protected override Vector2 GetContentSize()
|
||||
protected override Vector2 GetContentSize(Vector2 availableSize)
|
||||
{
|
||||
// Canvases do not change their size based on content. It's up to the parent to give
|
||||
// us our size.
|
||||
return Vector2.Zero;
|
||||
return availableSize;
|
||||
}
|
||||
|
||||
protected override void ArrangeChildren(IGuiContext context, LayoutRect availableSpace)
|
||||
|
@ -23,7 +23,7 @@ public sealed class CanvasPanel : ContainerWidget
|
|||
foreach (Widget child in Children)
|
||||
{
|
||||
var anchors = child.GetCustomProperties<CanvasAnchors>();
|
||||
var childSize = child.GetCachedContentSize();
|
||||
var childSize = child.GetCachedContentSize(availableSpace.Size);
|
||||
|
||||
var pivotOffset = childSize * anchors.Pivot;
|
||||
var pivotedPosition = anchors.AnchoredPosition - pivotOffset;
|
||||
|
|
|
@ -31,7 +31,7 @@ public class FlexPanel : ContainerWidget
|
|||
}
|
||||
}
|
||||
|
||||
protected override Vector2 GetContentSize()
|
||||
protected override Vector2 GetContentSize(Vector2 availableSize)
|
||||
{
|
||||
var result = Vector2.Zero;
|
||||
|
||||
|
@ -51,7 +51,7 @@ public class FlexPanel : ContainerWidget
|
|||
|
||||
foreach (Widget child in Children)
|
||||
{
|
||||
var childSize = child.GetCachedContentSize();
|
||||
var childSize = child.GetCachedContentSize(availableSize);
|
||||
|
||||
switch (direction)
|
||||
{
|
||||
|
@ -89,7 +89,7 @@ public class FlexPanel : ContainerWidget
|
|||
// Pass 1: Auto-sized elements
|
||||
foreach (Widget child in Children)
|
||||
{
|
||||
var childSize = child.GetCachedContentSize();
|
||||
var childSize = child.GetCachedContentSize(availableSpace.Size);
|
||||
var properties = child.GetCustomProperties<FlexPanelProperties>();
|
||||
|
||||
settingsObjects[i] = properties;
|
||||
|
|
|
@ -30,7 +30,7 @@ public sealed class StackPanel : ContainerWidget
|
|||
|
||||
public IOrderedCollection<Widget> ChildWidgets => Children;
|
||||
|
||||
protected override Vector2 GetContentSize()
|
||||
protected override Vector2 GetContentSize(Vector2 availableSize)
|
||||
{
|
||||
Vector2 result = Vector2.Zero;
|
||||
|
||||
|
@ -50,7 +50,7 @@ public sealed class StackPanel : ContainerWidget
|
|||
|
||||
foreach (Widget child in Children)
|
||||
{
|
||||
var childSize = child.GetCachedContentSize();
|
||||
var childSize = child.GetCachedContentSize(availableSize);
|
||||
|
||||
switch (direction)
|
||||
{
|
||||
|
@ -78,7 +78,7 @@ public sealed class StackPanel : ContainerWidget
|
|||
|
||||
foreach (Widget child in Children)
|
||||
{
|
||||
Vector2 childSize = child.GetCachedContentSize();
|
||||
Vector2 childSize = child.GetCachedContentSize(availableSpace.Size);
|
||||
|
||||
switch (direction)
|
||||
{
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
using System.Diagnostics.Metrics;
|
||||
using System.Formats.Tar;
|
||||
using System.Text;
|
||||
using System.Xml.XPath;
|
||||
using AcidicGUI.Layout;
|
||||
using AcidicGUI.Rendering;
|
||||
using AcidicGUI.TextRendering;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
@ -6,9 +11,46 @@ namespace AcidicGUI.Widgets;
|
|||
|
||||
public class TextWidget : Widget
|
||||
{
|
||||
private readonly List<TextElement> textElements = new();
|
||||
private readonly StringBuilder stringBuilder = new();
|
||||
|
||||
private FontInfo font;
|
||||
private string text = string.Empty;
|
||||
private bool useMarkup = true;
|
||||
private bool wordWrapping = false;
|
||||
private TextAlignment textAlignment;
|
||||
|
||||
public TextAlignment TextAlignment
|
||||
{
|
||||
get => textAlignment;
|
||||
set
|
||||
{
|
||||
textAlignment = value;
|
||||
InvalidateLayout();
|
||||
}
|
||||
}
|
||||
|
||||
public bool WordWrapping
|
||||
{
|
||||
get => wordWrapping;
|
||||
set
|
||||
{
|
||||
wordWrapping = value;
|
||||
InvalidateLayout();
|
||||
}
|
||||
}
|
||||
|
||||
public bool UseMarkup
|
||||
{
|
||||
get => useMarkup;
|
||||
set
|
||||
{
|
||||
useMarkup = value;
|
||||
InvalidateLayout();
|
||||
RebuildText();
|
||||
}
|
||||
}
|
||||
|
||||
public string Text
|
||||
{
|
||||
get => text;
|
||||
|
@ -16,6 +58,7 @@ public class TextWidget : Widget
|
|||
{
|
||||
text = value;
|
||||
InvalidateLayout();
|
||||
RebuildText();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -26,18 +69,301 @@ public class TextWidget : Widget
|
|||
{
|
||||
font = value;
|
||||
InvalidateLayout();
|
||||
InvalidateMeasurements();
|
||||
}
|
||||
}
|
||||
|
||||
protected override Vector2 GetContentSize()
|
||||
protected override Vector2 GetContentSize(Vector2 availableSize)
|
||||
{
|
||||
return font.GetFont(this).Measure(text);
|
||||
float wrapWidth = availableSize.X;
|
||||
|
||||
// Measure text elements
|
||||
MeasureElements();
|
||||
|
||||
Vector2 result = Vector2.Zero;
|
||||
float lineHeight = 0;
|
||||
float lineWidth = 0;
|
||||
|
||||
for (var i = 0; i < textElements.Count; i++)
|
||||
{
|
||||
var newline = textElements[i].IsNewLine;
|
||||
var measurement = textElements[i].MeasuredSize.GetValueOrDefault();
|
||||
var wrap = wordWrapping && (lineWidth + measurement.X > wrapWidth) && wrapWidth > 0;
|
||||
|
||||
if (newline || wrap)
|
||||
{
|
||||
result.X = Math.Max(result.X, lineWidth);
|
||||
result.Y += lineHeight;
|
||||
lineHeight = 0;
|
||||
lineWidth = 0;
|
||||
}
|
||||
|
||||
lineWidth += measurement.X;
|
||||
lineHeight = Math.Max(lineHeight, measurement.Y);
|
||||
}
|
||||
|
||||
result.Y += lineHeight;
|
||||
result.X = Math.Max(result.X, lineWidth);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
protected override void ArrangeChildren(IGuiContext context, LayoutRect availableSpace)
|
||||
{
|
||||
// Break words and figure out where lines start and end.
|
||||
var lines = BreakWords(availableSpace);
|
||||
|
||||
var y = availableSpace.Top;
|
||||
foreach ((int start, int end, float lineWidth) in lines)
|
||||
{
|
||||
float lineHeight = 0;
|
||||
float offset = 0;
|
||||
float widgetX = availableSpace.Left;
|
||||
|
||||
if (textAlignment == TextAlignment.Center)
|
||||
{
|
||||
widgetX += (availableSpace.Width - lineWidth) / 2;
|
||||
}
|
||||
else if (textAlignment == TextAlignment.Right)
|
||||
{
|
||||
widgetX += availableSpace.Width - lineWidth;
|
||||
}
|
||||
|
||||
for (var i = start; i < end; i++)
|
||||
{
|
||||
lineHeight = Math.Max(lineHeight, textElements[i].MeasuredSize!.Value.Y);
|
||||
float x = widgetX + offset;
|
||||
textElements[i].Position = new Vector2(x, y);
|
||||
offset += textElements[i].MeasuredSize!.Value.X;
|
||||
}
|
||||
|
||||
y += lineHeight;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void RebuildGeometry(GeometryHelper geometry)
|
||||
{
|
||||
var fontInstance = font.GetFont(this);
|
||||
foreach (TextElement element in textElements)
|
||||
{
|
||||
var fontInstance = (element.FontOverride ?? font).GetFont(this);
|
||||
|
||||
// TODO: Color from a property or the Visual Style.
|
||||
var color = (element.ColorOverride ?? Color.White);
|
||||
|
||||
if (element.MeasuredSize.HasValue && element.Highlight.A > 0)
|
||||
{
|
||||
var highlightRect = new LayoutRect(
|
||||
element.Position.X,
|
||||
element.Position.Y,
|
||||
element.MeasuredSize.Value.X,
|
||||
element.MeasuredSize.Value.Y
|
||||
);
|
||||
|
||||
geometry.AddQuad(highlightRect, element.Highlight);
|
||||
}
|
||||
|
||||
fontInstance.Draw(geometry, element.Position, color, element.Text);
|
||||
}
|
||||
}
|
||||
|
||||
private (int start, int end, float size)[] BreakWords(LayoutRect availableSpace)
|
||||
{
|
||||
var lines = new List<(int, int, float)>();
|
||||
int start = 0;
|
||||
float lineHeight = 0;
|
||||
Vector2 offset = Vector2.Zero;
|
||||
|
||||
for (var i = 0; i < textElements.Count; i++)
|
||||
{
|
||||
if (i == textElements.Count-1)
|
||||
{
|
||||
textElements[i].Text = textElements[i].Text.TrimEnd();
|
||||
textElements[i].MeasuredSize = (textElements[i].FontOverride ?? font).GetFont(this)
|
||||
.Measure(textElements[i].Text);
|
||||
}
|
||||
|
||||
var measurement = textElements[i].MeasuredSize.GetValueOrDefault();
|
||||
var isNewLine = textElements[i].IsNewLine;
|
||||
var wrap = wordWrapping && (offset.X + measurement.X > availableSpace.Width);
|
||||
|
||||
if (isNewLine || wrap)
|
||||
{
|
||||
if (i > 0)
|
||||
{
|
||||
offset.X -= textElements[i - 1].MeasuredSize!.Value.X;
|
||||
textElements[i - 1].Text = textElements[i - 1].Text.TrimEnd();
|
||||
textElements[i - 1].MeasuredSize = (textElements[i - 1].FontOverride ?? font).GetFont(this)
|
||||
.Measure(textElements[i - 1].Text);
|
||||
offset.X += textElements[i - 1].MeasuredSize!.Value.X;
|
||||
}
|
||||
|
||||
lines.Add((start, i, offset.X));
|
||||
start = i;
|
||||
|
||||
offset.X = 0;
|
||||
offset.Y += lineHeight;
|
||||
|
||||
lineHeight = measurement.Y;
|
||||
textElements[i].IsNewLine = true;
|
||||
}
|
||||
|
||||
offset.X += measurement.X;
|
||||
lineHeight = Math.Max(lineHeight, measurement.Y);
|
||||
}
|
||||
|
||||
lines.Add((start, textElements.Count, offset.X));
|
||||
start = textElements.Count;
|
||||
|
||||
fontInstance.Draw(geometry, ContentArea.TopLeft, Color.White, text);
|
||||
return lines.ToArray();
|
||||
}
|
||||
|
||||
private void RebuildText()
|
||||
{
|
||||
if (useMarkup)
|
||||
{
|
||||
RebuildTextWithMarkup();
|
||||
}
|
||||
else
|
||||
{
|
||||
RebuildTextWithoutMarkup();
|
||||
}
|
||||
}
|
||||
|
||||
private void RebuildTextWithMarkup()
|
||||
{
|
||||
// TODO: Do this.
|
||||
RebuildTextWithoutMarkup();
|
||||
}
|
||||
|
||||
private void RebuildTextWithoutMarkup()
|
||||
{
|
||||
var sourceStart = 0;
|
||||
|
||||
textElements.Clear();
|
||||
|
||||
ReadOnlySpan<char> chars = text.AsSpan();
|
||||
|
||||
for (var i = 0; i <= chars.Length; i++)
|
||||
{
|
||||
char? character = i < chars.Length ? chars[i] : null;
|
||||
|
||||
// End of text.
|
||||
if (!character.HasValue)
|
||||
{
|
||||
if (stringBuilder.Length > 0)
|
||||
{
|
||||
textElements.Add(new TextElement
|
||||
{
|
||||
Text = stringBuilder.ToString().TrimEnd(),
|
||||
SourceStart = sourceStart,
|
||||
SourceEnd = i
|
||||
});
|
||||
sourceStart = i;
|
||||
}
|
||||
|
||||
stringBuilder.Length = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (character.Value)
|
||||
{
|
||||
case '\r':
|
||||
continue;
|
||||
case '\n':
|
||||
{
|
||||
textElements.Add(new TextElement
|
||||
{
|
||||
Text = stringBuilder.ToString().TrimEnd(),
|
||||
IsNewLine = true,
|
||||
SourceStart = sourceStart,
|
||||
SourceEnd = i
|
||||
});
|
||||
|
||||
stringBuilder.Length = 0;
|
||||
sourceStart = i;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
stringBuilder.Append(character.Value);
|
||||
|
||||
if (char.IsWhiteSpace(character.Value))
|
||||
{
|
||||
textElements.Add(new TextElement
|
||||
{
|
||||
Text = stringBuilder.ToString(),
|
||||
SourceStart = sourceStart,
|
||||
SourceEnd = i
|
||||
});
|
||||
|
||||
sourceStart = i;
|
||||
stringBuilder.Length = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void InvalidateMeasurements()
|
||||
{
|
||||
for (var i = 0; i < textElements.Count; i++)
|
||||
{
|
||||
textElements[i].MeasuredSize = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void MeasureElements()
|
||||
{
|
||||
for (var i = 0; i < textElements.Count; i++)
|
||||
{
|
||||
if (textElements[i].MeasuredSize != null)
|
||||
continue;
|
||||
|
||||
var fontInstance = (textElements[i].FontOverride ?? font).GetFont(this);
|
||||
|
||||
textElements[i].MeasuredSize = fontInstance.Measure(textElements[i].Text);
|
||||
}
|
||||
}
|
||||
|
||||
public Vector2 GetPositionOfCharacter(int characterIndex)
|
||||
{
|
||||
if (characterIndex < 0 || characterIndex > text.Length)
|
||||
throw new ArgumentOutOfRangeException(nameof(characterIndex));
|
||||
|
||||
var i = 0;
|
||||
foreach (TextElement element in textElements)
|
||||
{
|
||||
if (characterIndex < element.SourceStart)
|
||||
break;
|
||||
|
||||
if (i == textElements.Count - 1 || characterIndex < element.SourceEnd)
|
||||
{
|
||||
if (i == element.SourceStart)
|
||||
return element.Position;
|
||||
|
||||
string textToMeasure =
|
||||
element.Text.Substring(0, Math.Max(element.Text.Length, i - element.SourceStart));
|
||||
|
||||
Vector2 measurement = (element.FontOverride ?? font).GetFont(this).Measure(textToMeasure);
|
||||
|
||||
return new Vector2(element.Position.X + measurement.X, element.Position.Y);
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
private class TextElement
|
||||
{
|
||||
public string Text;
|
||||
public Color? ColorOverride;
|
||||
public Color Highlight;
|
||||
public FontInfo? FontOverride;
|
||||
public Vector2 Position;
|
||||
public Vector2? MeasuredSize;
|
||||
public bool IsNewLine;
|
||||
public int SourceStart;
|
||||
public int SourceEnd;
|
||||
}
|
||||
}
|
|
@ -15,6 +15,7 @@ public partial class Widget
|
|||
private Padding margin;
|
||||
private Vector2 minimumSize;
|
||||
private Vector2 maximumSize;
|
||||
private Vector2 previousAvailableSize;
|
||||
|
||||
public Padding Margin
|
||||
{
|
||||
|
@ -108,7 +109,7 @@ public partial class Widget
|
|||
if (!layoutIsDirty)
|
||||
return;
|
||||
|
||||
var contentSize = GetCachedContentSize();
|
||||
var contentSize = GetCachedContentSize(availableSpace.Size);
|
||||
|
||||
var left = 0f;
|
||||
var top = 0f;
|
||||
|
@ -178,12 +179,20 @@ public partial class Widget
|
|||
layoutIsDirty = false;
|
||||
}
|
||||
|
||||
public Vector2 GetCachedContentSize()
|
||||
public Vector2 GetCachedContentSize(Vector2 availableSize)
|
||||
{
|
||||
if (MaximumSize.X > 0 && availableSize.X > MaximumSize.X)
|
||||
availableSize.X = MaximumSize.X;
|
||||
if (MaximumSize.Y > 0 && availableSize.Y > MaximumSize.Y)
|
||||
availableSize.Y = MaximumSize.Y;
|
||||
|
||||
if (previousAvailableSize != availableSize)
|
||||
cachedContentSize = null;
|
||||
|
||||
if (cachedContentSize != null)
|
||||
return cachedContentSize.Value;
|
||||
|
||||
Vector2 contentSize = GetContentSize();
|
||||
|
||||
Vector2 contentSize = GetContentSize(availableSize);
|
||||
|
||||
contentSize.X += margin.Horizontal;
|
||||
contentSize.Y += margin.Vertical;
|
||||
|
@ -216,13 +225,13 @@ public partial class Widget
|
|||
child.UpdateLayout(context, availableSpace);
|
||||
}
|
||||
|
||||
protected virtual Vector2 GetContentSize()
|
||||
protected virtual Vector2 GetContentSize(Vector2 availableSize)
|
||||
{
|
||||
var result = Vector2.Zero;
|
||||
|
||||
foreach (Widget child in children)
|
||||
{
|
||||
Vector2 childSize = child.GetCachedContentSize();
|
||||
Vector2 childSize = child.GetCachedContentSize(availableSize);
|
||||
|
||||
result.X = MathF.Max(result.X, childSize.X);
|
||||
result.Y = MathF.Max(result.Y, childSize.Y);
|
||||
|
|
|
@ -2,6 +2,7 @@ using AcidicGUI.CustomProperties;
|
|||
using AcidicGUI.Rendering;
|
||||
using AcidicGUI.TextRendering;
|
||||
using AcidicGUI.VisualStyles;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace AcidicGUI.Widgets;
|
||||
|
||||
|
@ -14,7 +15,53 @@ public abstract partial class Widget : IFontProvider
|
|||
private Widget? parent;
|
||||
private GuiManager? guiManager;
|
||||
private GuiMesh? cachedGeometry;
|
||||
private float renderOpacity = 1;
|
||||
private bool enabled = true;
|
||||
|
||||
public bool Enabled
|
||||
{
|
||||
get => enabled;
|
||||
set
|
||||
{
|
||||
enabled = value;
|
||||
InvalidateGeometry(true);
|
||||
}
|
||||
}
|
||||
|
||||
public float RenderOpacity
|
||||
{
|
||||
get => renderOpacity;
|
||||
set
|
||||
{
|
||||
renderOpacity = MathHelper.Clamp(value, 0, 1);
|
||||
this.InvalidateGeometry(true);
|
||||
}
|
||||
}
|
||||
|
||||
public bool HierarchyEnabled
|
||||
{
|
||||
get
|
||||
{
|
||||
// TODO: Caching caching caching!
|
||||
if (Parent == null)
|
||||
return enabled;
|
||||
|
||||
return Parent.HierarchyEnabled && enabled;
|
||||
}
|
||||
}
|
||||
|
||||
public float ComputedOpacity
|
||||
{
|
||||
get
|
||||
{
|
||||
// TODO: Caching caching caching!
|
||||
if (Parent == null)
|
||||
return renderOpacity;
|
||||
|
||||
return Parent.ComputedOpacity * renderOpacity;
|
||||
}
|
||||
}
|
||||
|
||||
public IVisualStyle? VisualStyleOverride
|
||||
{
|
||||
get => visualStyleOverride;
|
||||
|
@ -60,6 +107,17 @@ public abstract partial class Widget : IFontProvider
|
|||
this.children = new WidgetCollection(this);
|
||||
}
|
||||
|
||||
public void InvalidateGeometry(bool invalidateChildren = false)
|
||||
{
|
||||
cachedGeometry = null;
|
||||
|
||||
if (invalidateChildren)
|
||||
{
|
||||
foreach (Widget child in children)
|
||||
child.InvalidateGeometry(invalidateChildren);
|
||||
}
|
||||
}
|
||||
|
||||
public IVisualStyle GetVisualStyle()
|
||||
{
|
||||
if (visualStyleOverride != null)
|
||||
|
@ -80,7 +138,7 @@ public abstract partial class Widget : IFontProvider
|
|||
{
|
||||
if (cachedGeometry == null)
|
||||
{
|
||||
var geometryHelper = new GeometryHelper(renderer);
|
||||
var geometryHelper = new GeometryHelper(renderer, ComputedOpacity, !HierarchyEnabled);
|
||||
RebuildGeometry(geometryHelper);
|
||||
cachedGeometry = geometryHelper.ExportMesh();
|
||||
}
|
||||
|
|
|
@ -34,10 +34,12 @@ public sealed class GuiService :
|
|||
|
||||
test.ChildWidgets.Add(textWidget);
|
||||
|
||||
textWidget.HorizontalAlignment = HorizontalAlignment.Left;
|
||||
textWidget.VerticalAlignment = VerticalAlignment.Top;
|
||||
test.HorizontalAlignment = HorizontalAlignment.Center;
|
||||
test.VerticalAlignment = VerticalAlignment.Middle;
|
||||
textWidget.Text = "Ritchie is the cutest human in existence.";
|
||||
|
||||
test.MaximumSize = new Vector2(100, 0);
|
||||
textWidget.WordWrapping = true;
|
||||
textWidget.TextAlignment = TextAlignment.Center;
|
||||
test.Spacing = 6;
|
||||
test.Direction = Direction.Vertical;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue