mirror of
https://gitlab.acidiclight.dev/sociallydistant/sociallydistant.git
synced 2025-01-22 09:31:47 -05:00
Fix even more platform-related issues
* Show more descriptive OS version info in neofetch We now show the distribution name (i.e, "Arch Linux") as well as the OS architecture and .NET version in the "Host" string in neofetch. This should theoretically also mean Windows versions are displayed properly, but I don't have a local Windows system to test. Issue: #1 Signed-off-by: Ritchie Frodomar <alkalinethunder@gmail.com> * Document new visual style system * Use new style system for Progress Bars * Allow custom widgets to more easily take advantage of the visual style system * Add support for setting confirmations in toggles Signed-off-by: Ritchie Frodomar <alkalinethunder@gmail.com> * Fix background blur effect rendering solid black after fullscreen changes Changing the game resolution requires throwing away old blur buffers and generating new ones. This requires throwing away cached blur geometry on all widgets, but we were not doing that - resulting in sampling a destroyed texture. In MonoGame, the failure mode for this is sampling black. Issue: #1 Signed-off-by: Ritchie Frodomar <alkalinethunder@gmail.com> * Fix issue where toggling Fullscreen doesn't change window mode We did not apply the XNA presentation parameters this whole time. I feel stupid. Issue: #1 Signed-off-by: Ritchie Frodomar <alkalinethunder@gmail.com> * Prioritize Graphics category in System Settings * Fix annoying ordering of resolutions in Graphics Settings * 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 disabling background blur * Testing the DCO acknowledgement job Signed-off-by: Ritchie Frodomar <alkalinethunder@gmail.com>
This commit is contained in:
parent
75c8b5432c
commit
de73d0800b
37 changed files with 994 additions and 220 deletions
|
@ -27,3 +27,7 @@ items:
|
|||
href: ui/list-adapters.md
|
||||
- name: Optimizing UI for Performance
|
||||
href: ui/optimization.md
|
||||
- name: Advanced UI features
|
||||
items:
|
||||
- name: Visual Styles
|
||||
href: ui/advanced/visual-styles.md
|
212
docs/ui/advanced/visual-styles.md
Normal file
212
docs/ui/advanced/visual-styles.md
Normal file
|
@ -0,0 +1,212 @@
|
|||
# Visual Styles
|
||||
Visual Styles control how all widgets in the UI system look. They're responsible for determining both visual and layout attributes for each widget. While most game UI systems tend to allow direct control over how widgets look on an individual basis, Socially Distant (and AcidicGUI) **does not.**. This forces the user interface to maintain a consistent visual appearance across the entire game, to improve accessibility. All buttons should look like a button.
|
||||
|
||||
The visual style system is designed to allow custom widgets to take advantage of it. Therefore, the visual style system does take some learning.
|
||||
|
||||
## The `IVisualStyle` Interface
|
||||
The `IVisualStyle` interface defines methods that must be implemented by all visual styles in order for core widgets to function. AcidicGUI comes with a built-in `FallbackVisualStyle` that gets used when no other option is available. `SociallyDistant.Framework` provides `SociallyDistantVisualStyle`, which implements the visual appearance of the in-game operating system.
|
||||
|
||||
## The `IVisual<TVisualProperties>` interface
|
||||
This interface should be implemented by a widget that wants to be rendered using the visual style system. You use this interface to communicate important state about the widget being rendered. For example, when styling a progress bar, you will need to know its fill percentage. This is how the `IVisual` interface is used to implement that.
|
||||
|
||||
```csharp
|
||||
public sealed class ProgressBar : Widget,
|
||||
IVisual<ProgressBar.ProgressBarVisualProperties>
|
||||
{
|
||||
private ProgressBarVisualProperties visualProperties;
|
||||
|
||||
public ref readonly ProgressBarCisualProperties VisualProperties => ref visualProperties;
|
||||
|
||||
public float Value
|
||||
{
|
||||
get => visualProperties.FillPercentage;
|
||||
set
|
||||
{
|
||||
visualProperties.FillPercentage = value;
|
||||
InvalidateGeometry();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void RebuildGeometry(GeometryHelper geometry)
|
||||
{
|
||||
geometry.DrawVisual<ProgressBar, ProgressBarVisualProperties>(this);
|
||||
}
|
||||
|
||||
public struct ProgressBarVisualProperties
|
||||
{
|
||||
public float FillPercentage;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Note that `ProgressBar` itself does not implement any actual rendering code. It just provides a means of retrieving its fill percentage (and any other visual state) through the `VisualProperyies` property. Instead, we call the `DrawVisual()` extension method of `GeometryHelper`, which instructs the visual style system to paint the widget.
|
||||
|
||||
## The `IVisualRenderer<TVisualProperties>` Interface
|
||||
This interface allows you to define how a widget should be rendered, given its visual properties. If implemented by a class implementing `IVisualStyle`, the visual style system will be able to render any widgets implementing `IVisual<TVisualProperties>` using this implementation.
|
||||
|
||||
You should delegate the implementation of the `Draw` method to a nested type within your visual style implementation, like this:
|
||||
|
||||
```csharp
|
||||
public sealed class MyVisualStyle :
|
||||
IVisualStyle,
|
||||
IVisualRenderer<ProgressBar.ProgressBarVisualProperties>
|
||||
{
|
||||
private readonly ProgressBarStyle progressBarStyle = new();
|
||||
|
||||
`public void Draw(
|
||||
Widget widget,
|
||||
GeometryHelper geometry,
|
||||
in LayoutRect contentRect,
|
||||
ProgressBarVisualProperties properties
|
||||
)
|
||||
{
|
||||
progressBarStyle.Draw(widget, geometry, in contentRect, properties);
|
||||
}
|
||||
|
||||
public sealed class ProgressBarStyle :
|
||||
IVisualRenderer<ProgressBar.ProgressBarVisualProperties>
|
||||
{
|
||||
public void Draw(
|
||||
Widget widget,
|
||||
GeometryHelper geometry,
|
||||
in LayoutRect contentRect,
|
||||
ProgressBarVisualProperties properties
|
||||
)
|
||||
{
|
||||
var fillPercentage = properties.FillPercentage;
|
||||
|
||||
// Draw stuff!
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Because C# does not have a way to name the `Draw` method based on what type of visual properties being used, delegating the rendering implementation like this allows you to more easily navigate the visual style's code. It also allows you to more easily communicate that certain style attributes only affect progress bars, and therefore only need to cause progress bars to repaint when changed.
|
||||
|
||||
## The `IGetLayoutProperties<TLayoutProperties>` Interface
|
||||
Sometimes, widgets need to use properties from a visual style to determine their layout. For example, a progress bar's height is controlled by the visual style system. That's the role of this interface. When implemented by a visual style, the visual style system can query layout properties from your own visual style dynamically. If you don't implement this interface for a given widget's layout properties, the widget itself will define default values that it will not allow you to change directly.
|
||||
|
||||
Here's how you can change the height of all progress bars.
|
||||
|
||||
```csharp
|
||||
public sealed class MyVisualStyle :
|
||||
IVisualStyle,
|
||||
IVisualRenderer<ProgressBar.ProgressBarVisualProperties>,
|
||||
IGetLayoutProperties<ProgressBar.ProgressBarLayoutProperties>
|
||||
{
|
||||
private readonly ProgressBarStyle progressBarStyle = new();
|
||||
|
||||
public bool GetLayoutProperties(ref ProgressBar.ProgressBarLayoutProperties layoutProperties)
|
||||
{
|
||||
return progressBarStyle.GetLayoutProperties(ref layoutProperties);
|
||||
}
|
||||
|
||||
`public void Draw(
|
||||
Widget widget,
|
||||
GeometryHelper geometry,
|
||||
in LayoutRect contentRect,
|
||||
ProgressBarVisualProperties properties
|
||||
)
|
||||
{
|
||||
progressBarStyle.Draw(widget, geometry, in contentRect, properties);
|
||||
}
|
||||
|
||||
public sealed class ProgressBarStyle :
|
||||
IVisualRenderer<ProgressBar.ProgressBarVisualProperties>,
|
||||
IGetLayoutProperties<ProgressBar.ProgressBarLayoutProperties>
|
||||
{
|
||||
public bool GetLayoutProperties(ref ProgressBar.ProgressBarLayoutProperties layoutProperties)
|
||||
{
|
||||
layoutProperties.ProgressBarHeight = 4;
|
||||
return false;
|
||||
}
|
||||
|
||||
public void Draw(
|
||||
Widget widget,
|
||||
GeometryHelper geometry,
|
||||
in LayoutRect contentRect,
|
||||
ProgressBarVisualProperties properties
|
||||
)
|
||||
{
|
||||
var fillPercentage = properties.FillPercentage;
|
||||
|
||||
// Draw stuff!
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Just like `IVisualRenderer`, you should delegate your implementation of the `GetLayoutProperties()` method.
|
||||
|
||||
Note that the `GetLayoutProperties` method returns `bool`. When a widget asks for layout properties, the value you return determines whether that widget should invalidate its layout. You should return `true` if the layout properties are dirty, and `false` otherwise. If they're static, always return `false` as all widgets get dirtied automatically when a visual style is loaded.
|
||||
|
||||
## The `UserStyle` class in `SociallyDistant.Framework`
|
||||
This is an extremely simple base class for widget styles in Socially Distant. It handles the dirtiness of layout properties for you, and provides access to accessibility and UI settings set by the player.
|
||||
|
||||
You can adjust the above code example with `ProgressBarStyle` to make progress bar height dynamic.
|
||||
|
||||
```csharp
|
||||
public sealed class ProgressBarStyle :
|
||||
UserStyle,
|
||||
IVisualRenderer<ProgressBar.ProgressBarVisualProperties>,
|
||||
IGetLayoutProperties<ProgressBar.ProgressBarLayoutProperties>
|
||||
{
|
||||
public int ProgressBarHeight
|
||||
{
|
||||
get => progressBarHeight;
|
||||
set
|
||||
{
|
||||
if (progressBarHeight == value)
|
||||
return;
|
||||
|
||||
progressBarHeight = value;
|
||||
SetDirty();
|
||||
}
|
||||
}
|
||||
|
||||
public bool GetLayoutProperties(ref ProgressBar.ProgressBarLayoutProperties layoutProperties)
|
||||
{
|
||||
layoutProperties.ProgressBarHeight = progressBarHeight;
|
||||
return GetDirtyState();
|
||||
}
|
||||
|
||||
public void Draw(
|
||||
Widget widget,
|
||||
GeometryHelper geometry,
|
||||
in LayoutRect contentRect,
|
||||
ProgressBarVisualProperties properties
|
||||
)
|
||||
{
|
||||
var fillPercentage = properties.FillPercentage;
|
||||
|
||||
// Draw stuff!
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Accessing layout properties in a Widget
|
||||
Any widget can access the layout properties of any type of widget, so long as the struct is public.
|
||||
|
||||
Here's how you can access the layout properties of a `ProgressBar`.
|
||||
|
||||
```csharp
|
||||
public sealed class NotAProgressBar : Widget,
|
||||
IUpdateLayoutProperties
|
||||
{
|
||||
private ProgressBar.ProgressBarLayoutProperties progressBarLayout;
|
||||
|
||||
public void UpdateLayoutProperties()
|
||||
{
|
||||
this.GetLayoutProperties(ref progressBarLayout);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The widget implements `IUpdateLayoutProperties`, which is called every frame for every widget that implements it, before any actual layout updates occur. Calling `this.GetLayoutProperties()` fills the given field with layout properties from the current visual style. If the visual style doesn't implement `IGetLayoutProperties<TLayoutProperties>` for the given struct type, then it will be filled with default values instead.
|
||||
|
||||
By using `GetLayoutProperties()` within the context of `UpdateLayoutProperties()`, any changes reported by the visual style system will automatically invalidate the widget allowing you to immediately apply the layout properties.
|
||||
|
||||
## Visual Style Overrides
|
||||
Sometimes, you want a section of the UI to use a different visual style altogether. For example, in Socially Distant, a website may want to have a different look and feel than the in-game OS. This is where Visual Style Overrides come in.
|
||||
|
||||
Each widget has a `VisualStyleOverride` property you can set to an instance of a class implementing `IVisualStyle`. If `VisualStyleOverride` is not null, then this visual style will be used by the widget instead of the current global style of the game. This property is also inherited by child widgets, meaning overriding the style of a widget will affect every widget below it in the hierarchy.
|
19
src/AcidicGUI/Common/StateChange.cs
Normal file
19
src/AcidicGUI/Common/StateChange.cs
Normal file
|
@ -0,0 +1,19 @@
|
|||
namespace AcidicGUI.Common;
|
||||
|
||||
public sealed class StateChange<T>
|
||||
{
|
||||
public T CurrentValue { get; }
|
||||
public T NewValue { get; }
|
||||
public bool Canceled { get; private set; }
|
||||
|
||||
public StateChange(T currentValue, T newValue)
|
||||
{
|
||||
this.CurrentValue = currentValue;
|
||||
this.NewValue = newValue;
|
||||
}
|
||||
|
||||
public void Cancel()
|
||||
{
|
||||
Canceled = true;
|
||||
}
|
||||
}
|
|
@ -23,9 +23,6 @@ public sealed class GuiManager : IFontFamilyProvider
|
|||
private ButtonState rightShift;
|
||||
private ButtonState leftAlt;
|
||||
private ButtonState rightAlt;
|
||||
|
||||
|
||||
private IVisualStyle? visualStyleOverride;
|
||||
private int screenWidth;
|
||||
private int screenHeight;
|
||||
private bool isRendering;
|
||||
|
@ -44,7 +41,20 @@ public sealed class GuiManager : IFontFamilyProvider
|
|||
topLevels = new Widget.TopLevelCollection(this);
|
||||
renderer = new GuiRenderer(context);
|
||||
|
||||
visualStyleOverride = globalStyle;
|
||||
StyleManager.SetVisualStyle(globalStyle);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invalidates all layout and geometry data of the entire widget tree,
|
||||
/// forcing a full layout update and geometry repaint. Use only when changing
|
||||
/// graphics modes, as this is extremely expensive.
|
||||
/// </summary>
|
||||
public void ScheduleFullReRender()
|
||||
{
|
||||
foreach (Widget widget in TopLevels)
|
||||
{
|
||||
widget.InvalidateLayout();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -199,6 +209,12 @@ public sealed class GuiManager : IFontFamilyProvider
|
|||
{
|
||||
reachedFirstUpdate = true;
|
||||
|
||||
foreach (Widget widget in CollapseChildren())
|
||||
{
|
||||
if (widget is IUpdateLayoutProperties updateLayoutProperties)
|
||||
updateLayoutProperties.UpdateLayoutProperties();
|
||||
}
|
||||
|
||||
var mustRebuildLayout = false;
|
||||
var tolerance = 0.001f;
|
||||
|
||||
|
@ -229,12 +245,13 @@ public sealed class GuiManager : IFontFamilyProvider
|
|||
}
|
||||
}
|
||||
|
||||
[Obsolete("Please use the new StyleManager API instead.")]
|
||||
public IVisualStyle GetVisualStyle()
|
||||
{
|
||||
if (fallbackVisualStyle.FallbackFont == null)
|
||||
fallbackVisualStyle.FallbackFont = context.GetFallbackFont();
|
||||
|
||||
return visualStyleOverride ?? fallbackVisualStyle;
|
||||
return StyleManager.ActiveStyle ?? fallbackVisualStyle;
|
||||
}
|
||||
|
||||
public void Render()
|
||||
|
|
|
@ -6,9 +6,11 @@ using Microsoft.Xna.Framework;
|
|||
|
||||
namespace AcidicGUI.VisualStyles;
|
||||
|
||||
internal sealed class FallbackVisualStyle : IVisualStyle
|
||||
internal sealed class FallbackVisualStyle :
|
||||
IVisualStyle,
|
||||
IGetLayoutProperties<ProgressBar.ProgressBarLayoutProperties>,
|
||||
IVisualRenderer<ProgressBar.ProgressBarVisualProperties>
|
||||
{
|
||||
public int ProgressBarHeight => 16;
|
||||
public int SliderThickness => 12;
|
||||
public Point ToggleSize => new Point(18, 18);
|
||||
public Point SwitchSize => ToggleSize;
|
||||
|
@ -119,18 +121,6 @@ internal sealed class FallbackVisualStyle : IVisualStyle
|
|||
geometry.AddQuad(widget.ContentArea, Color.Gray * 0.25f);
|
||||
}
|
||||
|
||||
public void DrawProgressBar(ProgressBar widget, GeometryHelper geometry, float fillPercentage)
|
||||
{
|
||||
int width = widget.ContentArea.Width;
|
||||
int fillWidth = (int) MathHelper.Clamp(MathHelper.Lerp(0, width, fillPercentage), 0, width);
|
||||
|
||||
var background = widget.ContentArea;
|
||||
var fill = new LayoutRect(background.Left, background.Top, fillWidth, background.Height);
|
||||
|
||||
geometry.AddQuad(background, Color.Gray);
|
||||
geometry.AddQuad(fill, Color.Red);
|
||||
}
|
||||
|
||||
public void DrawSlider(
|
||||
Slider widget,
|
||||
GeometryHelper geometry,
|
||||
|
@ -153,4 +143,27 @@ internal sealed class FallbackVisualStyle : IVisualStyle
|
|||
geometry.AddQuad(new LayoutRect((int)MathHelper.Lerp(widget.ContentArea.Left + 1, widget.ContentArea.Right - 1, value), widget.ContentArea.Top, 2, widget.ContentArea.Height), Color.White);
|
||||
}
|
||||
}
|
||||
|
||||
public bool GetLayoutProperties(ref ProgressBar.ProgressBarLayoutProperties layoutProperties)
|
||||
{
|
||||
layoutProperties.ProgressBarHeight = 16;
|
||||
return false;
|
||||
}
|
||||
|
||||
public void Draw(
|
||||
Widget widget,
|
||||
GeometryHelper geometry,
|
||||
in LayoutRect contentRect,
|
||||
ProgressBar.ProgressBarVisualProperties properties
|
||||
)
|
||||
{
|
||||
int width = contentRect.Width;
|
||||
int fillWidth = (int) MathHelper.Clamp(MathHelper.Lerp(0, width, properties.FillPercentage), 0, width);
|
||||
|
||||
var background = contentRect;
|
||||
var fill = new LayoutRect(background.Left, background.Top, fillWidth, background.Height);
|
||||
|
||||
geometry.AddQuad(background, Color.Gray);
|
||||
geometry.AddQuad(fill, Color.Red);
|
||||
}
|
||||
}
|
34
src/AcidicGUI/VisualStyles/IGetLayoutProperties.cs
Normal file
34
src/AcidicGUI/VisualStyles/IGetLayoutProperties.cs
Normal file
|
@ -0,0 +1,34 @@
|
|||
using AcidicGUI.Widgets;
|
||||
|
||||
namespace AcidicGUI.VisualStyles;
|
||||
|
||||
/// <summary>
|
||||
/// Allows retrieving of layout properties of the given type.
|
||||
/// When implemented by a visual style, allows widgets to retrieve
|
||||
/// layout properties from said visual style via <see cref="Widget.GetLayoutProperties{TLayoutProperties}" />.
|
||||
/// </summary>
|
||||
/// <typeparam name="TLayoutProperties">Any value type containing layout properties for a given type of widget.</typeparam>
|
||||
public interface IGetLayoutProperties<TLayoutProperties>
|
||||
where TLayoutProperties : struct
|
||||
{
|
||||
/// <summary>
|
||||
/// Retrieves layout properties and stores them into the given layout properties reference.
|
||||
/// </summary>
|
||||
/// <param name="layoutProperties">The destination to which layout properties should be stored.</param>
|
||||
/// <returns>True if the layout properties have changed, false otherwise. If true, widgets needing these properties will invalidate their layout.</returns>
|
||||
bool GetLayoutProperties(ref TLayoutProperties layoutProperties);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When implemented by a widget, allows the widget
|
||||
/// to query layout properties from the visual style.
|
||||
/// </summary>
|
||||
public interface IUpdateLayoutProperties
|
||||
{
|
||||
/// <summary>
|
||||
/// Retrieves layout properties from the visual style. Called by the UI system on every
|
||||
/// frame before layout updates occur. If layout properties change during this method call,
|
||||
/// the widget will automatically invalidate its own layout.
|
||||
/// </summary>
|
||||
void UpdateLayoutProperties();
|
||||
}
|
14
src/AcidicGUI/VisualStyles/IVisual.cs
Normal file
14
src/AcidicGUI/VisualStyles/IVisual.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
namespace AcidicGUI.VisualStyles;
|
||||
|
||||
/// <summary>
|
||||
/// Interface for a widget that can be drawn by visual styles given properties
|
||||
/// controlled by the widget.
|
||||
/// </summary>
|
||||
/// <typeparam name="TVisualProperties">Any value type containing any widget state needed for visual rendering.</typeparam>
|
||||
public interface IVisual<TVisualProperties> where TVisualProperties : struct
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a read-only reference to the visual properties associated with the widget.
|
||||
/// </summary>
|
||||
public ref readonly TVisualProperties VisualProperties { get; }
|
||||
}
|
29
src/AcidicGUI/VisualStyles/IVisualRenderer.cs
Normal file
29
src/AcidicGUI/VisualStyles/IVisualRenderer.cs
Normal file
|
@ -0,0 +1,29 @@
|
|||
using AcidicGUI.Layout;
|
||||
using AcidicGUI.Rendering;
|
||||
using AcidicGUI.Widgets;
|
||||
|
||||
namespace AcidicGUI.VisualStyles;
|
||||
|
||||
/// <summary>
|
||||
/// When implemented by a visual style, any <see cref="Widget"/> implementing
|
||||
/// <see cref="IVisual{TVisualProperties}"/> to be rendered with the visual style
|
||||
/// via <see cref="StyleManager.DrawVisual{TWidget, TVisualProperties}"/> where <b>TWidget</b>
|
||||
/// is a <see cref="Widget" /> implementing <see cref="IVisual{TWidgetProperties}" />
|
||||
/// </summary>
|
||||
/// <typeparam name="TVisualProperties">The type of visual properties needed to render widgets of the given type.</typeparam>
|
||||
public interface IVisualRenderer<in TVisualProperties> where TVisualProperties : struct
|
||||
{
|
||||
/// <summary>
|
||||
/// Draws a widget using the specified visual properties.
|
||||
/// </summary>
|
||||
/// <param name="widget">The widget to draw</param>
|
||||
/// <param name="geometry">The destination to which the widget should be drawn</param>
|
||||
/// <param name="contentRect">The widget's calculated content bounds</param>
|
||||
/// <param name="properties">The visual properties associated with the widget</param>
|
||||
void Draw(
|
||||
Widget widget,
|
||||
GeometryHelper geometry,
|
||||
in LayoutRect contentRect,
|
||||
TVisualProperties properties
|
||||
);
|
||||
}
|
|
@ -6,9 +6,9 @@ using Microsoft.Xna.Framework;
|
|||
|
||||
namespace AcidicGUI.VisualStyles;
|
||||
|
||||
public interface IVisualStyle : IFontFamilyProvider
|
||||
public interface IVisualStyle :
|
||||
IFontFamilyProvider
|
||||
{
|
||||
int ProgressBarHeight { get; }
|
||||
int SliderThickness { get; }
|
||||
Point ToggleSize { get; }
|
||||
Point SwitchSize { get; }
|
||||
|
@ -62,12 +62,6 @@ public interface IVisualStyle : IFontFamilyProvider
|
|||
bool selected
|
||||
);
|
||||
|
||||
void DrawProgressBar(
|
||||
ProgressBar widget,
|
||||
GeometryHelper geometry,
|
||||
float fillPercentage
|
||||
);
|
||||
|
||||
void DrawSlider(
|
||||
Slider widget,
|
||||
GeometryHelper geometry,
|
||||
|
|
73
src/AcidicGUI/VisualStyles/StyleManager.cs
Normal file
73
src/AcidicGUI/VisualStyles/StyleManager.cs
Normal file
|
@ -0,0 +1,73 @@
|
|||
using AcidicGUI.CustomProperties;
|
||||
using AcidicGUI.Rendering;
|
||||
using AcidicGUI.Widgets;
|
||||
|
||||
namespace AcidicGUI.VisualStyles;
|
||||
|
||||
/// <summary>
|
||||
/// API for interacting with global styles throughout the UI.
|
||||
/// </summary>
|
||||
public static class StyleManager
|
||||
{
|
||||
private static IVisualStyle? activeStyle;
|
||||
|
||||
[Obsolete("This property is for compatibility with older widgets, and is destined to be removed.")]
|
||||
internal static IVisualStyle? ActiveStyle => activeStyle;
|
||||
|
||||
/// <summary>
|
||||
/// Changes the active visual style, without notifying any widgets that this has happened.
|
||||
/// </summary>
|
||||
/// <param name="newStyle">The new style to use. If null, styled widgets will use fallback rendering.</param>
|
||||
internal static void SetVisualStyle(IVisualStyle? newStyle)
|
||||
{
|
||||
activeStyle = newStyle;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets layout properties of the given type from the specified style override,
|
||||
/// or the active global style if the style override is null. If the style doesn't
|
||||
/// implement <see cref="IGetLayoutProperties{TLayoutProperties}"/>, then defaults
|
||||
/// will be returned instead.
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="styleOverride">An optional style override.</param>
|
||||
/// <param name="properties">A reference to a <see cref="TLayoutProperties"/> value where layouit properties will be written to.</param>
|
||||
/// <typeparam name="TLayoutProperties">Any value type.</typeparam>
|
||||
/// <returns>True if the properties have changed in the visual style, false otherwise.</returns>
|
||||
internal static bool GetLayoutProperties<TLayoutProperties>(IVisualStyle? styleOverride, ref TLayoutProperties properties)
|
||||
where TLayoutProperties : struct
|
||||
{
|
||||
var style = styleOverride ?? activeStyle;
|
||||
|
||||
properties = default(TLayoutProperties);
|
||||
|
||||
if (style is IGetLayoutProperties<TLayoutProperties> retriever)
|
||||
return retriever.GetLayoutProperties(ref properties);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Uses the current visual style to draw the given widget.
|
||||
/// If the visual style does not support drawing the given widget
|
||||
/// (it doesn't implement <see cref="IVisualRenderer{TWidget}"/>), then
|
||||
/// no geometry will be drawn.
|
||||
/// </summary>
|
||||
/// <param name="geometryHelper">The destination <see cref="GeometryHelper" /> to which geometry should be drawn.</param>
|
||||
/// <param name="widget">The widget to be drawn. Must implement <see cref="IVisual{TVisualProperties}" />.</param>
|
||||
/// <typeparam name="TWidget">Any type deriving <see cref="Widget"/> implementing <see cref="IVisual{TVisualProperties}" />.</typeparam>
|
||||
/// <typeparam name="TVisualProperties">Any value type.</typeparam>
|
||||
public static void DrawVisual<TWidget, TVisualProperties>(this GeometryHelper geometryHelper, TWidget widget)
|
||||
where TWidget : Widget, IVisual<TVisualProperties>
|
||||
where TVisualProperties : struct
|
||||
{
|
||||
var style = widget.GetVisualStyleOverride() ?? activeStyle;
|
||||
if (style is not IVisualRenderer<TVisualProperties> renderer)
|
||||
return;
|
||||
|
||||
var contentArea = widget.ContentArea;
|
||||
var styleProperties = widget.VisualProperties;
|
||||
|
||||
renderer.Draw(widget, geometryHelper, in contentArea, styleProperties);
|
||||
}
|
||||
}
|
|
@ -55,7 +55,7 @@ public abstract class Dropdown<TItemType, TView> : Widget,
|
|||
|
||||
protected override Point GetContentSize(Point availableSize)
|
||||
{
|
||||
Padding buttonPadding = GetVisualStyle().DropdownButtonPadding;
|
||||
Padding buttonPadding = GetVisualStyleOverride().DropdownButtonPadding;
|
||||
Point buttonSize = buttonBox.GetCachedContentSize(availableSize);
|
||||
|
||||
return new Point(buttonSize.X + buttonPadding.Horizontal, buttonSize.Y + buttonPadding.Vertical);
|
||||
|
@ -63,7 +63,7 @@ public abstract class Dropdown<TItemType, TView> : Widget,
|
|||
|
||||
protected override void ArrangeChildren(IGuiContext context, LayoutRect availableSpace)
|
||||
{
|
||||
Padding buttonPadding = GetVisualStyle().DropdownButtonPadding;
|
||||
Padding buttonPadding = GetVisualStyleOverride().DropdownButtonPadding;
|
||||
buttonBox.UpdateLayout(context, new LayoutRect(availableSpace.Left + buttonPadding.Left, availableSpace.Top + buttonPadding.Top, availableSpace.Width - buttonPadding.Horizontal, availableSpace.Height - buttonPadding.Vertical));
|
||||
|
||||
Point screen = new Point(context.CanvasWidth, context.CanvasHeight);
|
||||
|
@ -258,7 +258,7 @@ public abstract class Dropdown<TItemType, TView> : Widget,
|
|||
|
||||
protected override void RebuildGeometry(GeometryHelper geometry)
|
||||
{
|
||||
GetVisualStyle().DrawDropdownItemBackground(this, geometry, hovered, pressed, active);
|
||||
GetVisualStyleOverride().DrawDropdownItemBackground(this, geometry, hovered, pressed, active);
|
||||
}
|
||||
|
||||
public void OnMouseClick(MouseButtonEvent e)
|
||||
|
@ -404,7 +404,7 @@ public abstract class Dropdown<TItemType, TView> : Widget,
|
|||
|
||||
protected override void RebuildGeometry(GeometryHelper geometry)
|
||||
{
|
||||
GetVisualStyle().DrawDropdownItemsBackground(geometry, DropdownArea);
|
||||
GetVisualStyleOverride().DrawDropdownItemsBackground(geometry, DropdownArea);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -43,7 +43,7 @@ public sealed class Icon : Widget
|
|||
|
||||
protected override Point GetContentSize(Point availableSize)
|
||||
{
|
||||
Font? iconFont = GetVisualStyle().IconFont;
|
||||
Font? iconFont = GetVisualStyleOverride().IconFont;
|
||||
if (iconFont == null || string.IsNullOrEmpty(iconString))
|
||||
return Point.Zero;
|
||||
|
||||
|
@ -52,7 +52,7 @@ public sealed class Icon : Widget
|
|||
|
||||
protected override void RebuildGeometry(GeometryHelper geometry)
|
||||
{
|
||||
var font = GetVisualStyle().IconFont;
|
||||
var font = GetVisualStyleOverride().IconFont;
|
||||
|
||||
float x = ContentArea.Left + (ContentArea.Width - actualIconSize.X) / 2f;
|
||||
float y = ContentArea.Top + (ContentArea.Height - actualIconSize.Y) / 2f;
|
||||
|
|
|
@ -1,29 +1,58 @@
|
|||
using AcidicGUI.Animation;
|
||||
using AcidicGUI.Rendering;
|
||||
using AcidicGUI.VisualStyles;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace AcidicGUI.Widgets;
|
||||
|
||||
public sealed class ProgressBar : Widget
|
||||
public sealed class ProgressBar :
|
||||
Widget,
|
||||
IVisual<ProgressBar.ProgressBarVisualProperties>,
|
||||
IUpdateLayoutProperties
|
||||
{
|
||||
private float fillPercentage = 0;
|
||||
private ProgressBarVisualProperties visualProperties;
|
||||
private ProgressBarLayoutProperties layoutProperties;
|
||||
|
||||
/// <inheritdoc />
|
||||
public ref readonly ProgressBarVisualProperties VisualProperties => ref visualProperties;
|
||||
|
||||
public float Value
|
||||
{
|
||||
get => fillPercentage;
|
||||
get => visualProperties.FillPercentage;
|
||||
set
|
||||
{
|
||||
fillPercentage = MathHelper.Clamp(value, 0, 1);
|
||||
var newPercentage = MathHelper.Clamp(value, 0, 1);
|
||||
|
||||
if (Math.Abs(newPercentage - visualProperties.FillPercentage) < float.Epsilon)
|
||||
return;
|
||||
|
||||
visualProperties.FillPercentage = newPercentage;
|
||||
InvalidateGeometry();
|
||||
}
|
||||
}
|
||||
|
||||
protected override Point GetContentSize(Point availableSize)
|
||||
{
|
||||
return new Point(availableSize.X, GetVisualStyle().ProgressBarHeight);
|
||||
return new Point(availableSize.X, layoutProperties.ProgressBarHeight);
|
||||
}
|
||||
|
||||
protected override void RebuildGeometry(GeometryHelper geometry)
|
||||
{
|
||||
GetVisualStyle().DrawProgressBar(this, geometry, fillPercentage);
|
||||
geometry.DrawVisual<ProgressBar, ProgressBarVisualProperties>(this);
|
||||
}
|
||||
|
||||
public void UpdateLayoutProperties()
|
||||
{
|
||||
this.GetLayoutProperties(ref layoutProperties);
|
||||
}
|
||||
|
||||
public struct ProgressBarVisualProperties
|
||||
{
|
||||
public float FillPercentage;
|
||||
}
|
||||
|
||||
public struct ProgressBarLayoutProperties()
|
||||
{
|
||||
public int ProgressBarHeight = 16;
|
||||
}
|
||||
}
|
|
@ -60,7 +60,7 @@ public sealed class ScrollView :
|
|||
availableSpace = new LayoutRect(
|
||||
availableSpace.Left,
|
||||
availableSpace.Top,
|
||||
availableSpace.Width - GetVisualStyle().ScrollBarSize,
|
||||
availableSpace.Width - GetVisualStyleOverride().ScrollBarSize,
|
||||
availableSpace.Height
|
||||
);
|
||||
}
|
||||
|
@ -104,10 +104,10 @@ public sealed class ScrollView :
|
|||
|
||||
if (showScrollBar)
|
||||
{
|
||||
GetVisualStyle().DrawScrollBar(this, geometry, new LayoutRect(
|
||||
ContentArea.Right - GetVisualStyle().ScrollBarSize,
|
||||
GetVisualStyleOverride().DrawScrollBar(this, geometry, new LayoutRect(
|
||||
ContentArea.Right - GetVisualStyleOverride().ScrollBarSize,
|
||||
ContentArea.Top,
|
||||
GetVisualStyle().ScrollBarSize,
|
||||
GetVisualStyleOverride().ScrollBarSize,
|
||||
ContentArea.Height
|
||||
), pageOffset, innerSize);
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ public sealed class Slider : Widget,
|
|||
|
||||
protected override Point GetContentSize(Point availableSize)
|
||||
{
|
||||
int thickness = GetVisualStyle().SliderThickness;
|
||||
int thickness = GetVisualStyleOverride().SliderThickness;
|
||||
|
||||
return direction == Direction.Horizontal
|
||||
? new Point(100, thickness)
|
||||
|
@ -50,7 +50,7 @@ public sealed class Slider : Widget,
|
|||
|
||||
protected override void RebuildGeometry(GeometryHelper geometry)
|
||||
{
|
||||
GetVisualStyle().DrawSlider(this, geometry, hovered, pressed, direction == Direction.Vertical, currentValue);
|
||||
GetVisualStyleOverride().DrawSlider(this, geometry, hovered, pressed, direction == Direction.Vertical, currentValue);
|
||||
}
|
||||
|
||||
private void SetValue(float newValue)
|
||||
|
|
|
@ -282,13 +282,13 @@ public class TextWidget : Widget,
|
|||
}
|
||||
|
||||
var renderColor = element.MarkupData.IsSelected
|
||||
? GetVisualStyle().TextSelectionForeground
|
||||
: (element.MarkupData.ColorOverride ?? TextColor) ?? GetVisualStyle().GetTextColor(this);
|
||||
? GetVisualStyleOverride().TextSelectionForeground
|
||||
: (element.MarkupData.ColorOverride ?? TextColor) ?? GetVisualStyleOverride().GetTextColor(this);
|
||||
|
||||
if (element.MeasuredSize.HasValue && element.MarkupData.Highlight.A > 0 || element.MarkupData.IsSelected)
|
||||
{
|
||||
var highlight = element.MarkupData.IsSelected
|
||||
? GetVisualStyle().TextSelectionBackground
|
||||
? GetVisualStyleOverride().TextSelectionBackground
|
||||
: element.MarkupData.Highlight;
|
||||
|
||||
var highlightRect = new LayoutRect(
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
using AcidicGUI.Common;
|
||||
using AcidicGUI.Events;
|
||||
using AcidicGUI.Layout;
|
||||
using AcidicGUI.Rendering;
|
||||
|
@ -20,6 +21,7 @@ public sealed class Toggle : Widget,
|
|||
private bool focused;
|
||||
private bool toggleValue;
|
||||
|
||||
public event Action<StateChange<bool>>? OnBeforeValueChanged;
|
||||
public event Action<bool>? OnValueChanged;
|
||||
|
||||
public bool UseSwitchVariant
|
||||
|
@ -41,7 +43,7 @@ public sealed class Toggle : Widget,
|
|||
return;
|
||||
|
||||
toggleValue = value;
|
||||
NotifyChange();
|
||||
OnValueChanged?.Invoke(value);
|
||||
InvalidateGeometry();
|
||||
}
|
||||
}
|
||||
|
@ -51,22 +53,22 @@ public sealed class Toggle : Widget,
|
|||
protected override Point GetContentSize(Point availableSize)
|
||||
{
|
||||
return isSwitchVariant
|
||||
? GetVisualStyle().SwitchSize
|
||||
: GetVisualStyle().ToggleSize;
|
||||
? GetVisualStyleOverride().SwitchSize
|
||||
: GetVisualStyleOverride().ToggleSize;
|
||||
}
|
||||
|
||||
protected override void RebuildGeometry(GeometryHelper geometry)
|
||||
{
|
||||
var size = isSwitchVariant
|
||||
? GetVisualStyle().SwitchSize
|
||||
: GetVisualStyle().ToggleSize;
|
||||
? GetVisualStyleOverride().SwitchSize
|
||||
: GetVisualStyleOverride().ToggleSize;
|
||||
|
||||
var rect = new LayoutRect(ContentArea.Left + ((ContentArea.Width - size.X) / 2), ContentArea.Top + ((ContentArea.Height - size.Y) / 2), size.X, size.Y);
|
||||
|
||||
if (isSwitchVariant)
|
||||
GetVisualStyle().DrawToggleSwitch(this, geometry, rect, hovered, pressed, focused, toggleValue);
|
||||
GetVisualStyleOverride().DrawToggleSwitch(this, geometry, rect, hovered, pressed, focused, toggleValue);
|
||||
else
|
||||
GetVisualStyle().DrawToggle(this, geometry, rect, hovered, pressed, focused, toggleValue);
|
||||
GetVisualStyleOverride().DrawToggle(this, geometry, rect, hovered, pressed, focused, toggleValue);
|
||||
}
|
||||
|
||||
public void OnMouseEnter(MouseMoveEvent e)
|
||||
|
@ -122,13 +124,21 @@ public sealed class Toggle : Widget,
|
|||
if (!hovered)
|
||||
return;
|
||||
|
||||
toggleValue = !toggleValue;
|
||||
InvalidateGeometry();
|
||||
NotifyChange();
|
||||
NotifyChange(!toggleValue);
|
||||
}
|
||||
|
||||
private void NotifyChange()
|
||||
private void NotifyChange(bool newValue)
|
||||
{
|
||||
var currentValue = toggleValue;
|
||||
var change = new StateChange<bool>(currentValue, newValue);
|
||||
|
||||
OnBeforeValueChanged?.Invoke(change);
|
||||
|
||||
if (change.Canceled)
|
||||
return;
|
||||
|
||||
toggleValue = newValue;
|
||||
OnValueChanged?.Invoke(toggleValue);
|
||||
}
|
||||
}
|
|
@ -1,10 +1,12 @@
|
|||
using AcidicGUI.Layout;
|
||||
using AcidicGUI.VisualStyles;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace AcidicGUI.Widgets;
|
||||
|
||||
public partial class Widget
|
||||
{
|
||||
private readonly HashSet<Type> dirtyLayoutPropertyTypes = new();
|
||||
private Widget? layoutRoot;
|
||||
private bool layoutIsDirty = true;
|
||||
private HorizontalAlignment horizontalAlignment;
|
||||
|
@ -126,6 +128,19 @@ public partial class Widget
|
|||
}
|
||||
}
|
||||
|
||||
protected void GetLayoutProperties<TLayoutProperties>(ref TLayoutProperties properties)
|
||||
where TLayoutProperties : struct
|
||||
{
|
||||
var wasDirty = StyleManager.GetLayoutProperties<TLayoutProperties>(GetVisualStyleOverride(), ref properties)
|
||||
|| dirtyLayoutPropertyTypes.Contains(typeof(TLayoutProperties));
|
||||
|
||||
if (wasDirty)
|
||||
{
|
||||
dirtyLayoutPropertyTypes.Add(typeof(TLayoutProperties));
|
||||
InvalidateLayout();
|
||||
}
|
||||
}
|
||||
|
||||
public void InvalidateOwnLayout()
|
||||
{
|
||||
InvalidateLayoutInternal();
|
||||
|
@ -145,6 +160,8 @@ public partial class Widget
|
|||
if (!layoutIsDirty)
|
||||
return;
|
||||
|
||||
dirtyLayoutPropertyTypes.Clear();
|
||||
|
||||
if (visibility == Visibility.Collapsed)
|
||||
{
|
||||
calculatedLayoutRect = new LayoutRect(0, 0, 0, 0);
|
||||
|
|
|
@ -103,6 +103,9 @@ public abstract partial class Widget : IFontFamilyProvider
|
|||
get => visualStyleOverride;
|
||||
set
|
||||
{
|
||||
if (visualStyleOverride != value)
|
||||
return;
|
||||
|
||||
visualStyleOverride = value;
|
||||
InvalidateLayout();
|
||||
}
|
||||
|
@ -154,20 +157,27 @@ public abstract partial class Widget : IFontFamilyProvider
|
|||
}
|
||||
}
|
||||
|
||||
public IVisualStyle GetVisualStyle()
|
||||
/// <summary>
|
||||
/// Walks up the widget tree from this widget to the top-level, returning
|
||||
/// the first visual style override that isn't null. If no widgets in the
|
||||
/// path override the global visual style, then null will be returned.
|
||||
/// </summary>
|
||||
/// <returns>The visual style override for this widget or one of its parents, or null if there are no overriding widgets.</returns>
|
||||
public IVisualStyle? GetVisualStyleOverride()
|
||||
{
|
||||
if (visualStyleOverride != null)
|
||||
return visualStyleOverride;
|
||||
|
||||
if (parent != null)
|
||||
return parent.GetVisualStyle();
|
||||
return parent.GetVisualStyleOverride();
|
||||
|
||||
return GuiManager!.GetVisualStyle();
|
||||
// TODO: Return null and remove that property when we've ported all the widgets.
|
||||
return StyleManager.ActiveStyle;
|
||||
}
|
||||
|
||||
protected virtual void RebuildGeometry(GeometryHelper geometry)
|
||||
{
|
||||
GetVisualStyle().DrawWidgetBackground(this, geometry);
|
||||
GetVisualStyleOverride().DrawWidgetBackground(this, geometry);
|
||||
}
|
||||
|
||||
private LayoutRect? GetClippingRectangle()
|
||||
|
|
|
@ -1,14 +1,66 @@
|
|||
namespace SociallyDistant.Core.Core.Config
|
||||
{
|
||||
/// <summary>
|
||||
/// Interfaces for building settings pages, and adding
|
||||
/// fields to said pages.
|
||||
/// </summary>
|
||||
public interface ISettingsUiBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new labeled section to categorize fields under.
|
||||
/// </summary>
|
||||
/// <param name="sectionTitle">The name of the section as shown in the UI</param>
|
||||
/// <param name="sectionId">A unique identifier representing the section</param>
|
||||
/// <returns></returns>
|
||||
ISettingsUiBuilder AddSection(string sectionTitle, out int sectionId);
|
||||
|
||||
ISettingsUiBuilder WithLabel(string labelText, int sectionId);
|
||||
ISettingsUiBuilder WithToggle(string title, string? description, bool value, Action<bool> changeCallback, int sectionId);
|
||||
ISettingsUiBuilder WithSlider(string title, string? description, float value, float minimum, float maximum, Action<float> changeCallback, int sectionId);
|
||||
ISettingsUiBuilder WithSlider(string title, string? description, int value, int minimum, int maximum, Action<int> changeCallback, int sectionId);
|
||||
ISettingsUiBuilder WithTextField(string title, string? description, string? currentValue, Action<string?> changeCallback, int sectionId);
|
||||
ISettingsUiBuilder WithStringDropdown(string title, string? description, int currentIndex, string[] choices, Action<int> changeCallback, int sectionId);
|
||||
|
||||
ISettingsUiBuilder WithToggle(
|
||||
string title,
|
||||
string? description,
|
||||
bool value,
|
||||
Action<bool> changeCallback,
|
||||
int sectionId,
|
||||
bool requireConfirmation = false,
|
||||
string? confirmationMessage = null
|
||||
);
|
||||
|
||||
ISettingsUiBuilder WithSlider(
|
||||
string title,
|
||||
string? description,
|
||||
float value,
|
||||
float minimum,
|
||||
float maximum,
|
||||
Action<float> changeCallback,
|
||||
int sectionId
|
||||
);
|
||||
|
||||
ISettingsUiBuilder WithSlider(
|
||||
string title,
|
||||
string? description,
|
||||
int value,
|
||||
int minimum,
|
||||
int maximum,
|
||||
Action<int> changeCallback,
|
||||
int sectionId
|
||||
);
|
||||
|
||||
ISettingsUiBuilder WithTextField(
|
||||
string title,
|
||||
string? description,
|
||||
string? currentValue,
|
||||
Action<string?> changeCallback,
|
||||
int sectionId
|
||||
);
|
||||
|
||||
ISettingsUiBuilder WithStringDropdown(
|
||||
string title,
|
||||
string? description,
|
||||
int currentIndex,
|
||||
string[] choices,
|
||||
Action<int> changeCallback,
|
||||
int sectionId
|
||||
);
|
||||
}
|
||||
}
|
|
@ -11,6 +11,7 @@
|
|||
public string Name => reflectionAttribute.DisplayName;
|
||||
public string SectionName => reflectionAttribute.SectionName;
|
||||
public bool Hidden => reflectionAttribute.Hidden;
|
||||
public int Priority => reflectionAttribute.Priority;
|
||||
|
||||
protected SettingsCategory(ISettingsManager settingsManager)
|
||||
{
|
||||
|
|
|
@ -3,17 +3,19 @@
|
|||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
|
||||
public class SettingsCategoryAttribute : Attribute
|
||||
{
|
||||
public SettingsCategoryAttribute(string key, string displayName, string sectionName, bool hidden = false)
|
||||
public SettingsCategoryAttribute(string key, string displayName, string sectionName, bool hidden = false, int priority = 0)
|
||||
{
|
||||
Key = key;
|
||||
DisplayName = displayName;
|
||||
Hidden = hidden;
|
||||
SectionName = sectionName;
|
||||
this.Priority = priority;
|
||||
}
|
||||
|
||||
public string SectionName { get; }
|
||||
public string Key { get; }
|
||||
public string DisplayName { get; }
|
||||
public bool Hidden { get; }
|
||||
public int Priority { get; }
|
||||
}
|
||||
}
|
|
@ -5,7 +5,7 @@ using SociallyDistant.Core.Core.Config;
|
|||
|
||||
namespace SociallyDistant.Core.Config.SystemConfigCategories
|
||||
{
|
||||
[SettingsCategory("graphics", "Graphics", CommonSettingsCategorySections.Hardware)]
|
||||
[SettingsCategory("graphics", "Graphics", CommonSettingsCategorySections.Hardware, priority: 1)]
|
||||
public class GraphicsSettings : SettingsCategory
|
||||
{
|
||||
public string? DisplayResolution
|
||||
|
@ -62,7 +62,13 @@ namespace SociallyDistant.Core.Config.SystemConfigCategories
|
|||
|
||||
private string[] GetAvailableResolutions()
|
||||
{
|
||||
return GraphicsAdapter.DefaultAdapter.SupportedDisplayModes.OrderByDescending(x => x.Width * x.Height).Where(x => x.Height > 720).Select(x => $"{x.Width}x{x.Height}").Distinct().ToArray();
|
||||
return GraphicsAdapter.DefaultAdapter.SupportedDisplayModes.
|
||||
OrderByDescending(x => x.Width)
|
||||
.ThenByDescending(x => x.Height)
|
||||
.Where(x => x.Height >= 720)
|
||||
.Select(x => $"{x.Width}x{x.Height}")
|
||||
.Distinct()
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
@ -79,9 +85,7 @@ namespace SociallyDistant.Core.Config.SystemConfigCategories
|
|||
if (currentIndex == -1)
|
||||
currentIndex = 0;
|
||||
#endif
|
||||
|
||||
uiBuilder.AddSection("Display", out int display)
|
||||
.WithStringDropdown(
|
||||
uiBuilder.AddSection("Display", out int display).WithStringDropdown(
|
||||
"Resolution",
|
||||
null,
|
||||
currentIndex,
|
||||
|
@ -102,18 +106,19 @@ namespace SociallyDistant.Core.Config.SystemConfigCategories
|
|||
display
|
||||
);
|
||||
|
||||
uiBuilder.AddSection("Desktop Effects", out int effects)
|
||||
.WithToggle("Background blurs",
|
||||
uiBuilder.AddSection("Desktop Effects", out int effects).WithToggle(
|
||||
"Background blurs",
|
||||
"Adds translucent, blurred backgrounds to various interface elements like the terminal",
|
||||
BlurEffect,
|
||||
x => BlurEffect = x,
|
||||
effects)
|
||||
.WithToggle("Bloom",
|
||||
effects
|
||||
).WithToggle(
|
||||
"Bloom",
|
||||
"Adds subtle glow to brighter UI elements and text",
|
||||
BloomEffect,
|
||||
x => BloomEffect = x,
|
||||
effects)
|
||||
.WithToggle(
|
||||
effects
|
||||
).WithToggle(
|
||||
"Glitch bands",
|
||||
"Enable or disable the flashing bands of color that appear during malware infections and high system load.",
|
||||
EnableXorgGlitches,
|
||||
|
@ -123,8 +128,7 @@ namespace SociallyDistant.Core.Config.SystemConfigCategories
|
|||
|
||||
if (OperatingSystem.IsLinux())
|
||||
{
|
||||
uiBuilder.AddSection("Linux", out int linux)
|
||||
.WithToggle(
|
||||
uiBuilder.AddSection("Linux", out int linux).WithToggle(
|
||||
"Wayland Support",
|
||||
"Change whether Socially Distant uses Wayland or X11 to communicate with your desktop environment. Disabling Wayland support may work around bugs on certain desktops. Changing this setting requires a game restart.",
|
||||
UseWaylandBackend,
|
||||
|
|
|
@ -3,6 +3,7 @@ using System.Security;
|
|||
using System.Text;
|
||||
using SociallyDistant.Core.Modules;
|
||||
using SociallyDistant.Core.OS.Network;
|
||||
using SociallyDistant.Core.Shell.Windowing;
|
||||
|
||||
namespace SociallyDistant.Core.Core
|
||||
{
|
||||
|
@ -34,6 +35,21 @@ namespace SociallyDistant.Core.Core
|
|||
|
||||
public static readonly string PlayerHomeId = "player";
|
||||
|
||||
public static Task<MessageDialogResult> GetResultAsync(this IMessageDialog messageDialog)
|
||||
{
|
||||
var completionSource = new TaskCompletionSource<MessageDialogResult>();
|
||||
|
||||
messageDialog.DismissCallback += OnDialogDismiss;
|
||||
|
||||
return completionSource.Task;
|
||||
|
||||
void OnDialogDismiss(MessageDialogResult result)
|
||||
{
|
||||
messageDialog.DismissCallback -= OnDialogDismiss;
|
||||
completionSource.SetResult(result);
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<Exception> UnravelAggregateExceptions(AggregateException? aggregate)
|
||||
{
|
||||
if (aggregate == null)
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.InteropServices;
|
||||
using AcidicGUI.Widgets;
|
||||
using Microsoft.Win32;
|
||||
using Serilog;
|
||||
|
||||
namespace SociallyDistant.Core.Modules;
|
||||
|
||||
public abstract class Application
|
||||
public abstract class Application : IDisposable
|
||||
{
|
||||
private static Application? _current;
|
||||
private bool started;
|
||||
|
@ -37,6 +38,14 @@ public abstract class Application
|
|||
}
|
||||
}
|
||||
|
||||
public void Restart()
|
||||
{
|
||||
if (!started)
|
||||
return;
|
||||
|
||||
OnRestart();
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
if (started)
|
||||
|
@ -55,11 +64,12 @@ public abstract class Application
|
|||
}
|
||||
|
||||
protected abstract void Run();
|
||||
protected abstract void OnRestart();
|
||||
|
||||
private void DetermineHardwareData()
|
||||
{
|
||||
Log.Information("Determining system info and whether we can run...");
|
||||
OperatingSystem = Environment.OSVersion.ToString();
|
||||
OperatingSystem = $"{RuntimeInformation.OSDescription} {RuntimeInformation.OSArchitecture} ({RuntimeInformation.FrameworkDescription})";
|
||||
Log.Information($"Operating system is {OperatingSystem}");
|
||||
|
||||
switch (Environment.OSVersion.Platform)
|
||||
|
@ -120,4 +130,9 @@ public abstract class Application
|
|||
// Pick the first.
|
||||
CpuName = cpuModels[0];
|
||||
}
|
||||
|
||||
public virtual void Dispose()
|
||||
{
|
||||
_current = null;
|
||||
}
|
||||
}
|
|
@ -40,19 +40,41 @@ public sealed class BackgroundBlurWidgetEffect : IWidgetEffect,
|
|||
ColorSourceBlend = Blend.SourceAlpha, ColorDestinationBlend = Blend.InverseSourceAlpha, AlphaSourceBlend = Blend.One, AlphaDestinationBlend = Blend.InverseSourceAlpha,
|
||||
};
|
||||
|
||||
private BackgroundBlurWidgetEffect(MonoGameEffect defaultUiShader, Effect bluReFfect)
|
||||
private BackgroundBlurWidgetEffect(MonoGameEffect defaultUiShader, Effect blurEffect)
|
||||
{
|
||||
this.defaultUiShader = defaultUiShader;
|
||||
this.gaussianShader = bluReFfect;
|
||||
this.gaussianShader = blurEffect;
|
||||
|
||||
blurrinessParameter = bluReFfect.Parameters["Blurriness"];
|
||||
texelSizeParameter = bluReFfect.Parameters["TexelSize"];
|
||||
curveParameter = bluReFfect.Parameters["Curve"];
|
||||
blurrinessParameter = blurEffect.Parameters["Blurriness"];
|
||||
texelSizeParameter = blurEffect.Parameters["TexelSize"];
|
||||
curveParameter = blurEffect.Parameters["Curve"];
|
||||
|
||||
vertexBuffer = new VertexBuffer(bluReFfect.GraphicsDevice, typeof(VertexPositionColorTexture), 4, BufferUsage.None);
|
||||
indexBuffer = new IndexBuffer(bluReFfect.GraphicsDevice, typeof(int), 6, BufferUsage.None);
|
||||
vertexBuffer = new VertexBuffer(blurEffect.GraphicsDevice, typeof(VertexPositionColorTexture), 4, BufferUsage.None);
|
||||
indexBuffer = new IndexBuffer(blurEffect.GraphicsDevice, typeof(int), 6, BufferUsage.None);
|
||||
|
||||
vertexBuffer.SetData(new VertexPositionColorTexture[] { new VertexPositionColorTexture(new Vector3(-1, -1, 0), Color.White, new Vector2(0, 0)), new VertexPositionColorTexture(new Vector3(1, -1, 0), Color.White, new Vector2(1, 0)), new VertexPositionColorTexture(new Vector3(-1, 1, 0), Color.White, new Vector2(0, 1)), new VertexPositionColorTexture(new Vector3(1, 1, 0), Color.White, new Vector2(1, 1)) });
|
||||
vertexBuffer.SetData(new VertexPositionColorTexture[]
|
||||
{
|
||||
new VertexPositionColorTexture(
|
||||
new Vector3(-1, -1, 0),
|
||||
Color.White,
|
||||
new Vector2(0, 0)
|
||||
),
|
||||
new VertexPositionColorTexture(
|
||||
new Vector3(1, -1, 0),
|
||||
Color.White,
|
||||
new Vector2(1, 0)
|
||||
),
|
||||
new VertexPositionColorTexture(
|
||||
new Vector3(-1, 1, 0),
|
||||
Color.White,
|
||||
new Vector2(0, 1)
|
||||
),
|
||||
new VertexPositionColorTexture(
|
||||
new Vector3(1, 1, 0),
|
||||
Color.White,
|
||||
new Vector2(1, 1)
|
||||
)
|
||||
});
|
||||
indexBuffer.SetData(new int[] { 0, 1, 2, 2, 1, 3 });
|
||||
|
||||
settingsObserver = Application.Instance.Context.SettingsManager.ObserveChanges(OnGameSettingsChanged);
|
||||
|
@ -87,14 +109,18 @@ public sealed class BackgroundBlurWidgetEffect : IWidgetEffect,
|
|||
}
|
||||
|
||||
var blurSettings = widget.GetCustomProperties<BackgroundBlurProperties>();
|
||||
var cachedGeometry = widget.GetCustomProperties<CachedGeometry>();
|
||||
|
||||
blurriness = blurSettings.Blurriness;
|
||||
curve = blurSettings.ComputedCurve;
|
||||
|
||||
Viewport viewport = renderer.GraphicsDevice.Viewport;
|
||||
|
||||
ResizeIfNeeded(ref blurTarget1, viewport.Width, viewport.Height);
|
||||
ResizeIfNeeded(ref blurTarget2, viewport.Width, viewport.Height);
|
||||
var needsToResize = ResizeIfNeeded(ref blurTarget1, viewport.Width, viewport.Height);
|
||||
needsToResize |= ResizeIfNeeded(ref blurTarget2, viewport.Width, viewport.Height);
|
||||
|
||||
if (needsToResize)
|
||||
cachedGeometry.Geometry = null;
|
||||
|
||||
if (blurTarget1 == null)
|
||||
return;
|
||||
|
@ -146,13 +172,17 @@ public sealed class BackgroundBlurWidgetEffect : IWidgetEffect,
|
|||
{
|
||||
}
|
||||
|
||||
private void ResizeIfNeeded(ref RenderTarget2D? target, int width, int height)
|
||||
private bool ResizeIfNeeded(ref RenderTarget2D? target, int width, int height)
|
||||
{
|
||||
if (target == null || target.Width != width || target.Height != height)
|
||||
{
|
||||
target?.Dispose();
|
||||
target = new RenderTarget2D(gaussianShader.GraphicsDevice, width, height, false, SurfaceFormat.Color, DepthFormat.Depth24Stencil8, 4, RenderTargetUsage.PreserveContents);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void DoBlur(RenderTarget2D source, RenderTarget2D destination, int pass)
|
||||
|
|
|
@ -50,6 +50,11 @@ public sealed class GuiService :
|
|||
refreshGeometryListener = EventBus.Listen<RefreshGeometryEvent>(OnGeometryRefreshRequested);
|
||||
}
|
||||
|
||||
public void ScheduleFullReRender()
|
||||
{
|
||||
acidicGui.ScheduleFullReRender();
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
|
|
@ -40,10 +40,25 @@ public class WidgetListSettingsUiBuilder : ISettingsUiBuilder
|
|||
string? description,
|
||||
bool value,
|
||||
Action<bool> changeCallback,
|
||||
int sectionId
|
||||
int sectionId,
|
||||
bool requireConfirmation,
|
||||
string? confirmationMessage = null
|
||||
)
|
||||
{
|
||||
widgets.AddWidget(new SettingsFieldWidget { Title = title, Description = description, Slot = new SwitchWidget { IsActive = value, Callback = changeCallback }, UseReverseLayout = false }, sectionMap[sectionId]);
|
||||
widgets.AddWidget(new SettingsFieldWidget
|
||||
{
|
||||
Title = title,
|
||||
Description = description,
|
||||
Slot = new SwitchWidget
|
||||
{
|
||||
IsActive = value,
|
||||
Callback = changeCallback,
|
||||
RequireConfirmation = requireConfirmation,
|
||||
ConfirmationTitle = title,
|
||||
ConfirmationMessage = confirmationMessage
|
||||
},
|
||||
UseReverseLayout = false
|
||||
}, sectionMap[sectionId]);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
|
|
@ -6,9 +6,20 @@ public sealed class SwitchWidget : IWidget
|
|||
{
|
||||
public bool IsActive { get; set; }
|
||||
public Action<bool>? Callback { get; set; }
|
||||
public bool RequireConfirmation { get; set; }
|
||||
public string? ConfirmationTitle { get; set; }
|
||||
public string? ConfirmationMessage { get; set; }
|
||||
|
||||
|
||||
public RecyclableWidgetController Build()
|
||||
{
|
||||
return new SwitchWidgetController() { IsActive = IsActive, Callback = Callback };
|
||||
return new SwitchWidgetController()
|
||||
{
|
||||
IsActive = IsActive,
|
||||
Callback = Callback,
|
||||
RequireConfirmation = this.RequireConfirmation,
|
||||
ConfirmationTitle = this.ConfirmationTitle,
|
||||
ConfirmationMessage = this.ConfirmationMessage
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,5 +1,9 @@
|
|||
using AcidicGUI.Common;
|
||||
using AcidicGUI.ListAdapters;
|
||||
using AcidicGUI.Widgets;
|
||||
using SociallyDistant.Core.Core;
|
||||
using SociallyDistant.Core.Modules;
|
||||
using SociallyDistant.Core.Shell.Windowing;
|
||||
|
||||
namespace SociallyDistant.Core.UI.Recycling;
|
||||
|
||||
|
@ -9,7 +13,9 @@ public sealed class SwitchWidgetController : RecyclableWidgetController
|
|||
|
||||
public bool IsActive { get; set; }
|
||||
public Action<bool>? Callback { get; set; }
|
||||
|
||||
public bool RequireConfirmation { get; set; }
|
||||
public string? ConfirmationTitle { get; set; }
|
||||
public string? ConfirmationMessage { get; set; }
|
||||
|
||||
public override void Build(ContentWidget destination)
|
||||
{
|
||||
|
@ -17,22 +23,53 @@ public sealed class SwitchWidgetController : RecyclableWidgetController
|
|||
|
||||
toggle.UseSwitchVariant = true;
|
||||
toggle.ToggleValue = IsActive;
|
||||
toggle.OnValueChanged += HandleValueChanged;
|
||||
toggle.OnBeforeValueChanged += HandleValueChanged;
|
||||
|
||||
destination.Content = toggle;
|
||||
}
|
||||
|
||||
private void HandleValueChanged(bool value)
|
||||
private void HandleValueChanged(StateChange<bool> change)
|
||||
{
|
||||
Callback?.Invoke(value);
|
||||
if (!RequireConfirmation)
|
||||
{
|
||||
Callback?.Invoke(change.NewValue);
|
||||
return;
|
||||
}
|
||||
|
||||
change.Cancel();
|
||||
|
||||
ConfirmChange(change.NewValue);
|
||||
}
|
||||
|
||||
public override void Recycle()
|
||||
{
|
||||
if (toggle != null)
|
||||
toggle.OnValueChanged -= HandleValueChanged;
|
||||
toggle.OnBeforeValueChanged -= HandleValueChanged;
|
||||
|
||||
Recyclewidget(toggle);
|
||||
toggle = null;
|
||||
}
|
||||
|
||||
private async void ConfirmChange(bool newValue)
|
||||
{
|
||||
var sociallyDistant = Application.Instance.Context;
|
||||
|
||||
var messageDialog = sociallyDistant.Shell.CreateMessageDialog(ConfirmationTitle ?? "Confirm setting change");
|
||||
messageDialog.Message = ConfirmationMessage ?? "Are you sure you want to change this setting?";
|
||||
messageDialog.MessageType = MessageBoxType.Warning;
|
||||
|
||||
messageDialog.Buttons.Add(new MessageBoxButtonData("Yes", MessageDialogResult.Yes));
|
||||
messageDialog.Buttons.Add(new MessageBoxButtonData("No", MessageDialogResult.No));
|
||||
|
||||
var result = await messageDialog.GetResultAsync();
|
||||
|
||||
if (result != MessageDialogResult.Yes)
|
||||
return;
|
||||
|
||||
if (toggle == null)
|
||||
return;
|
||||
|
||||
toggle.ToggleValue = newValue;
|
||||
Callback?.Invoke(newValue);
|
||||
}
|
||||
}
|
|
@ -13,10 +13,30 @@ using SociallyDistant.Core.UI.Common;
|
|||
|
||||
namespace SociallyDistant.Core.UI.VisualStyles;
|
||||
|
||||
public class SociallyDistantVisualStyle : IVisualStyle
|
||||
public abstract class UserStyle
|
||||
{
|
||||
private bool isDirty = true;
|
||||
|
||||
protected bool GetDirtyState()
|
||||
{
|
||||
var wasDirty = isDirty;
|
||||
isDirty = false;
|
||||
return wasDirty;
|
||||
}
|
||||
|
||||
protected void SetDirty()
|
||||
{
|
||||
isDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
public class SociallyDistantVisualStyle : IVisualStyle,
|
||||
IGetLayoutProperties<ProgressBar.ProgressBarLayoutProperties>,
|
||||
IVisualRenderer<ProgressBar.ProgressBarVisualProperties>
|
||||
{
|
||||
private readonly IGameContext game;
|
||||
|
||||
private readonly ProgressBarStyle progressBarSTyle = new();
|
||||
private readonly Color mainBackground = new Color(0x11, 0x13, 0x15);
|
||||
private readonly Color statusBarColor = new Color(0x01, 0x22, 0x37, 0xff);
|
||||
private readonly Color accentPrimary = new Color(0x13, 0x85, 0xC3, 0xff);
|
||||
|
@ -53,7 +73,6 @@ public class SociallyDistantVisualStyle : IVisualStyle
|
|||
private IFontFamily monospace = null!;
|
||||
private Texture2D? checkboxEmblem;
|
||||
|
||||
public int ProgressBarHeight => 16;
|
||||
public int SliderThickness => 18;
|
||||
public Point ToggleSize => new Point(20, 20);
|
||||
public Point SwitchSize => new Point(40, 22);
|
||||
|
@ -540,18 +559,6 @@ public class SociallyDistantVisualStyle : IVisualStyle
|
|||
}
|
||||
}
|
||||
|
||||
public void DrawProgressBar(ProgressBar widget, GeometryHelper geometry, float fillPercentage)
|
||||
{
|
||||
int width = widget.ContentArea.Width;
|
||||
int fillWidth = (int) MathHelper.Clamp(MathHelper.Lerp(0, width, fillPercentage), 0, width);
|
||||
|
||||
var background = widget.ContentArea;
|
||||
var fill = new LayoutRect(background.Left, background.Top, fillWidth, background.Height);
|
||||
|
||||
geometry.AddQuad(background, Color.Gray);
|
||||
geometry.AddQuad(fill, Color.Red);
|
||||
}
|
||||
|
||||
public void DrawSlider(
|
||||
Slider widget,
|
||||
GeometryHelper geometry,
|
||||
|
@ -605,6 +612,56 @@ public class SociallyDistantVisualStyle : IVisualStyle
|
|||
|
||||
geometry.AddRoundedRectangleOutline(new LayoutRect(nubOffsetX, nubOffsetY, SliderThickness, SliderThickness), thickness, halfThickness, border);
|
||||
}
|
||||
|
||||
public sealed class ProgressBarStyle : UserStyle,
|
||||
IGetLayoutProperties<ProgressBar.ProgressBarLayoutProperties>,
|
||||
IVisualRenderer<ProgressBar.ProgressBarVisualProperties>
|
||||
{
|
||||
private readonly int progressBarHeight = 16;
|
||||
|
||||
public bool GetLayoutProperties(ref ProgressBar.ProgressBarLayoutProperties layoutProperties)
|
||||
{
|
||||
layoutProperties.ProgressBarHeight = progressBarHeight;
|
||||
return GetDirtyState();
|
||||
}
|
||||
|
||||
public void Draw(
|
||||
Widget widget,
|
||||
GeometryHelper geometry,
|
||||
in LayoutRect contentRect,
|
||||
ProgressBar.ProgressBarVisualProperties properties
|
||||
)
|
||||
{
|
||||
int width = contentRect.Width;
|
||||
int fillWidth = (int) MathHelper.Clamp(MathHelper.Lerp(0, width, properties.FillPercentage), 0, width);
|
||||
|
||||
var background = contentRect;
|
||||
var fill = new LayoutRect(background.Left, background.Top, fillWidth, background.Height);
|
||||
|
||||
geometry.AddQuad(background, Color.Gray);
|
||||
geometry.AddQuad(fill, Color.Red);
|
||||
}
|
||||
}
|
||||
|
||||
public bool GetLayoutProperties(ref ProgressBar.ProgressBarLayoutProperties layoutProperties)
|
||||
{
|
||||
return progressBarSTyle.GetLayoutProperties(ref layoutProperties);
|
||||
}
|
||||
|
||||
public void Draw(
|
||||
Widget widget,
|
||||
GeometryHelper geometry,
|
||||
in LayoutRect contentRect,
|
||||
ProgressBar.ProgressBarVisualProperties properties
|
||||
)
|
||||
{
|
||||
progressBarSTyle.Draw(
|
||||
widget,
|
||||
geometry,
|
||||
in contentRect,
|
||||
properties
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public enum InputFieldStyle
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using SociallyDistant.Core.Core.Config;
|
||||
using SociallyDistant.Core.Modules;
|
||||
|
||||
namespace SociallyDistant.Core.Config
|
||||
{
|
||||
|
@ -41,8 +42,14 @@ namespace SociallyDistant.Core.Config
|
|||
"Mod debug mode",
|
||||
"Enable or disable Mod Debug Mode. Mod Debug mode sets up a safe, temporary environment for testing in-development mods with, and doesn't allow you to play regular save files. Changing this setting requires restarting the game.",
|
||||
ModDebugMode,
|
||||
x => ModDebugMode = x,
|
||||
development
|
||||
x =>
|
||||
{
|
||||
ModDebugMode = x;
|
||||
Application.Instance.Restart();
|
||||
},
|
||||
development,
|
||||
true,
|
||||
"Changing this setting requires a game restart. Any unsaved progress will be lost."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
using System.Diagnostics;
|
||||
using Serilog;
|
||||
using SociallyDistant.Core.Config;
|
||||
using SociallyDistant.Core.Config.SystemConfigCategories;
|
||||
|
@ -7,14 +8,14 @@ using SociallyDistant.Core.Shell;
|
|||
|
||||
namespace SociallyDistant;
|
||||
|
||||
internal sealed class GameApplication : Application,
|
||||
IDisposable
|
||||
internal sealed class GameApplication : Application
|
||||
{
|
||||
private readonly SociallyDistantGame game;
|
||||
private readonly SettingsManager settingsManager = new();
|
||||
|
||||
public override IGameContext Context => game;
|
||||
|
||||
public bool RestartWasRequested { get; private set; }
|
||||
public ISettingsManager SettingsManager => settingsManager;
|
||||
|
||||
public GameApplication()
|
||||
|
@ -52,8 +53,32 @@ internal sealed class GameApplication : Application,
|
|||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
protected override void OnRestart()
|
||||
{
|
||||
game.Exit();
|
||||
|
||||
var processPath = Environment.ProcessPath;
|
||||
var args = Environment.GetCommandLineArgs();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(processPath))
|
||||
return;
|
||||
|
||||
var startInfo = new ProcessStartInfo();
|
||||
startInfo.FileName = processPath;
|
||||
|
||||
foreach (string arg in args)
|
||||
startInfo.ArgumentList.Add(arg);
|
||||
|
||||
startInfo.UseShellExecute = false;
|
||||
startInfo.WorkingDirectory = Environment.CurrentDirectory;
|
||||
|
||||
Process.Start(startInfo);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Dispose()
|
||||
{
|
||||
game.Dispose();
|
||||
base.Dispose();
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
using System.Reactive.Subjects;
|
||||
using System.Text;
|
||||
using AcidicGUI;
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Serilog;
|
||||
|
@ -26,6 +27,7 @@ using SociallyDistant.Core.Shell;
|
|||
using SociallyDistant.Core.Shell.Windowing;
|
||||
using SociallyDistant.Core.Social;
|
||||
using SociallyDistant.Core.UI;
|
||||
using SociallyDistant.Core.UI.Effects;
|
||||
using SociallyDistant.DevTools;
|
||||
using SociallyDistant.GamePlatform;
|
||||
using SociallyDistant.GamePlatform.ContentManagement;
|
||||
|
@ -610,15 +612,24 @@ internal sealed class SociallyDistantGame :
|
|||
Log.Fatal(e.ExceptionObject.ToString() ?? "Unknown exception details.");
|
||||
}
|
||||
|
||||
restart:
|
||||
|
||||
var restartRequested = false;
|
||||
|
||||
try
|
||||
{
|
||||
using var game = new GameApplication();
|
||||
game.Start();
|
||||
|
||||
restartRequested = game.RestartWasRequested;
|
||||
}
|
||||
finally
|
||||
{
|
||||
Log.CloseAndFlush();
|
||||
}
|
||||
|
||||
if (restartRequested)
|
||||
goto restart;
|
||||
}
|
||||
|
||||
public static void ScheduleAction(Action action)
|
||||
|
@ -692,6 +703,10 @@ internal sealed class SociallyDistantGame :
|
|||
|
||||
if (explicitApply)
|
||||
{
|
||||
graphicsManager.PreferredBackBufferWidth = parameters.BackBufferWidth;
|
||||
graphicsManager.PreferredBackBufferHeight = parameters.BackBufferHeight;
|
||||
graphicsManager.IsFullScreen = settings.Fullscreen;
|
||||
|
||||
graphicsManager.ApplyChanges();
|
||||
ApplyVirtualDisplayMode(settings);
|
||||
}
|
||||
|
|
|
@ -83,7 +83,9 @@ public class SystemSettingsController :
|
|||
{
|
||||
var models = new List<SettingsCategoryModel>();
|
||||
|
||||
foreach (SettingsCategory category in game.SettingsManager.GetCategoriesInSection(sectionTitle))
|
||||
foreach (SettingsCategory category in game.SettingsManager.GetCategoriesInSection(sectionTitle)
|
||||
.OrderByDescending(x=>x.Priority)
|
||||
.ThenBy(x=> x.Name))
|
||||
{
|
||||
var model = new SettingsCategoryModel
|
||||
{
|
||||
|
|
|
@ -28,7 +28,7 @@ public sealed class DockIconView : Widget
|
|||
|
||||
protected override void RebuildGeometry(GeometryHelper geometry)
|
||||
{
|
||||
var color = GetVisualStyle().SelectionColor;
|
||||
var color = GetVisualStyleOverride().SelectionColor;
|
||||
|
||||
if (isActive)
|
||||
{
|
||||
|
|
|
@ -5,8 +5,13 @@
|
|||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_EXISTING_INITIALIZER_ARRANGEMENT/@EntryValue">False</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_USER_LINEBREAKS/@EntryValue">False</s:Boolean>
|
||||
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/MAX_FORMAL_PARAMETERS_ON_LINE/@EntryValue">3</s:Int64>
|
||||
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/MAX_PRIMARY_CONSTRUCTOR_PARAMETERS_ON_LINE/@EntryValue">10000</s:Int64>
|
||||
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/MAX_INVOCATION_ARGUMENTS_ON_LINE/@EntryValue">3</s:Int64>
|
||||
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/MAX_PRIMARY_CONSTRUCTOR_PARAMETERS_ON_LINE/@EntryValue">3</s:Int64>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_TYPE_CONSTRAINTS_ON_SAME_LINE/@EntryValue">False</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_AFTER_INVOCATION_LPAR/@EntryValue">True</s:Boolean>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_ARGUMENTS_STYLE/@EntryValue">CHOP_IF_LONG</s:String>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_BEFORE_INVOCATION_RPAR/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_BEFORE_PRIMARY_CONSTRUCTOR_DECLARATION_LPAR/@EntryValue">False</s:Boolean>
|
||||
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_EXTENDS_LIST_STYLE/@EntryValue">CHOP_ALWAYS</s:String>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_LINES/@EntryValue">False</s:Boolean>
|
||||
|
@ -15,7 +20,7 @@
|
|||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_AFTER_DECLARATION_LPAR/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_BEFORE_PRIMARY_CONSTRUCTOR_DECLARATION_RPAR/@EntryValue">False</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_BEFORE_DECLARATION_RPAR/@EntryValue">True</s:Boolean>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_PRIMARY_CONSTRUCTOR_PARAMETERS_STYLE/@EntryValue">CHOP_ALWAYS</s:String>
|
||||
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_TERNARY_EXPR_STYLE/@EntryValue">CHOP_ALWAYS</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_LINQ_EXPRESSIONS/@EntryValue">CHOP_ALWAYS</s:String>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
|
|
Loading…
Reference in a new issue