Fix various platform issues and slightly improve UI

* Fix crash on lower screen resolutions when clicking on Terminal

Mouse coordinates were not being scaled from screenspace into
canvas space, resulting in a crash when clicking on the Terminal.

Also add documentation for internal VirtualScreen
implementation, and define dynamic properties for
CanvasWidth and CanvasHeight in the UI system.

Issue: #13
Signed-off-by: Ritchie Frodomar <alkalinethunder@gmail.com>

* Remove old GuiService virtual screen and document IVirtualScreen

Whoever wrote that old code was high, fuck... wait a minute...

Signed-off-by: Ritchie Frodomar <alkalinethunder@gmail.com>

* Fix various errors after emergency rebase

* Remove Rider caches

* Attempt to enable Wayland support

* Add support for enabling/disabling Wayland support on Linux

* Move SettingsManager to GameApplication so settings can be loaded before the game engine fully boots.

* Test commit.

Signed-off-by: Ritchie Frodomar <alkalinethunder@gmail.com>

* Move Terminals to their own tile

* Add support for getting CPU name on Windows via registry

* Add support for disabling background blur

* Fix window hints not being restored when switching between main tool tabs

* Testing the DCO acknowledgement job

Signed-off-by: Ritchie Frodomar <alkalinethunder@gmail.com>
This commit is contained in:
Ritchie Frodomar 2024-10-28 03:17:01 +00:00
parent 7f8f7dbf42
commit 75c8b5432c
1256 changed files with 7908 additions and 7874 deletions

0
.config/dotnet-tools.json Normal file → Executable file
View file

3
.gitignore vendored
View file

@ -1,3 +1,6 @@
# Rider caches
.idea/
# docfx # docfx
api/ api/
public/ public/

0
.gitlab-ci.yml Normal file → Executable file
View file

0
Assets/Assets/Core/WebSites.meta Normal file → Executable file
View file

0
Assets/Assets/Core/WebSites/404.prefab Normal file → Executable file
View file

0
Assets/Assets/Core/WebSites/404.prefab.meta Normal file → Executable file
View file

0
Assets/Assets/Core/WebSites/TheMissingPage.asset Normal file → Executable file
View file

0
Assets/Assets/Core/WebSites/TheMissingPage.asset.meta Normal file → Executable file
View file

0
Assets/Assets/Main/Websites/NewCipherToday.asset Normal file → Executable file
View file

0
Assets/Assets/Main/Websites/NewCipherToday.asset.meta Normal file → Executable file
View file

0
Assets/Assets/Main/Websites/NewCipherToday.prefab Normal file → Executable file
View file

0
Assets/Assets/Main/Websites/NewCipherToday.prefab.meta Normal file → Executable file
View file

0
Assets/Editor/CustomImporters/ArticleImporter.cs Normal file → Executable file
View file

View file

View file

0
Assets/Scripts/UI/Shell/ShellNavigationLink.cs Normal file → Executable file
View file

0
Assets/Scripts/UI/Shell/ShellNavigationLink.cs.meta Normal file → Executable file
View file

0
Assets/Scripts/UI/Websites/News.meta Normal file → Executable file
View file

0
Assets/Scripts/UI/Websites/News/NewsWebSite.cs Normal file → Executable file
View file

0
Assets/Scripts/UI/Websites/News/NewsWebSite.cs.meta Normal file → Executable file
View file

0
Assets/Scripts/UI/Widgets/Prefabs/SocialPost.prefab Normal file → Executable file
View file

View file

View file

View file

0
Assets/Scripts/UI/Widgets/Settings/SocialPostWidget.cs Normal file → Executable file
View file

View file

View file

View file

View file

View file

0
CONTRIBUTING.md Normal file → Executable file
View file

0
LICENSE Normal file → Executable file
View file

0
README.md Normal file → Executable file
View file

0
mgfxc-wine-setup.sh Normal file → Executable file
View file

View file

@ -1,13 +0,0 @@
# Default ignored files
/shelf/
/workspace.xml
# Rider ignored files
/contentModel.xml
/.idea.sociallydistant.iml
/projectSettingsUpdater.xml
/modules.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

View file

@ -1 +0,0 @@
sociallydistant

View file

@ -1,4 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" addBOMForNewFiles="with BOM under Windows, with no BOM otherwise" />
</project>

View file

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="UserContentModel">
<attachedFolders>
<Path>../docs</Path>
</attachedFolders>
<explicitIncludes />
<explicitExcludes />
</component>
</project>

View file

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
</component>
</project>

0
src/AcidicGUI/AcidicGUI.csproj Normal file → Executable file
View file

0
src/AcidicGUI/ColorHelpers.cs Normal file → Executable file
View file

0
src/AcidicGUI/CustomProperties/CanvasAnchors.cs Normal file → Executable file
View file

0
src/AcidicGUI/CustomProperties/CustomPropertyObject.cs Normal file → Executable file
View file

2
src/AcidicGUI/CustomProperties/FlexPanelProperties.cs Normal file → Executable file
View file

@ -14,7 +14,7 @@ public sealed class FlexPanelProperties : CustomPropertyObject
get => proportionalValue; get => proportionalValue;
set set
{ {
proportionalValue = MathHelper.Clamp(value, 0f, 1f); proportionalValue = Math.Max(0, value);
Widget.InvalidateLayout(); Widget.InvalidateLayout();
} }
} }

0
src/AcidicGUI/CustomProperties/StructProperty.cs Normal file → Executable file
View file

18
src/AcidicGUI/Effects/IEffect.cs Normal file → Executable file
View file

@ -1,11 +1,9 @@
using Microsoft.Xna.Framework; namespace AcidicGUI.Effects;
namespace AcidicGUI.Effects; public interface IEffect
{
public interface IEffect int PassesCount { get; }
{
int PassesCount { get; } void Use(int pass);
void UpdateOpacity(float opacity);
void Use(int pass);
void UpdateWidgetParameters(float opacity, Matrix widgetTransform);
} }

0
src/AcidicGUI/Effects/IWidgetEffect.cs Normal file → Executable file
View file

0
src/AcidicGUI/Events/FocusEvent.cs Normal file → Executable file
View file

0
src/AcidicGUI/Events/GuiEvent.cs Normal file → Executable file
View file

0
src/AcidicGUI/Events/IDragEndHandler.cs Normal file → Executable file
View file

0
src/AcidicGUI/Events/IDragHandler.cs Normal file → Executable file
View file

0
src/AcidicGUI/Events/IDragStartHandler.cs Normal file → Executable file
View file

0
src/AcidicGUI/Events/IGainFocusHandler.cs Normal file → Executable file
View file

0
src/AcidicGUI/Events/IKeyCharHandler.cs Normal file → Executable file
View file

0
src/AcidicGUI/Events/IKeyDownHandler.cs Normal file → Executable file
View file

0
src/AcidicGUI/Events/IKeyUpHandler.cs Normal file → Executable file
View file

0
src/AcidicGUI/Events/ILoseFocusHandler.cs Normal file → Executable file
View file

0
src/AcidicGUI/Events/IMouseClickHandler.cs Normal file → Executable file
View file

0
src/AcidicGUI/Events/IMouseDownHandler.cs Normal file → Executable file
View file

0
src/AcidicGUI/Events/IMouseEnterHandler.cs Normal file → Executable file
View file

0
src/AcidicGUI/Events/IMouseHandler.cs Normal file → Executable file
View file

0
src/AcidicGUI/Events/IMouseLeaveHandler.cs Normal file → Executable file
View file

0
src/AcidicGUI/Events/IMouseMoveHandler.cs Normal file → Executable file
View file

0
src/AcidicGUI/Events/IMouseScrollHandler.cs Normal file → Executable file
View file

0
src/AcidicGUI/Events/IMouseUpHandler.cs Normal file → Executable file
View file

0
src/AcidicGUI/Events/IPreviewKeyCharHandler.cs Normal file → Executable file
View file

0
src/AcidicGUI/Events/IPreviewKeyDownHandler.cs Normal file → Executable file
View file

0
src/AcidicGUI/Events/IPreviewKeyUpHandler.cs Normal file → Executable file
View file

0
src/AcidicGUI/Events/IUpdateHandler.cs Normal file → Executable file
View file

0
src/AcidicGUI/Events/KeyCharEvent.cs Normal file → Executable file
View file

0
src/AcidicGUI/Events/KeyEvent.cs Normal file → Executable file
View file

0
src/AcidicGUI/Events/ModifierKeys.cs Normal file → Executable file
View file

0
src/AcidicGUI/Events/MouseButtonEvent.cs Normal file → Executable file
View file

0
src/AcidicGUI/Events/MouseEvent.cs Normal file → Executable file
View file

0
src/AcidicGUI/Events/MouseMoveEvent.cs Normal file → Executable file
View file

0
src/AcidicGUI/Events/MouseScrollEvent.cs Normal file → Executable file
View file

901
src/AcidicGUI/GuiManager.cs Normal file → Executable file
View file

