diff --git a/FGEGraphics/ClientSystem/ViewUI2D.cs b/FGEGraphics/ClientSystem/ViewUI2D.cs
index be5b2b2c..bec939d8 100644
--- a/FGEGraphics/ClientSystem/ViewUI2D.cs
+++ b/FGEGraphics/ClientSystem/ViewUI2D.cs
@@ -20,7 +20,6 @@
using FGEGraphics.GraphicsHelpers.FontSets;
using FGEGraphics.GraphicsHelpers.Shaders;
using FGEGraphics.UISystem;
-using FreneticUtilities.FreneticExtensions;
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL4;
diff --git a/FGEGraphics/GraphicsHelpers/FontSets/FontSetEngine.cs b/FGEGraphics/GraphicsHelpers/FontSets/FontSetEngine.cs
index ecee5dad..ede62c49 100644
--- a/FGEGraphics/GraphicsHelpers/FontSets/FontSetEngine.cs
+++ b/FGEGraphics/GraphicsHelpers/FontSets/FontSetEngine.cs
@@ -16,6 +16,7 @@
using FGECore.UtilitySystems;
using FGEGraphics.GraphicsHelpers.Shaders;
using OpenTK.Mathematics;
+using FGECore.CoreSystems;
namespace FGEGraphics.GraphicsHelpers.FontSets;
@@ -44,6 +45,9 @@ public class FontSetEngine(GLFontEngine fontEngine)
/// A list of all currently loaded font sets.
public Dictionary<(string, int), FontSet> Fonts = [];
+ /// A cache of font names and sizes to their closest font set.
+ public Dictionary<(string, int), FontSet> ApproximateFonts = [];
+
/// Helper function to get a language data.
public Func GetLanguageHelper;
@@ -123,4 +127,24 @@ public FontSet GetFont(string fontname, int fontsize)
Fonts.Add((toret.Name, fontsize), toret);
return toret;
}
+
+ /// Returns the closest from the given font name and size.
+ /// The name of the font.
+ /// The size of the font.
+ public FontSet GetApproximateFont(string fontname, int fontsize)
+ {
+ if (ApproximateFonts.TryGetValue((fontname, fontsize), out FontSet found))
+ {
+ return found;
+ }
+ IEnumerable> fontVariants = Fonts.Where(pair => pair.Value.Name == fontname);
+ if (!fontVariants.Any())
+ {
+ return null;
+ }
+ IEnumerable> fittingFonts = fontVariants.Where(pair => pair.Key.Item2 <= fontsize);
+ ((string, int) _, FontSet font) = fittingFonts.Any() ? fittingFonts.MinBy(pair => fontsize - pair.Key.Item2) : fontVariants.MinBy(pair => Math.Abs(fontsize - pair.Key.Item2));
+ ApproximateFonts[(fontname, fontsize)] = font;
+ return font;
+ }
}
diff --git a/FGEGraphics/UISystem/UI3DSubEngine.cs b/FGEGraphics/UISystem/UI3DSubEngine.cs
index 2f3c29e0..c5760216 100644
--- a/FGEGraphics/UISystem/UI3DSubEngine.cs
+++ b/FGEGraphics/UISystem/UI3DSubEngine.cs
@@ -33,7 +33,7 @@ public class UI3DSubEngine : UIElement
/// Constructs a new 3D sub-engine.
/// The layout of the element.
/// Whether to have an alpha background.
- public UI3DSubEngine(UILayout layout, bool alphaBack) : base(UIStyling.Empty, layout)
+ public UI3DSubEngine(UILayout layout, bool alphaBack) : base(null, layout)
{
SubEngine = new GameEngine3D
{
diff --git a/FGEGraphics/UISystem/UIBox.cs b/FGEGraphics/UISystem/UIBox.cs
index e1a4d71d..2394bf59 100644
--- a/FGEGraphics/UISystem/UIBox.cs
+++ b/FGEGraphics/UISystem/UIBox.cs
@@ -40,37 +40,37 @@ public class UIBox : UIElement
/// Text to display inside the box.
public UIBox(UIStyling styling, UILayout layout, string text = null) : base(styling, layout)
{
- AddChild(Label = new UILabel(text, styling.Bind(this), new UILayout().SetAnchor(UIAnchor.CENTER)) { IsEnabled = false });
+ AddChild(Label = new UILabel(text, styling?.Bind(this), new UILayout().SetAnchor(UIAnchor.CENTER)) { IsEnabled = false });
}
///
public override void Render(double delta, UIStyle style)
{
Vector3 rotation = new(-0.5f, -0.5f, Rotation);
- bool any = style.DropShadowLength > 0 || style.BorderColor.A > 0 || style.BaseColor.A > 0;
+ bool any = style.ShadowSize > 0 || style.Stroke.A > 0 || style.Fill.A > 0;
if (any)
{
View.Engine.Textures.White.Bind();
- if (style.DropShadowLength > 0)
+ if (style.ShadowSize > 0)
{
Renderer2D.SetColor(new Color4F(0, 0, 0, 0.5f));
- View.Rendering.RenderRectangle(View.UIContext, X, Y, X + Width + style.DropShadowLength, Y + Height + style.DropShadowLength, rotation);
+ View.Rendering.RenderRectangle(View.UIContext, X, Y, X + Width + style.ShadowSize, Y + Height + style.ShadowSize, rotation);
}
- if (style.BorderColor.A > 0 && style.BorderThickness > 0)
+ if (style.Stroke.A > 0 && style.StrokeWeight > 0)
{
- Renderer2D.SetColor(style.BorderColor);
+ Renderer2D.SetColor(style.Stroke);
View.Rendering.RenderRectangle(View.UIContext, X, Y, X + Width, Y + Height, rotation);
}
- if (style.BaseColor.A > 0)
+ if (style.Fill.A > 0)
{
- Renderer2D.SetColor(style.BaseColor);
- View.Rendering.RenderRectangle(View.UIContext, X + style.BorderThickness, Y + style.BorderThickness, X + Width - style.BorderThickness, Y + Height - style.BorderThickness, rotation);
+ Renderer2D.SetColor(style.Fill);
+ View.Rendering.RenderRectangle(View.UIContext, X + style.StrokeWeight, Y + style.StrokeWeight, X + Width - style.StrokeWeight, Y + Height - style.StrokeWeight, rotation);
}
Renderer2D.SetColor(Color4F.White);
}
- if (style.BaseTexture is not null)
+ if (style.Texture is not null)
{
- style.BaseTexture.Bind();
+ style.Texture.Bind();
float ymin = Flip ? Y + Height : Y;
float ymax = Flip ? Y : Y + Height;
View.Rendering.RenderRectangle(View.UIContext, X, ymin, X + Width, ymax, rotation);
diff --git a/FGEGraphics/UISystem/UIDropdown.cs b/FGEGraphics/UISystem/UIDropdown.cs
index 7f3660dc..c19a20ab 100644
--- a/FGEGraphics/UISystem/UIDropdown.cs
+++ b/FGEGraphics/UISystem/UIDropdown.cs
@@ -76,8 +76,8 @@ public struct InternalData()
public UIDropdown(int boxPadding, int listSpacing, UIStyling buttonStyling, UIStyling boxStyling, UILayout layout, string text = null, UIElement layer = null) : base(buttonStyling, layout)
{
PlaceholderInfo = text ?? "null";
- AddChild(Button = new UIBox(buttonStyling, layout.AtOrigin(), text) { OnClick = Open });
- Box = new UIBox(boxStyling, layout.AtOrigin());
+ AddChild(Button = new UIBox(buttonStyling, layout.Container(), text) { OnClick = Open });
+ Box = new UIBox(boxStyling, layout.Container());
Box.AddChild(Entries = new UIListGroup(listSpacing, new UILayout().SetAnchor(UIAnchor.TOP_CENTER).SetPosition(0, boxPadding)));
Box.Layout.SetHeight(() => Entries.Layout.Height + boxPadding * 2);
Internal.Layer = layer ?? this;
@@ -133,7 +133,7 @@ public void DeselectChoice()
public void AddChoice(UIElement choice, Func label)
{
// TODO: configurable appearance
- UIStyle containerStyle = Entries.Items.Count % 2 == 0 ? UIStyle.Empty : new UIStyle { BaseColor = new(0, 0, 0, 0.25f) };
+ UIStyling containerStyle = Entries.Items.Count % 2 == 0 ? null : new UIStyling { Fill = new Color4F(0, 0, 0, 0.25f) };
UIBox container = new(containerStyle, new UILayout().SetSize(() => Box.Width, () => choice.Height));
choice.Layout.SetAnchor(UIAnchor.TOP_CENTER);
container.AddChild(choice);
diff --git a/FGEGraphics/UISystem/UIElement.cs b/FGEGraphics/UISystem/UIElement.cs
index b60b6871..35fe5382 100644
--- a/FGEGraphics/UISystem/UIElement.cs
+++ b/FGEGraphics/UISystem/UIElement.cs
@@ -33,7 +33,7 @@ public abstract class UIElement
/// The parent element, null if this element is the root or hasn't been added as a child.
public UIElement Parent;
- /// Gets the UI view this element is attached to.
+ /// The UI view this element is attached to.
public ViewUI2D View;
/// Styling logic for this element.
@@ -99,7 +99,7 @@ public abstract class UIElement
public object Tag = null;
/// Whether this element should render itself. If false, may be called manually.
- public bool RenderSelf = true;
+ public UIRenderMode RenderMode = UIRenderMode.FULL;
/// The debug name of this element.
public virtual string Name { get; set; } = null;
@@ -173,9 +173,6 @@ public struct ElementInternalData()
/// The current style of this element.
public UIStyle Style = UIStyle.Empty;
-
- /// Styles registered on this element.
- public HashSet Styles = [];
}
/// Data internal to a instance.
@@ -373,7 +370,7 @@ public void SetStyle(UIStyle style)
{
return;
}
- ElementInternal.Styles.Add(style);
+ ElementInternal.Style = style;
StyleChanged(previousStyle, ElementInternal.Style = style);
OnStyleChange?.Invoke(previousStyle, style);
}
@@ -381,7 +378,7 @@ public void SetStyle(UIStyle style)
/// If a is present, attempts to update the current style.
public void UpdateStyle()
{
- SetStyle(Styling.Get(this));
+ SetStyle(Styling?.Get(this) ?? UIStyle.Empty);
}
///
@@ -637,13 +634,19 @@ public virtual void Render(double delta, UIStyle style)
public virtual void RenderAll(double delta)
{
GraphicsUtil.CheckError("UIElement - PreRender");
- Render(delta);
+ if (RenderMode == UIRenderMode.FULL)
+ {
+ Render(delta);
+ }
GraphicsUtil.CheckError("UIElement - PostRenderSelf", this);
- foreach (UIElement child in ElementInternal.Children)
+ if (RenderMode != UIRenderMode.NONE)
{
- if (child.IsValid && child.RenderSelf)
+ foreach (UIElement child in ElementInternal.Children)
{
- child.RenderAll(delta);
+ if (child.IsValid)
+ {
+ child.RenderAll(delta);
+ }
}
}
GraphicsUtil.CheckError("UIElement - PostRenderAll", this);
@@ -791,11 +794,11 @@ public List GetBaseDebugInfo(string baseColor, bool detailed)
{
info.Add(transforms);
info.Add($"^7Enabled: ^{(IsEnabled ? "2" : "1")}{IsEnabled}^&, ^7Hovered: ^{(IsHovered ? "2" : "1")}{IsHovered}^&, ^7Pressed: ^{(IsPressed ? "2" : "1")}{IsPressed}^&, ^7Selected: ^{(IsFocused ? "2" : "1")}{IsFocused}");
- if (ElementInternal.Styles.Count > 0)
+ /*if (ElementInternal.Styles.Count > 0)
{
List styleNames = [.. ElementInternal.Styles.Select(style => style.Name is not null ? $"^{(style == ElementInternal.Style ? "3" : "7")}{style.Name}" : "^1unnamed")];
info.Add($"^7Styles: {string.Join("^&, ", styleNames)}");
- }
+ }*/
}
return info;
}
diff --git a/FGEGraphics/UISystem/UIGroup.cs b/FGEGraphics/UISystem/UIGroup.cs
index b797f0a8..450ca0df 100644
--- a/FGEGraphics/UISystem/UIGroup.cs
+++ b/FGEGraphics/UISystem/UIGroup.cs
@@ -22,7 +22,7 @@ public class UIGroup : UIElement
/// Constructs a new group.
/// The layout of the element.
- public UIGroup(UILayout layout) : base(UIStyling.Empty, layout)
+ public UIGroup(UILayout layout) : base(null, layout)
{
IsEnabled = false;
ScaleSize = false;
diff --git a/FGEGraphics/UISystem/UIImage.cs b/FGEGraphics/UISystem/UIImage.cs
index e568f0f4..8c705a7c 100644
--- a/FGEGraphics/UISystem/UIImage.cs
+++ b/FGEGraphics/UISystem/UIImage.cs
@@ -23,7 +23,7 @@ namespace FGEGraphics.UISystem;
/// Constructs an image.
/// The image to display.
/// The layout of the element.
-public class UIImage(Texture image, UILayout layout) : UIElement(UIStyling.Empty, layout)
+public class UIImage(Texture image, UILayout layout) : UIElement(null, layout)
{
///
public override string Name => $"Image \"{Image.Name}\"";
diff --git a/FGEGraphics/UISystem/UIInputBox.cs b/FGEGraphics/UISystem/UIInputBox.cs
index 48ce2018..9e268962 100644
--- a/FGEGraphics/UISystem/UIInputBox.cs
+++ b/FGEGraphics/UISystem/UIInputBox.cs
@@ -32,7 +32,7 @@ namespace FGEGraphics.UISystem;
/// The font to use.
/// The position of the element.
// TODO: Remove
-public class UIInputBox(string text, string info, FontSet fonts, UILayout pos) : UIElement(UIStyling.Empty, pos.Height <= 0 ? pos.SetHeight(fonts.Height) : pos)
+public class UIInputBox(string text, string info, FontSet fonts, UILayout pos) : UIElement(null, pos.Height <= 0 ? pos.SetHeight(fonts.Height) : pos)
{
/// The current text in this input box.
public string Text = text;
diff --git a/FGEGraphics/UISystem/UIInputLabel.cs b/FGEGraphics/UISystem/UIInputLabel.cs
index 4bbd0a90..8edb5535 100644
--- a/FGEGraphics/UISystem/UIInputLabel.cs
+++ b/FGEGraphics/UISystem/UIInputLabel.cs
@@ -28,7 +28,7 @@ namespace FGEGraphics.UISystem;
// TODO: Text alignment
// TODO: Cap text length
// TODO: HasEdited
-public class UIInputLabel : UIElement
+public class UIInputLabel : UIBox
{
/// An enumeration of operations.
public enum EditType
@@ -44,26 +44,12 @@ public enum EditType
SUBMIT
}
- /// Wraps a instance with logic specific to input labels.
- /// The base interaction styles.
- public struct Styles(UIInteractionStyles styles)
- {
- /// The styling logic for an input label.
- public readonly UIStyle Styling(UIElement element) => element.IsFocused ? styles.Press : styles.Styling(element);
-
- /// Calls .
- public static implicit operator UIStyling(Styles styles) => new(styles.Styling);
- }
-
///
public override string Name => "Input Label";
/// The paragraph to display the state of this input label.
public UIInputParagraph Paragraph;
- /// The box behind the input label.
- public UIBox Box = null;
-
/// The scroll group containing the label text.
public UIScrollGroup ScrollGroup;
@@ -101,15 +87,9 @@ public string Content
/// The current number of input text lines.
public int Lines => Paragraph.Internal.Renderables.Sum(piece => piece.Text.Lines.Length);
- /// The padding offset for the rendered text, if any.
- public int TextPadding => Box is not null ? (Internal.BoxPadding - ElementInternal.Style.BorderThickness) : 0;
-
/// Data internal to a instance.
public struct InternalData()
{
- /// The padding between the and the label.
- public int BoxPadding = 0;
-
/// Whether to enforce max width or use a horizontal scroll group.
public bool HasMaxWidth;
}
@@ -121,32 +101,12 @@ public struct InternalData()
/// The style of normal input content.
/// The style of highlighted input content.
/// The layout of the element.
- /// Whether to enforce a max width. If false, will use horizontal scrolling.
- /// Whether to render a box behind the input label.
- /// The padding between the box and the label.
- /// The styles for the scroll bar.
- /// The width of the scroll bar.
- /// Whether to add a horizontal scroll bar.
- /// Whether to add a vertical scroll bar.
- /// The anchor of the horizontal scroll bar.
- /// The anchor of the vertical scroll bar.
- public UIInputLabel(string placeholderInfo, string defaultText, UIStyling styling, UIStyling inputStyling, UIStyling highlightStyling, UILayout layout, bool maxWidth = true, bool renderBox = false, int boxPadding = 0, UIInteractionStyles scrollBarStyles = null, int scrollBarWidth = 0, bool scrollBarX = false, bool scrollBarY = false, UIAnchor scrollBarXAnchor = null, UIAnchor scrollBarYAnchor = null) : base(styling, layout)
+ public UIInputLabel(string placeholderInfo, string defaultText, UIStyling styling, UIStyling inputStyling, UIStyling highlightStyling, UILayout layout) : base(styling, layout)
{
- if (renderBox)
- {
- Internal.BoxPadding = boxPadding;
- // TODO: properly handle padding
- //UILayout baseLayout = new(layout);
- //layout.SetSize(() => baseLayout.Width + boxPadding * 2, () => baseLayout.Height + boxPadding * 2);
- AddChild(Box = new(styling.Bind(this), new UILayout().SetSize(() => layout.Width, () => layout.Height)) { IsEnabled = false });
- }
- int Inset() => Box is not null ? ElementInternal.Style.BorderThickness : 0; // there should definitely be a system for this
- UILayout scrollGroupLayout = new UILayout().SetPosition(Inset, Inset).SetSize(() => layout.Width - Inset() * 2, () => layout.Height - Inset() * 2);
- ScrollGroup = new(scrollGroupLayout, scrollBarStyles ?? UIStyling.Empty, scrollBarWidth, !maxWidth && scrollBarX, scrollBarY, scrollBarXAnchor, scrollBarYAnchor) { IsEnabled = false };
- ScrollGroup.AddScrollableChild(PlaceholderInfo = new UILabel(placeholderInfo, styling.Bind(this), new UILayout().SetPosition(() => TextPadding, () => TextPadding)) { IsEnabled = false });
- ScrollGroup.AddScrollableChild(Paragraph = new UIInputParagraph(highlightStyling, inputStyling, highlightStyling, new UILayout().SetPosition(() => TextPadding, () => TextPadding)) { IsEnabled = false });
+ ScrollGroup = new(layout.Container()) { IsEnabled = false };
+ ScrollGroup.AddScrollableChild(PlaceholderInfo = new UILabel(placeholderInfo, styling?.Bind(this), new UILayout()) { IsEnabled = false });
+ ScrollGroup.AddScrollableChild(Paragraph = new UIInputParagraph(highlightStyling, inputStyling, highlightStyling, new UILayout()) { IsEnabled = false });
AddChild(ScrollGroup);
- Internal.HasMaxWidth = maxWidth;
Paragraph.SetContent(defaultText);
}
@@ -161,7 +121,7 @@ public override void Focused()
///
public override void Unfocused()
{
- if ((ScrollGroup.ScrollX.ScrollBar?.IsPressed | ScrollGroup.ScrollY.ScrollBar?.IsPressed) ?? false)
+ if ((ScrollGroup.XAxis.ScrollBar?.IsPressed | ScrollGroup.YAxis.ScrollBar?.IsPressed) ?? false)
{
IsFocused = true;
return;
@@ -170,9 +130,9 @@ public override void Unfocused()
Paragraph.SetCursorPosition(0);
Paragraph.RenderCursor = false;
Paragraph.UpdateRenderState();
- ScrollGroup.ScrollX.Reset();
- ScrollGroup.ScrollY.Reset();
- PlaceholderInfo.RenderSelf = Content.Length == 0;
+ ScrollGroup.XAxis.Reset();
+ ScrollGroup.YAxis.Reset();
+ PlaceholderInfo.RenderMode = Content.Length == 0 ? UIRenderMode.FULL : UIRenderMode.NONE;
}
// FIXME: Paragraph.Width still retains last value when deleting all, incorrect MaxValue calculation
@@ -183,9 +143,9 @@ public void UpdateScrollGroupX()
{
return;
}
- ScrollGroup.ScrollX.MaxValue = Math.Max(Paragraph.Width + TextPadding * 2 - ScrollGroup.Width, 0);
- ScrollGroup.ScrollX.ScrollToPos((int)Paragraph.InputInternal.CursorRenderOffset.X, (int)Paragraph.InputInternal.CursorRenderOffset.X + TextPadding * 2 - ScrollGroup.ScrollX.Value);
- ScrollGroup.ScrollX.Clamp();
+ ScrollGroup.XAxis.MaxValue = Math.Max(Paragraph.Width - ScrollGroup.Width, 0);
+ ScrollGroup.XAxis.ScrollToPos((int)Paragraph.InputInternal.CursorRenderOffset.X, (int)Paragraph.InputInternal.CursorRenderOffset.X - ScrollGroup.XAxis.Value);
+ ScrollGroup.XAxis.Clamp();
}
/// Updates the vertical scroll values based on the text height and cursor position.
@@ -193,13 +153,13 @@ public void UpdateScrollGroupY()
{
if (Paragraph.Internal.Renderables.Count == 0)
{
- ScrollGroup.ScrollY.Reset();
+ ScrollGroup.YAxis.Reset();
return;
}
- int lastLineHeight = Paragraph.InputInternal.LabelRight.Style.FontHeight + TextPadding * 2;
- ScrollGroup.ScrollY.MaxValue = Math.Max((int)Paragraph.Internal.Renderables[^1].YOffset + lastLineHeight - ScrollGroup.Height, 0);
- ScrollGroup.ScrollY.ScrollToPos((int)Paragraph.InputInternal.CursorRenderOffset.Y, (int)Paragraph.InputInternal.CursorRenderOffset.Y + lastLineHeight - ScrollGroup.ScrollY.Value);
- ScrollGroup.ScrollX.Clamp();
+ int lastLineHeight = Paragraph.InputInternal.LabelRight.Style.TextFont?.Height ?? 0;
+ ScrollGroup.YAxis.MaxValue = Math.Max((int)Paragraph.Internal.Renderables[^1].YOffset + lastLineHeight - ScrollGroup.Height, 0);
+ ScrollGroup.YAxis.ScrollToPos((int)Paragraph.InputInternal.CursorRenderOffset.Y, (int)Paragraph.InputInternal.CursorRenderOffset.Y + lastLineHeight - ScrollGroup.YAxis.Value);
+ ScrollGroup.XAxis.Clamp();
}
/// Updates the values.
@@ -212,12 +172,12 @@ public void UpdateScrollGroup()
}
}
- /// Updates the text components based on the cursor positions.
+ /// Updates the visual state of this label.
public void UpdateRenderState()
{
Paragraph.UpdateRenderState();
UpdateScrollGroup();
- PlaceholderInfo.RenderSelf = Content.Length == 0;
+ PlaceholderInfo.RenderMode = Content.Length == 0 ? UIRenderMode.FULL : UIRenderMode.NONE;
}
/// Performs a user edit on the text content.
@@ -365,7 +325,7 @@ public void TickMouse()
return;
}
bool BarPressed(UIBox bar) => (bar?.IsPressed | bar?.SelfContains((int)View.Client.MouseX, (int)View.Client.MouseY)) ?? false;
- if (BarPressed(ScrollGroup.ScrollX.ScrollBar) || BarPressed(ScrollGroup.ScrollY.ScrollBar))
+ if (BarPressed(ScrollGroup.XAxis.ScrollBar) || BarPressed(ScrollGroup.YAxis.ScrollBar))
{
return;
}
diff --git a/FGEGraphics/UISystem/UIInputParagraph.cs b/FGEGraphics/UISystem/UIInputParagraph.cs
index ce58a22a..1cc3f7f0 100644
--- a/FGEGraphics/UISystem/UIInputParagraph.cs
+++ b/FGEGraphics/UISystem/UIInputParagraph.cs
@@ -98,9 +98,9 @@ public UIInputParagraph(UIStyling styling, UIStyling textStyling, UIStyling high
Styling = styling;
TextStyling = textStyling;
HighlightStyling = highlightStyling;
- AddLabel(InputInternal.LabelLeft = new UILabel("", textStyling, new UILayout()));
- AddLabel(InputInternal.LabelCenter = new UILabel("", highlightStyling, new UILayout()));
- AddLabel(InputInternal.LabelRight = new UILabel("", textStyling, new UILayout()));
+ AddLabel(InputInternal.LabelLeft = new UILabel("", textStyling, new UILayout()) { IsEnabled = false });
+ AddLabel(InputInternal.LabelCenter = new UILabel("", highlightStyling, new UILayout()) { IsEnabled = false });
+ AddLabel(InputInternal.LabelRight = new UILabel("", textStyling, new UILayout()) { IsEnabled = false });
}
/// Ensures the cursor positions lie within the current paragraph .
@@ -188,8 +188,8 @@ public override void Render(double delta, UIStyle style)
return;
}
View.Engine.Textures.White.Bind();
- Renderer2D.SetColor(style.BorderColor);
- int lineWidth = style.BorderThickness / 2;
+ Renderer2D.SetColor(style.Stroke);
+ int lineWidth = style.StrokeWeight / 2;
int lineHeight = style.TextFont.Height;
View.Rendering.RenderRectangle(View.UIContext, X + InputInternal.CursorRenderOffset.XF - lineWidth, Y + InputInternal.CursorRenderOffset.YF, X + InputInternal.CursorRenderOffset.XF + lineWidth, Y + InputInternal.CursorRenderOffset.YF + lineHeight);
Renderer2D.SetColor(Color4.White);
diff --git a/FGEGraphics/UISystem/UIInteractionStyles.cs b/FGEGraphics/UISystem/UIInteractionStyles.cs
deleted file mode 100644
index 7134efa2..00000000
--- a/FGEGraphics/UISystem/UIInteractionStyles.cs
+++ /dev/null
@@ -1,60 +0,0 @@
-//
-// This file is part of the Frenetic Game Engine, created by Frenetic LLC.
-// This code is Copyright (C) Frenetic LLC under the terms of a strict license.
-// See README.md or LICENSE.txt in the FreneticGameEngine source root for the contents of the license.
-// If neither of these are available, assume that neither you nor anyone other than the copyright holder
-// hold any right or permission to use this software until such time as the official license is identified.
-//
-
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using FGEGraphics.GraphicsHelpers.Textures;
-
-namespace FGEGraphics.UISystem;
-
-/// Represents styles that display an element's interaction state.
-/// The default style to use.
-/// The style to use on hover.
-/// The style to use on press.
-/// The style to use when disabled.
-public class UIInteractionStyles(UIStyle normal, UIStyle hover, UIStyle press, UIStyle disabled)
-{
- /// Empty interaction styles.
- public static readonly UIInteractionStyles Empty = new(null, null, null, null);
-
- /// The style of an element not being interacted with.
- public UIStyle Normal = normal;
-
- /// The style of an element about to be interacted with.
- public UIStyle Hover = hover;
-
- /// The style of an element being interacted with.
- public UIStyle Press = press;
-
- /// The style of a disabled element.
- public UIStyle Disabled = disabled;
-
- /// The styling logic for an interactable element.
- public virtual UIStyle Styling(UIElement element) => element.IsPressed ? Press
- : element.IsHovered ? Hover
- : !element.IsEnabled ? Disabled
- : Normal;
-
- /// Creates interaction styles based on a standard texture set.
- /// The base interaction style.
- /// The engine to get textures from.
- /// The name of the texture set.
- public static UIInteractionStyles Textured(UIStyle baseStyle, TextureEngine textures, string textureSet)
- {
- UIStyle normal = new(baseStyle) { BaseTexture = textures.GetTexture($"{textureSet}_none") };
- UIStyle hover = new(baseStyle) { BaseTexture = textures.GetTexture($"{textureSet}_hover") };
- UIStyle press = new(baseStyle) { BaseTexture = textures.GetTexture($"{textureSet}_press") };
- UIStyle disabled = new(baseStyle) { BaseTexture = textures.GetTexture($"{textureSet}_disabled") };
- return new(normal, hover, press, disabled);
- }
-
- /// Calls .
- public static implicit operator UIStyling(UIInteractionStyles styles) => new(styles.Styling);
-}
diff --git a/FGEGraphics/UISystem/UILabel.cs b/FGEGraphics/UISystem/UILabel.cs
index c50a37c1..14a8a7e3 100644
--- a/FGEGraphics/UISystem/UILabel.cs
+++ b/FGEGraphics/UISystem/UILabel.cs
@@ -42,11 +42,8 @@ public struct InternalData()
/// The maximum width of the text content.
public int MaxWidth;
- /// A cache of UI styles and their corresponding renderable objects.
- public Dictionary Renderables = [];
-
- /// Fired after the renderables cache is updated.
- public Action OnRenderablesUpdate;
+ /// The cached renderable object.
+ public RenderableText Renderable = RenderableText.Empty;
}
/// Data internal to a instance.
@@ -62,7 +59,7 @@ public string Content
set
{
Internal.Content = value ?? "";
- UpdateRenderables();
+ UpdateRenderable();
}
}
@@ -77,7 +74,7 @@ public int MaxWidth
set
{
Internal.MaxWidth = value;
- UpdateRenderables();
+ UpdateRenderable();
}
}
@@ -88,7 +85,7 @@ public int MaxWidth
public UILabel(string text, UIStyling styling, UILayout layout) : base(styling, layout)
{
Internal = new() { Content = text ?? "" };
- UpdateRenderables();
+ UpdateRenderable();
}
/// Creates a object from given a style.
@@ -101,15 +98,12 @@ public RenderableText CreateRenderable(UIStyle style)
{
return RenderableText.Empty;
}
- // TODO: cache this somewhere, as it's likely for many elements with text to have the same scale value
- IEnumerable> fontVariants = style.TextFont.Engine.Fonts.Where(pair => pair.Value.Name == style.TextFont.Name);
- if (!fontVariants.Any())
+ FontSet font = style.TextFont.Engine.GetApproximateFont(style.TextFont.Name, fontSize);
+ if (font is null)
{
return RenderableText.Empty;
}
- IEnumerable> fittingFonts = fontVariants.Where(pair => pair.Key.Item2 <= fontSize);
- ((string, int) _, FontSet font) = fittingFonts.Any() ? fittingFonts.MinBy(pair => fontSize - pair.Key.Item2) : fontVariants.MinBy(pair => Math.Abs(fontSize - pair.Key.Item2));
- string styledContent = style.TextStyling(Internal.Content); // FIXME: this doesn't play well with translatable text.
+ string styledContent = style.TextStyling?.Invoke(Internal.Content) ?? Internal.Content; // FIXME: this doesn't play well with translatable text.
RenderableText renderable = font.ParseFancyText(styledContent, style.TextBaseColor);
if (Internal.MaxWidth > 0)
{
@@ -123,56 +117,40 @@ public RenderableText CreateRenderable(UIStyle style)
/// If is true, clears the cache.
/// Otherwise, recreates all renderable objects in the cache.
///
- public void UpdateRenderables()
+ public void UpdateRenderable(UIStyle style = null)
{
- if (IsEmpty)
- {
- Internal.Renderables.Clear();
- }
- else
- {
- Internal.Renderables = ElementInternal.Styles
- .Select(style => (style, CreateRenderable(style)))
- .ToDictionary();
- Internal.OnRenderablesUpdate?.Invoke();
- }
+ style ??= Style;
+ Internal.Renderable = !IsEmpty && style.TextFont is not null ? CreateRenderable(style) : RenderableText.Empty;
}
///
public override void StyleChanged(UIStyle from, UIStyle to)
{
- if (!IsEmpty && to.CanRenderText && !Internal.Renderables.ContainsKey(to))
- {
- Internal.Renderables[to] = CreateRenderable(to);
- }
- if (Scale != 0 && Internal.Renderables.TryGetValue(to, out RenderableText renderable))
+ UpdateRenderable(to);
+ if (Scale != 0)
{
- Layout.SetSize(renderable.GetTrueSize(to.TextFont));
+ Layout.SetSize(Internal.Renderable.GetTrueSize(to.TextFont));
}
}
///
public override void ScaleChanged(float from, float to)
{
- UpdateRenderables();
- if (from == 0 && GetRenderable(Style) is RenderableText renderable)
+ UpdateRenderable();
+ if (from == 0 && !Internal.Renderable.IsEmpty)
{
- Layout.SetSize(renderable.GetTrueSize(Style.TextFont));
+ Layout.SetSize(Internal.Renderable.GetTrueSize(Style.TextFont));
}
}
- /// Returns the cached object corresponding to the given style, or null if none is present.
- /// The UI style to query.
- public RenderableText GetRenderable(UIStyle style) => !IsEmpty && Internal.Renderables.TryGetValue(style, out RenderableText renderable) ? renderable : null;
-
///
public override void Render(double delta, UIStyle style)
{
- if (GetRenderable(style) is RenderableText renderable)
+ if (!Internal.Renderable.IsEmpty)
{
- int trueX = X + Layout.Anchor.AlignmentX.GetPosition(Width, renderable.Width);
- int trueY = Y + Layout.Anchor.AlignmentY.GetPosition(Height, renderable.Height);
- style.TextFont.DrawFancyText(renderable, new Location(trueX, trueY, 0));
+ int trueX = X + Layout.Anchor.AlignmentX.GetPosition(Width, Internal.Renderable.Width);
+ int trueY = Y + Layout.Anchor.AlignmentY.GetPosition(Height, Internal.Renderable.Height);
+ style.TextFont.DrawFancyText(Internal.Renderable, new Location(trueX, trueY, 0));
}
}
@@ -187,7 +165,7 @@ public override void Render(double delta, UIStyle style)
public static (UILabel Label, UIImage Icon, UIListGroup List) WithIcon(string text, Texture icon, int spacing, UIStyling styling, UILayout layout, UIAnchor listAnchor = null)
{
UIListGroup list = new(spacing, layout, vertical: false, anchor: listAnchor ?? UIAnchor.TOP_LEFT);
- UILabel label = new(text, styling, layout.AtOrigin());
+ UILabel label = new(text, styling, layout.Copy().SetOrigin());
UIImage image = new(icon, new UILayout().SetSize(() => label.Height, () => label.Height));
list.AddListItem(label);
list.AddListItem(image);
diff --git a/FGEGraphics/UISystem/UILayout.cs b/FGEGraphics/UISystem/UILayout.cs
index 077e14eb..f93d5d44 100644
--- a/FGEGraphics/UISystem/UILayout.cs
+++ b/FGEGraphics/UISystem/UILayout.cs
@@ -68,6 +68,10 @@ public UILayout(UILayout layout)
Internal = layout.Internal;
}
+ /// Returns a copy of this layout.
+ ///
+ public UILayout Copy() => new(this);
+
/// Sets the anchor.
/// This object.
public UILayout SetAnchor(UIAnchor anchor)
@@ -244,8 +248,20 @@ public UILayout SetScale(Func scale)
/// This object.
public UILayout SetOrigin() => SetAnchor(UIAnchor.TOP_LEFT).SetPosition(0, 0);
- /// Returns a copy of this layout fixed at the top-left origin.
- public UILayout AtOrigin() => new UILayout(this).SetOrigin();
+ /// Flips X and Y, width and height, and the rotation direction.
+ /// This object.
+ public UILayout Transpose()
+ {
+ (Internal.X, Internal.Y) = (Internal.Y, Internal.X);
+ (Internal.Width, Internal.Height) = (Internal.Height, Internal.Width);
+ // TODO: flip the sign of rotation efficiently
+ return this;
+ }
+
+ /// Returns a new inhabiting the interior space of this one.
+ public UILayout Container() => new UILayout()
+ .SetPosition(() => Element.Style.Inset, () => Element.Style.Inset)
+ .SetSize(() => Width - Element.Style.Inset * 2, () => Height - Element.Style.Inset * 2);
/// Gets the relative X value.
public int X => Internal.X.Get() + (Element.Parent != null ? Anchor.GetX(Element) : 0);
diff --git a/FGEGraphics/UISystem/UINativeTexture.cs b/FGEGraphics/UISystem/UINativeTexture.cs
index 5cb30403..88c43186 100644
--- a/FGEGraphics/UISystem/UINativeTexture.cs
+++ b/FGEGraphics/UISystem/UINativeTexture.cs
@@ -24,7 +24,7 @@ namespace FGEGraphics.UISystem;
///
/// The texture to display.
/// The layout of the element.
-public class UINativeTexture(Func texture, UILayout layout) : UIElement(UIStyling.Empty, layout)
+public class UINativeTexture(Func texture, UILayout layout) : UIElement(null, layout)
{
///
public override string Name => "Native Texture";
diff --git a/FGEGraphics/UISystem/UINumberInputLabel.cs b/FGEGraphics/UISystem/UINumberInputLabel.cs
index 9b5365c2..a3377095 100644
--- a/FGEGraphics/UISystem/UINumberInputLabel.cs
+++ b/FGEGraphics/UISystem/UINumberInputLabel.cs
@@ -74,19 +74,13 @@ public double Value
/// Constructs a number input label.
/// Whether the label should be an integer.
/// The styling logic of the element.
- /// The style of normal input content.
- /// The style of highlighted input content.
+ /// The style of normal input content.
+ /// The style of highlighted input content.
/// The layout of the element.
/// The default number value.
/// The format string for the label.
/// The text to display when the input is empty.
- /// Whether to render a box behind the label.
- /// The padding between the box and the label.
- /// The styles for the scroll bar.
- /// The width of the scroll bar.
- /// Whether to add a horizontal scroll bar.
- /// The anchor of the horizontal scroll bar.
- public UINumberInputLabel(bool integer, UIStyling styling, UIStyle inputStyle, UIStyle highlightStyle, UILayout layout, double defaultValue = 0, string format = null, string placeholderInfo = "", bool renderBox = false, int boxPadding = 0, UIInteractionStyles scrollBarStyles = null, int scrollBarWidth = 0, bool scrollBarX = false, UIAnchor scrollBarXAnchor = null) : base(placeholderInfo, placeholderInfo.Length == 0 ? defaultValue.ToString(format) : "", styling, inputStyle, highlightStyle, layout, false, renderBox, boxPadding, scrollBarStyles, scrollBarWidth, scrollBarX, false, scrollBarXAnchor, null)
+ public UINumberInputLabel(bool integer, UIStyling styling, UIStyling inputStyling, UIStyling highlightStyling, UILayout layout, double defaultValue = 0, string format = null, string placeholderInfo = "") : base(placeholderInfo, placeholderInfo.Length == 0 ? defaultValue.ToString(format) : "", styling, inputStyling, highlightStyling, layout)
{
Integer = integer;
Format = format ?? (integer ? "0" : "0.0");
diff --git a/FGEGraphics/UISystem/UINumberSlider.cs b/FGEGraphics/UISystem/UINumberSlider.cs
index f223e375..180bf42b 100644
--- a/FGEGraphics/UISystem/UINumberSlider.cs
+++ b/FGEGraphics/UISystem/UINumberSlider.cs
@@ -69,7 +69,7 @@ public class UINumberSlider : UIElement
/// Whether to use integers instead of decimals.
/// The clickable styles.
/// The layout of the element.
- public UINumberSlider(double min, double max, double defaultValue, double interval, bool integer, UIStyling styling, UILayout layout) : base(styling,layout)
+ public UINumberSlider(double min, double max, double defaultValue, double interval, bool integer, UIStyling styling, UILayout layout) : base(styling, layout)
{
Integer = integer;
Interval = Integer ? Math.Max((int)interval, 1.0) : interval;
@@ -82,7 +82,7 @@ public UINumberSlider(double min, double max, double defaultValue, double interv
Max = Min + Interval * maxStep;
}
Value = Math.Clamp(Integer ? (int)Default : Default, Min, Max); // TODO: is this correct?
- AddChild(Button = new(UIStyle.Empty, layout.AtOrigin().SetWidth(layout.Height / 2)) { RenderSelf = false, IsEnabled = false });
+ AddChild(Button = new(null, layout.Copy().SetOrigin().SetWidth(layout.Height / 2)) { RenderMode = UIRenderMode.NONE, IsEnabled = false });
Button.Layout.SetX(() => (int)(Progress * Width) - Button.Width / 2);
}
@@ -126,8 +126,8 @@ public override void Tick(double delta)
public override void Render(double delta, UIStyle style)
{
View.Engine.Textures.White.Bind();
- Renderer2D.SetColor(style.BorderColor);
- int lineWidth = style.BorderThickness / 2;
+ Renderer2D.SetColor(style.Stroke);
+ int lineWidth = style.StrokeWeight / 2;
int centerY = Y + Height / 2;
View.Rendering.RenderRectangle(View.UIContext, X, centerY - lineWidth, X + Width, centerY + lineWidth);
if (Interval > 0.0)
diff --git a/FGEGraphics/UISystem/UIParagraph.cs b/FGEGraphics/UISystem/UIParagraph.cs
index 8a168bb9..fde9b294 100644
--- a/FGEGraphics/UISystem/UIParagraph.cs
+++ b/FGEGraphics/UISystem/UIParagraph.cs
@@ -23,7 +23,7 @@ namespace FGEGraphics.UISystem;
/// Represents multiple s chained together.
/// The layout of the element.
-public class UIParagraph(UILayout layout) : UIElement(UIStyle.Empty, layout)
+public class UIParagraph(UILayout layout) : UIElement(null, layout)
{
///
public override string Name => "Paragraph";
@@ -62,7 +62,7 @@ public void AddLabel(UILabel label)
{
Labels.Add(label);
AddChild(label);
- label.RenderSelf = false;
+ label.RenderMode = UIRenderMode.NONE;
//label.Internal.OnRenderablesUpdate += UpdateRenderables;
}
@@ -74,11 +74,11 @@ public void UpdateRenderables()
List<(FontSet Font, RenderableTextLine Line)> lines = [];
foreach (UILabel label in Labels)
{
- if (label.GetRenderable(label.Style) is not RenderableText renderable)
+ if (label.Internal.Renderable.IsEmpty)
{
continue;
}
- List textLines = [.. renderable.Lines];
+ List textLines = [.. label.Internal.Renderable.Lines];
if (lines.Count != 0)
{
RenderableTextLine combinedLine = new([.. lines[^1].Line.Parts, .. textLines[0].Parts]);
diff --git a/FGEGraphics/UISystem/UIRenderMode.cs b/FGEGraphics/UISystem/UIRenderMode.cs
new file mode 100644
index 00000000..863eba48
--- /dev/null
+++ b/FGEGraphics/UISystem/UIRenderMode.cs
@@ -0,0 +1,26 @@
+//
+// This file is part of the Frenetic Game Engine, created by Frenetic LLC.
+// This code is Copyright (C) Frenetic LLC under the terms of a strict license.
+// See README.md or LICENSE.txt in the FreneticGameEngine source root for the contents of the license.
+// If neither of these are available, assume that neither you nor anyone other than the copyright holder
+// hold any right or permission to use this software until such time as the official license is identified.
+//
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace FGEGraphics.UISystem;
+
+/// Represents the way a renders to the screen.
+public enum UIRenderMode
+{
+ /// Render the element and its children.
+ FULL,
+ /// Skip the element and render only its children.
+ SKIP_SELF,
+ /// Skip rendering the element and all of its children.
+ NONE
+}
diff --git a/FGEGraphics/UISystem/UIRenderable.cs b/FGEGraphics/UISystem/UIRenderable.cs
index a2e8fc59..ab9918e1 100644
--- a/FGEGraphics/UISystem/UIRenderable.cs
+++ b/FGEGraphics/UISystem/UIRenderable.cs
@@ -17,7 +17,7 @@ namespace FGEGraphics.UISystem;
/// Represents a simple renderer that can be attached to any element.
/// The renderer method. See .
-public class UIRenderable(Action renderer) : UIElement(UIStyling.Empty, new UILayout())
+public class UIRenderable(Action renderer) : UIElement(null, new UILayout())
{
///
public override string Name => "Renderable";
diff --git a/FGEGraphics/UISystem/UIScreen.cs b/FGEGraphics/UISystem/UIScreen.cs
index 133d9cbb..3e0ba68e 100644
--- a/FGEGraphics/UISystem/UIScreen.cs
+++ b/FGEGraphics/UISystem/UIScreen.cs
@@ -38,7 +38,7 @@ public class UIScreen : UIElement
/// Constructs a .
/// The client UI view.
/// The layout of the element. If null, defaults to a layout covering the parent view.
- public UIScreen(ViewUI2D view, UILayout layout = null) : base(UIStyling.Empty, layout)
+ public UIScreen(ViewUI2D view, UILayout layout = null) : base(null, layout)
{
View = view;
IsEnabled = false;
diff --git a/FGEGraphics/UISystem/UIScrollGroup.cs b/FGEGraphics/UISystem/UIScrollGroup.cs
index d15b3486..55b5ec32 100644
--- a/FGEGraphics/UISystem/UIScrollGroup.cs
+++ b/FGEGraphics/UISystem/UIScrollGroup.cs
@@ -26,58 +26,37 @@ public class UIScrollGroup : UIElement
/// The horizontal scroll axis.
[UIDebug]
- public Axis ScrollX;
+ public ScrollAxis XAxis;
/// The vertical scroll axis.
[UIDebug]
- public Axis ScrollY;
-
- /// The scrollable scissor layer for child elements.
- public UIScissorGroup ScrollableLayer;
+ public ScrollAxis YAxis;
/// The scroll bar layer (above the scissor layer).
public UIGroup ScrollBarLayer;
+ /// The scrollable scissor layer for child elements.
+ public UIScissorGroup ScrollableLayer;
+
/// Whether either of the scroll bars are pressed.
- public bool ScrollBarPressed => ScrollX.ScrollBar?.IsPressed ?? ScrollY.ScrollBar?.IsPressed ?? false;
+ public bool ScrollBarPressed => XAxis.ScrollBar?.IsPressed ?? YAxis.ScrollBar?.IsPressed ?? false;
/// Constructs the UI scroll group.
/// The layout of the element.
- /// The scroll bar styles.
- /// The width of the scroll bars.
- /// Whether to add a horizontal scroll bar.
- /// Whether to add a vertical scroll bar.
- /// The anchor of the horizontal scroll bar.
- /// The anchor of the vertical scroll bar.
- public UIScrollGroup(UILayout layout, UIStyling barStyling = default, int barWidth = 0, bool barX = false, bool barY = false, UIAnchor barXAnchor = null, UIAnchor barYAnchor = null) : base(UIStyling.Empty, layout)
+ public UIScrollGroup(UILayout layout) : base(null, layout)
{
- if (barXAnchor?.AlignmentX == UIAlignment.CENTER || barYAnchor?.AlignmentY == UIAlignment.CENTER)
- {
- throw new Exception("UIScrollGroup scroll bars must have non-central scroll directions");
- }
// TODO: Fix scroll bar overlap
- ScrollX = new(false, () => Width/* - (barY ? barWidth : 0)*/, barX, barWidth, barStyling, new UILayout().SetAnchor(barXAnchor ?? UIAnchor.BOTTOM_LEFT));
- ScrollY = new(true, () => Height/* - (barX ? barWidth : 0)*/, barY, barWidth, barStyling, new UILayout().SetAnchor(barYAnchor ?? UIAnchor.TOP_RIGHT));
- if (ScrollX.ScrollBar is not null || ScrollY.ScrollBar is not null)
- {
- base.AddChild(ScrollBarLayer = new(layout.AtOrigin().SetSize(() => Width, () => Height)));
- if (ScrollX.ScrollBar is not null)
- {
- ScrollBarLayer.AddChild(ScrollX.ScrollBar);
- }
- if (ScrollY.ScrollBar is not null)
- {
- ScrollBarLayer.AddChild(ScrollY.ScrollBar);
- }
- }
- base.AddChild(ScrollableLayer = new UIScissorGroup(layout.AtOrigin().SetSize(() => Width, () => Height)));
+ XAxis = new(this, false);
+ YAxis = new(this, true);
+ AddChild(ScrollBarLayer = new UIGroup(layout.Container()));
+ AddChild(ScrollableLayer = new UIScissorGroup(layout.Container()));
}
///
public void AddScrollableChild(UIElement child)
{
UILayout original = new(child.Layout);
- child.Layout.SetPosition(() => original.Internal.X.Get() - ScrollX.Value, () => original.Internal.Y.Get() - ScrollY.Value);
+ child.Layout.SetPosition(() => original.Internal.X.Get() - XAxis.Value, () => original.Internal.Y.Get() - YAxis.Value);
ScrollableLayer.AddChild(child);
}
@@ -85,13 +64,13 @@ public void AddScrollableChild(UIElement child)
public override void Tick(double delta)
{
base.Tick(delta);
- if (ScrollX.ScrollBar is not null)
+ if (XAxis.ScrollBar is not null)
{
- ScrollX.TickMouseDrag(View.Client.MouseX, X);
+ XAxis.TickMouseDrag(View.Client.MouseX, X);
}
- if (ScrollY.ScrollBar is not null)
+ if (YAxis.ScrollBar is not null)
{
- ScrollY.TickMouseDrag(View.Client.MouseY, Y);
+ YAxis.TickMouseDrag(View.Client.MouseY, Y);
}
}
@@ -100,8 +79,8 @@ public override void Navigated(int horizontal, int vertical)
{
if (!ScrollBarPressed)
{
- ScrollX.TickMouseScroll(horizontal);
- ScrollY.TickMouseScroll(vertical);
+ XAxis.TickMouseScroll(horizontal);
+ YAxis.TickMouseScroll(vertical);
}
}
@@ -112,28 +91,34 @@ public override bool MouseScrolled(float horizontal, float vertical)
{
return true;
}
- if (ScrollY.MaxValue == 0 || View.Client.Window.KeyboardState.IsKeyDown(Keys.LeftShift))
+ if (YAxis.MaxValue == 0 || View.Client.Window.KeyboardState.IsKeyDown(Keys.LeftShift))
{
horizontal = vertical;
vertical = 0;
}
- ScrollX.TickMouseScroll(horizontal);
- ScrollY.TickMouseScroll(vertical);
+ XAxis.TickMouseScroll(horizontal);
+ YAxis.TickMouseScroll(vertical);
return true;
}
///
public override void ScaleChanged(float from, float to)
{
- ScrollX.Clamp();
- ScrollY.Clamp();
+ XAxis.Clamp();
+ YAxis.Clamp();
}
- /// Contains scroll state for a direction.
- /// Whether the direction is vertical or horizontal.
- /// The length of the outer group's relevant dimension.
- public class Axis(bool vertical, Func rangeLength)
+ /// Represents the state of a scroll axis.
+ /// The parent scroll group of the axis.
+ /// Whether the axis is vertical or horizontal.
+ public class ScrollAxis(UIScrollGroup scrollGroup, bool vertical)
{
+ /// The parent scroll group of the axis.
+ public UIScrollGroup ScrollGroup = scrollGroup;
+
+ /// Whether the axis is vertical or horizontal.
+ public bool Vertical = vertical;
+
/// The current scroll position.
public int Value = 0;
@@ -146,42 +131,53 @@ public class Axis(bool vertical, Func rangeLength)
/// The scroll bar button, if any.
public UIBox ScrollBar = null;
+ // TODO: maybe explain more? this is the mouse position relative to the bar position when first clicking / dragging it
/// The held position offset of the scroll bar.
public int BarHeldOffset = -1;
/// The length of the outer group's relevant dimension.
- public int RangeLength => rangeLength();
+ public int Length => Vertical ? ScrollGroup.Height : ScrollGroup.Width;
/// The length of the scroll bar.
- public int BarLength => MaxValue > 0 ? (int)((double)RangeLength / (MaxValue + RangeLength) * RangeLength) : 0;
+ public int BarLength => MaxValue > 0 ? (int)((double)Length / (MaxValue + Length) * Length) : 0;
/// The scroll bar's position offset.
- public int BarPosition => MaxValue > 0 ? (int)((RangeLength - BarLength) * ((double)Value / MaxValue)) : 0;
-
- /// Constructs a scroll direction.
- /// Whether the direction is vertical or horizontal.
- /// The length of the outer group's relevant dimension.
- /// Whether to create the .
- /// The width of the .
- /// The styles.
- /// The base layout of the .
- public Axis(bool vertical, Func rangeLength, bool hasBar, int width, UIStyling styling, UILayout layout) : this(vertical, rangeLength)
+ public int BarPosition => MaxValue > 0 ? (int)((Length - BarLength) * ((double)Value / MaxValue)) : 0;
+
+ /// Adds a scroll bar to the parent that can be dragged by the mouse.
+ /// The styling logic of the scroll bar.
+ /// The width of the scroll bar (where the length aligns with the scroll direction).
+ /// The positional anchor of the scroll bar.
+ public void AddScrollBar(UIStyling styling, int width, UIAnchor anchor = null)
{
- if (!hasBar)
+ if ((Vertical && anchor?.AlignmentY == UIAlignment.CENTER) || (!Vertical && anchor?.AlignmentX == UIAlignment.CENTER))
{
- return;
+ throw new Exception($"Tried to add a scroll bar with a central scroll direction: {anchor}");
}
- if (vertical)
+ if (ScrollBar is not null)
{
- layout.SetY(() => BarPosition).SetHeight(() => BarLength).SetWidth(() => (int)(width * ScrollBar.Scale));
+ throw new Exception("TODO");
}
- else
+ UILayout layout = new UILayout()
+ .SetAnchor(anchor ?? (Vertical ? UIAnchor.TOP_RIGHT : UIAnchor.BOTTOM_LEFT))
+ .SetY(() => BarPosition).SetHeight(() => BarLength)
+ .SetWidth(() => (int)(width * ScrollBar.Scale));
+ if (!Vertical)
{
- layout.SetX(() => BarPosition).SetWidth(() => BarLength).SetHeight(() => (int)(width * ScrollBar.Scale));
+ layout.Transpose();
}
ScrollBar = new(styling, layout) { ScaleSize = false };
+ ScrollGroup.ScrollBarLayer.AddChild(ScrollBar);
+ }
+
+ /// Removes the from the parent .
+ public void RemoveScrollBar()
+ {
+ ScrollGroup.ScrollBarLayer.RemoveChild(ScrollBar);
+ ScrollBar = null;
}
+ // TODO: why is this needed??
/// Sets the and to 0.
public void Reset()
{
@@ -198,6 +194,7 @@ public void Clamp()
}
}
+ // TODO: what does this do??
/// Scrolls to encompass a min/max offset pair.
/// The min offset.
/// The max offset.
@@ -207,9 +204,9 @@ public void ScrollToPos(int min, int max)
{
Value = min;
}
- else if (max > RangeLength)
+ else if (max > Length)
{
- Value += max - RangeLength;
+ Value += max - Length;
}
}
@@ -225,9 +222,9 @@ public void TickMouseDrag(float mousePos, int groupPos)
}
if (BarHeldOffset == -1)
{
- BarHeldOffset = (int)mousePos - (vertical ? ScrollBar.Y : ScrollBar.X);
+ BarHeldOffset = (int)mousePos - (Vertical ? ScrollBar.Y : ScrollBar.X);
}
- Value = (int)((double)(mousePos - groupPos - BarHeldOffset) / (RangeLength - BarLength) * MaxValue);
+ Value = (int)((double)(mousePos - groupPos - BarHeldOffset) / (Length - BarLength) * MaxValue);
Clamp();
}
diff --git a/FGEGraphics/UISystem/UIStyle.cs b/FGEGraphics/UISystem/UIStyle.cs
index 2a9f9b82..f4b7a74f 100644
--- a/FGEGraphics/UISystem/UIStyle.cs
+++ b/FGEGraphics/UISystem/UIStyle.cs
@@ -19,61 +19,41 @@
namespace FGEGraphics.UISystem;
/// Represents the rendering style of a .
-public record UIStyle
+public class UIStyle
{
/// An empty element style.
public static readonly UIStyle Empty = new() { Name = "Empty" };
- /// What base color to use (or for none).
- public Color4F BaseColor = Color4F.Transparent;
+ /// The color to fill an element's interior with.
+ public Color4F Fill;
- /// What texture to display (or null for none).
- public Texture BaseTexture;
+ /// The texture to draw on an element.
+ public Texture Texture;
- /// What border outline color to use (or for none).
- public Color4F BorderColor = Color4F.Transparent;
+ /// The color to draw an element's outline with.
+ public Color4F Stroke;
- /// How thick the border outline should be (or 0 for none).
- public int BorderThickness = 0;
+ /// The thickness to draw an element's outline with.
+ public int StrokeWeight;
- /// How big the drop-shadow effect should be (or 0 for none).
- public int DropShadowLength = 0;
+ /// The distance between an element's outline and its interior content.
+ public int Padding;
- /// The text font (or null for none).
+ /// The size of the drop shadow on an element.
+ public int ShadowSize;
+
+ /// The text font.
public FontSet TextFont;
- // TODO: Does the usage of 'Func' work properly in the context of a 'record' that's going into a Dictionary (ie hashcode/equality checks)?
- /// The styling effect for text.
- public Func TextStyling = str => str;
+ /// The text styling effect.
+ public Func TextStyling;
- /// The base color effect for text (consider if unsure).
- public string TextBaseColor = TextStyle.Simple;
+ /// The base color effect for text.
+ public string TextBaseColor;
- /// The name of the element style (for debug info).
+ /// The name of the style (for debug info).
public string Name;
- /// Constructs a default element style.
- public UIStyle()
- {
- }
-
- /// Constructs a new style as a copy of another style.
- /// The style to copy.
- public UIStyle(UIStyle style)
- {
- BaseColor = style.BaseColor;
- BaseTexture = style.BaseTexture;
- BorderColor = style.BorderColor;
- BorderThickness = style.BorderThickness;
- DropShadowLength = style.DropShadowLength;
- TextFont = style.TextFont;
- TextStyling = style.TextStyling;
- TextBaseColor = style.TextBaseColor;
- }
-
- /// Returns the font height, or 0 if is null.
- public int FontHeight => TextFont?.Height ?? 0;
-
- /// Returns whether this style can render text in general.
- public bool CanRenderText => TextFont is not null;
+ /// The distance between an element's boundary and its interior content.
+ public int Inset => StrokeWeight + Padding;
}
diff --git a/FGEGraphics/UISystem/UIStyleValue.cs b/FGEGraphics/UISystem/UIStyleValue.cs
new file mode 100644
index 00000000..a603c89a
--- /dev/null
+++ b/FGEGraphics/UISystem/UIStyleValue.cs
@@ -0,0 +1,61 @@
+//
+// This file is part of the Frenetic Game Engine, created by Frenetic LLC.
+// This code is Copyright (C) Frenetic LLC under the terms of a strict license.
+// See README.md or LICENSE.txt in the FreneticGameEngine source root for the contents of the license.
+// If neither of these are available, assume that neither you nor anyone other than the copyright holder
+// hold any right or permission to use this software until such time as the official license is identified.
+//
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace FGEGraphics.UISystem;
+
+/// Represents a single value of a object that resolves to a value each frame.
+public readonly struct UIStyleValue
+{
+ /// A constant value.
+ public readonly T Constant;
+
+ /// A dynamic style.
+ public readonly Func Dynamic;
+
+ /// Constructs a constant style value.
+ /// The constant value.
+ public UIStyleValue(T constant)
+ {
+ Constant = constant;
+ }
+
+ /// Constructs a dynamic style value.
+ /// The dynamic value.
+ public UIStyleValue(Func dynamic)
+ {
+ Dynamic = dynamic;
+ }
+
+ /// Returns the style value for the specified .
+ /// The element to be styled.
+ public readonly T Get(UIElement element) => Dynamic is not null ? Dynamic.Invoke(element) : Constant;
+
+ /// Calls .
+ public static implicit operator UIStyleValue(T constant) => new(constant);
+
+ /// Calls .
+ public static implicit operator UIStyleValue(Func dynamic) => new(dynamic);
+
+ /// Constructs a style value dependent on the interaction state of an element.
+ /// The value to use when the element is not being interacted with.
+ /// The value to use when the element is hovered.
+ /// The value to use when the element is pressed.
+ /// The value to use when the element is disabled.
+ public static UIStyleValue Interactive(T idle, T hovered, T pressed, T disabled) => new(
+ element => element.IsPressed ? pressed
+ : element.IsHovered ? hovered
+ : !element.IsEnabled ? disabled
+ : idle
+ );
+}
diff --git a/FGEGraphics/UISystem/UIStyling.cs b/FGEGraphics/UISystem/UIStyling.cs
index 4720f9e0..6ca4a21b 100644
--- a/FGEGraphics/UISystem/UIStyling.cs
+++ b/FGEGraphics/UISystem/UIStyling.cs
@@ -6,6 +6,10 @@
// hold any right or permission to use this software until such time as the official license is identified.
//
+using FGECore.ConsoleHelpers;
+using FGECore.MathHelpers;
+using FGEGraphics.GraphicsHelpers.FontSets;
+using FGEGraphics.GraphicsHelpers.Textures;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -14,60 +18,60 @@
namespace FGEGraphics.UISystem;
-/// Represents the styling logic for a .
-public struct UIStyling
+/// Represents the styling logic of a .
+public record UIStyling
{
- /// Empty styling logic. Resolves to .
- public static readonly UIStyling Empty = new((UIStyle)null);
+ ///
+ /// The element bound to this style.
+ /// If present, calls to will use this element rather than the one passed as an argument.
+ ///
+ public UIElement Element = null;
- /// A constant style.
- public UIStyle Constant;
+ /// The color to fill an element's interior with.
+ public UIStyleValue Fill = Color4F.Transparent;
- /// A dynamic style. If present, updates the relevant every frame.
- public Func Dynamic;
+ /// The texture to draw on an element.
+ public UIStyleValue Texture = default;
- /// Whether this style is equivalent to
- public readonly bool IsEmpty => Constant is null && Dynamic is null;
+ /// The color to draw an element's outline with.
+ public UIStyleValue Stroke = Color4F.Transparent;
- /// Constructs styling logic using a constant style.
- /// The constant style.
- public UIStyling(UIStyle style)
- {
- Constant = style;
- }
+ /// The thickness to draw an element's outline with.
+ public UIStyleValue StrokeWeight = 0;
- /// Constructs styling logic using a dynamic style.
- /// The dynamic style.
- public UIStyling(Func styling)
- {
- Dynamic = styling;
- }
+ /// The distance between an element's outline and its interior content.
+ public UIStyleValue Padding = 0;
- ///
- /// Returns the style for the specified based on this styling logic.
- /// Tries to evaluate then . If neither are present, resolves to .
- ///
- /// The element to be styled.
- public readonly UIStyle Get(UIElement element) => Constant ?? Dynamic?.Invoke(element) ?? UIStyle.Empty;
+ /// The size of the drop shadow on an element.
+ public UIStyleValue ShadowSize = 0;
- ///
- /// If is present, returns a new instance with the specified bound to the dynamic logic.
- /// Otherwise, returns this instance unaltered.
- ///
- /// The element to bind.
- public readonly UIStyling Bind(UIElement element)
+ /// The text font.
+ public UIStyleValue TextFont = default;
+
+ /// The text styling effect.
+ public UIStyleValue> TextStyling = default;
+
+ /// The base color effect for text.
+ public UIStyleValue TextBaseColor = TextStyle.Simple;
+
+ /// Returns a new using style values based on the given .
+ public UIStyle Get(UIElement element)
{
- if (Dynamic is not null)
+ element = Element ?? element;
+ return new()
{
- Func dynamic = Dynamic;
- return new(_ => dynamic(element));
- }
- return this;
+ Fill = Fill.Get(element),
+ Texture = Texture.Get(element),
+ Stroke = Stroke.Get(element),
+ StrokeWeight = StrokeWeight.Get(element),
+ Padding = Padding.Get(element),
+ ShadowSize = ShadowSize.Get(element),
+ TextFont = TextFont.Get(element),
+ TextStyling = TextStyling.Get(element),
+ TextBaseColor = TextBaseColor.Get(element)
+ };
}
- /// Calls .
- public static implicit operator UIStyling(UIStyle style) => new(style);
-
- /// Calls .
- public static implicit operator UIStyling(Func styling) => new(styling);
+ /// Returns this styling instance with set to .
+ public UIStyling Bind(UIElement element) => this with { Element = element };
}
diff --git a/FGEGraphics/UISystem/UIToggleBox.cs b/FGEGraphics/UISystem/UIToggleBox.cs
index ac2cb157..db910291 100644
--- a/FGEGraphics/UISystem/UIToggleBox.cs
+++ b/FGEGraphics/UISystem/UIToggleBox.cs
@@ -67,11 +67,11 @@ public void Toggle()
/// The styling of the label.
/// The anchor to use when positioning the box and the icon in a list.
/// A tuple of the toggle box, label, and their list container.
- public static (UIToggleBox Box, UILabel Label, UIListGroup List) WithLabel(string text, int spacing, UIStyling styling, UILayout layout, bool toggled = false, UIStyling labelStyling = default, UIAnchor listAnchor = null)
+ public static (UIToggleBox Box, UILabel Label, UIListGroup List) WithLabel(string text, int spacing, UIStyling styling, UILayout layout, bool toggled = false, UIStyling labelStyling = null, UIAnchor listAnchor = null)
{
- UIToggleBox box = new(styling, layout.AtOrigin(), toggled);
+ UIToggleBox box = new(styling, layout.Copy().SetOrigin(), toggled);
UIListGroup list = new(spacing, layout, vertical: false, anchor: listAnchor ?? UIAnchor.TOP_LEFT);
- UILabel label = new(text, labelStyling.IsEmpty ? styling.Bind(box) : labelStyling, new UILayout());
+ UILabel label = new(text, labelStyling ?? styling?.Bind(box), new UILayout());
list.AddListItem(box);
list.AddListItem(label);
return (box, label, list);