Add UI Scale dropdown to the settings menu.

This commit is contained in:
Paul Chote
2020-01-30 22:31:00 +00:00
committed by teinarss
parent 6388a6bff4
commit 9a0916afbb
8 changed files with 150 additions and 3 deletions

View File

@@ -57,6 +57,7 @@ namespace OpenRA
IHardwareCursor CreateHardwareCursor(string name, Size size, byte[] data, int2 hotspot, bool pixelDouble); IHardwareCursor CreateHardwareCursor(string name, Size size, byte[] data, int2 hotspot, bool pixelDouble);
void SetHardwareCursor(IHardwareCursor cursor); void SetHardwareCursor(IHardwareCursor cursor);
void SetRelativeMouseMode(bool mode); void SetRelativeMouseMode(bool mode);
void SetScaleModifier(float scale);
} }
public interface IGraphicsContext : IDisposable public interface IGraphicsContext : IDisposable

View File

@@ -91,6 +91,11 @@ namespace OpenRA
return new Size(size.X, size.Y); return new Size(size.X, size.Y);
} }
public void SetUIScale(float scale)
{
Window.SetScaleModifier(scale);
}
public void InitializeFonts(ModData modData) public void InitializeFonts(ModData modData)
{ {
if (Fonts != null) if (Fonts != null)

View File

@@ -10,6 +10,7 @@
#endregion #endregion
using System.Collections.Generic; using System.Collections.Generic;
using OpenRA.Primitives;
namespace OpenRA namespace OpenRA
{ {
@@ -23,6 +24,8 @@ namespace OpenRA
public readonly int MaxZoomWindowHeight = 240; public readonly int MaxZoomWindowHeight = 240;
public readonly bool AllowNativeZoom = true; public readonly bool AllowNativeZoom = true;
public readonly Size MinEffectiveResolution = new Size(1024, 720);
public int2 GetSizeRange(WorldViewport distance) public int2 GetSizeRange(WorldViewport distance)
{ {
return distance == WorldViewport.Close ? CloseWindowHeights return distance == WorldViewport.Close ? CloseWindowHeights

View File

@@ -112,6 +112,15 @@ namespace OpenRA.Mods.Common.LoadScreens
public virtual bool BeforeLoad() public virtual bool BeforeLoad()
{ {
// Reset the UI scaling if the user has configured a UI scale that pushes us below the minimum allowed effective resolution
var minResolution = ModData.Manifest.Get<WorldViewportSizes>().MinEffectiveResolution;
var resolution = Game.Renderer.Resolution;
if ((resolution.Width < minResolution.Width || resolution.Height < minResolution.Height) && Game.Settings.Graphics.UIScale > 1.0f)
{
Game.Settings.Graphics.UIScale = 1.0f;
Game.Renderer.SetUIScale(1.0f);
}
// If a ModContent section is defined then we need to make sure that the // If a ModContent section is defined then we need to make sure that the
// required content is installed or switch to the defined content installer. // required content is installed or switch to the defined content installer.
if (!ModData.Manifest.Contains<ModContent>()) if (!ModData.Manifest.Contains<ModContent>())

View File

@@ -14,6 +14,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using OpenRA.Graphics; using OpenRA.Graphics;
using OpenRA.Primitives; using OpenRA.Primitives;
using OpenRA.Support;
using OpenRA.Widgets; using OpenRA.Widgets;
namespace OpenRA.Mods.Common.Widgets.Logic namespace OpenRA.Mods.Common.Widgets.Logic
@@ -34,6 +35,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
readonly ModData modData; readonly ModData modData;
readonly WorldRenderer worldRenderer; readonly WorldRenderer worldRenderer;
readonly WorldViewportSizes viewportSizes;
readonly Dictionary<string, MiniYaml> logicArgs; readonly Dictionary<string, MiniYaml> logicArgs;
SoundDevice soundDevice; SoundDevice soundDevice;
@@ -61,6 +63,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
this.worldRenderer = worldRenderer; this.worldRenderer = worldRenderer;
this.modData = modData; this.modData = modData;
this.logicArgs = logicArgs; this.logicArgs = logicArgs;
viewportSizes = modData.Manifest.Get<WorldViewportSizes>();
panelContainer = widget.Get("SETTINGS_PANEL"); panelContainer = widget.Get("SETTINGS_PANEL");
tabContainer = widget.Get("TAB_CONTAINER"); tabContainer = widget.Get("TAB_CONTAINER");
@@ -261,7 +264,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
var battlefieldCameraDropDown = panel.Get<DropDownButtonWidget>("BATTLEFIELD_CAMERA_DROPDOWN"); var battlefieldCameraDropDown = panel.Get<DropDownButtonWidget>("BATTLEFIELD_CAMERA_DROPDOWN");
var battlefieldCameraLabel = new CachedTransform<WorldViewport, string>(vs => ViewportSizeNames[vs]); var battlefieldCameraLabel = new CachedTransform<WorldViewport, string>(vs => ViewportSizeNames[vs]);
battlefieldCameraDropDown.OnMouseDown = _ => ShowBattlefieldCameraDropdown(battlefieldCameraDropDown, ds); battlefieldCameraDropDown.OnMouseDown = _ => ShowBattlefieldCameraDropdown(battlefieldCameraDropDown, viewportSizes, ds);
battlefieldCameraDropDown.GetText = () => battlefieldCameraLabel.Update(ds.ViewportDistance); battlefieldCameraDropDown.GetText = () => battlefieldCameraLabel.Update(ds.ViewportDistance);
// Update vsync immediately // Update vsync immediately
@@ -273,6 +276,19 @@ namespace OpenRA.Mods.Common.Widgets.Logic
Game.Renderer.SetVSyncEnabled(ds.VSync); Game.Renderer.SetVSyncEnabled(ds.VSync);
}; };
var uiScaleDropdown = panel.Get<DropDownButtonWidget>("UI_SCALE_DROPDOWN");
var uiScaleLabel = new CachedTransform<float, string>(s => "{0}%".F((int)(100 * s)));
uiScaleDropdown.OnMouseDown = _ => ShowUIScaleDropdown(uiScaleDropdown, ds);
uiScaleDropdown.GetText = () => uiScaleLabel.Update(ds.UIScale);
var minResolution = viewportSizes.MinEffectiveResolution;
var resolution = Game.Renderer.Resolution;
var disableUIScale = worldRenderer.World.Type != WorldType.Shellmap ||
resolution.Width * ds.UIScale < 1.25f * minResolution.Width ||
resolution.Height * ds.UIScale < 1.25f * minResolution.Height;
uiScaleDropdown.IsDisabled = () => disableUIScale;
panel.Get("WINDOW_RESOLUTION").IsVisible = () => ds.Mode == WindowMode.Windowed; panel.Get("WINDOW_RESOLUTION").IsVisible = () => ds.Mode == WindowMode.Windowed;
var windowWidth = panel.Get<TextFieldWidget>("WINDOW_WIDTH"); var windowWidth = panel.Get<TextFieldWidget>("WINDOW_WIDTH");
var origWidthText = windowWidth.Text = ds.WindowedSize.X.ToString(); var origWidthText = windowWidth.Text = ds.WindowedSize.X.ToString();
@@ -357,6 +373,15 @@ namespace OpenRA.Mods.Common.Widgets.Logic
ds.CursorDouble = dds.CursorDouble; ds.CursorDouble = dds.CursorDouble;
ds.ViewportDistance = dds.ViewportDistance; ds.ViewportDistance = dds.ViewportDistance;
if (ds.UIScale != dds.UIScale)
{
var oldScale = ds.UIScale;
ds.UIScale = dds.UIScale;
Game.Renderer.SetUIScale(dds.UIScale);
RecalculateWidgetLayout(Ui.Root);
Viewport.LastMousePos = (Viewport.LastMousePos.ToFloat2() * oldScale / ds.UIScale).ToInt2();
}
ps.Color = dps.Color; ps.Color = dps.Color;
ps.Name = dps.Name; ps.Name = dps.Name;
}; };
@@ -827,7 +852,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
dropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 500, options.Keys, setupItem); dropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 500, options.Keys, setupItem);
} }
static void ShowBattlefieldCameraDropdown(DropDownButtonWidget dropdown, GraphicSettings gs) static void ShowBattlefieldCameraDropdown(DropDownButtonWidget dropdown, WorldViewportSizes viewportSizes, GraphicSettings gs)
{ {
Func<WorldViewport, ScrollItemWidget, ScrollItemWidget> setupItem = (o, itemTemplate) => Func<WorldViewport, ScrollItemWidget, ScrollItemWidget> setupItem = (o, itemTemplate) =>
{ {
@@ -840,7 +865,6 @@ namespace OpenRA.Mods.Common.Widgets.Logic
return item; return item;
}; };
var viewportSizes = Game.ModData.Manifest.Get<WorldViewportSizes>();
var windowHeight = Game.Renderer.NativeResolution.Height; var windowHeight = Game.Renderer.NativeResolution.Height;
var validSizes = new List<WorldViewport>() { WorldViewport.Close }; var validSizes = new List<WorldViewport>() { WorldViewport.Close };
@@ -857,6 +881,78 @@ namespace OpenRA.Mods.Common.Widgets.Logic
dropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 500, validSizes, setupItem); dropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 500, validSizes, setupItem);
} }
static void RecalculateWidgetLayout(Widget w, bool insideScrollPanel = false)
{
// HACK: Recalculate the widget bounds to fit within the new effective window bounds
// This is fragile, and only works when called when Settings is opened via the main menu.
// HACK: Skip children badges container on the main menu
// This has a fixed size, with calculated size and children positions that break if we adjust them here
if (w.Id == "BADGES_CONTAINER")
return;
var parentBounds = w.Parent == null
? new Rectangle(0, 0, Game.Renderer.Resolution.Width, Game.Renderer.Resolution.Height)
: w.Parent.Bounds;
var substitutions = new Dictionary<string, int>();
substitutions.Add("WINDOW_RIGHT", Game.Renderer.Resolution.Width);
substitutions.Add("WINDOW_BOTTOM", Game.Renderer.Resolution.Height);
substitutions.Add("PARENT_RIGHT", parentBounds.Width);
substitutions.Add("PARENT_LEFT", parentBounds.Left);
substitutions.Add("PARENT_TOP", parentBounds.Top);
substitutions.Add("PARENT_BOTTOM", parentBounds.Height);
var width = Evaluator.Evaluate(w.Width, substitutions);
var height = Evaluator.Evaluate(w.Height, substitutions);
substitutions.Add("WIDTH", width);
substitutions.Add("HEIGHT", height);
if (insideScrollPanel)
w.Bounds = new Rectangle(w.Bounds.X, w.Bounds.Y, width, w.Bounds.Height);
else
w.Bounds = new Rectangle(Evaluator.Evaluate(w.X, substitutions),
Evaluator.Evaluate(w.Y, substitutions),
width,
height);
foreach (var c in w.Children)
RecalculateWidgetLayout(c, insideScrollPanel || w is ScrollPanelWidget);
}
static void ShowUIScaleDropdown(DropDownButtonWidget dropdown, GraphicSettings gs)
{
Func<float, ScrollItemWidget, ScrollItemWidget> setupItem = (o, itemTemplate) =>
{
var item = ScrollItemWidget.Setup(itemTemplate,
() => gs.UIScale == o,
() =>
{
Game.RunAfterTick(() =>
{
var oldScale = gs.UIScale;
gs.UIScale = o;
Game.Renderer.SetUIScale(o);
RecalculateWidgetLayout(Ui.Root);
Viewport.LastMousePos = (Viewport.LastMousePos.ToFloat2() * oldScale / gs.UIScale).ToInt2();
});
});
var label = "{0}%".F((int)(100 * o));
item.Get<LabelWidget>("LABEL").GetText = () => label;
return item;
};
var viewportSizes = Game.ModData.Manifest.Get<WorldViewportSizes>();
var maxScales = new float2(Game.Renderer.NativeResolution) / new float2(viewportSizes.MinEffectiveResolution);
var maxScale = Math.Min(maxScales.X, maxScales.Y);
var validScales = new[] { 1f, 1.25f, 1.5f, 1.75f, 2f }.Where(x => x <= maxScale);
dropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 500, validScales, setupItem);
}
void MakeMouseFocusSettingsLive() void MakeMouseFocusSettingsLive()
{ {
var gameSettings = Game.Settings.Game; var gameSettings = Game.Settings.Game;

View File

@@ -443,5 +443,12 @@ namespace OpenRA.Platforms.Default
SDL.SDL_DestroyWindow(window); SDL.SDL_DestroyWindow(window);
return true; return true;
} }
public void SetScaleModifier(float scale)
{
var oldScaleModifier = scaleModifier;
scaleModifier = scale;
OnWindowScaleChanged(windowScale, windowScale * oldScaleModifier, windowScale, windowScale * scaleModifier);
}
} }
} }

View File

@@ -140,6 +140,19 @@ Container@SETTINGS_PANEL:
Width: 160 Width: 160
Height: 25 Height: 25
Font: Regular Font: Regular
Label@UI_SCALE:
X: 15
Y: 100
Width: 120
Height: 25
Text: UI Scale:
Align: Right
DropDownButton@UI_SCALE_DROPDOWN:
X: 140
Y: 100
Width: 160
Height: 25
Font: Regular
Label@STATUS_BARS: Label@STATUS_BARS:
X: 265 X: 265
Y: 100 Y: 100

View File

@@ -154,6 +154,19 @@ Background@SETTINGS_PANEL:
Width: 160 Width: 160
Height: 25 Height: 25
Font: Regular Font: Regular
Label@UI_SCALE:
X: 15
Y: 100
Width: 120
Height: 25
Text: UI Scale:
Align: Right
DropDownButton@UI_SCALE_DROPDOWN:
X: 140
Y: 100
Width: 160
Height: 25
Font: Regular
Label@STATUS_BARS: Label@STATUS_BARS:
X: 265 X: 265
Y: 100 Y: 100