@ -1,446 +1,457 @@
using AcidicGUI.Animation; using AcidicGUI.Events;
using AcidicGUI.Events; using AcidicGUI.Layout;
using AcidicGUI.Layout; using AcidicGUI.Rendering;
using AcidicGUI.Rendering; using AcidicGUI.TextRendering;
using AcidicGUI.TextRendering; using AcidicGUI.VisualStyles;
using AcidicGUI.VisualStyles; using AcidicGUI.Widgets;
using AcidicGUI.Widgets; using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Input;
namespace AcidicGUI;
namespace AcidicGUI;
public sealed class GuiManager : IFontFamilyProvider
public sealed class GuiManager : IFontFamilyProvider {
{ private readonly IGuiContext context;
private readonly IGuiContext context; private readonly Widget.TopLevelCollection topLevels;
private readonly Widget.TopLevelCollection topLevels; private readonly GuiRenderer renderer;
private readonly GuiRenderer renderer; private readonly FallbackVisualStyle fallbackVisualStyle = new FallbackVisualStyle();
private readonly FallbackVisualStyle fallbackVisualStyle = new FallbackVisualStyle(); private MouseButton previousMouseButtonFlags;
private MouseButton previousMouseButtonFlags; private ButtonState leftControl;
private ButtonState leftControl; private ButtonState rightControl;
private ButtonState rightControl; private ButtonState leftShift;
private ButtonState leftShift; private ButtonState rightShift;
private ButtonState rightShift; private ButtonState leftAlt;
private ButtonState leftAlt; private ButtonState rightAlt;
private ButtonState rightAlt;
private IVisualStyle? visualStyleOverride;
private IVisualStyle? visualStyleOverride; private int screenWidth;
private int screenWidth; private int screenHeight;
private int screenHeight; private bool isRendering;
private bool isRendering; private Widget? hoveredWidget;
private Widget? hoveredWidget; private Widget? widgetBeingDragged;
private Widget? widgetBeingDragged; private Widget? keyboardFocus;
private Widget? keyboardFocus; private MouseState? previousMouseState;
private MouseState? previousMouseState; private bool reachedFirstUpdate;
private bool reachedFirstUpdate;
public bool IsRendering => isRendering;
public bool IsRendering => isRendering; public IOrderedCollection<Widget> TopLevels => topLevels;
public IOrderedCollection<Widget> TopLevels => topLevels;
public GuiManager(IGuiContext context, IVisualStyle? globalStyle = null)
public GuiManager(IGuiContext context, IVisualStyle? globalStyle = null) {
{ this.context = context;
this.context = context; topLevels = new Widget.TopLevelCollection(this);
topLevels = new Widget.TopLevelCollection(this); renderer = new GuiRenderer(context);
renderer = new GuiRenderer(context);
visualStyleOverride = globalStyle;
visualStyleOverride = globalStyle; }
}
/// <summary>
public bool IsFocused(Widget widget) /// Invalidates the geometry of all interface elements recursively. Use this when changing a global UI setting that affects how all widgets render.
{ /// Note: This is expensive. That should be obvious. So do not call it every frame.
return widget == keyboardFocus; /// </summary>
} public void InvalidateAllGeometry()
{
public void SetFocusedWidget(Widget? widgetToFocus) foreach (var toplevel in TopLevels)
{ {
if (widgetToFocus == keyboardFocus) toplevel.InvalidateGeometry(true);
return; }
}
var e = new FocusEvent(widgetToFocus);
public bool IsFocused(Widget widget)
if (keyboardFocus != null) {
{ return widget == keyboardFocus;
Bubble<ILoseFocusHandler, FocusEvent>(keyboardFocus, e, x => x.OnFocusLost); }
}
public void SetFocusedWidget(Widget? widgetToFocus)
keyboardFocus = widgetToFocus; {
if (widgetToFocus == keyboardFocus)
if (keyboardFocus != null) return;
{
Bubble<IGainFocusHandler, FocusEvent>(keyboardFocus, e, x => x.OnFocusGained); var e = new FocusEvent(widgetToFocus);
}
} if (keyboardFocus != null)
{
public void SendMouseButton(MouseButtonEventArgs e) Bubble<ILoseFocusHandler, FocusEvent>(keyboardFocus, e, x => x.OnFocusLost);
{ }
var previousState = (previousMouseButtonFlags & e.Button) != 0
? ButtonState.Pressed keyboardFocus = widgetToFocus;
: ButtonState.Released;
if (keyboardFocus != null)
HandleMouseButton(e.Button, previousState, e.ButtonState, e.Position, Point.Zero); {
Bubble<IGainFocusHandler, FocusEvent>(keyboardFocus, e, x => x.OnFocusGained);
if (e.ButtonState == ButtonState.Pressed) }
{ }
previousMouseButtonFlags = previousMouseButtonFlags | e.Button;
} public void SendMouseButton(MouseButtonEventArgs e)
else {
{ var previousState = (previousMouseButtonFlags & e.Button) != 0
previousMouseButtonFlags = previousMouseButtonFlags & ~e.Button; ? ButtonState.Pressed
} : ButtonState.Released;
}
HandleMouseButton(e.Button, previousState, e.ButtonState, TranslateMousePosition(e.Position), Point.Zero);
public void SendCharacter(Keys key, char character)
{ if (e.ButtonState == ButtonState.Pressed)
var e = new KeyCharEvent(key, GetKeyModifiers(), character); {
previousMouseButtonFlags = previousMouseButtonFlags | e.Button;
if (hoveredWidget != null) }
{ else
Bubble<IPreviewKeyCharHandler, KeyCharEvent>(hoveredWidget, e, x => x.OnPreviewKeyChar); {
} previousMouseButtonFlags = previousMouseButtonFlags & ~e.Button;
}
if (keyboardFocus != null) }
{
Bubble<IKeyCharHandler, KeyCharEvent>(keyboardFocus, e, x => x.OnKeyChar); public void SendCharacter(Keys key, char character)
} {
} var e = new KeyCharEvent(key, GetKeyModifiers(), character);
private ModifierKeys GetKeyModifiers() if (hoveredWidget != null)
{ {
var result = ModifierKeys.None; Bubble<IPreviewKeyCharHandler, KeyCharEvent>(hoveredWidget, e, x => x.OnPreviewKeyChar);
}
if (leftControl == ButtonState.Pressed || rightControl == ButtonState.Pressed)
result |= ModifierKeys.Control; if (keyboardFocus != null)
{
if (leftShift == ButtonState.Pressed || rightShift == ButtonState.Pressed) Bubble<IKeyCharHandler, KeyCharEvent>(keyboardFocus, e, x => x.OnKeyChar);
result |= ModifierKeys.Shift; }
}
if (leftAlt == ButtonState.Pressed || rightAlt == ButtonState.Pressed)
result |= ModifierKeys.Alt; private ModifierKeys GetKeyModifiers()
{
return result; var result = ModifierKeys.None;
}
if (leftControl == ButtonState.Pressed || rightControl == ButtonState.Pressed)
public void SendKey(Keys key, ButtonState state) result |= ModifierKeys.Control;
{
switch (key) if (leftShift == ButtonState.Pressed || rightShift == ButtonState.Pressed)
{ result |= ModifierKeys.Shift;
case Keys.LeftControl:
leftControl = state; if (leftAlt == ButtonState.Pressed || rightAlt == ButtonState.Pressed)
break; result |= ModifierKeys.Alt;
case Keys.RightControl:
rightControl = state; return result;
break; }
case Keys.LeftShift:
leftShift = state; public void SendKey(Keys key, ButtonState state)
break; {
case Keys.RightShift: switch (key)
rightShift = state; {
break; case Keys.LeftControl:
case Keys.LeftAlt: leftControl = state;
leftAlt = state; break;
break; case Keys.RightControl:
case Keys.RightAlt: rightControl = state;
rightAlt = state; break;
break; case Keys.LeftShift:
} leftShift = state;
break;
var e = new KeyEvent(key, GetKeyModifiers()); case Keys.RightShift:
rightShift = state;
if (hoveredWidget != null) break;
{ case Keys.LeftAlt:
if (state == ButtonState.Pressed) leftAlt = state;
{ break;
Bubble<IPreviewKeyDownHandler, KeyEvent>(hoveredWidget, e, x => x.OnPreviewKeyDown); case Keys.RightAlt:
} rightAlt = state;
else break;
{ }
Bubble<IPreviewKeyUpHandler, KeyEvent>(hoveredWidget, e, x => x.OnPreviewKeyUp);
} var e = new KeyEvent(key, GetKeyModifiers());
}
if (hoveredWidget != null)
if (keyboardFocus != null) {
{ if (state == ButtonState.Pressed)
if (state == ButtonState.Pressed) {
{ Bubble<IPreviewKeyDownHandler, KeyEvent>(hoveredWidget, e, x => x.OnPreviewKeyDown);
Bubble<IKeyDownHandler, KeyEvent>(keyboardFocus, e, x => x.OnKeyDown); }
} else
else {
{ Bubble<IPreviewKeyUpHandler, KeyEvent>(hoveredWidget, e, x => x.OnPreviewKeyUp);
Bubble<IKeyUpHandler, KeyEvent>(keyboardFocus, e, x => x.OnKeyUp); }
} }
}
} if (keyboardFocus != null)
{
public void SetMouseState(MouseState state) if (state == ButtonState.Pressed)
{ {
if (previousMouseState == null) Bubble<IKeyDownHandler, KeyEvent>(keyboardFocus, e, x => x.OnKeyDown);
previousMouseState = state; }
else if (previousMouseState.Value == state) else
return; {
Bubble<IKeyUpHandler, KeyEvent>(keyboardFocus, e, x => x.OnKeyUp);
HandleMouseEvents(previousMouseState.Value, state); }
previousMouseState = state; }
} }
public void UpdateLayout() public void SetMouseState(MouseState state)
{ {
reachedFirstUpdate = true; if (previousMouseState == null)
previousMouseState = state;
var mustRebuildLayout = false; else if (previousMouseState.Value == state)
var tolerance = 0.001f; return;
if (MathF.Abs(screenWidth - context.PhysicalScreenWidth) >= tolerance HandleMouseEvents(previousMouseState.Value, state);
|| MathF.Abs(screenHeight - context.PhysicalScreenHeight) >= tolerance) previousMouseState = state;
{ }
screenWidth = context.PhysicalScreenWidth;
screenHeight = context.PhysicalScreenHeight; public void UpdateLayout()
{
mustRebuildLayout = true; reachedFirstUpdate = true;
}
var mustRebuildLayout = false;
if (mustRebuildLayout) var tolerance = 0.001f;
{
foreach (Widget topLevel in topLevels) if (MathF.Abs(screenWidth - context.CanvasWidth) >= tolerance
{ || MathF.Abs(screenHeight - context.CanvasHeight) >= tolerance)
topLevel.InvalidateLayout(); {
} screenWidth = context.CanvasWidth;
} screenHeight = context.CanvasHeight;
}
mustRebuildLayout = true;
public void Update(float deltaTime) }
{
Animator.Update(deltaTime); if (mustRebuildLayout)
{
foreach (Widget widget in this.CollapseChildren()) foreach (Widget topLevel in topLevels)
{ {
if (widget is IUpdateHandler handler) topLevel.InvalidateLayout();
handler.Update(deltaTime); }
} }
} }
public IVisualStyle GetVisualStyle() public void Update(float deltaTime)
{ {
if (fallbackVisualStyle.FallbackFont == null) foreach (Widget widget in CollapseChildren())
fallbackVisualStyle.FallbackFont = context.GetFallbackFont(); {
if (widget is IUpdateHandler handler)
return visualStyleOverride ?? fallbackVisualStyle; handler.Update(deltaTime);
} }
}
public void Render()
{ public IVisualStyle GetVisualStyle()
isRendering = true; {
if (fallbackVisualStyle.FallbackFont == null)
//renderer.SetLayer(-32768); fallbackVisualStyle.FallbackFont = context.GetFallbackFont();
foreach (Widget widget in topLevels) return visualStyleOverride ?? fallbackVisualStyle;
widget.RenderInternal(renderer, Matrix.Identity); }
isRendering = false; public void Render()
} {
isRendering = true;
internal void SubmitForLayoutUpdateInternal(Widget widget)
{ renderer.SetLayer(-32768);
if (!reachedFirstUpdate)
return; foreach (Widget widget in topLevels)
widget.RenderInternal(renderer);
if (widget.Parent != null)
{ renderer.RenderBatches();
widget.UpdateLayout(context, widget.ContentArea);
} isRendering = false;
else }
{
widget.UpdateLayout(context, new LayoutRect(0, 0, screenWidth, screenHeight)); internal void SubmitForLayoutUpdateInternal(Widget widget)
} {
} if (!reachedFirstUpdate)
return;
public IFontFamily GetFont(PresetFontFamily family)
{ if (widget.Parent != null)
return GetVisualStyle().GetFont(family); {
} widget.UpdateLayout(context, widget.ContentArea);
}
internal GraphicsDevice GetGraphicsDeviceInternal() else
{ {
return context.GraphicsDevice; widget.UpdateLayout(context, new LayoutRect(0, 0, screenWidth, screenHeight));
} }
}
private IEnumerable<Widget> CollapseChildren()
{ public IFontFamily GetFont(PresetFontFamily family)
for (var i = topLevels.Count - 1; i >= 0; i--) {
{ return GetVisualStyle().GetFont(family);
Widget toplevel = topLevels[i]; }
if (!toplevel.Enabled)
continue; internal GraphicsDevice GetGraphicsDeviceInternal()
{
foreach (Widget child in toplevel.CollapseChildren(true, false)) return context.GraphicsDevice;
{ }
yield return child;
} private IEnumerable<Widget> CollapseChildren()
{
yield return toplevel; for (var i = topLevels.Count - 1; i >= 0; i--)
} {
} Widget toplevel = topLevels[i];
if (!toplevel.Enabled)
private Point TranslateMousePosition(Point mousePosition) continue;
{
return new Vector2( foreach (Widget child in toplevel.CollapseChildren(true, false))
((float)mousePosition.X / context.GraphicsDevice.PresentationParameters.BackBufferWidth) * {
context.PhysicalScreenWidth, yield return child;
((float)mousePosition.Y / context.GraphicsDevice.PresentationParameters.BackBufferHeight) * }
context.PhysicalScreenHeight
).ToPoint(); yield return toplevel;
} }
}
private void HandleMouseEvents(MouseState previous, MouseState current)
{ private Point TranslateMousePosition(Point mousePosition)
var prevPosition = TranslateMousePosition(previous.Position); {
var currPosition = TranslateMousePosition(current.Position); return new Vector2(
((float)mousePosition.X / context.GraphicsDevice.PresentationParameters.BackBufferWidth) *
var prevWheel = previous.ScrollWheelValue; context.CanvasWidth,
var currWheel = current.ScrollWheelValue; ((float)mousePosition.Y / context.GraphicsDevice.PresentationParameters.BackBufferHeight) *
context.CanvasHeight
var positionDelta = currPosition - prevPosition; ).ToPoint();
var wheelDelta = -(currWheel - prevWheel); }
HandleMouseMovement(new MouseMoveEvent(currPosition, positionDelta)); private void HandleMouseEvents(MouseState previous, MouseState current)
HandleMouseScroll(new MouseScrollEvent(currPosition, wheelDelta)); {
var prevPosition = TranslateMousePosition(previous.Position);
HandleDragEvents(MouseButton.Left, previous.LeftButton, current.LeftButton, currPosition, positionDelta); var currPosition = TranslateMousePosition(current.Position);
HandleDragEvents(MouseButton.Middle, previous.MiddleButton, current.MiddleButton, currPosition, positionDelta);
HandleDragEvents(MouseButton.Right, previous.RightButton, current.RightButton, currPosition, positionDelta); var prevWheel = previous.ScrollWheelValue;
HandleDragEvents(MouseButton.X1, previous.XButton1, current.XButton1, currPosition, positionDelta); var currWheel = current.ScrollWheelValue;
HandleDragEvents(MouseButton.X2, previous.XButton2, current.XButton2, currPosition, positionDelta);
} var positionDelta = currPosition - prevPosition;
var wheelDelta = -(currWheel - prevWheel);
private void HandleDragEvents(
MouseButton button, HandleMouseMovement(new MouseMoveEvent(currPosition, positionDelta));
ButtonState previousState, HandleMouseScroll(new MouseScrollEvent(currPosition, wheelDelta));
ButtonState currentState,
Point position, HandleDragEvents(MouseButton.Left, previous.LeftButton, current.LeftButton, currPosition, positionDelta);
Point movement HandleDragEvents(MouseButton.Middle, previous.MiddleButton, current.MiddleButton, currPosition, positionDelta);
) HandleDragEvents(MouseButton.Right, previous.RightButton, current.RightButton, currPosition, positionDelta);
{ HandleDragEvents(MouseButton.X1, previous.XButton1, current.XButton1, currPosition, positionDelta);
if (previousState != currentState) HandleDragEvents(MouseButton.X2, previous.XButton2, current.XButton2, currPosition, positionDelta);
return; }
if (currentState == ButtonState.Released) private void HandleDragEvents(
return; MouseButton button,
ButtonState previousState,
if (widgetBeingDragged == null) ButtonState currentState,
return; Point position,
Point movement
var e = new MouseButtonEvent(position, movement, button, currentState); )
Bubble<IDragHandler, MouseButtonEvent>(widgetBeingDragged, e, x => x.OnDrag); {
} if (previousState != currentState)
return;
private void HandleMouseButton(MouseButton button, ButtonState previous, ButtonState current, Point position, Point delta)
{ if (currentState == ButtonState.Released)
var e = new MouseButtonEvent(position, delta, button, current); return;
if (previous == ButtonState.Released) if (widgetBeingDragged == null)
{ return;
if (hoveredWidget == null)
return; var e = new MouseButtonEvent(position, movement, button, currentState);
Bubble<IDragHandler, MouseButtonEvent>(widgetBeingDragged, e, x => x.OnDrag);
Bubble<IMouseDownHandler, MouseButtonEvent>(hoveredWidget, e, x => x.OnMouseDown); }
if (e.Handled) private void HandleMouseButton(MouseButton button, ButtonState previous, ButtonState current, Point position, Point delta)
e.Unhandle(); {
var e = new MouseButtonEvent(position, delta, button, current);
widgetBeingDragged = hoveredWidget;
Bubble<IDragStartHandler, MouseButtonEvent>(hoveredWidget, e, x => x.OnDragStart); if (previous == ButtonState.Released)
} {
else if (previous == ButtonState.Pressed) if (hoveredWidget == null)
{ return;
if (widgetBeingDragged != null)
{ Bubble<IMouseDownHandler, MouseButtonEvent>(hoveredWidget, e, x => x.OnMouseDown);
Bubble<IDragEndHandler, MouseButtonEvent>(widgetBeingDragged, e, x => x.OnDragEnd);
widgetBeingDragged = null; if (e.Handled)
} e.Unhandle();
if (hoveredWidget != null) widgetBeingDragged = hoveredWidget;
{ Bubble<IDragStartHandler, MouseButtonEvent>(hoveredWidget, e, x => x.OnDragStart);
Bubble<IMouseClickHandler, MouseButtonEvent>(hoveredWidget, e, x => x.OnMouseClick); }
Bubble<IMouseUpHandler, MouseButtonEvent>(hoveredWidget, e, x => x.OnMouseUp); else if (previous == ButtonState.Pressed)
} {
if (widgetBeingDragged != null)
if (!e.FocusWanted && hoveredWidget?.IsFocused == false) {
SetFocusedWidget(null); Bubble<IDragEndHandler, MouseButtonEvent>(widgetBeingDragged, e, x => x.OnDragEnd);
widgetBeingDragged = null;
} }
}
if (hoveredWidget != null)
private void Bubble<THandler, TEvent>(Widget widget, TEvent e, Func<THandler, Action<TEvent>> getHandler) {
where TEvent : GuiEvent Bubble<IMouseClickHandler, MouseButtonEvent>(hoveredWidget, e, x => x.OnMouseClick);
{ Bubble<IMouseUpHandler, MouseButtonEvent>(hoveredWidget, e, x => x.OnMouseUp);
Widget? next = widget; }
while (next != null && !e.Handled) if (!e.FocusWanted && hoveredWidget?.IsFocused == false)
{ SetFocusedWidget(null);
if (next is THandler handler)
{ }
getHandler(handler)(e); }
}
private void Bubble<THandler, TEvent>(Widget widget, TEvent e, Func<THandler, Action<TEvent>> getHandler)
if (e.FocusWanted) where TEvent : GuiEvent
{ {
SetFocusedWidget(next); Widget? next = widget;
break;
} while (next != null && !e.Handled)
{
next = next.Parent; if (next is THandler handler)
} {
} getHandler(handler)(e);
}
private void HandleMouseMovement(MouseMoveEvent e)
{ if (e.FocusWanted)
Widget? newHoveredWidget = null; {
SetFocusedWidget(next);
foreach (Widget widget in CollapseChildren()) break;
{ }
if (!widget.IsVisible)
continue; next = next.Parent;
}
if (widget is not IMouseHandler) }
continue;
private void HandleMouseMovement(MouseMoveEvent e)
if (widget.ClippedContentArea.Contains(e.Position)) {
{ Widget? newHoveredWidget = null;
newHoveredWidget = widget;
break; foreach (Widget widget in CollapseChildren())
} {
} if (!widget.IsVisible)
continue;
if (newHoveredWidget != hoveredWidget)
{ if (widget is not IMouseHandler)
if (hoveredWidget != null && !hoveredWidget.ContainsChild(newHoveredWidget)) continue;
{
Bubble<IMouseLeaveHandler, MouseMoveEvent>(hoveredWidget, e, x => x.OnMouseLeave); if (widget.ClippedContentArea.Contains(e.Position))
} {
newHoveredWidget = widget;
hoveredWidget = newHoveredWidget; break;
}
if (hoveredWidget != null) }
{
Bubble<IMouseEnterHandler, MouseMoveEvent>(hoveredWidget, e, x => x.OnMouseEnter); if (newHoveredWidget != hoveredWidget)
} {
} if (hoveredWidget != null && !hoveredWidget.ContainsChild(newHoveredWidget))
{
if (hoveredWidget != null) Bubble<IMouseLeaveHandler, MouseMoveEvent>(hoveredWidget, e, x => x.OnMouseLeave);
{ }
Bubble<IMouseMoveHandler, MouseMoveEvent>(hoveredWidget, e, x => x.OnMouseMove);
} hoveredWidget = newHoveredWidget;
}
if (hoveredWidget != null)
private void HandleMouseScroll(MouseScrollEvent e) {
{ Bubble<IMouseEnterHandler, MouseMoveEvent>(hoveredWidget, e, x => x.OnMouseEnter);
if (e.ScrollDelta == 0) }
return; }
if (hoveredWidget == null) if (hoveredWidget != null)
return; {
Bubble<IMouseMoveHandler, MouseMoveEvent>(hoveredWidget, e, x => x.OnMouseMove);
Bubble<IMouseScrollHandler, MouseScrollEvent>(hoveredWidget, e, x => x.OnMouseScroll); }
} }
private void HandleMouseScroll(MouseScrollEvent e)
{
if (e.ScrollDelta == 0)
return;
if (hoveredWidget == null)
return;
Bubble<IMouseScrollHandler, MouseScrollEvent>(hoveredWidget, e, x => x.OnMouseScroll);
}
} }

