Files
RobustToolbox/Robust.Client/UserInterface/UserInterfaceManager.Scaling.cs
e49515956a Add a basic loading screen! (#6003)
* Added basic loading screen

* Make it look better!

* I forgor xD

* Fix test fails

* Add comment

* Removed unused import

* Only write to file if the number of sections changed

* Servers can now have their own settings

* Minor optionzation and rare colors

* Remove some of the cvars

* debug only loading messages

* Added a few more steps

* Only one section at a time

* nullable section name

* Lock out functions if finished

* Get rid of saving the ccvar

* Cleanup

* Forgot!

* A few tweaks

* Disable vsync

* remove colors

* remove outdated vsync functions

* Silly me xD

* What I get for trying to be clever... ;(

* Better seconds display

* Simplify drawing logic + it looks better

* Type does not need to be partial

* Make interface to expose to content

* Use correct define to gate showing debug info

Should be TOOLS instead of DEBUG

* Use appropriate exception type in BeginLoadingSection

* Fix exception when closing window during loading screen

Would try to stop the main loop before it exists.

* Rename CVars, put debug info behind CVar instead of conditional compilation.

* Add to RELEASE-NOTES.md

* Add UI scaling support

* Make ILoadingScreenManager fully internal

Didn't realize content can't touch it as it'd break the total amount of sections

* Don't re-enable vsync manually, GameController does it at the end of init

* Add command to show top load time usage.

* Improve verbosity of debug time tracking

More steps and some steps named better

---------

Co-authored-by: PJB3005 <pieterjan.briers+git@gmail.com>
2025-10-30 00:38:59 +01:00

174 lines
5.9 KiB
C#

using System;
using Robust.Client.Graphics;
using Robust.Client.UserInterface.Controls;
using Robust.Shared;
using Robust.Shared.Configuration;
using Robust.Shared.Maths;
using Robust.Shared.ViewVariables;
namespace Robust.Client.UserInterface;
internal partial class UserInterfaceManager
{
[ViewVariables] public float DefaultUIScale => _clyde.DefaultWindowScale.X;
[ViewVariables] private Vector2i _resolutionAutoScaleUpper;
[ViewVariables] private Vector2i _resolutionAutoScaleLower;
[ViewVariables] private bool _autoScaleEnabled;
[ViewVariables] private float _resolutionAutoScaleMinValue;
private void _initScaling()
{
_clyde.OnWindowResized += WindowSizeChanged;
_clyde.OnWindowScaleChanged += WindowContentScaleChanged;
RegisterAutoscaleCVarListeners();
_uiScaleChanged(_configurationManager.GetCVar(CVars.DisplayUIScale));
}
private void _uiScaleChanged(float newValue)
{
foreach (var root in _roots)
{
UpdateUIScale(root);
}
}
private void WindowContentScaleChanged(WindowContentScaleEventArgs args)
{
if (_windowsToRoot.TryGetValue(args.Window.Id, out var root))
{
UpdateUIScale(root);
_fontManager.ClearFontCache();
}
}
private void RegisterAutoscaleCVarListeners()
{
_configurationManager.OnValueChanged(CVars.ResAutoScaleEnabled, i =>
{
_autoScaleEnabled = i;
foreach (var root in _roots)
{
root.UIScaleSet = 1;
_propagateUIScaleChanged(root);
root.InvalidateMeasure();
}
}, true);
_configurationManager.OnValueChanged(CVars.ResAutoScaleLowX, i =>
{
_resolutionAutoScaleLower.X = i;
foreach (var root in _roots)
{
UpdateUIScale(root);
}
}, true);
_configurationManager.OnValueChanged(CVars.ResAutoScaleLowY, i =>
{
_resolutionAutoScaleLower.Y = i;
foreach (var root in _roots)
{
UpdateUIScale(root);
}
}, true);
_configurationManager.OnValueChanged(CVars.ResAutoScaleUpperX, i =>
{
_resolutionAutoScaleUpper.X = i;
foreach (var root in _roots)
{
UpdateUIScale(root);
}
}, true);
_configurationManager.OnValueChanged(CVars.ResAutoScaleUpperY, i =>
{
_resolutionAutoScaleUpper.Y = i;
foreach (var root in _roots)
{
UpdateUIScale(root);
}
}, true);
_configurationManager.OnValueChanged(CVars.ResAutoScaleMin, i =>
{
_resolutionAutoScaleMinValue = i;
foreach (var root in _roots)
{
UpdateUIScale(root);
}
}, true);
}
internal static float CalculateUIScale(float osScale, IConfigurationManager cfg)
{
var cfgScale = cfg.GetCVar(CVars.DisplayUIScale);
return cfgScale == 0 ? osScale : cfgScale;
}
private float CalculateAutoScale(WindowRoot root)
{
//Grab the OS UIScale or the value set through CVAR debug
var osScale = CalculateUIScale(root.Window.ContentScale.X, _configurationManager);
var windowSize = root.Window.RenderTarget.Size;
//Only run autoscale if it is enabled, otherwise default to just use OS UIScale
if (!_autoScaleEnabled || root.DisableAutoScaling || windowSize.X <= 0 || windowSize.Y <= 0)
return osScale;
var maxScaleRes = _resolutionAutoScaleUpper;
var minScaleRes = _resolutionAutoScaleLower;
var autoScaleMin = _resolutionAutoScaleMinValue;
float scaleRatioX;
float scaleRatioY;
//Calculate the scale ratios and clamp it between the maximums and minimums
scaleRatioX = Math.Clamp(((float) windowSize.X - minScaleRes.X) / (maxScaleRes.X - minScaleRes.X) * osScale, autoScaleMin, osScale);
scaleRatioY = Math.Clamp(((float) windowSize.Y - minScaleRes.Y) / (maxScaleRes.Y - minScaleRes.Y) * osScale, autoScaleMin, osScale);
//Take the smallest UIScale value and use it for UI scaling
return Math.Min(scaleRatioX, scaleRatioY);
}
private void UpdateUIScale(WindowRoot root)
{
var newScale = CalculateAutoScale(root);
// ReSharper disable once CompareOfFloatsByEqualityOperator
if (newScale == root.UIScaleSet)
return;
root.UIScaleSet = newScale;
_propagateUIScaleChanged(root);
root.InvalidateMeasure();
}
private static void _propagateUIScaleChanged(Control control)
{
control.UIScaleChanged();
foreach (var child in control.Children)
{
_propagateUIScaleChanged(child);
}
}
private void WindowSizeChanged(WindowResizedEventArgs windowResizedEventArgs)
{
if (!_windowsToRoot.TryGetValue(windowResizedEventArgs.Window.Id, out var root))
return;
root.UIScaleUpdateNeeded = true;
root.InvalidateMeasure();
}
private void CheckRootUIScaleUpdate(WindowRoot root)
{
if (!root.UIScaleUpdateNeeded)
return;
using (_prof.Group("UIScaleUpdate"))
{
UpdateUIScale(root);
}
root.UIScaleUpdateNeeded = false;
}
}