0
src/AcidicGUI/GuiSynchronizationContext.cs Normal file → Executable file
View file

62
src/AcidicGUI/IGuiContext.cs Normal file → Executable file
View file

@ -1,36 +1,28 @@
using AcidicGUI.Effects; using AcidicGUI.Effects;
using AcidicGUI.Layout; using AcidicGUI.Layout;
using AcidicGUI.TextRendering; using AcidicGUI.TextRendering;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Graphics;
namespace AcidicGUI;
namespace AcidicGUI;
public interface IGuiContext
public interface IGuiContext {
{ int PhysicalWidth { get; }
int PhysicalScreenWidth { get; } int PhysicalHeight { get; }
int PhysicalScreenHeight { get; } int CanvasWidth { get; }
GraphicsDevice GraphicsDevice { get; } int CanvasHeight { get; }
GraphicsDevice GraphicsDevice { get; }
void Render(
VertexBuffer vertices, void Render(VertexPositionColorTexture[] vertices, int[] indices, Texture2D? texture, LayoutRect? clipRect = null);
IndexBuffer indices, void Render(VertexBuffer vertices, IndexBuffer indices, int offset, int primitiveCount, Texture2D? texture, LayoutRect? clipRect = null, IEffect? effectOverride = null, float opacity = 1);
int offset,
int primitiveCount, /// <summary>
Texture2D? texture, /// Renders the user interface to the given RenderTarget2D. Useful for certain widget rendering techniques such as background blurs.
LayoutRect? clipRect = null, /// </summary>
IEffect? effectOverride = null, /// <param name="destination"></param>
float opacity = 1, void Grab(RenderTarget2D destination);
Matrix? widgetTransform = null
); void RestoreRenderState();
/// <summary> IFontFamily GetFallbackFont();
/// Renders the user interface to the given RenderTarget2D. Useful for certain widget rendering techniques such as background blurs.
/// </summary>
/// <param name="destination"></param>
void Grab(RenderTarget2D destination);
void RestoreRenderState();
IFontFamily GetFallbackFont();
} }

0
src/AcidicGUI/IOrderedCollection.cs Normal file → Executable file
View file

0
src/AcidicGUI/Layout/Direction.cs Normal file → Executable file
View file

0
src/AcidicGUI/Layout/FlexMode.cs Normal file → Executable file
View file

0
src/AcidicGUI/Layout/HorizontalAlignment.cs Normal file → Executable file
View file

0
src/AcidicGUI/Layout/LayoutRect.cs Normal file → Executable file
View file

0
src/AcidicGUI/Layout/Padding.cs Normal file → Executable file
View file

0
src/AcidicGUI/Layout/TextAlignment.cs Normal file → Executable file
View file

0
src/AcidicGUI/Layout/VerticalAlignment.cs Normal file → Executable file
View file

0
src/AcidicGUI/ListAdapters/DataHelper.cs Normal file → Executable file
View file

0
src/AcidicGUI/ListAdapters/INotifyDataChanged.cs Normal file → Executable file
View file

0
src/AcidicGUI/ListAdapters/ListAdapter.cs Normal file → Executable file
View file

View file

0
src/AcidicGUI/ListAdapters/RecycleBin.cs Normal file → Executable file
View file

0
src/AcidicGUI/ListAdapters/ViewHolder.cs Normal file → Executable file
View file

729
src/AcidicGUI/Rendering/GeometryHelper.cs Normal file → Executable file
View file

@ -1,363 +1,368 @@
using AcidicGUI.Layout; using AcidicGUI.Layout;
using FontStashSharp.Interfaces; using FontStashSharp.Interfaces;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
namespace AcidicGUI.Rendering; namespace AcidicGUI.Rendering;
public class GeometryHelper : IFontStashRenderer2 public class GeometryHelper : IFontStashRenderer2
{ {
private readonly GuiMeshBuilder whiteMesh; private readonly GuiMeshBuilder whiteMesh;
private readonly Dictionary<Texture2D, GuiMeshBuilder> meshes = new(); private readonly Dictionary<Texture2D, GuiMeshBuilder> meshes = new();
private readonly GuiRenderer guiRenderer; private readonly GuiRenderer guiRenderer;
private readonly bool desaturate; private readonly bool desaturate;
private readonly LayoutRect? clipRect; private readonly LayoutRect? clipRect;
private readonly List<GuiSubMesh> flushedMeshes = new(); private readonly List<GuiSubMesh> flushedMeshes = new();
public float Layer => guiRenderer.Layer; public float Layer => guiRenderer.Layer;
public GeometryHelper(GuiRenderer guiRenderer, bool desaturate, LayoutRect? clipRect) public GeometryHelper(GuiRenderer guiRenderer, bool desaturate, LayoutRect? clipRect)
{ {
this.desaturate = desaturate; this.desaturate = desaturate;
this.guiRenderer = guiRenderer; this.guiRenderer = guiRenderer;
this.clipRect = clipRect; this.clipRect = clipRect;
whiteMesh = new GuiMeshBuilder(this, null, desaturate); whiteMesh = new GuiMeshBuilder(this, null, desaturate);
} }
private void Flush() public void PushLayer()
{ {
flushedMeshes.Add(whiteMesh.ExportSubMesh()); guiRenderer.PushLayer();
whiteMesh.Clear(); }
foreach (GuiMeshBuilder mesh in meshes.Values) private void Flush()
{ {
flushedMeshes.Add(mesh.ExportSubMesh()); flushedMeshes.Add(whiteMesh.ExportSubMesh());
mesh.Clear(); whiteMesh.Clear();
}
} foreach (GuiMeshBuilder mesh in meshes.Values)
{
public GuiMesh ExportMesh() flushedMeshes.Add(mesh.ExportSubMesh());
{ mesh.Clear();
Flush(); }
}
var meshList = flushedMeshes.ToArray();
public GuiMesh ExportMesh()
return new GuiMesh(meshList, clipRect); {
} Flush();
public GuiMeshBuilder GetMeshBuilder(Texture2D? texture) var meshList = flushedMeshes.ToArray();
{
if (texture == null) return new GuiMesh(meshList, clipRect);
return whiteMesh; }
if (!meshes.TryGetValue(texture, out GuiMeshBuilder? builder)) public GuiMeshBuilder GetMeshBuilder(Texture2D? texture)
{ {
builder = new GuiMeshBuilder(this, texture, desaturate); if (texture == null)
meshes.Add(texture, builder); return whiteMesh;
}
if (!meshes.TryGetValue(texture, out GuiMeshBuilder? builder))
return meshes[texture]; {
} builder = new GuiMeshBuilder(this, texture, desaturate);
meshes.Add(texture, builder);
public void AddRoundedRectangle(LayoutRect rectangle, float uniformRadius, Color color, Texture2D? texture = null) }
{
AddRoundedRectangle(rectangle, uniformRadius, uniformRadius, uniformRadius, uniformRadius, color, texture); return meshes[texture];
} }
public void AddQuadOutline( public void AddRoundedRectangle(LayoutRect rectangle, float uniformRadius, Color color, Texture2D? texture = null)
LayoutRect rectangle, {
float thickness, AddRoundedRectangle(rectangle, uniformRadius, uniformRadius, uniformRadius, uniformRadius, color, texture);
Color color, }
Texture2D? texture = null
) public void AddQuadOutline(
{ LayoutRect rectangle,
float smallerHalf = Math.Min(rectangle.Width, rectangle.Height) / 2f; float thickness,
if (smallerHalf <= thickness) Color color,
{ Texture2D? texture = null
AddQuad(rectangle, color, texture); )
return; {
} float smallerHalf = Math.Min(rectangle.Width, rectangle.Height) / 2f;
if (smallerHalf <= thickness)
float texelWidth = 1f / (texture?.Width ?? 1f); {
float texelHeight = 1f / (texture?.Height ?? 1f); AddQuad(rectangle, color, texture);
return;
var mesh = GetMeshBuilder(texture); }
var tl = mesh.AddVertex(new Vector2(rectangle.Left, rectangle.Top), color, new Vector2(0, 0)); float texelWidth = 1f / (texture?.Width ?? 1f);
var tr = mesh.AddVertex(new Vector2(rectangle.Right, rectangle.Top), color, new Vector2(1, 0)); float texelHeight = 1f / (texture?.Height ?? 1f);
var bl = mesh.AddVertex(new Vector2(rectangle.Left, rectangle.Bottom), color, new Vector2(0, 1));
var br = mesh.AddVertex(new Vector2(rectangle.Right, rectangle.Bottom), color, new Vector2(1, 1)); var mesh = GetMeshBuilder(texture);
var tlInner = mesh.AddVertex(new Vector2(rectangle.Left + thickness, rectangle.Top + thickness), color, new Vector2(texelWidth * thickness, texelHeight * thickness));
var trInner = mesh.AddVertex(new Vector2(rectangle.Right - thickness, rectangle.Top + thickness), color, new Vector2(1 - texelWidth * thickness, texelHeight * thickness)); var tl = mesh.AddVertex(new Vector2(rectangle.Left, rectangle.Top), color, new Vector2(0, 0));
var blInner = mesh.AddVertex(new Vector2(rectangle.Left + thickness, rectangle.Bottom - thickness), color, new Vector2(texelWidth * thickness, 1 - texelHeight * thickness)); var tr = mesh.AddVertex(new Vector2(rectangle.Right, rectangle.Top), color, new Vector2(1, 0));
var brInner = mesh.AddVertex(new Vector2(rectangle.Right - thickness, rectangle.Bottom - thickness), color, new Vector2(1 - texelWidth * thickness, 1 - texelHeight * thickness)); var bl = mesh.AddVertex(new Vector2(rectangle.Left, rectangle.Bottom), color, new Vector2(0, 1));
var br = mesh.AddVertex(new Vector2(rectangle.Right, rectangle.Bottom), color, new Vector2(1, 1));
mesh.AddQuad(tl, tr, tlInner, trInner); var tlInner = mesh.AddVertex(new Vector2(rectangle.Left + thickness, rectangle.Top + thickness), color, new Vector2(texelWidth * thickness, texelHeight * thickness));
mesh.AddQuad(tl, tlInner, bl, blInner); var trInner = mesh.AddVertex(new Vector2(rectangle.Right - thickness, rectangle.Top + thickness), color, new Vector2(1 - texelWidth * thickness, texelHeight * thickness));
mesh.AddQuad(trInner, tr, brInner, br); var blInner = mesh.AddVertex(new Vector2(rectangle.Left + thickness, rectangle.Bottom - thickness), color, new Vector2(texelWidth * thickness, 1 - texelHeight * thickness));
mesh.AddQuad(blInner, brInner, bl, br); var brInner = mesh.AddVertex(new Vector2(rectangle.Right - thickness, rectangle.Bottom - thickness), color, new Vector2(1 - texelWidth * thickness, 1 - texelHeight * thickness));
}
mesh.AddQuad(tl, tr, tlInner, trInner);
public void AddQuad(LayoutRect rectangle, Color color, Texture2D? texture = null) mesh.AddQuad(tl, tlInner, bl, blInner);
{ mesh.AddQuad(trInner, tr, brInner, br);
if (color.A == 0) mesh.AddQuad(blInner, brInner, bl, br);
return; }
var mesh = GetMeshBuilder(texture); public void AddQuad(LayoutRect rectangle, Color color, Texture2D? texture = null)
{
int tl = mesh.AddVertex(new Vector2(rectangle.Left, rectangle.Top), color, new Vector2(0, 0)); if (color.A == 0)
int tr = mesh.AddVertex(new Vector2(rectangle.Right, rectangle.Top), color, new Vector2(1, 0)); return;
int bl = mesh.AddVertex(new Vector2(rectangle.Left, rectangle.Bottom), color, new Vector2(0, 1));
int br = mesh.AddVertex(new Vector2(rectangle.Right, rectangle.Bottom), color, new Vector2(1, 1)); var mesh = GetMeshBuilder(texture);
mesh.AddQuad(tl, tr, bl, br); int tl = mesh.AddVertex(new Vector2(rectangle.Left, rectangle.Top), color, new Vector2(0, 0));
} int tr = mesh.AddVertex(new Vector2(rectangle.Right, rectangle.Top), color, new Vector2(1, 0));
int bl = mesh.AddVertex(new Vector2(rectangle.Left, rectangle.Bottom), color, new Vector2(0, 1));
public void AddRoundedRectangle( int br = mesh.AddVertex(new Vector2(rectangle.Right, rectangle.Bottom), color, new Vector2(1, 1));
LayoutRect rectangle,
float radiusTopLeft, mesh.AddQuad(tl, tr, bl, br);
float radiusTopRight, }
float radiusBottomLeft,
float radiusBottomRight, public void AddRoundedRectangle(
Color color, LayoutRect rectangle,
Texture2D? texture = null float radiusTopLeft,
) float radiusTopRight,
{ float radiusBottomLeft,
var mesh = GetMeshBuilder(texture); float radiusBottomRight,
Color color,
float halfWidth = rectangle.Width / 2f; Texture2D? texture = null
float halfHeight = rectangle.Height / 2f; )
{
float smallerHalf = MathF.Min(halfWidth, halfHeight); var mesh = GetMeshBuilder(texture);
radiusTopLeft = MathHelper.Clamp(radiusTopLeft, 0, smallerHalf); float halfWidth = rectangle.Width / 2f;
radiusBottomLeft = MathHelper.Clamp(radiusBottomLeft, 0, smallerHalf); float halfHeight = rectangle.Height / 2f;
radiusTopRight = MathHelper.Clamp(radiusTopRight, 0, smallerHalf);
radiusBottomRight = MathHelper.Clamp(radiusBottomRight, 0, smallerHalf); float smallerHalf = MathF.Min(halfWidth, halfHeight);
if (radiusTopLeft <= 0 && radiusTopRight <= 0 && radiusBottomLeft <= 0 && radiusBottomRight <= 0) radiusTopLeft = MathHelper.Clamp(radiusTopLeft, 0, smallerHalf);
{ radiusBottomLeft = MathHelper.Clamp(radiusBottomLeft, 0, smallerHalf);
AddQuad(rectangle, color); radiusTopRight = MathHelper.Clamp(radiusTopRight, 0, smallerHalf);
return; radiusBottomRight = MathHelper.Clamp(radiusBottomRight, 0, smallerHalf);
}
if (radiusTopLeft <= 0 && radiusTopRight <= 0 && radiusBottomLeft <= 0 && radiusBottomRight <= 0)
float radiusTopLeftUv = radiusTopLeft / rectangle.Width; {
float radiusTopRightUv = radiusTopRight / rectangle.Width; AddQuad(rectangle, color);
float radiusBottomLeftUv = radiusBottomLeft / rectangle.Width; return;
float radiusBottomRightUv = radiusBottomRight / rectangle.Width; }
float radiusTopLeftUv = radiusTopLeft / rectangle.Width;
int innerTl = mesh.AddVertex(new Vector2(rectangle.Left + radiusTopLeft, rectangle.Top + radiusTopLeft), color, new Vector2(radiusTopLeftUv, radiusTopLeftUv)); float radiusTopRightUv = radiusTopRight / rectangle.Width;
int innerTr = mesh.AddVertex(new Vector2(rectangle.Right - radiusTopRight, rectangle.Top + radiusTopRight), color, new Vector2(1 - radiusTopRightUv, radiusTopRightUv)); float radiusBottomLeftUv = radiusBottomLeft / rectangle.Width;
int innerBl = mesh.AddVertex(new Vector2(rectangle.Left + radiusBottomLeft, rectangle.Bottom - radiusBottomLeft), color, new Vector2(radiusBottomLeftUv, 1 - radiusBottomLeftUv)); float radiusBottomRightUv = radiusBottomRight / rectangle.Width;
int innerBr = mesh.AddVertex(new Vector2(rectangle.Right - radiusBottomRight, rectangle.Bottom - radiusBottomRight), color, new Vector2(1 - radiusBottomRightUv, 1 - radiusBottomRightUv));
int outerTl1 = mesh.AddVertex(new Vector2(rectangle.Left, rectangle.Top + radiusTopLeft), color, new Vector2(0, radiusTopLeftUv)); int innerTl = mesh.AddVertex(new Vector2(rectangle.Left + radiusTopLeft, rectangle.Top + radiusTopLeft), color, new Vector2(radiusTopLeftUv, radiusTopLeftUv));
int outerTl2 = mesh.AddVertex(new Vector2(rectangle.Left + radiusTopLeft, rectangle.Top), color, new Vector2(radiusTopLeftUv, 0)); int innerTr = mesh.AddVertex(new Vector2(rectangle.Right - radiusTopRight, rectangle.Top + radiusTopRight), color, new Vector2(1 - radiusTopRightUv, radiusTopRightUv));
int outerTr1 = mesh.AddVertex(new Vector2(rectangle.Right, rectangle.Top + radiusTopRight), color, new Vector2(1, radiusTopRightUv)); int innerBl = mesh.AddVertex(new Vector2(rectangle.Left + radiusBottomLeft, rectangle.Bottom - radiusBottomLeft), color, new Vector2(radiusBottomLeftUv, 1 - radiusBottomLeftUv));
int outerTr2 = mesh.AddVertex(new Vector2(rectangle.Right - radiusTopRight, rectangle.Top), color, new Vector2(1 - radiusTopRightUv, 0)); int innerBr = mesh.AddVertex(new Vector2(rectangle.Right - radiusBottomRight, rectangle.Bottom - radiusBottomRight), color, new Vector2(1 - radiusBottomRightUv, 1 - radiusBottomRightUv));
int outerBl1 = mesh.AddVertex(new Vector2(rectangle.Left, rectangle.Bottom - radiusBottomLeft), color, new Vector2(0, 1 - radiusBottomLeftUv));
int outerBl2 = mesh.AddVertex(new Vector2(rectangle.Left + radiusBottomLeft, rectangle.Bottom), color, new Vector2(radiusBottomLeftUv, 1)); int outerTl1 = mesh.AddVertex(new Vector2(rectangle.Left, rectangle.Top + radiusTopLeft), color, new Vector2(0, radiusTopLeftUv));
int outerBr1 = mesh.AddVertex(new Vector2(rectangle.Right, rectangle.Bottom - radiusBottomRight), color, new Vector2(1, 1 - radiusBottomRightUv)); int outerTl2 = mesh.AddVertex(new Vector2(rectangle.Left + radiusTopLeft, rectangle.Top), color, new Vector2(radiusTopLeftUv, 0));
int outerBr2 = mesh.AddVertex(new Vector2(rectangle.Right - radiusBottomRight, rectangle.Bottom), color, new Vector2(1 - radiusBottomRightUv, 1)); int outerTr1 = mesh.AddVertex(new Vector2(rectangle.Right, rectangle.Top + radiusTopRight), color, new Vector2(1, radiusTopRightUv));
int outerTr2 = mesh.AddVertex(new Vector2(rectangle.Right - radiusTopRight, rectangle.Top), color, new Vector2(1 - radiusTopRightUv, 0));
mesh.AddQuad(outerTl1, innerTl, outerBl1, innerBl); int outerBl1 = mesh.AddVertex(new Vector2(rectangle.Left, rectangle.Bottom - radiusBottomLeft), color, new Vector2(0, 1 - radiusBottomLeftUv));
mesh.AddQuad(outerTl2, outerTr2, innerTl, innerTr); int outerBl2 = mesh.AddVertex(new Vector2(rectangle.Left + radiusBottomLeft, rectangle.Bottom), color, new Vector2(radiusBottomLeftUv, 1));
mesh.AddQuad(innerTr, outerTr1, innerBr, outerBr1); int outerBr1 = mesh.AddVertex(new Vector2(rectangle.Right, rectangle.Bottom - radiusBottomRight), color, new Vector2(1, 1 - radiusBottomRightUv));
mesh.AddQuad(innerBl, innerBr, outerBl2, outerBr2); int outerBr2 = mesh.AddVertex(new Vector2(rectangle.Right - radiusBottomRight, rectangle.Bottom), color, new Vector2(1 - radiusBottomRightUv, 1));
mesh.AddQuad(innerTl, innerTr, innerBl, innerBr);
mesh.AddQuad(outerTl1, innerTl, outerBl1, innerBl);
AddQuarterCircleAroundPivotVertices(mesh, innerTl, outerTl2, radiusTopLeft, -1, -1, true, color, radiusTopLeftUv, radiusTopLeftUv, radiusTopLeftUv); mesh.AddQuad(outerTl2, outerTr2, innerTl, innerTr);
AddQuarterCircleAroundPivotVertices(mesh, innerTr, outerTr2, radiusTopRight, 1, -1, false, color, 1 - radiusTopRightUv, radiusTopRightUv, radiusTopRightUv); mesh.AddQuad(innerTr, outerTr1, innerBr, outerBr1);
AddQuarterCircleAroundPivotVertices(mesh, innerBl, outerBl2, radiusBottomLeft, -1, 1, false, color, radiusBottomLeftUv, 1 - radiusBottomLeftUv, radiusBottomLeftUv); mesh.AddQuad(innerBl, innerBr, outerBl2, outerBr2);
AddQuarterCircleAroundPivotVertices(mesh, innerBr, outerBr2, radiusBottomRight, 1, 1, true, color, 1 - radiusBottomRightUv, 1 - radiusBottomRightUv, radiusBottomRightUv); mesh.AddQuad(innerTl, innerTr, innerBl, innerBr);
}
AddQuarterCircleAroundPivotVertices(mesh, innerTl, outerTl2, radiusTopLeft, -1, -1, true, color, radiusTopLeftUv, radiusTopLeftUv, radiusTopLeftUv);
private void AddQuarterCircleAroundPivotVertices(GuiMeshBuilder mesh, int pivotVertex, int extent, float radius, float directionH, float directionV, bool reverseWinding, Color color, float uvX, float uvY, float texelRadius) AddQuarterCircleAroundPivotVertices(mesh, innerTr, outerTr2, radiusTopRight, 1, -1, false, color, 1 - radiusTopRightUv, radiusTopRightUv, radiusTopRightUv);
{ AddQuarterCircleAroundPivotVertices(mesh, innerBl, outerBl2, radiusBottomLeft, -1, 1, false, color, radiusBottomLeftUv, 1 - radiusBottomLeftUv, radiusBottomLeftUv);
Vector3 center = mesh[pivotVertex].Position; AddQuarterCircleAroundPivotVertices(mesh, innerBr, outerBr2, radiusBottomRight, 1, 1, true, color, 1 - radiusBottomRightUv, 1 - radiusBottomRightUv, radiusBottomRightUv);
}
int last = extent;
private void AddQuarterCircleAroundPivotVertices(GuiMeshBuilder mesh, int pivotVertex, int extent, float radius, float directionH, float directionV, bool reverseWinding, Color color, float uvX, float uvY, float texelRadius)
const int segments = 16; {
Vector3 center = mesh[pivotVertex].Position;
for (var i = 0; i < segments; i++)
{ int last = extent;
var t = (i / (segments - 1f)) * MathF.PI * 0.5f;
var x = MathF.Sin(t); const int segments = 16;
var y = MathF.Cos(t);
for (var i = 0; i < segments; i++)
float x1 = x * (radius * directionH) + center.X; {
float y1 = y * (radius * directionV) + center.Y; var t = (i / (segments - 1f)) * MathF.PI * 0.5f;
var x = MathF.Sin(t);
float u = (x * (texelRadius * directionH)) + uvX; var y = MathF.Cos(t);
float v = (y * (texelRadius * directionV)) + uvY;
float x1 = x * (radius * directionH) + center.X;
float y1 = y * (radius * directionV) + center.Y;
int next = mesh.AddVertex(new Vector2(x1, y1), color, new Vector2(u, v)); float u = (x * (texelRadius * directionH)) + uvX;
float v = (y * (texelRadius * directionV)) + uvY;
if (reverseWinding)
mesh.AddTriangle(pivotVertex, next, last);
else
mesh.AddTriangle(last, next, pivotVertex); int next = mesh.AddVertex(new Vector2(x1, y1), color, new Vector2(u, v));
last = next; if (reverseWinding)
} mesh.AddTriangle(pivotVertex, next, last);
} else
mesh.AddTriangle(last, next, pivotVertex);
public void AddRoundedRectangleOutline(LayoutRect rectangle, float thickness, float uniformRadius, Color color, Texture2D? texture = null)
{ last = next;
AddRoundedRectangleOutline(rectangle, thickness, uniformRadius, uniformRadius, uniformRadius, uniformRadius, }
color, texture); }
}
public void AddRoundedRectangleOutline(LayoutRect rectangle, float thickness, float uniformRadius, Color color, Texture2D? texture = null)
public void AddRoundedRectangleOutline(LayoutRect rectangle, float thickness, float radiusTopLeft, {
float radiusTopRight, float radiusBottomLeft, float radiusBottomRight, Color color, Texture2D? texture = null) AddRoundedRectangleOutline(rectangle, thickness, uniformRadius, uniformRadius, uniformRadius, uniformRadius,
{ color, texture);
if (thickness <= 0) }
return;
public void AddRoundedRectangleOutline(LayoutRect rectangle, float thickness, float radiusTopLeft,
float halfWidth = rectangle.Width / 2f; float radiusTopRight, float radiusBottomLeft, float radiusBottomRight, Color color, Texture2D? texture = null)
float halfHeight = rectangle.Height / 2f; {
if (thickness <= 0)
float smallerHalf = MathF.Min(halfWidth, halfHeight); return;
if (thickness >= smallerHalf) float halfWidth = rectangle.Width / 2f;
{ float halfHeight = rectangle.Height / 2f;
AddRoundedRectangle(rectangle, radiusTopLeft, radiusTopRight, radiusBottomLeft, radiusBottomRight, color, texture);
return; float smallerHalf = MathF.Min(halfWidth, halfHeight);
}
if (thickness >= smallerHalf)
radiusTopLeft = MathHelper.Clamp(radiusTopLeft, 0, smallerHalf); {
radiusBottomLeft = MathHelper.Clamp(radiusBottomLeft, 0, smallerHalf); AddRoundedRectangle(rectangle, radiusTopLeft, radiusTopRight, radiusBottomLeft, radiusBottomRight, color, texture);
radiusTopRight = MathHelper.Clamp(radiusTopRight, 0, smallerHalf); return;
radiusBottomRight = MathHelper.Clamp(radiusBottomRight, 0, smallerHalf); }
radiusTopLeft = MathHelper.Clamp(radiusTopLeft, 0, smallerHalf);
if (radiusTopLeft <= 0 && radiusTopRight <= 0 && radiusBottomLeft <= 0 && radiusBottomRight <= 0) radiusBottomLeft = MathHelper.Clamp(radiusBottomLeft, 0, smallerHalf);
return; radiusTopRight = MathHelper.Clamp(radiusTopRight, 0, smallerHalf);
radiusBottomRight = MathHelper.Clamp(radiusBottomRight, 0, smallerHalf);
var mesh = GetMeshBuilder(texture);
int innerTl = mesh.AddVertex(new Vector2(rectangle.Left + thickness, rectangle.Top + thickness), color); if (radiusTopLeft <= 0 && radiusTopRight <= 0 && radiusBottomLeft <= 0 && radiusBottomRight <= 0)
int innerTr = mesh.AddVertex(new Vector2(rectangle.Right - thickness, rectangle.Top + thickness), color); return;
int innerBl = mesh.AddVertex(new Vector2(rectangle.Left + thickness, rectangle.Bottom - thickness), color);
int innerBr = mesh.AddVertex(new Vector2(rectangle.Right - thickness, rectangle.Bottom - thickness), color); var mesh = GetMeshBuilder(texture);
int innerTl2 = mesh.AddVertex(new Vector2(rectangle.Left + radiusTopLeft, rectangle.Top + thickness), color);
int innerTr2 = mesh.AddVertex(new Vector2(rectangle.Right - radiusTopRight, rectangle.Top + thickness), color); int innerTl = mesh.AddVertex(new Vector2(rectangle.Left + thickness, rectangle.Top + thickness), color);
int innerBl2 = mesh.AddVertex(new Vector2(rectangle.Left + radiusBottomLeft, rectangle.Bottom - thickness), color); int innerTr = mesh.AddVertex(new Vector2(rectangle.Right - thickness, rectangle.Top + thickness), color);
int innerBr2 = mesh.AddVertex(new Vector2(rectangle.Right - radiusBottomRight, rectangle.Bottom - thickness), color); int innerBl = mesh.AddVertex(new Vector2(rectangle.Left + thickness, rectangle.Bottom - thickness), color);
int innerTl3 = mesh.AddVertex(new Vector2(rectangle.Left + thickness, rectangle.Top + radiusTopLeft), color); int innerBr = mesh.AddVertex(new Vector2(rectangle.Right - thickness, rectangle.Bottom - thickness), color);
int innerTr3 = mesh.AddVertex(new Vector2(rectangle.Right - thickness, rectangle.Top + radiusTopRight), color); int innerTl2 = mesh.AddVertex(new Vector2(rectangle.Left + radiusTopLeft, rectangle.Top + thickness), color);
int innerBl3 = mesh.AddVertex(new Vector2(rectangle.Left + thickness, rectangle.Bottom - radiusBottomLeft), color); int innerTr2 = mesh.AddVertex(new Vector2(rectangle.Right - radiusTopRight, rectangle.Top + thickness), color);
int innerBr3 = mesh.AddVertex(new Vector2(rectangle.Right - thickness, rectangle.Bottom - radiusBottomRight), color); int innerBl2 = mesh.AddVertex(new Vector2(rectangle.Left + radiusBottomLeft, rectangle.Bottom - thickness), color);
int innerBr2 = mesh.AddVertex(new Vector2(rectangle.Right - radiusBottomRight, rectangle.Bottom - thickness), color);
int outerTl1 = mesh.AddVertex(new Vector2(rectangle.Left, rectangle.Top + radiusTopLeft), color); int innerTl3 = mesh.AddVertex(new Vector2(rectangle.Left + thickness, rectangle.Top + radiusTopLeft), color);
int outerTl2 = mesh.AddVertex(new Vector2(rectangle.Left + radiusTopLeft, rectangle.Top), color); int innerTr3 = mesh.AddVertex(new Vector2(rectangle.Right - thickness, rectangle.Top + radiusTopRight), color);
int outerTr1 = mesh.AddVertex(new Vector2(rectangle.Right, rectangle.Top + radiusTopRight), color); int innerBl3 = mesh.AddVertex(new Vector2(rectangle.Left + thickness, rectangle.Bottom - radiusBottomLeft), color);
int outerTr2 = mesh.AddVertex(new Vector2(rectangle.Right - radiusTopRight, rectangle.Top), color); int innerBr3 = mesh.AddVertex(new Vector2(rectangle.Right - thickness, rectangle.Bottom - radiusBottomRight), color);
int outerBl1 = mesh.AddVertex(new Vector2(rectangle.Left, rectangle.Bottom - radiusBottomLeft), color);
int outerBl2 = mesh.AddVertex(new Vector2(rectangle.Left + radiusBottomLeft, rectangle.Bottom), color); int outerTl1 = mesh.AddVertex(new Vector2(rectangle.Left, rectangle.Top + radiusTopLeft), color);
int outerBr1 = mesh.AddVertex(new Vector2(rectangle.Right, rectangle.Bottom - radiusBottomRight), color); int outerTl2 = mesh.AddVertex(new Vector2(rectangle.Left + radiusTopLeft, rectangle.Top), color);
int outerBr2 = mesh.AddVertex(new Vector2(rectangle.Right - radiusBottomRight, rectangle.Bottom), color); int outerTr1 = mesh.AddVertex(new Vector2(rectangle.Right, rectangle.Top + radiusTopRight), color);
int outerTr2 = mesh.AddVertex(new Vector2(rectangle.Right - radiusTopRight, rectangle.Top), color);
Vector2 cTl = new Vector2(rectangle.Left + radiusTopLeft, rectangle.Top + radiusTopLeft); int outerBl1 = mesh.AddVertex(new Vector2(rectangle.Left, rectangle.Bottom - radiusBottomLeft), color);
Vector2 cTr = new Vector2(rectangle.Right - radiusTopRight, rectangle.Top + radiusTopRight); int outerBl2 = mesh.AddVertex(new Vector2(rectangle.Left + radiusBottomLeft, rectangle.Bottom), color);
Vector2 cBl = new Vector2(rectangle.Left + radiusBottomLeft, rectangle.Bottom - radiusBottomLeft); int outerBr1 = mesh.AddVertex(new Vector2(rectangle.Right, rectangle.Bottom - radiusBottomRight), color);
Vector2 cBr = new Vector2(rectangle.Right - radiusBottomRight, rectangle.Bottom - radiusBottomRight); int outerBr2 = mesh.AddVertex(new Vector2(rectangle.Right - radiusBottomRight, rectangle.Bottom), color);
mesh.AddQuad(outerTl1, innerTl3, outerBl1, innerBl3); Vector2 cTl = new Vector2(rectangle.Left + radiusTopLeft, rectangle.Top + radiusTopLeft);
mesh.AddQuad(outerTl2, outerTr2, innerTl2, innerTr2); Vector2 cTr = new Vector2(rectangle.Right - radiusTopRight, rectangle.Top + radiusTopRight);
mesh.AddQuad(innerTr3, outerTr1, innerBr3, outerBr1); Vector2 cBl = new Vector2(rectangle.Left + radiusBottomLeft, rectangle.Bottom - radiusBottomLeft);
mesh.AddQuad(innerBl2, innerBr2, outerBl2, outerBr2); Vector2 cBr = new Vector2(rectangle.Right - radiusBottomRight, rectangle.Bottom - radiusBottomRight);
AddRoundedRectangleOutlineCurve(mesh, cTl, innerTl, thickness, radiusTopLeft, -1, -1, true, color); mesh.AddQuad(outerTl1, innerTl3, outerBl1, innerBl3);
AddRoundedRectangleOutlineCurve(mesh, cTr, innerTr, thickness, radiusTopRight, 1, -1, false, color); mesh.AddQuad(outerTl2, outerTr2, innerTl2, innerTr2);
AddRoundedRectangleOutlineCurve(mesh, cBl, innerBl, thickness, radiusBottomLeft, -1, 1, false, color); mesh.AddQuad(innerTr3, outerTr1, innerBr3, outerBr1);
AddRoundedRectangleOutlineCurve(mesh, cBr, innerBr, thickness, radiusBottomRight, 1, 1, true, color); mesh.AddQuad(innerBl2, innerBr2, outerBl2, outerBr2);
}
AddRoundedRectangleOutlineCurve(mesh, cTl, innerTl, thickness, radiusTopLeft, -1, -1, true, color);
private void AddRoundedRectangleOutlineCurve(GuiMeshBuilder mesh, Vector2 center, int innerCorner, float thickness, float radius, float directionH, float directionV, bool reverseWinding, Color color) AddRoundedRectangleOutlineCurve(mesh, cTr, innerTr, thickness, radiusTopRight, 1, -1, false, color);
{ AddRoundedRectangleOutlineCurve(mesh, cBl, innerBl, thickness, radiusBottomLeft, -1, 1, false, color);
const int segments = 16; AddRoundedRectangleOutlineCurve(mesh, cBr, innerBr, thickness, radiusBottomRight, 1, 1, true, color);
}
var connectToInnerCorner = false;
if (radius < thickness) private void AddRoundedRectangleOutlineCurve(GuiMeshBuilder mesh, Vector2 center, int innerCorner, float thickness, float radius, float directionH, float directionV, bool reverseWinding, Color color)
{ {
connectToInnerCorner = true; const int segments = 16;
thickness = radius;
} var connectToInnerCorner = false;
if (radius < thickness)
int currentInner = 0; {
int currentOuter = 0; connectToInnerCorner = true;
thickness = radius;
for (var i = 0; i < segments; i++) }
{
var t = (i / (segments - 1f)) * MathF.PI * 0.5f; int currentInner = 0;
var x = MathF.Sin(t); int currentOuter = 0;
var y = MathF.Cos(t);
for (var i = 0; i < segments; i++)
var xOuter = x * (radius * directionH) + center.X; {
var yOuter = y * (radius * directionV) + center.Y; var t = (i / (segments - 1f)) * MathF.PI * 0.5f;
var xInner = x * ((radius * directionH) + (thickness * -directionH)) + center.X; var x = MathF.Sin(t);
var yInner = y * ((radius * directionV) + (thickness * -directionV)) + center.Y; var y = MathF.Cos(t);
int nextInner = mesh.AddVertex(new Vector2(xInner, yInner), color); var xOuter = x * (radius * directionH) + center.X;
int nextOuter = mesh.AddVertex(new Vector2(xOuter, yOuter), color); var yOuter = y * (radius * directionV) + center.Y;
var xInner = x * ((radius * directionH) + (thickness * -directionH)) + center.X;
if (reverseWinding) var yInner = y * ((radius * directionV) + (thickness * -directionV)) + center.Y;
{
if (i > 0) int nextInner = mesh.AddVertex(new Vector2(xInner, yInner), color);
{ int nextOuter = mesh.AddVertex(new Vector2(xOuter, yOuter), color);
mesh.AddTriangle(nextInner, currentOuter, currentInner);
mesh.AddTriangle(nextOuter, currentOuter, nextInner); if (reverseWinding)
} {
if (i > 0)
if (connectToInnerCorner) {
{ mesh.AddTriangle(nextInner, currentOuter, currentInner);
if (i == 0 || i == segments - 1) mesh.AddTriangle(nextOuter, currentOuter, nextInner);
{ }
mesh.AddTriangle(innerCorner, nextInner, nextOuter);
} if (connectToInnerCorner)
} {
} if (i == 0 || i == segments - 1)
else {
{ mesh.AddTriangle(innerCorner, nextInner, nextOuter);
if (i > 0) }
{ }
mesh.AddTriangle(currentInner, currentOuter, nextInner); }
mesh.AddTriangle(nextInner, currentOuter, nextOuter); else
} {
if (i > 0)
if (connectToInnerCorner) {
{ mesh.AddTriangle(currentInner, currentOuter, nextInner);
if (i == 0 || i == segments - 1) mesh.AddTriangle(nextInner, currentOuter, nextOuter);
{ }
mesh.AddTriangle(nextOuter, nextInner, innerCorner);
} if (connectToInnerCorner)
} {
} if (i == 0 || i == segments - 1)
{
currentInner = nextInner; mesh.AddTriangle(nextOuter, nextInner, innerCorner);
currentOuter = nextOuter; }
} }
} }
public void DrawQuad(Texture2D texture, ref VertexPositionColorTexture topLeft, ref VertexPositionColorTexture topRight, currentInner = nextInner;
ref VertexPositionColorTexture bottomLeft, ref VertexPositionColorTexture bottomRight) currentOuter = nextOuter;
{ }
var mesh = GetMeshBuilder(texture); }
int i1 = mesh.AddVertex(topLeft); public void DrawQuad(Texture2D texture, ref VertexPositionColorTexture topLeft, ref VertexPositionColorTexture topRight,
int i2 = mesh.AddVertex(topRight); ref VertexPositionColorTexture bottomLeft, ref VertexPositionColorTexture bottomRight)
int i3 = mesh.AddVertex(bottomLeft); {
int i4 = mesh.AddVertex(bottomRight); var mesh = GetMeshBuilder(texture);
mesh.AddTriangle(i1, i2, i3); int i1 = mesh.AddVertex(topLeft);
mesh.AddTriangle(i3, i2, i4); int i2 = mesh.AddVertex(topRight);
} int i3 = mesh.AddVertex(bottomLeft);
int i4 = mesh.AddVertex(bottomRight);
public GraphicsDevice GraphicsDevice => guiRenderer.GraphicsDevice;
mesh.AddTriangle(i1, i2, i3);
mesh.AddTriangle(i3, i2, i4);
}
public GraphicsDevice GraphicsDevice => guiRenderer.GraphicsDevice;
} }

0
src/AcidicGUI/Rendering/GuiMesh.cs Normal file → Executable file
View file

0
src/AcidicGUI/Rendering/GuiMeshBuilder.cs Normal file → Executable file
View file

448
src/AcidicGUI/Rendering/Vertex.cs Normal file → Executable file
View file

@ -1,229 +1,221 @@
using AcidicGUI.Effects; using AcidicGUI.Effects;
using AcidicGUI.Layout; using AcidicGUI.Layout;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Graphics;
namespace AcidicGUI.Rendering;
namespace AcidicGUI.Rendering;
public sealed class GuiRenderer
public sealed class GuiRenderer {
{ private GuiBatch? whiteBatch;
private readonly Dictionary<Texture2D, GuiBatch> batches = new(); private readonly Dictionary<Texture2D, GuiBatch> batches = new();
private readonly IGuiContext context; private readonly IGuiContext context;
private readonly Stack<Matrix> previousMatrices = new(); private float layer;
private Matrix currentMatrix = Matrix.Identity;
private GuiBatch? whiteBatch; public GuiRenderer(IGuiContext context)
private float layer; {
this.context = context;
public GuiRenderer(IGuiContext context) }
{
this.context = context; public float Layer => layer;
}
public void SetLayer(float newLayer)
public float Layer => layer; {
layer = newLayer;
public void PushTransformMatrix(Matrix matrix) }
{
previousMatrices.Push(currentMatrix); public void PushLayer()
currentMatrix *= matrix; {
} layer += 0.5f;
}
public void PopTransformMatrix()
{ public void PopLayer()
if (previousMatrices.Count == 0) {
{ layer--;
currentMatrix = Matrix.Identity; }
return;
} public void RenderGuiMesh(GuiMesh mesh)
{
currentMatrix = previousMatrices.Pop(); foreach (GuiSubMesh subMesh in mesh.SubMeshes)
} {
SubmitSubMesh(subMesh);
public void RenderGuiMesh(GuiMesh mesh) }
{ }
foreach (GuiSubMesh subMesh in mesh.SubMeshes)
{ public int GetVertexCount(Texture2D? texture)
SubmitSubMesh(subMesh); {
} if (texture == null)
} {
if (whiteBatch==null)
public int GetVertexCount(Texture2D? texture) whiteBatch = new GuiBatch(context, GraphicsDevice, null);
{
if (texture == null) return whiteBatch.VertexCount;
{ }
if (whiteBatch==null)
whiteBatch = new GuiBatch(context, GraphicsDevice, null); if (!batches.TryGetValue(texture, out GuiBatch? batch))
{
return whiteBatch.VertexCount; batch = new GuiBatch(context, GraphicsDevice, texture);
} batches.Add(texture, batch);
}
if (!batches.TryGetValue(texture, out GuiBatch? batch))
{ return batch.VertexCount;
batch = new GuiBatch(context, GraphicsDevice, texture); }
batches.Add(texture, batch);
} private void SubmitSubMesh(GuiSubMesh subMesh)
{
return batch.VertexCount; if (subMesh.Texture != null)
} {
if (!batches.TryGetValue(subMesh.Texture, out GuiBatch? batch))
private void SubmitSubMesh(GuiSubMesh subMesh) {
{ batch = new GuiBatch(context, GraphicsDevice, subMesh.Texture);
if (subMesh.Texture != null) batches.Add(subMesh.Texture, batch);
{ }
if (!batches.TryGetValue(subMesh.Texture, out GuiBatch? batch))
{ batch.Submit(subMesh);
batch = new GuiBatch(context, GraphicsDevice, subMesh.Texture); return;
batches.Add(subMesh.Texture, batch); }
}
if (whiteBatch==null)
batch.Submit(subMesh); whiteBatch = new GuiBatch(context, GraphicsDevice, null);
return;
} whiteBatch.Submit(subMesh);
}
if (whiteBatch==null)
whiteBatch = new GuiBatch(context, GraphicsDevice, null); public void RenderBatches(IEffect? effectOverride = null, float opacity = 1, LayoutRect? clipRect = null)
{
whiteBatch.Submit(subMesh); whiteBatch?.DrawBatch(effectOverride, opacity, clipRect);
}
foreach (GuiBatch batch in batches.Values)
public void RenderBatches(IEffect? effectOverride = null, float opacity = 1, LayoutRect? clipRect = null) batch.DrawBatch(effectOverride, opacity, clipRect);
{ }
whiteBatch?.DrawBatch(effectOverride, opacity, clipRect, currentMatrix);
public void Grab(RenderTarget2D destination)
foreach (GuiBatch batch in batches.Values) {
batch.DrawBatch(effectOverride, opacity, clipRect, currentMatrix); context.Grab(destination);
} }
public void Grab(RenderTarget2D destination) public void Restore()
{ {
context.Grab(destination); context.RestoreRenderState();
} }
public void Restore() public GraphicsDevice GraphicsDevice => context.GraphicsDevice;
{ }
context.RestoreRenderState();
} public sealed class GrowingList<T>
{
public GraphicsDevice GraphicsDevice => context.GraphicsDevice; private readonly List<T> list = new();
} private int insertIndex;
public sealed class GrowingList<T> public IEnumerator<T> GetEnumerator()
{ {
private readonly List<T> list = new(); return list.GetEnumerator();
private int insertIndex; }
public IEnumerator<T> GetEnumerator() public void AddRange(IEnumerable<T> items)
{ {
return list.GetEnumerator(); foreach (T item in items)
} Add(item);
}
public void AddRange(IEnumerable<T> items)
{ public void Add(T item)
foreach (T item in items) {
Add(item); if (insertIndex == list.Count)
} list.Add(item);
else
public void Add(T item) list[insertIndex] = item;
{
if (insertIndex == list.Count) insertIndex++;
list.Add(item); }
else
list[insertIndex] = item; public void Clear()
{
insertIndex++; insertIndex = 0;
} }
public void Clear() public int Count => insertIndex;
{
insertIndex = 0; public T[] ToArray()
} {
return list.ToArray().AsSpan().Slice(0, insertIndex).ToArray();
public int Count => insertIndex; }
}
public T[] ToArray()
{ public sealed class GuiBatch
return list.ToArray().AsSpan().Slice(0, insertIndex).ToArray(); {
} private readonly GraphicsDevice device;
} private VertexBuffer? vertexBuffer;
private IndexBuffer? indexBuffer;
public sealed class GuiBatch private readonly Texture2D? texture;
{ private readonly IGuiContext context;
private readonly GraphicsDevice device; private readonly GrowingList<VertexPositionColorTexture> cpuVertices = new();
private VertexBuffer? vertexBuffer; private readonly GrowingList<int> cpuIndices = new();
private IndexBuffer? indexBuffer;
private readonly Texture2D? texture; private int vertexDirtiness;
private readonly IGuiContext context; private int indexDirtiness;
private readonly GrowingList<VertexPositionColorTexture> cpuVertices = new(); private bool dirty;
private readonly GrowingList<int> cpuIndices = new();
public GuiBatch(IGuiContext context, GraphicsDevice device, Texture2D? texture)
private int vertexDirtiness; {
private int indexDirtiness; this.context = context;
private bool dirty; this.device = device;
this.texture = texture;
public GuiBatch(IGuiContext context, GraphicsDevice device, Texture2D? texture) }
{
this.context = context; public int VertexCount => cpuVertices.Count;
this.device = device;
this.texture = texture; public void Submit(GuiSubMesh subMesh)
} {
if (subMesh.IsNew)
public int VertexCount => cpuVertices.Count; {
subMesh.MarkAsOld();
public void Submit(GuiSubMesh subMesh) if (!dirty)
{ {
if (subMesh.Vertices.Length == 0 || subMesh.Indices.Length == 0) vertexDirtiness = cpuVertices.Count;
return; indexDirtiness = cpuIndices.Count;
}
if (subMesh.IsNew)
{ dirty = true;
subMesh.MarkAsOld(); }
if (!dirty)
{ cpuVertices.AddRange(subMesh.Vertices);
vertexDirtiness = cpuVertices.Count; cpuIndices.AddRange(subMesh.Indices);
indexDirtiness = cpuIndices.Count; }
}
public void DrawBatch(IEffect? effectOverride, float opacity, LayoutRect? clipRect)
dirty = true; {
} if (cpuIndices.Count == 0 || cpuVertices.Count == 0)
return;
cpuVertices.AddRange(subMesh.Vertices);
cpuIndices.AddRange(subMesh.Indices); if (vertexBuffer == null || vertexBuffer.VertexCount < cpuVertices.Count)
} {
vertexBuffer?.Dispose();
public void DrawBatch(IEffect? effectOverride, float opacity, LayoutRect? clipRect, Matrix widgetTransform) dirty = true;
{ vertexDirtiness = 0;
if (cpuIndices.Count == 0 || cpuVertices.Count == 0) vertexBuffer = new VertexBuffer(device, typeof(VertexPositionColorTexture), cpuVertices.Count, BufferUsage.None);
return; }
if (vertexBuffer == null || vertexBuffer.VertexCount < cpuVertices.Count) if (indexBuffer == null || indexBuffer.IndexCount < cpuIndices.Count)
{ {
vertexBuffer?.Dispose(); indexBuffer?.Dispose();
dirty = true; dirty = true;
vertexDirtiness = 0; indexDirtiness = 0;
vertexBuffer = new VertexBuffer(device, typeof(VertexPositionColorTexture), cpuVertices.Count, BufferUsage.None); indexBuffer = new IndexBuffer(device, typeof(int), cpuIndices.Count, BufferUsage.None);
} }
if (indexBuffer == null || indexBuffer.IndexCount < cpuIndices.Count) if (dirty)
{ {
indexBuffer?.Dispose(); var vertexData = cpuVertices.ToArray();
dirty = true; var indexData = cpuIndices.ToArray();
indexDirtiness = 0;
indexBuffer = new IndexBuffer(device, typeof(int), cpuIndices.Count, BufferUsage.None); vertexBuffer.SetData(vertexData, vertexDirtiness, vertexData.Length - vertexDirtiness);
} indexBuffer.SetData(indexData, indexDirtiness, indexData.Length - indexDirtiness);
if (dirty) dirty = false;
{ }
var vertexData = cpuVertices.ToArray();
var indexData = cpuIndices.ToArray(); context.Render(vertexBuffer, indexBuffer, 0, cpuIndices.Count / 3, texture, clipRect, effectOverride, opacity);
vertexBuffer.SetData(vertexData, vertexDirtiness, vertexData.Length - vertexDirtiness); cpuVertices.Clear();
indexBuffer.SetData(indexData, indexDirtiness, indexData.Length - indexDirtiness); cpuIndices.Clear();
}
dirty = false;
}
context.Render(vertexBuffer, indexBuffer, 0, cpuIndices.Count / 3, texture, clipRect, effectOverride, opacity, widgetTransform);
cpuVertices.Clear();
cpuIndices.Clear();
}
} }

0
src/AcidicGUI/Sdl@platform.cs Normal file → Executable file
View file

0
src/AcidicGUI/TextRendering/DynamicFont.cs Normal file → Executable file
View file

0
src/AcidicGUI/TextRendering/Font.cs Normal file → Executable file
View file

Some files were not shown because too many files have changed in this diff Show more