UI Scaling Support. (#809)

Not quite perfect, but quite usable.

Adds the ability to scale the UI. Scaling is controlled via a cvar,
and can be changed at runtime.
Fractional scaling is supported.

Some controls could be better (SpriteView, TextureRect),
but for now it's a good start.
This commit is contained in:
Pieter-Jan Briers
2019-05-11 16:04:14 +02:00
committed by GitHub
parent c4fb96b5e8
commit 17ebeac107
31 changed files with 611 additions and 376 deletions

View File

@@ -430,8 +430,10 @@ namespace Robust.Client.Console.Commands
{
var window = new SS14Window(IoCManager.Resolve<IDisplayManager>(), "UITest");
window.AddToScreen();
var tabContainer = new TabContainer();
window.Contents.AddChild(tabContainer);
var scroll = new ScrollContainer();
window.Contents.AddChild(scroll);
tabContainer.AddChild(scroll);
scroll.SetAnchorAndMarginPreset(Control.LayoutPreset.Wide);
var vBox = new VBoxContainer();
scroll.AddChild(vBox);
@@ -468,6 +470,25 @@ namespace Robust.Client.Console.Commands
rich.SetMessage(message);
vBox.AddChild(rich);
var itemList = new ItemList();
tabContainer.AddChild(itemList);
for (var i = 0; i < 10; i++)
{
itemList.AddItem(i.ToString());
}
var grid = new GridContainer {Columns = 3};
tabContainer.AddChild(grid);
for (var y = 0; y < 3; y++)
for (var x = 0; x < 3; x++)
{
grid.AddChild(new Button
{
CustomMinimumSize = (50, 50),
Text = $"{x}, {y}"
});
}
return false;
}
}

View File

@@ -32,11 +32,11 @@ namespace Robust.Client.GameStates
private void DrawString(DrawingHandleScreen handle, Font font, Vector2 pos, string str)
{
var baseLine = new Vector2(pos.X, font.Ascent + pos.Y);
var baseLine = new Vector2(pos.X, font.GetAscent(1) + pos.Y);
foreach (var chr in str)
{
var advance = font.DrawChar(handle, chr, baseLine, Color.White);
var advance = font.DrawChar(handle, chr, baseLine, 1, Color.White);
baseLine += new Vector2(advance, 0);
}
}

View File

@@ -15,30 +15,39 @@ namespace Robust.Client.Graphics
public abstract class Font
{
/// <summary>
/// The maximum amount a glyph goes above the baseline.
/// The maximum amount a glyph goes above the baseline, in pixels.
/// </summary>
public virtual int Ascent => default;
public abstract int GetAscent(float scale);
/// <summary>
/// The maximum glyph height of a line of text, not relative to the baseline.
/// The maximum glyph height of a line of text in pixels, not relative to the baseline.
/// </summary>
public virtual int Height => default;
public abstract int GetHeight(float scale);
/// <summary>
/// The maximum amount a glyph drops below the baseline.
/// The maximum amount a glyph drops below the baseline, in pixels.
/// </summary>
public virtual int Descent => default;
public abstract int GetDescent(float scale);
/// <summary>
/// The distance between the baselines of two consecutive lines.
/// The distance between the baselines of two consecutive lines, in pixels.
/// Basically, if you encounter a new line, this is how much you need to move down the cursor.
/// </summary>
public virtual int LineHeight => Height;
public abstract int GetLineHeight(float scale);
/// <summary>
/// The distance between the edges of two consecutive lines.
/// The distance between the edges of two consecutive lines, in pixels.
/// </summary>
public int LineSeparation => LineHeight - Height;
public int GetLineSeparation(float scale)
{
return GetLineHeight(scale) - GetHeight(scale);
}
[Obsolete("Use GetAscent")] public int Ascent => GetAscent(1);
[Obsolete("Use GetHeight")] public int Height => GetHeight(1);
[Obsolete("Use GetDescent")] public int Descent => GetDescent(1);
[Obsolete("Use GetLineHeight")] public int LineHeight => GetLineHeight(1);
[Obsolete("Use GetLineSeparation")] public int LineSeparation => GetLineSeparation(1);
// Yes, I am aware that using char is bad.
// At the same time the font system is nowhere close to rendering Unicode so...
@@ -53,7 +62,14 @@ namespace Robust.Client.Graphics
/// <param name="baseline">The baseline from which to draw the character.</param>
/// <param name="color">The color of the character to draw.</param>
/// <returns>How much to advance the cursor to draw the next character.</returns>
public abstract float DrawChar(DrawingHandleScreen handle, char chr, Vector2 baseline, Color color);
[Obsolete("Use DrawChar with scale support.")]
public float DrawChar(DrawingHandleScreen handle, char chr, Vector2 baseline, Color color)
{
return DrawChar(handle, chr, baseline, 1, color);
}
public abstract float DrawChar(DrawingHandleScreen handle, char chr, Vector2 baseline, float scale,
Color color);
/// <summary>
/// Gets metrics describing the dimensions and positioning of a single glyph in the font.
@@ -64,14 +80,26 @@ namespace Robust.Client.Graphics
/// otherwise the metrics you asked for.
/// </returns>
/// <seealso cref="TryGetCharMetrics"/>
public abstract CharMetrics? GetCharMetrics(char chr);
[Obsolete("Use GetCharMetrics with scale support.")]
public CharMetrics? GetCharMetrics(char chr)
{
return GetCharMetrics(chr, 1);
}
public abstract CharMetrics? GetCharMetrics(char chr, float scale);
/// <summary>
/// Try-pattern version of <see cref="GetCharMetrics"/>.
/// </summary>
[Obsolete("Use TryGetCharMetrics with scale support.")]
public bool TryGetCharMetrics(char chr, out CharMetrics metrics)
{
var maybe = GetCharMetrics(chr);
return TryGetCharMetrics(chr, 1, out metrics);
}
public bool TryGetCharMetrics(char chr, float scale, out CharMetrics metrics)
{
var maybe = GetCharMetrics(chr, scale);
if (maybe.HasValue)
{
metrics = maybe.Value;
@@ -92,26 +120,26 @@ namespace Robust.Client.Graphics
internal IFontInstanceHandle Handle { get; }
public override int Ascent => Handle?.Ascent ?? base.Ascent;
public override int Descent => Handle?.Descent ?? base.Descent;
public override int Height => Handle?.Height ?? base.Height;
public override int LineHeight => Handle?.LineHeight ?? base.LineHeight;
public VectorFont(FontResource res, int size)
{
Size = size;
Handle = IoCManager.Resolve<IFontManagerInternal>().MakeInstance(res.FontFaceHandle, size);
}
public override float DrawChar(DrawingHandleScreen handle, char chr, Vector2 baseline, Color color)
public override int GetAscent(float scale) => Handle.GetAscent(scale);
public override int GetHeight(float scale) => Handle.GetHeight(scale);
public override int GetDescent(float scale) => Handle.GetDescent(scale);
public override int GetLineHeight(float scale) => Handle.GetLineHeight(scale);
public override float DrawChar(DrawingHandleScreen handle, char chr, Vector2 baseline, float scale, Color color)
{
var metrics = Handle.GetCharMetrics(chr);
var metrics = Handle.GetCharMetrics(chr, scale);
if (!metrics.HasValue)
{
return 0;
}
var texture = Handle.GetCharTexture(chr);
var texture = Handle.GetCharTexture(chr, scale);
if (texture == null)
{
return metrics.Value.Advance;
@@ -122,21 +150,26 @@ namespace Robust.Client.Graphics
return metrics.Value.Advance;
}
public override CharMetrics? GetCharMetrics(char chr)
public override CharMetrics? GetCharMetrics(char chr, float scale)
{
return Handle.GetCharMetrics(chr);
return Handle.GetCharMetrics(chr, scale);
}
}
public sealed class DummyFont : Font
{
public override float DrawChar(DrawingHandleScreen handle, char chr, Vector2 baseline, Color color)
public override int GetAscent(float scale) => default;
public override int GetHeight(float scale) => default;
public override int GetDescent(float scale) => default;
public override int GetLineHeight(float scale) => default;
public override float DrawChar(DrawingHandleScreen handle, char chr, Vector2 baseline, float scale, Color color)
{
// Nada, it's a dummy after all.
return 0;
}
public override CharMetrics? GetCharMetrics(char chr)
public override CharMetrics? GetCharMetrics(char chr, float scale)
{
// Nada, it's a dummy after all.
return null;

View File

@@ -18,7 +18,7 @@ namespace Robust.Client.Graphics
{
[Dependency] private readonly IConfigurationManager _configuration;
private uint FontDPI;
private uint BaseFontDPI;
private readonly Library _library;
@@ -32,12 +32,14 @@ namespace Robust.Client.Graphics
public IFontFaceHandle Load(ReadOnlySpan<byte> data)
{
unsafe
{
var face = new Face(_library, data.ToArray(), 0);
var handle = new FontFaceHandle(face);
return handle;
}
var face = new Face(_library, data.ToArray(), 0);
var handle = new FontFaceHandle(face);
return handle;
}
void IFontManagerInternal.Initialize()
{
BaseFontDPI = (uint) _configuration.GetCVar<int>("display.fontdpi");
}
public IFontInstanceHandle MakeInstance(IFontFaceHandle handle, int size)
@@ -48,50 +50,63 @@ namespace Robust.Client.Graphics
return instance;
}
var face = fontFaceHandle.Face;
var (atlasData, glyphMap, metricsMap) = _generateAtlas(face, size);
var ascent = face.Size.Metrics.Ascender.ToInt32();
var descent = -face.Size.Metrics.Descender.ToInt32();
var height = face.Size.Metrics.Height.ToInt32();
var instanceHandle = new FontInstanceHandle(this, atlasData, size, fontFaceHandle.Face, glyphMap, ascent,
descent, height, metricsMap);
_loadedInstances.Add((fontFaceHandle, size), instanceHandle);
return instanceHandle;
var glyphMap = _generateGlyphMap(fontFaceHandle.Face);
instance = new FontInstanceHandle(this, size, glyphMap, fontFaceHandle);
_loadedInstances.Add((fontFaceHandle, size), instance);
return instance;
}
void IFontManagerInternal.Initialize()
private ScaledFontData _generateScaledDatum(FontInstanceHandle instance, float scale)
{
FontDPI = (uint) _configuration.GetCVar<int>("display.fontdpi");
var ftFace = instance.FaceHandle.Face;
ftFace.SetCharSize(0, instance.Size, 0, (uint) (BaseFontDPI * scale));
var ascent = ftFace.Size.Metrics.Ascender.ToInt32();
var descent = -ftFace.Size.Metrics.Descender.ToInt32();
var lineHeight = ftFace.Size.Metrics.Height.ToInt32();
var (atlas, metricsMap) = _generateAtlas(instance, scale);
return new ScaledFontData(metricsMap, ascent, descent, ascent + descent, lineHeight, atlas);
}
private (FontTextureAtlas, Dictionary<char, uint> glyphMap, Dictionary<uint, CharMetrics> metricsMap)
_generateAtlas(Face face, int size)
private (FontTextureAtlas, Dictionary<uint, CharMetrics> metricsMap)
_generateAtlas(FontInstanceHandle instance, float scale)
{
// TODO: This could use a better box packing algorithm.
// Right now we treat each glyph bitmap as having the max size among all glyphs.
// So we can divide the atlas into equal-size rectangles.
// This wastes a lot of space though because there's a lot of tiny glyphs.
face.SetCharSize(0, size, 0, FontDPI);
var face = instance.FaceHandle.Face;
var maxGlyphSize = Vector2i.Zero;
var count = 0;
// TODO: Render more than extended ASCII, somehow. Does it make sense to just render every glyph in the font?
// Render all the extended ASCII characters.
const uint startIndex = 32;
const uint endIndex = 255;
for (var i = startIndex; i <= endIndex; i++)
var metricsMap = new Dictionary<uint, CharMetrics>();
foreach (var glyph in instance.GlyphMap.Values)
{
var glyphIndex = face.GetCharIndex(i);
if (glyphIndex == 0)
if (metricsMap.ContainsKey(glyph))
{
continue;
}
face.LoadChar(i, LoadFlags.Default, LoadTarget.Normal);
face.LoadGlyph(glyph, LoadFlags.Default, LoadTarget.Normal);
face.Glyph.RenderGlyph(RenderMode.Normal);
var glyphMetrics = face.Glyph.Metrics;
var metrics = new CharMetrics(glyphMetrics.HorizontalBearingX.ToInt32(),
glyphMetrics.HorizontalBearingY.ToInt32(),
glyphMetrics.HorizontalAdvance.ToInt32(),
glyphMetrics.Width.ToInt32(),
glyphMetrics.Height.ToInt32());
metricsMap.Add(glyph, metrics);
maxGlyphSize = Vector2i.ComponentMax(maxGlyphSize,
new Vector2i(face.Glyph.Bitmap.Width, face.Glyph.Bitmap.Rows));
count += 1;
}
@@ -107,33 +122,12 @@ namespace Robust.Client.Graphics
(int) Math.Round(atlasEntriesVertical * maxGlyphSize.Y / 4f, MidpointRounding.AwayFromZero) * 4;
var atlas = new Image<Alpha8>(atlasDimX, atlasDimY);
var glyphMap = new Dictionary<char, uint>();
var metricsMap = new Dictionary<uint, CharMetrics>();
var atlasRegions = new Dictionary<uint, UIBox2>();
count = 0;
for (var i = startIndex; i <= endIndex; i++)
foreach (var glyph in metricsMap.Keys)
{
var glyphIndex = face.GetCharIndex(i);
if (glyphIndex == 0)
{
continue;
}
glyphMap.Add((char) i, glyphIndex);
if (metricsMap.ContainsKey(glyphIndex))
{
continue;
}
face.LoadGlyph(glyphIndex, LoadFlags.Default, LoadTarget.Normal);
face.LoadGlyph(glyph, LoadFlags.Default, LoadTarget.Normal);
face.Glyph.RenderGlyph(RenderMode.Normal);
var glyphMetrics = face.Glyph.Metrics;
var metrics = new CharMetrics(glyphMetrics.HorizontalBearingX.ToInt32(),
glyphMetrics.HorizontalBearingY.ToInt32(),
glyphMetrics.HorizontalAdvance.ToInt32(),
glyphMetrics.Width.ToInt32(),
glyphMetrics.Height.ToInt32());
metricsMap.Add(glyphIndex, metrics);
var bitmap = face.Glyph.Bitmap;
if (bitmap.Pitch == 0)
@@ -185,18 +179,19 @@ namespace Robust.Client.Graphics
atlas.Mutate(x => x.DrawImage(bitmapImage, new Point(column * maxGlyphSize.X, row * maxGlyphSize.Y),
PixelColorBlendingMode.Overlay, 1));
count += 1;
atlasRegions.Add(glyphIndex, UIBox2i.FromDimensions(offsetX, offsetY, bitmap.Width, bitmap.Rows));
atlasRegions.Add(glyph, UIBox2i.FromDimensions(offsetX, offsetY, bitmap.Width, bitmap.Rows));
}
var atlasDictionary = new Dictionary<uint, AtlasTexture>();
var texture = Texture.LoadFromImage(atlas, $"font-{face.FamilyName}-{size}");
var texture = Texture.LoadFromImage(atlas,
$"font-{face.FamilyName}-{instance.Size}-{(uint) (BaseFontDPI * scale)}");
foreach (var (glyph, region) in atlasRegions)
{
atlasDictionary.Add(glyph, new AtlasTexture(texture, region));
}
return (new FontTextureAtlas(texture, atlasDictionary), glyphMap, metricsMap);
return (new FontTextureAtlas(texture, atlasDictionary), metricsMap);
}
private static Image<Alpha8> MonoBitMapToImage(FTBitmap bitmap)
@@ -226,6 +221,27 @@ namespace Robust.Client.Graphics
return bitmapImage;
}
private Dictionary<char, uint> _generateGlyphMap(Face face)
{
var map = new Dictionary<char, uint>();
// TODO: Render more than extended ASCII, somehow. Does it make sense to just render every glyph in the font?
// Render all the extended ASCII characters.
const uint startIndex = 32;
const uint endIndex = 255;
for (var i = startIndex; i <= endIndex; i++)
{
var glyphIndex = face.GetCharIndex(i);
if (glyphIndex == 0)
{
continue;
}
map.Add((char) i, glyphIndex);
}
return map;
}
private class FontFaceHandle : IFontFaceHandle
{
public Face Face { get; }
@@ -239,42 +255,22 @@ namespace Robust.Client.Graphics
[PublicAPI]
private class FontInstanceHandle : IFontInstanceHandle
{
public Face Face { get; }
public FontFaceHandle FaceHandle { get; }
public int Size { get; }
private readonly Dictionary<char, uint> _glyphMap;
private readonly Dictionary<uint, CharMetrics> _metricsMap;
public int Ascent { get; }
public int Descent { get; }
public int Height { get; }
public int LineHeight { get; }
private readonly Dictionary<float, ScaledFontData> _scaledData = new Dictionary<float, ScaledFontData>();
public readonly IReadOnlyDictionary<char, uint> GlyphMap;
private readonly FontManager _fontManager;
public FontInstanceHandle(FontManager manager, FontTextureAtlas atlas, int size, Face face,
Dictionary<char, uint> glyphMap,
int ascent, int descent, int lineHeight, Dictionary<uint, CharMetrics> metricsMap)
public FontInstanceHandle(FontManager fontManager, int size, IReadOnlyDictionary<char, uint> glyphMap,
FontFaceHandle faceHandle)
{
_fontManager = manager;
Atlas = atlas;
_fontManager = fontManager;
Size = size;
Face = face;
_glyphMap = glyphMap;
Ascent = ascent;
Descent = descent;
LineHeight = lineHeight;
Height = ascent + descent;
_metricsMap = metricsMap;
GlyphMap = glyphMap;
FaceHandle = faceHandle;
}
public FontTextureAtlas Atlas { get; }
public Texture GetCharTexture(char chr)
{
var glyph = _getGlyph(chr);
Atlas.AtlasData.TryGetValue(glyph, out var ret);
return ret;
}
public CharMetrics? GetCharMetrics(char chr)
public Texture GetCharTexture(char chr, float scale)
{
var glyph = _getGlyph(chr);
if (glyph == 0)
@@ -282,19 +278,89 @@ namespace Robust.Client.Graphics
return null;
}
_metricsMap.TryGetValue(glyph, out var metrics);
return metrics;
var scaled = _getScaleDatum(scale);
scaled.Atlas.AtlasData.TryGetValue(glyph, out var texture);
return texture;
}
public CharMetrics? GetCharMetrics(char chr, float scale)
{
var glyph = _getGlyph(chr);
if (glyph == 0)
{
return null;
}
var scaled = _getScaleDatum(scale);
return scaled.MetricsMap[glyph];
}
public int GetAscent(float scale)
{
var scaled = _getScaleDatum(scale);
return scaled.Ascent;
}
public int GetDescent(float scale)
{
var scaled = _getScaleDatum(scale);
return scaled.Descent;
}
public int GetHeight(float scale)
{
var scaled = _getScaleDatum(scale);
return scaled.Height;
}
public int GetLineHeight(float scale)
{
var scaled = _getScaleDatum(scale);
return scaled.LineHeight;
}
private uint _getGlyph(char chr)
{
if (_glyphMap.TryGetValue(chr, out var glyph))
if (GlyphMap.TryGetValue(chr, out var glyph))
{
return glyph;
}
return 0;
}
private ScaledFontData _getScaleDatum(float scale)
{
if (_scaledData.TryGetValue(scale, out var datum))
{
return datum;
}
datum = _fontManager._generateScaledDatum(this, scale);
_scaledData.Add(scale, datum);
return datum;
}
}
private class ScaledFontData
{
public ScaledFontData(IReadOnlyDictionary<uint, CharMetrics> metricsMap, int ascent, int descent,
int height, int lineHeight, FontTextureAtlas atlas)
{
MetricsMap = metricsMap;
Ascent = ascent;
Descent = descent;
Height = height;
LineHeight = lineHeight;
Atlas = atlas;
}
public IReadOnlyDictionary<uint, CharMetrics> MetricsMap { get; }
public int Ascent { get; }
public int Descent { get; }
public int Height { get; }
public int LineHeight { get; }
public FontTextureAtlas Atlas { get; }
}
private class FontTextureAtlas

View File

@@ -22,12 +22,12 @@ namespace Robust.Client.Interfaces.Graphics
internal interface IFontInstanceHandle
{
Texture GetCharTexture(char chr);
CharMetrics? GetCharMetrics(char chr);
int Ascent { get; }
int Descent { get; }
int Height { get; }
int LineHeight { get; }
Texture GetCharTexture(char chr, float scale);
CharMetrics? GetCharMetrics(char chr, float scale);
int GetAscent(float scale);
int GetDescent(float scale);
int GetHeight(float scale);
int GetLineHeight(float scale);
}
/// <summary>

View File

@@ -24,6 +24,8 @@ namespace Robust.Client.Interfaces.UserInterface
Control CurrentlyHovered { get; }
float UIScale { get; }
/// <summary>
/// The "root" control to which all other controls are parented,
/// potentially indirectly.

View File

@@ -107,15 +107,21 @@ namespace Robust.Client.UserInterface
/// </summary>
public Vector2 GlobalPosition { get; }
public Vector2 GlobalPixelPosition { get; }
/// <summary>
/// Position of the mouse, relative to the current control.
/// </summary>
public Vector2 RelativePosition { get; internal set; }
public Vector2 RelativePixelPosition { get; internal set; }
protected GUIMouseEventArgs(Control sourceControl,
Mouse.ButtonMask buttonMask,
Vector2 globalPosition,
Vector2 globalPixelPosition,
Vector2 relativePosition,
Vector2 relativePixelPosition,
bool alt,
bool control,
bool shift,
@@ -126,6 +132,8 @@ namespace Robust.Client.UserInterface
ButtonMask = buttonMask;
GlobalPosition = globalPosition;
RelativePosition = relativePosition;
RelativePixelPosition = relativePixelPosition;
GlobalPixelPosition = globalPixelPosition;
}
}
@@ -147,12 +155,14 @@ namespace Robust.Client.UserInterface
Control sourceControl,
Mouse.ButtonMask buttonMask,
Vector2 globalPosition,
Vector2 globalPixelPosition,
Vector2 relativePosition,
Vector2 relativePixelPosition,
bool alt,
bool control,
bool shift,
bool system)
: base(sourceControl, buttonMask, globalPosition, relativePosition, alt, control, shift, system)
: base(sourceControl, buttonMask, globalPosition, globalPixelPosition, relativePosition, relativePixelPosition, alt, control, shift, system)
{
Button = button;
DoubleClick = doubleClick;
@@ -179,12 +189,14 @@ namespace Robust.Client.UserInterface
Control sourceControl,
Mouse.ButtonMask buttonMask,
Vector2 globalPosition,
Vector2 globalPixelPosition,
Vector2 relativePosition,
Vector2 relativePixelPosition,
bool alt,
bool control,
bool shift,
bool system)
: base(sourceControl, buttonMask, globalPosition, relativePosition, alt, control, shift, system)
: base(sourceControl, buttonMask, globalPosition, globalPixelPosition, relativePosition, relativePixelPosition, alt, control, shift, system)
{
Relative = relative;
Speed = speed;
@@ -201,13 +213,15 @@ namespace Robust.Client.UserInterface
public GUIMouseWheelEventArgs(Mouse.Wheel wheelDirection,
Control sourceControl,
Mouse.ButtonMask buttonMask,
Shared.Maths.Vector2 globalPosition,
Shared.Maths.Vector2 relativePosition,
Vector2 globalPosition,
Vector2 globalPixelPosition,
Vector2 relativePosition,
Vector2 relativePixelPosition,
bool alt,
bool control,
bool shift,
bool system)
: base(sourceControl, buttonMask, globalPosition, relativePosition, alt, control, shift, system)
: base(sourceControl, buttonMask, globalPosition, globalPixelPosition, relativePosition, relativePixelPosition, alt, control, shift, system)
{
WheelDirection = wheelDirection;
}

View File

@@ -310,6 +310,13 @@ namespace Robust.Client.UserInterface
EnteredTree();
}
protected internal virtual void UIScaleChanged()
{
MinimumSizeChanged();
}
protected float UIScale => UserInterfaceManager.UIScale;
// _marginSetSize is the size calculated by the margins,
// but it's different from _size if min size is higher.
private Vector2 _sizeByMargins;
@@ -328,10 +335,18 @@ namespace Robust.Client.UserInterface
}
}
[ViewVariables]
public Vector2i PixelSize => (Vector2i) (_size * UserInterfaceManager.UIScale);
public UIBox2 SizeBox => new UIBox2(Vector2.Zero, Size);
public UIBox2i PixelSizeBox => new UIBox2i(Vector2i.Zero, PixelSize);
public float Width => Size.X;
public float Height => Size.Y;
public int PixelWidth => PixelSize.X;
public int PixelHeight => PixelSize.Y;
private Vector2 _position;
[ViewVariables]
@@ -349,6 +364,9 @@ namespace Robust.Client.UserInterface
}
}
[ViewVariables]
public Vector2i PixelPosition => (Vector2i)(_position * UserInterfaceManager.UIScale);
[ViewVariables]
public Vector2 GlobalPosition
{
@@ -366,26 +384,27 @@ namespace Robust.Client.UserInterface
}
}
[ViewVariables]
public Vector2i GlobalPixelPosition
{
get
{
var offset = PixelPosition;
var parent = Parent;
while (parent != null)
{
offset += parent.PixelPosition;
parent = parent.Parent;
}
return offset;
}
}
public UIBox2 Rect => UIBox2.FromDimensions(_position, _size);
public UIBox2i PixelRect => UIBox2i.FromDimensions(PixelPosition, PixelSize);
public Vector2 Scale
{
get => default;
set
{
}
}
private string _tooltip;
public string ToolTip
{
get => _tooltip;
set
{
_tooltip = value;
}
}
public string ToolTip { get; set; }
[ViewVariables]
public MouseFilterMode MouseFilter { get; set; } = MouseFilterMode.Stop;
@@ -493,6 +512,8 @@ namespace Robust.Client.UserInterface
}
}
public Vector2i CombinedPixelMinimumSize => (Vector2i)(CombinedMinimumSize * UIScale);
private Vector2? _calculatedMinimumSize;
private Vector2 _customMinimumSize;

View File

@@ -39,7 +39,7 @@ namespace Robust.Client.UserInterface.Controls
protected override void SortChildren()
{
var separation = ActualSeparation;
var separation = (int) (ActualSeparation * UIScale);
// Step one: figure out the sizes of all our children and whether they want to stretch.
var sizeList = new List<(Control control, int minSize, int finalSize, bool stretch)>();
@@ -53,18 +53,18 @@ namespace Robust.Client.UserInterface.Controls
{
continue;
}
var childMinSize = child.CombinedMinimumSize;
var (minX, minY) = child.CombinedPixelMinimumSize;
int minSize;
bool stretch;
if (Vertical)
{
minSize = (int) childMinSize.Y;
minSize = (int) minY;
stretch = (child.SizeFlagsVertical & SizeFlags.Expand) == SizeFlags.Expand;
}
else
{
minSize = (int) childMinSize.X;
minSize = (int) minX;
stretch = (child.SizeFlagsHorizontal & SizeFlags.Expand) == SizeFlags.Expand;
}
@@ -80,15 +80,7 @@ namespace Robust.Client.UserInterface.Controls
sizeList.Add((child, minSize, minSize, stretch));
}
int stretchMax;
if (Vertical)
{
stretchMax = (int) Size.Y;
}
else
{
stretchMax = (int) Size.X;
}
var stretchMax = Vertical ? PixelHeight : PixelWidth;
stretchMax -= separation * (ChildCount - 1);
// This is the amount of space allocated for stretchable children.
@@ -159,17 +151,17 @@ namespace Robust.Client.UserInterface.Controls
first = false;
UIBox2 targetBox;
UIBox2i targetBox;
if (Vertical)
{
targetBox = new UIBox2(0, offset, Size.X, offset+size);
targetBox = new UIBox2i(0, offset, PixelWidth, offset+size);
}
else
{
targetBox = new UIBox2(offset, 0, offset+size, Size.Y);
targetBox = new UIBox2i(offset, 0, offset+size, PixelHeight);
}
FitChildInBox(control, targetBox);
FitChildInPixelBox(control, targetBox);
offset += size;
}

View File

@@ -101,7 +101,7 @@ namespace Robust.Client.UserInterface.Controls
base.Draw(handle);
var style = ActualStyleBox;
var drawBox = SizeBox;
var drawBox = PixelSizeBox;
style.Draw(handle, drawBox);
if (_text == null)
@@ -142,19 +142,19 @@ namespace Robust.Client.UserInterface.Controls
}
var color = ActualFontColor;
var offsetY = (int) (box.Height - font.Height) / 2;
var baseLine = new Vector2i(drawOffset, offsetY + font.Ascent) + box.TopLeft;
var offsetY = (int) (box.Height - font.GetHeight(UIScale)) / 2;
var baseLine = new Vector2i(drawOffset, offsetY + font.GetAscent(UIScale)) + box.TopLeft;
foreach (var chr in _text)
{
if (!font.TryGetCharMetrics(chr, out var metrics))
if (!font.TryGetCharMetrics(chr, UIScale, out var metrics))
{
continue;
}
if (!(ClipText && (baseLine.X < box.Left || baseLine.X + metrics.Advance > box.Right)))
{
font.DrawChar(handle, chr, baseLine, color);
font.DrawChar(handle, chr, baseLine, UIScale, color);
}
baseLine += (metrics.Advance, 0);
@@ -166,16 +166,16 @@ namespace Robust.Client.UserInterface.Controls
var style = ActualStyleBox;
var font = ActualFont;
var fontHeight = font.Height;
var fontHeight = font.GetHeight(UIScale) / UIScale;
if (ClipText)
{
return (0, fontHeight) + style.MinimumSize;
return (0, fontHeight) + style.MinimumSize/UIScale;
}
var width = EnsureWidthCache();
return new Vector2(width, fontHeight) + style.MinimumSize;
return (width / UIScale, fontHeight) + style.MinimumSize/UIScale;
}
protected override void Initialize()
@@ -224,7 +224,7 @@ namespace Robust.Client.UserInterface.Controls
var textWidth = 0;
foreach (var chr in _text)
{
var metrics = font.GetCharMetrics(chr);
var metrics = font.GetCharMetrics(chr, UIScale);
if (metrics == null)
{
continue;
@@ -263,5 +263,12 @@ namespace Robust.Client.UserInterface.Controls
ClipText = (bool) value;
}
}
protected internal override void UIScaleChanged()
{
_textWidthCache = null;
base.UIScaleChanged();
}
}
}

View File

@@ -25,23 +25,23 @@ namespace Robust.Client.UserInterface.Controls
if (icon != null)
{
offset += _getIcon().Width + _getHSeparation();
handle.DrawTexture(icon, Vector2.Zero);
handle.DrawTextureRect(icon, UIBox2.FromDimensions(Vector2.Zero, icon.Size * UIScale), false);
}
var box = new UIBox2(offset, 0, Width, Height);
var box = new UIBox2(offset, 0, PixelWidth, PixelHeight);
DrawTextInternal(handle, box);
}
protected override Vector2 CalculateMinimumSize()
{
var minSize = _getIcon()?.Size ?? Vector2i.Zero;
var minSize = _getIcon()?.Size / UIScale ?? Vector2.Zero;
var font = ActualFont;
if (!string.IsNullOrWhiteSpace(Text) && !ClipText)
{
minSize += new Vector2i(EnsureWidthCache() + _getHSeparation(), 0);
minSize += (EnsureWidthCache() / UIScale + _getHSeparation() / UIScale, 0);
}
minSize = Vector2i.ComponentMax(minSize, new Vector2i(0, font.Height));
minSize = Vector2.ComponentMax(minSize, (0, font.GetHeight(UIScale) / UIScale));
return minSize;
}

View File

@@ -39,6 +39,14 @@ namespace Robust.Client.UserInterface.Controls
SortChildren();
}
protected void FitChildInPixelBox(Control child, UIBox2i pixelBox)
{
var topLeft = pixelBox.TopLeft / UIScale;
var bottomRight = pixelBox.BottomRight / UIScale;
FitChildInBox(child, new UIBox2(topLeft, bottomRight));
}
protected void FitChildInBox(Control child, UIBox2 box)
{
DebugTools.Assert(child.Parent == this);

View File

@@ -70,15 +70,15 @@ namespace Robust.Client.UserInterface.Controls
set => _hSeparationOverride = value;
}
private (int h, int v) Separations => (_hSeparationOverride ?? 4, _vSeparationOverride ?? 4);
private Vector2i Separations => (_hSeparationOverride ?? 4, _vSeparationOverride ?? 4);
protected override Vector2 CalculateMinimumSize()
{
var firstRow = true;
var totalMinSize = Vector2.Zero;
var thisRowSize = Vector2.Zero;
var totalMinSize = Vector2i.Zero;
var thisRowSize = Vector2i.Zero;
var currentRowCount = 0;
var (h, v) = Separations;
var (h, v) = (Vector2i)(Separations * UIScale);
foreach (var child in Children)
{
@@ -87,7 +87,7 @@ namespace Robust.Client.UserInterface.Controls
continue;
}
var (minSizeX, minSizeY) = child.CombinedMinimumSize;
var (minSizeX, minSizeY) = child.CombinedPixelMinimumSize;
thisRowSize = (thisRowSize.X + minSizeX, Math.Max(thisRowSize.Y, minSizeY));
if (currentRowCount != 0)
{
@@ -103,7 +103,7 @@ namespace Robust.Client.UserInterface.Controls
}
firstRow = false;
thisRowSize = Vector2.Zero;
thisRowSize = Vector2i.Zero;
currentRowCount = 0;
}
}
@@ -117,7 +117,7 @@ namespace Robust.Client.UserInterface.Controls
}
}
return totalMinSize;
return totalMinSize / UIScale;
}
protected override void SortChildren()
@@ -148,15 +148,15 @@ namespace Robust.Client.UserInterface.Controls
var row = index / _columns;
var column = index % _columns;
var (minSizeX, minSizeY) = child.CombinedMinimumSize;
columnSizes[column] = Math.Max((int) minSizeX, columnSizes[column]);
rowSizes[row] = Math.Max((int) minSizeY, rowSizes[row]);
var (minSizeX, minSizeY) = child.CombinedPixelMinimumSize;
columnSizes[column] = Math.Max(minSizeX, columnSizes[column]);
rowSizes[row] = Math.Max(minSizeY, rowSizes[row]);
columnExpand[column] = columnExpand[column] || (child.SizeFlagsHorizontal & SizeFlags.Expand) != 0;
rowExpand[row] = rowExpand[row] || (child.SizeFlagsVertical & SizeFlags.Expand) != 0;
}
// Basically now we just apply BoxContainer logic on rows and columns.
var (vSep, hSep) = Separations;
var (vSep, hSep) = (Vector2i)(Separations * UIScale);
var stretchMinX = 0;
var stretchMinY = 0;
// We do not use stretch ratios because Godot doesn't,
@@ -191,8 +191,8 @@ namespace Robust.Client.UserInterface.Controls
}
}
var stretchMaxX = Size.X - hSep * (_columns - 1);
var stretchMaxY = Size.Y - vSep * (rows - 1);
var stretchMaxX = Width - hSep * (_columns - 1);
var stretchMaxY = Height - vSep * (rows - 1);
var stretchAvailX = Math.Max(0, stretchMaxX - stretchMinX);
var stretchAvailY = Math.Max(0, stretchMaxY - stretchMinY);
@@ -243,8 +243,8 @@ namespace Robust.Client.UserInterface.Controls
}
}
var box = UIBox2.FromDimensions(hOffset, vOffset, columnSizes[column], rowSizes[row]);
FitChildInBox(child, box);
var box = UIBox2i.FromDimensions(hOffset, vOffset, columnSizes[column], rowSizes[row]);
FitChildInPixelBox(child, box);
hOffset += columnSizes[column] + hSep;
}

View File

@@ -4,7 +4,6 @@ using System.Diagnostics.Contracts;
using Robust.Client.Graphics;
using Robust.Client.Graphics.Drawing;
using Robust.Client.Input;
using Robust.Client.Utility;
using Robust.Shared.Maths;
using Color = Robust.Shared.Maths.Color;
using Font = Robust.Client.Graphics.Font;
@@ -16,7 +15,6 @@ namespace Robust.Client.UserInterface.Controls
{
private bool _isAtBottom = true;
private int _totalContentHeight;
private float _itemListHeight;
private VScrollBar _scrollBar = new VScrollBar();
private readonly List<Item> _itemList = new List<Item>();
@@ -33,8 +31,6 @@ namespace Robust.Client.UserInterface.Controls
public bool ScrollFollowing { get; set; } = true;
private int ScrollLimit => Math.Max(0, _totalContentHeight - (int) _getContentBox().Height + 1);
public ItemListSelectMode SelectMode { get; set; } = ItemListSelectMode.Single;
public ItemList()
@@ -55,12 +51,19 @@ namespace Robust.Client.UserInterface.Controls
_totalContentHeight = 0;
foreach (var item in _itemList)
{
var itemHeight = item.Icon != null
? Math.Max(item.IconSize.Y, ActualFont.Height) + ActualItemBackground.MinimumSize.Y
: ActualFont.Height + ActualItemBackground.MinimumSize.Y;
var itemHeight = 0f;
if (item.Icon != null)
{
itemHeight = item.IconSize.Y;
}
_totalContentHeight += (int)itemHeight;
itemHeight = Math.Max(itemHeight, ActualFont.GetHeight(UIScale));
itemHeight += ActualItemBackground.MinimumSize.Y;
_totalContentHeight += (int)Math.Ceiling(itemHeight);
}
_scrollBar.MaxValue = Math.Max(_scrollBar.Page, _totalContentHeight);
}
public void AddItem(string text, Texture icon = null, bool selectable = true)
@@ -70,8 +73,7 @@ namespace Robust.Client.UserInterface.Controls
RecalculateContentHeight();
if (_isAtBottom && ScrollFollowing)
{
_scrollBar.Value = ScrollLimit;
_scrollBar.MoveToEnd();
}
}
@@ -271,7 +273,7 @@ namespace Robust.Client.UserInterface.Controls
public void ScrollToBottom()
{
_scrollBar.Value = ScrollLimit;
_scrollBar.MoveToEnd();
_isAtBottom = true;
}
@@ -285,10 +287,9 @@ namespace Robust.Client.UserInterface.Controls
var iconSelectedBg = ActualSelectedItemBackground;
var iconDisabledBg = ActualDisabledItemBackground;
_itemListHeight = 0f;
Vector2 separation = (0, -_scrollBar.Value);
var offset = -_scrollBar.Value;
listBg.Draw(handle, SizeBox);
listBg.Draw(handle, PixelSizeBox);
foreach (var item in _itemList)
{
@@ -300,11 +301,16 @@ namespace Robust.Client.UserInterface.Controls
if (item.Selected)
bg = iconSelectedBg;
var itemHeight = item.Icon != null
? Math.Max(item.IconSize.Y, font.Height) + bg.MinimumSize.Y
: font.Height + bg.MinimumSize.Y;
var itemHeight = 0f;
if (item.Icon != null)
{
itemHeight = item.IconSize.Y;
}
item.Region = UIBox2.FromDimensions(separation, (SizeBox.Width, itemHeight));
itemHeight = Math.Max(itemHeight, ActualFont.GetHeight(UIScale));
itemHeight += ActualItemBackground.MinimumSize.Y;
item.Region = UIBox2.FromDimensions((0, offset), (PixelWidth, itemHeight));
bg.Draw(handle, item.Region.Value);
@@ -312,36 +318,23 @@ namespace Robust.Client.UserInterface.Controls
{
if (item.IconRegion.Size == Vector2.Zero)
{
handle.DrawTextureRect(item.Icon, UIBox2.FromDimensions(separation, item.Icon.Size), false, item.IconModulate, item.IconTranspose);
handle.DrawTextureRect(item.Icon, UIBox2.FromDimensions((0, offset), item.Icon.Size), false, item.IconModulate, item.IconTranspose);
}
else
{
handle.DrawTextureRectRegion(item.Icon, UIBox2.FromDimensions(separation, item.Icon.Size), item.IconRegion, item.IconModulate);
handle.DrawTextureRectRegion(item.Icon, UIBox2.FromDimensions((0, offset), item.Icon.Size), item.IconRegion, item.IconModulate);
}
}
if (item.Text != null)
{
DrawTextInternal(handle, item.Text,
UIBox2.FromDimensions((item.IconSize.X, separation.Y), (SizeBox.Width-item.IconSize.X,font.Height*2))
UIBox2.FromDimensions((item.IconSize.X, offset), (PixelWidth-item.IconSize.X,font.GetHeight(UIScale)))
);
}
separation += (0, itemHeight);
_itemListHeight += itemHeight;
offset += itemHeight;
}
if (_itemListHeight > Size.Y)
{
_scrollBar.MaxValue = _itemListHeight;
_scrollBar.Page = Size.Y - ActualBackground.MinimumSize.Y;
}
else
{
_scrollBar.MaxValue = 0f;
_scrollBar.Page = 0f;
}
}
protected void DrawTextInternal(DrawingHandleScreen handle, string text, UIBox2 box)
@@ -349,19 +342,19 @@ namespace Robust.Client.UserInterface.Controls
var font = ActualFont;
var color = ActualFontColor;
var offsetY = (int) (box.Height - font.Height) / 2;
var baseLine = new Vector2i(0, offsetY + font.Ascent) + box.TopLeft;
var offsetY = (int) (box.Height - font.GetHeight(UIScale)) / 2;
var baseLine = new Vector2i(0, offsetY + font.GetAscent(UIScale)) + box.TopLeft;
foreach (var chr in text)
{
if (!font.TryGetCharMetrics(chr, out var metrics))
if (!font.TryGetCharMetrics(chr, UIScale, out var metrics))
{
continue;
}
if (!(baseLine.X < box.Left || baseLine.X + metrics.Advance > box.Right))
{
font.DrawChar(handle, chr, baseLine, color);
font.DrawChar(handle, chr, baseLine, UIScale, color);
}
baseLine += (metrics.Advance, 0);
@@ -370,7 +363,13 @@ namespace Robust.Client.UserInterface.Controls
protected override Vector2 CalculateMinimumSize()
{
return (ActualBackground?.MinimumSize.X ?? 0, _itemListHeight + ActualBackground?.MinimumSize.Y ?? 0);
var size = Vector2.Zero;
if (ActualBackground != null)
{
size += ActualBackground.MinimumSize / UIScale;
}
return size;
}
protected internal override void MouseMove(GUIMouseMoveEventArgs args)
@@ -393,16 +392,12 @@ namespace Robust.Client.UserInterface.Controls
if (args.WheelDirection == Mouse.Wheel.Up)
{
_scrollBar.Value = _scrollBar.Value - _getScrollSpeed();
_scrollBar.Value -= _getScrollSpeed();
_isAtBottom = false;
}
else if (args.WheelDirection == Mouse.Wheel.Down)
{
var limit = ScrollLimit;
if (_scrollBar.Value + _getScrollSpeed() < limit)
_scrollBar.Value = _scrollBar.Value + _getScrollSpeed();
else
ScrollToBottom();
_scrollBar.Value += _getScrollSpeed();
if (_scrollBar.IsAtEnd)
{
_isAtBottom = true;
@@ -446,7 +441,7 @@ namespace Robust.Client.UserInterface.Controls
private int _getScrollSpeed()
{
var font = ActualFont;
return font.Height * 2;
return font.GetHeight(UIScale) * 2;
}
[Pure]
@@ -456,6 +451,23 @@ namespace Robust.Client.UserInterface.Controls
return style?.GetContentBox(SizeBox) ?? SizeBox;
}
protected override void Resized()
{
base.Resized();
var styleBoxSize = ActualBackground?.MinimumSize.Y ?? 0;
_scrollBar.Page = PixelSize.Y - styleBoxSize;
RecalculateContentHeight();
}
protected internal override void UIScaleChanged()
{
RecalculateContentHeight();
base.UIScaleChanged();
}
public sealed class Item
{
public string Text = null;

View File

@@ -41,15 +41,6 @@ namespace Robust.Client.UserInterface.Controls
}
}
[ViewVariables]
public bool AutoWrap
{
get => default;
set
{
}
}
[ViewVariables]
public AlignMode Align { get; set; }
@@ -124,10 +115,10 @@ namespace Robust.Client.UserInterface.Controls
break;
case AlignMode.Center:
case AlignMode.Fill:
hOffset = (int) (Size.X - _textDimensionCache.Value.X) / 2;
hOffset = (PixelSize.X - _textDimensionCache.Value.X) / 2;
break;
case AlignMode.Right:
hOffset = (int) (Size.X - _textDimensionCache.Value.X);
hOffset = PixelSize.X - _textDimensionCache.Value.X;
break;
default:
throw new ArgumentOutOfRangeException();
@@ -141,10 +132,10 @@ namespace Robust.Client.UserInterface.Controls
break;
case VAlignMode.Fill:
case VAlignMode.Center:
vOffset = (int) (Size.Y - _textDimensionCache.Value.Y) / 2;
vOffset = (PixelSize.Y - _textDimensionCache.Value.Y) / 2;
break;
case VAlignMode.Bottom:
vOffset = (int) (Size.Y - _textDimensionCache.Value.Y);
vOffset = PixelSize.Y - _textDimensionCache.Value.Y;
break;
default:
throw new ArgumentOutOfRangeException();
@@ -152,17 +143,17 @@ namespace Robust.Client.UserInterface.Controls
var newlines = 0;
var font = ActualFont;
var baseLine = new Vector2(hOffset, font.Ascent + vOffset);
var baseLine = new Vector2(hOffset, font.GetAscent(UIScale) + vOffset);
var actualFontColor = ActualFontColor;
foreach (var chr in _text)
{
if (chr == '\n')
{
newlines += 1;
baseLine = new Vector2(hOffset, font.Ascent + font.LineHeight * newlines);
baseLine = new Vector2(hOffset, font.GetAscent(UIScale) + font.GetLineHeight(UIScale) * newlines);
}
var advance = font.DrawChar(handle, chr, baseLine, actualFontColor);
var advance = font.DrawChar(handle, chr, baseLine, UIScale, actualFontColor);
baseLine += new Vector2(advance, 0);
}
}
@@ -194,6 +185,13 @@ namespace Robust.Client.UserInterface.Controls
return _textDimensionCache.Value;
}
protected internal override void UIScaleChanged()
{
_textDimensionCache = null;
base.UIScaleChanged();
}
private void _calculateTextDimension()
{
if (_text == null)
@@ -203,7 +201,7 @@ namespace Robust.Client.UserInterface.Controls
}
var font = ActualFont;
var height = font.Height;
var height = font.GetHeight(UIScale);
var maxLineSize = 0;
var currentLineSize = 0;
foreach (var chr in _text)
@@ -212,11 +210,11 @@ namespace Robust.Client.UserInterface.Controls
{
maxLineSize = Math.Max(currentLineSize, maxLineSize);
currentLineSize = 0;
height += font.LineHeight;
height += font.GetLineHeight(UIScale);
}
else
{
var metrics = font.GetCharMetrics(chr);
var metrics = font.GetCharMetrics(chr, UIScale);
if (!metrics.HasValue)
{
continue;
@@ -228,7 +226,7 @@ namespace Robust.Client.UserInterface.Controls
maxLineSize = Math.Max(currentLineSize, maxLineSize);
_textDimensionCache = new Vector2i(maxLineSize, height);
_textDimensionCache = (Vector2i)(new Vector2(maxLineSize, height) / UIScale);
}
protected override void StylePropertiesChanged()

View File

@@ -135,14 +135,14 @@ namespace Robust.Client.UserInterface.Controls
protected internal override void Draw(DrawingHandleScreen handle)
{
var styleBox = _getStyleBox();
var drawBox = SizeBox;
var drawBox = PixelSizeBox;
var contentBox = styleBox.GetContentBox(drawBox);
styleBox.Draw(handle, drawBox);
var font = _getFont();
var renderedTextColor = _getFontColor();
var offsetY = (int) (contentBox.Height - font.Height) / 2;
var baseLine = new Vector2i(0, offsetY + font.Ascent) + contentBox.TopLeft;
var offsetY = (int) (contentBox.Height - font.GetHeight(UIScale)) / 2;
var baseLine = new Vector2i(0, offsetY + font.GetAscent(UIScale)) + contentBox.TopLeft;
string renderedText;
@@ -165,7 +165,7 @@ namespace Robust.Client.UserInterface.Controls
var count = 0;
foreach (var chr in renderedText)
{
if (!font.TryGetCharMetrics(chr, out var metrics))
if (!font.TryGetCharMetrics(chr, UIScale, out var metrics))
{
count += 1;
continue;
@@ -177,7 +177,7 @@ namespace Robust.Client.UserInterface.Controls
break;
}
font.DrawChar(handle, chr, baseLine, renderedTextColor);
font.DrawChar(handle, chr, baseLine, UIScale, renderedTextColor);
baseLine += new Vector2(metrics.Advance, 0);
count += 1;
if (count == _cursorPosition)
@@ -210,7 +210,7 @@ namespace Robust.Client.UserInterface.Controls
{
var font = _getFont();
var style = _getStyleBox();
return new Vector2(0, font.Height) + style.MinimumSize;
return new Vector2(0, font.GetHeight(UIScale)/UIScale) + style.MinimumSize/UIScale;
}
protected internal override void TextEntered(GUITextEventArgs args)
@@ -305,9 +305,9 @@ namespace Robust.Client.UserInterface.Controls
// Find closest cursor position under mouse.
var style = _getStyleBox();
var contentBox = style.GetContentBox(SizeBox);
var contentBox = style.GetContentBox(PixelSizeBox);
var clickPosX = args.RelativePosition.X;
var clickPosX = args.RelativePosition.X * UIScale;
var font = _getFont();
var index = 0;
@@ -315,7 +315,7 @@ namespace Robust.Client.UserInterface.Controls
var lastChrPostX = contentBox.Left;
foreach (var chr in _text)
{
if (!font.TryGetCharMetrics(chr, out var metrics))
if (!font.TryGetCharMetrics(chr, UIScale, out var metrics))
{
index += 1;
continue;

View File

@@ -4,7 +4,6 @@ using JetBrains.Annotations;
using Robust.Client.Graphics;
using Robust.Client.Graphics.Drawing;
using Robust.Client.Input;
using Robust.Client.Utility;
using Robust.Shared.Maths;
using Robust.Shared.Utility;
using PureAttribute = System.Diagnostics.Contracts.PureAttribute;
@@ -28,8 +27,6 @@ namespace Robust.Client.UserInterface.Controls
public bool ScrollFollowing { get; set; } = true;
private int ScrollLimit => Math.Max(0, _totalContentHeight - (int) _getContentBox().Height + 1);
public StyleBox StyleBoxOverride
{
get => _styleBoxOverride;
@@ -56,7 +53,7 @@ namespace Robust.Client.UserInterface.Controls
_entries.RemoveAt(line);
var font = _getFont();
_totalContentHeight -= entry.Height + font.LineSeparation;
_totalContentHeight -= entry.Height + font.GetLineSeparation(UIScale);
if (_entries.Count == 0)
{
Clear();
@@ -68,7 +65,7 @@ namespace Robust.Client.UserInterface.Controls
{
var entry = new RichTextEntry(message);
entry.Update(_getFont(), _getContentBox().Width);
entry.Update(_getFont(), _getContentBox().Width, UIScale);
_entries.Add(entry);
var font = _getFont();
@@ -79,19 +76,19 @@ namespace Robust.Client.UserInterface.Controls
}
else
{
_totalContentHeight += font.LineSeparation;
_totalContentHeight += font.GetLineSeparation(UIScale);
}
_scrollBar.MaxValue = Math.Max(_scrollBar.Page, _totalContentHeight);
if (_isAtBottom && ScrollFollowing)
{
_scrollBar.Value = ScrollLimit;
_scrollBar.MoveToEnd();
}
}
public void ScrollToBottom()
{
_scrollBar.Value = ScrollLimit;
_scrollBar.MoveToEnd();
_isAtBottom = true;
}
@@ -111,7 +108,7 @@ namespace Robust.Client.UserInterface.Controls
var style = _getStyleBox();
var font = _getFont();
style?.Draw(handle, SizeBox);
style?.Draw(handle, PixelSizeBox);
var contentBox = _getContentBox();
var entryOffset = -_scrollBar.Value;
@@ -125,7 +122,7 @@ namespace Robust.Client.UserInterface.Controls
{
if (entryOffset + entry.Height < 0)
{
entryOffset += entry.Height + font.LineSeparation;
entryOffset += entry.Height + font.GetLineSeparation(UIScale);
continue;
}
@@ -134,9 +131,9 @@ namespace Robust.Client.UserInterface.Controls
break;
}
entry.Draw(handle, font, contentBox, entryOffset, formatStack);
entry.Draw(handle, font, contentBox, entryOffset, formatStack, UIScale);
entryOffset += entry.Height + font.LineSeparation;
entryOffset += entry.Height + font.GetLineSeparation(UIScale);
}
}
@@ -146,12 +143,12 @@ namespace Robust.Client.UserInterface.Controls
if (args.WheelDirection == Mouse.Wheel.Up)
{
_scrollBar.Value = _scrollBar.Value - _getScrollSpeed();
_scrollBar.Value -= _getScrollSpeed();
_isAtBottom = false;
}
else if (args.WheelDirection == Mouse.Wheel.Down)
{
_scrollBar.Value = _scrollBar.Value + _getScrollSpeed();
_scrollBar.Value += _getScrollSpeed();
if (_scrollBar.IsAtEnd)
{
_isAtBottom = true;
@@ -172,7 +169,7 @@ namespace Robust.Client.UserInterface.Controls
var styleBoxSize = _getStyleBox()?.MinimumSize.Y ?? 0;
_scrollBar.Page = Size.Y - styleBoxSize;
_scrollBar.Page = PixelSize.Y - styleBoxSize;
_invalidateEntries();
}
@@ -189,15 +186,15 @@ namespace Robust.Client.UserInterface.Controls
for (var i = 0; i < _entries.Count; i++)
{
var entry = _entries[i];
entry.Update(font, sizeX);
entry.Update(font, sizeX, UIScale);
_entries[i] = entry;
_totalContentHeight += entry.Height + font.LineSeparation;
_totalContentHeight += entry.Height + font.GetLineSeparation(UIScale);
}
_scrollBar.MaxValue = Math.Max(_scrollBar.Page, _totalContentHeight);
if (_isAtBottom && ScrollFollowing)
{
_scrollBar.Value = ScrollLimit;
_scrollBar.MoveToEnd();
}
}
@@ -229,14 +226,21 @@ namespace Robust.Client.UserInterface.Controls
private int _getScrollSpeed()
{
var font = _getFont();
return font.Height * 2;
return font.GetLineHeight(UIScale) * 2;
}
[Pure]
private UIBox2 _getContentBox()
{
var style = _getStyleBox();
return style?.GetContentBox(SizeBox) ?? SizeBox;
return style?.GetContentBox(PixelSizeBox) ?? PixelSizeBox;
}
protected internal override void UIScaleChanged()
{
_invalidateEntries();
base.UIScaleChanged();
}
}
}

View File

@@ -48,7 +48,7 @@ namespace Robust.Client.UserInterface.Controls
base.Draw(handle);
var panel = ActualPanel;
panel.Draw(handle, SizeBox);
panel.Draw(handle, PixelSizeBox);
}
private protected override void SetGodotProperty(string property, object value, GodotAssetScene context)
@@ -63,7 +63,7 @@ namespace Robust.Client.UserInterface.Controls
protected override Vector2 CalculateMinimumSize()
{
return ActualPanel.MinimumSize;
return ActualPanel.MinimumSize/UIScale;
}
}
}

View File

@@ -31,18 +31,18 @@ namespace Robust.Client.UserInterface.Controls
base.Draw(handle);
var style = _getStyleBox();
style?.Draw(handle, SizeBox);
style?.Draw(handle, PixelSizeBox);
}
protected override void SortChildren()
{
base.SortChildren();
var contentBox = _getStyleBox()?.GetContentBox(SizeBox) ?? SizeBox;
var contentBox = _getStyleBox()?.GetContentBox(PixelSizeBox) ?? SizeBox;
foreach (var child in Children)
{
FitChildInBox(child, contentBox);
FitChildInPixelBox(child, (UIBox2i) contentBox);
}
}
@@ -55,7 +55,7 @@ namespace Robust.Client.UserInterface.Controls
childSize = Vector2.ComponentMax(childSize, child.CombinedMinimumSize);
}
return styleSize + childSize;
return styleSize / UIScale + childSize;
}
[System.Diagnostics.Contracts.Pure]

View File

@@ -2,7 +2,6 @@
using JetBrains.Annotations;
using Robust.Client.Graphics;
using Robust.Client.Graphics.Drawing;
using Robust.Client.Utility;
using Robust.Shared.Maths;
using Robust.Shared.Utility;
@@ -35,7 +34,7 @@ namespace Robust.Client.UserInterface.Controls
return Vector2.Zero;
}
return (0, _entry.Height);
return (0, _entry.Height / UIScale);
}
private void _updateEntry()
@@ -45,7 +44,7 @@ namespace Robust.Client.UserInterface.Controls
if (_message != null)
{
var oldHeight = _entry.Height;
_entry.Update(font, Width);
_entry.Update(font, Width, UIScale);
if (oldHeight != _entry.Height)
{
MinimumSizeChanged();
@@ -62,7 +61,7 @@ namespace Robust.Client.UserInterface.Controls
return;
}
_entry.Draw(handle, _getFont(), SizeBox, 0, new Stack<FormattedMessage.Tag>());
_entry.Draw(handle, _getFont(), SizeBox, 0, new Stack<FormattedMessage.Tag>(), UIScale);
}
protected override void Resized()

View File

@@ -31,6 +31,12 @@ namespace Robust.Client.UserInterface.Controls
}
}
public void MoveToEnd()
{
// Will be clamped as necessary.
Value = MaxValue;
}
protected internal override void Draw(DrawingHandleScreen handle)
{
var styleBox = _getGrabberStyleBox();
@@ -121,11 +127,11 @@ namespace Robust.Client.UserInterface.Controls
if (_orientation == OrientationMode.Horizontal)
{
return new UIBox2(grabberOffset, 0, grabberEnd, Height);
return new UIBox2(grabberOffset, 0, grabberEnd, PixelHeight);
}
else
{
return new UIBox2(0, grabberOffset, Width, grabberEnd);
return new UIBox2(0, grabberOffset, PixelWidth, grabberEnd);
}
}
@@ -146,11 +152,11 @@ namespace Robust.Client.UserInterface.Controls
{
if (_orientation == OrientationMode.Horizontal)
{
return Width;
return PixelWidth;
}
else
{
return Height;
return PixelHeight;
}
}

View File

@@ -88,7 +88,7 @@ namespace Robust.Client.UserInterface.Controls
return;
}
handle.DrawEntity(_sprite.Owner, GlobalPosition + Size / 2);
handle.DrawEntity(_sprite.Owner, GlobalPixelPosition + PixelSize / 2);
}
}
}

View File

@@ -135,7 +135,7 @@ namespace Robust.Client.UserInterface.Controls
// First, draw panel.
var headerSize = _getHeaderSize();
var panel = _getPanel();
var panelBox = new UIBox2(0, headerSize, Width, Height);
var panelBox = new UIBox2(0, headerSize, PixelWidth, PixelHeight);
panel?.Draw(handle, panelBox);
@@ -156,7 +156,7 @@ namespace Robust.Client.UserInterface.Controls
// Get string length.
foreach (var chr in title)
{
if (!font.TryGetCharMetrics(chr, out var metrics))
if (!font.TryGetCharMetrics(chr, UIScale, out var metrics))
{
continue;
}
@@ -169,7 +169,7 @@ namespace Robust.Client.UserInterface.Controls
UIBox2 contentBox;
var topLeft = new Vector2(headerOffset, 0);
var size = new Vector2(titleLength, font.Height);
var size = new Vector2(titleLength, font.GetHeight(UIScale));
float boxAdvance;
if (box != null)
@@ -185,16 +185,16 @@ namespace Robust.Client.UserInterface.Controls
contentBox = UIBox2.FromDimensions(topLeft, size);
}
var baseLine = new Vector2(0, font.Ascent) + contentBox.TopLeft;
var baseLine = new Vector2(0, font.GetAscent(UIScale)) + contentBox.TopLeft;
foreach (var chr in title)
{
if (!font.TryGetCharMetrics(chr, out var metrics))
if (!font.TryGetCharMetrics(chr, UIScale, out var metrics))
{
continue;
}
font.DrawChar(handle, chr, baseLine, active ? fontColorActive : fontColorInactive);
font.DrawChar(handle, chr, baseLine, UIScale, active ? fontColorActive : fontColorInactive);
baseLine += new Vector2(metrics.Advance, 0);
}
@@ -204,30 +204,30 @@ namespace Robust.Client.UserInterface.Controls
protected override Vector2 CalculateMinimumSize()
{
var total = new Vector2();
var total = Vector2i.Zero;
foreach (var child in Children)
{
if (child.Visible)
{
total = Vector2.ComponentMax(child.CombinedMinimumSize, total);
total = Vector2i.ComponentMax(child.CombinedPixelMinimumSize, total);
}
}
if (TabsVisible)
{
total += new Vector2(0, _getHeaderSize());
total += (0, _getHeaderSize());
}
var panel = _getPanel();
total += panel?.MinimumSize ?? Vector2.Zero;
total += (Vector2i)(panel?.MinimumSize ?? Vector2.Zero);
return total;
return total / UIScale;
}
private void _fixChildMargins(Control child)
{
FitChildInBox(child, _getContentBox());
FitChildInPixelBox(child, _getContentBox());
}
protected override void SortChildren()
@@ -254,14 +254,14 @@ namespace Robust.Client.UserInterface.Controls
}
// Outside of header size, ignore.
if (args.RelativePosition.Y < 0 || args.RelativePosition.Y > _getHeaderSize())
if (args.RelativePixelPosition.Y < 0 || args.RelativePixelPosition.Y > _getHeaderSize())
{
return;
}
args.Handle();
var relX = args.RelativePosition.X;
var relX = args.RelativePixelPosition.X;
var font = _getFont();
var boxActive = _getTabBoxActive();
@@ -277,7 +277,7 @@ namespace Robust.Client.UserInterface.Controls
// Get string length.
foreach (var chr in title)
{
if (!font.TryGetCharMetrics(chr, out var metrics))
if (!font.TryGetCharMetrics(chr, UIScale, out var metrics))
{
continue;
}
@@ -301,18 +301,22 @@ namespace Robust.Client.UserInterface.Controls
}
[Pure]
private UIBox2 _getContentBox()
private UIBox2i _getContentBox()
{
var headerSize = _getHeaderSize();
var panel = _getPanel();
var panelBox = new UIBox2(0, headerSize, Width, Height);
return panel?.GetContentBox(panelBox) ?? panelBox;
var panelBox = new UIBox2i(0, headerSize, PixelWidth, PixelHeight);
if (panel != null)
{
return (UIBox2i) panel.GetContentBox(panelBox);
}
return panelBox;
}
[Pure]
private float _getHeaderSize()
private int _getHeaderSize()
{
var headerSize = 0f;
var headerSize = 0;
if (TabsVisible)
{
@@ -323,8 +327,8 @@ namespace Robust.Client.UserInterface.Controls
var activeSize = active?.MinimumSize ?? Vector2.Zero;
var inactiveSize = inactive?.MinimumSize ?? Vector2.Zero;
headerSize = Math.Max(activeSize.Y, inactiveSize.Y);
headerSize += font.Height;
headerSize = (int) Math.Max(activeSize.Y, inactiveSize.Y);
headerSize += font.GetHeight(UIScale);
}
return headerSize;

View File

@@ -77,7 +77,7 @@ namespace Robust.Client.UserInterface.Controls
}
}
handle.DrawTextureRect(texture, SizeBox, false);
handle.DrawTextureRect(texture, PixelSizeBox, false);
}
private protected override void SetGodotProperty(string property, object value, GodotAssetScene context)

View File

@@ -58,7 +58,7 @@ namespace Robust.Client.UserInterface.Controls
break;
case StretchMode.KeepCentered:
{
var position = (Vector2i) (Size - _texture.Size) / 2;
var position = (PixelSize - _texture.Size) / 2;
handle.DrawTexture(_texture, position);
break;
}

View File

@@ -4,7 +4,6 @@ using Robust.Client.Graphics;
using Robust.Client.Graphics.Drawing;
using Robust.Client.Utility;
using Robust.Shared.Maths;
using Robust.Shared.Utility;
namespace Robust.Client.UserInterface.Controls
{
@@ -134,8 +133,8 @@ namespace Robust.Client.UserInterface.Controls
bool DoSearch(Item item, float hOffset)
{
var itemHeight = font.Height;
var itemBox = UIBox2.FromDimensions((hOffset, vOffset), (Width - hOffset, itemHeight));
var itemHeight = font.GetHeight(UIScale);
var itemBox = UIBox2.FromDimensions((hOffset, vOffset), (PixelWidth - hOffset, itemHeight));
if (itemBox.Contains(position))
{
final = item;
@@ -190,7 +189,7 @@ namespace Robust.Client.UserInterface.Controls
var hOffset = 0f;
if (background != null)
{
background.Draw(handle, SizeBox);
background.Draw(handle, PixelSizeBox);
var (bho, bvo) = background.GetContentOffset(Vector2.Zero);
vOffset += bvo;
hOffset += bho;
@@ -213,20 +212,20 @@ namespace Robust.Client.UserInterface.Controls
DrawingHandleScreen handle, ref float vOffset, float hOffset, Item item,
Font font, StyleBox itemSelected)
{
var itemHeight = font.Height + itemSelected.MinimumSize.Y;
var itemHeight = font.GetHeight(UIScale) + itemSelected.MinimumSize.Y;
var selected = item.Index == _selectedIndex;
if (selected)
{
itemSelected.Draw(handle, UIBox2.FromDimensions(hOffset, vOffset, Width - hOffset, itemHeight));
itemSelected.Draw(handle, UIBox2.FromDimensions(hOffset, vOffset, PixelWidth - hOffset, itemHeight));
}
if (!string.IsNullOrWhiteSpace(item.Text))
{
var offset = itemSelected.GetContentOffset(Vector2.Zero);
var baseLine = offset + (hOffset, vOffset + font.Ascent);
var baseLine = offset + (hOffset, vOffset + font.GetAscent(UIScale));
foreach (var chr in item.Text)
{
baseLine += (font.DrawChar(handle, chr, baseLine, Color.White), 0);
baseLine += (font.DrawChar(handle, chr, baseLine, UIScale, Color.White), 0);
}
}
@@ -264,7 +263,7 @@ namespace Robust.Client.UserInterface.Controls
private float _getItemHeight(Item item, Font font)
{
float sum = font.Height;
float sum = font.GetHeight(UIScale);
foreach (var child in item.Children)
{
@@ -278,8 +277,8 @@ namespace Robust.Client.UserInterface.Controls
{
var internalHeight = _getInternalHeight();
_scrollBar.MaxValue = internalHeight;
_scrollBar.Page = Height;
_scrollBar.Visible = internalHeight > Height;
_scrollBar.Page = PixelHeight;
_scrollBar.Visible = internalHeight > PixelHeight;
}
protected override void Resized()

View File

@@ -177,9 +177,6 @@ namespace Robust.Client.UserInterface.CustomControls
if (tex != null)
{
rect.Texture = tex.Default;
// Ok I can't find a way to make this TextureRect scale down sanely so let's do this.
var scale = (float) TARGET_ICON_HEIGHT / tex.Default.Height;
rect.Scale = new Vector2(scale, scale);
}
else
{

View File

@@ -66,9 +66,9 @@ namespace Robust.Client.UserInterface.CustomControls
{
var currentFrameIndex = MathHelper.Mod(_frameIndex - 1 - i, TrackedFrames);
var frameTime = _frameTimes[currentFrameIndex];
var x = FrameWidth * (TrackedFrames - 1 - i);
var x = FrameWidth * UserInterfaceManager.UIScale * (TrackedFrames - 1 - i);
var frameHeight = FrameHeight * (frameTime / (1f / TargetFrameRate));
var rect = new UIBox2(x, Height - frameHeight, x + FrameWidth, Height);
var rect = new UIBox2(x, PixelHeight - frameHeight, x + FrameWidth * UserInterfaceManager.UIScale, PixelHeight);
Color color;
if (frameTime > 1f / (TargetFrameRate / 2 - 1))

View File

@@ -166,10 +166,15 @@ namespace Robust.Client.UserInterface.CustomControls
{
base.MouseMove(args);
if (Parent == null)
{
return;
}
if (CurrentDrag == DragMode.Move)
{
var globalPos = args.GlobalPosition;
globalPos = Vector2.Clamp(globalPos, Vector2.Zero, _displayManager.ScreenSize);
globalPos = Vector2.Clamp(globalPos, Vector2.Zero, Parent.Size);
Position = globalPos - DragOffsetTopLeft;
return;
}

View File

@@ -41,12 +41,13 @@ namespace Robust.Client.UserInterface
/// </summary>
/// <param name="font">The font being used for display.</param>
/// <param name="sizeX">The horizontal size of the container of this entry.</param>
public void Update(Font font, float sizeX)
/// <param name="uiScale"></param>
public void Update(Font font, float sizeX, float uiScale)
{
// This method is gonna suck due to complexity.
// Bear with me here.
// I am so deeply sorry for the person adding stuff to this in the future.
Height = font.Height;
Height = font.GetHeight(uiScale);
LineBreaks.Clear();
// Index we put into the LineBreaks list when a line break should occur.
@@ -89,7 +90,7 @@ namespace Robust.Client.UserInterface
// We ran into a word boundary and the word is too big to fit the previous line.
// So we insert the line break BEFORE the last word.
LineBreaks.Add(wordStartBreakIndex.Value);
Height += font.LineHeight;
Height += font.GetLineHeight(uiScale);
posX = wordSizePixels;
}
@@ -103,7 +104,7 @@ namespace Robust.Client.UserInterface
if (chr == '\n')
{
LineBreaks.Add(breakIndexCounter);
Height += font.LineHeight;
Height += font.GetLineHeight(uiScale);
posX = 0;
lastChar = chr;
wordStartBreakIndex = null;
@@ -112,7 +113,7 @@ namespace Robust.Client.UserInterface
}
// Uh just skip unknown characters I guess.
if (!font.TryGetCharMetrics(chr, out var metrics))
if (!font.TryGetCharMetrics(chr, uiScale, out var metrics))
{
lastChar = chr;
continue;
@@ -142,7 +143,7 @@ namespace Robust.Client.UserInterface
// Reset forceSplitData so that we can split again if necessary.
forceSplitData = null;
LineBreaks.Add(breakIndex);
Height += font.LineHeight;
Height += font.GetLineHeight(uiScale);
wordSizePixels -= splitWordSize;
wordStartBreakIndex = null;
posX = wordSizePixels;
@@ -159,7 +160,7 @@ namespace Robust.Client.UserInterface
DebugTools.Assert(wordStartBreakIndex.HasValue,
"wordStartBreakIndex can only be null if the word begins at a new line, in which case this branch shouldn't be reached as the word would be split due to being longer than a single line.");
LineBreaks.Add(wordStartBreakIndex.Value);
Height += font.LineHeight;
Height += font.GetLineHeight(uiScale);
}
}
@@ -171,14 +172,14 @@ namespace Robust.Client.UserInterface
// A stack for format tags.
// This stack contains the format tag to RETURN TO when popped off.
// So when a new color tag gets hit this stack gets the previous color pushed on.
Stack<FormattedMessage.Tag> formatStack)
Stack<FormattedMessage.Tag> formatStack, float uiScale)
{
// The tag currently doing color.
var currentColorTag = TagWhite;
var globalBreakCounter = 0;
var lineBreakIndex = 0;
var baseLine = drawBox.TopLeft + new Vector2(0, font.Ascent + verticalOffset);
var baseLine = drawBox.TopLeft + new Vector2(0, font.GetAscent(uiScale) + verticalOffset);
formatStack.Clear();
foreach (var tag in Message.Tags)
{
@@ -209,11 +210,11 @@ namespace Robust.Client.UserInterface
if (lineBreakIndex < LineBreaks.Count &&
LineBreaks[lineBreakIndex] == globalBreakCounter)
{
baseLine = new Vector2(drawBox.Left, baseLine.Y + font.LineHeight);
baseLine = new Vector2(drawBox.Left, baseLine.Y + font.GetLineHeight(uiScale));
lineBreakIndex += 1;
}
var advance = font.DrawChar(handle, chr, baseLine, currentColorTag.Color);
var advance = font.DrawChar(handle, chr, baseLine, uiScale, currentColorTag.Color);
baseLine += new Vector2(advance, 0);
}

View File

@@ -2,11 +2,9 @@
using System.Collections.Generic;
using System.Linq;
using Robust.Client.Console;
using Robust.Client.Graphics;
using Robust.Client.Graphics.Clyde;
using Robust.Client.Graphics.Drawing;
using Robust.Client.Input;
using Robust.Client.Interfaces;
using Robust.Client.Interfaces.Graphics;
using Robust.Client.Interfaces.Graphics.ClientEye;
using Robust.Client.Interfaces.Input;
@@ -17,7 +15,9 @@ using Robust.Client.Player;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.Utility;
using Robust.Shared.Configuration;
using Robust.Shared.Input;
using Robust.Shared.Interfaces.Configuration;
using Robust.Shared.Interfaces.Map;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.Interfaces.Resources;
@@ -27,7 +27,7 @@ using Robust.Shared.Maths;
namespace Robust.Client.UserInterface
{
internal sealed class UserInterfaceManager : IDisposable, IUserInterfaceManagerInternal
internal sealed class UserInterfaceManager : IDisposable, IUserInterfaceManagerInternal, IPostInjectInit
{
[Dependency] private readonly IInputManager _inputManager;
[Dependency] private readonly IDisplayManager _displayManager;
@@ -40,6 +40,7 @@ namespace Robust.Client.UserInterface
[Dependency] private readonly IStateManager _stateManager;
[Dependency] private readonly IClientNetManager _netManager;
[Dependency] private readonly IMapManager _mapManager;
[Dependency] private readonly IConfigurationManager _configurationManager;
public UITheme ThemeDefaults { get; private set; }
public Stylesheet Stylesheet { get; set; }
@@ -51,6 +52,7 @@ namespace Robust.Client.UserInterface
public Control StateRoot { get; private set; }
public Control CurrentlyHovered { get; private set; }
public float UIScale { get; private set; } = 1;
public Control RootControl { get; private set; }
public Control WindowRoot { get; private set; }
public DebugConsole DebugConsole { get; private set; }
@@ -69,6 +71,7 @@ namespace Robust.Client.UserInterface
public void Initialize()
{
UIScale = _configurationManager.GetCVar<float>("display.uiScale");
ThemeDefaults = new UIThemeDefault();
_initializeCommon();
@@ -98,8 +101,8 @@ namespace Robust.Client.UserInterface
IsInsideTree = true
};
RootControl.SetAnchorPreset(Control.LayoutPreset.Wide);
RootControl.Size = _displayManager.ScreenSize;
_displayManager.OnWindowResized += args => RootControl.Size = args.NewSize;
RootControl.Size = _displayManager.ScreenSize / UIScale;
_displayManager.OnWindowResized += args => _updateRootSize();
StateRoot = new Control("StateRoot")
{
@@ -154,8 +157,8 @@ namespace Robust.Client.UserInterface
if (_modalStack.Count != 0)
{
var top = _modalStack[_modalStack.Count - 1];
var offset = args.Position - top.GlobalPosition;
if (!top.HasPoint(offset))
var offset = args.Position - top.GlobalPixelPosition;
if (!top.HasPoint(offset / UIScale))
{
RemoveModal(top);
args.Handle();
@@ -178,7 +181,8 @@ namespace Robust.Client.UserInterface
}
var guiArgs = new GUIMouseButtonEventArgs(args.Button, args.DoubleClick, control, Mouse.ButtonMask.None,
args.Position, args.Position - control.GlobalPosition, args.Alt, args.Control, args.Shift,
args.Position / UIScale, args.Position, args.Position / UIScale - control.GlobalPosition,
args.Position - control.GlobalPixelPosition, args.Alt, args.Control, args.Shift,
args.System);
_doMouseGuiInput(control, guiArgs, (c, ev) => c.MouseDown(ev));
@@ -198,7 +202,8 @@ namespace Robust.Client.UserInterface
var guiArgs = new GUIMouseButtonEventArgs(args.Button, args.DoubleClick, _mouseFocused,
Mouse.ButtonMask.None,
args.Position, args.Position - _mouseFocused.GlobalPosition, args.Alt, args.Control, args.Shift,
args.Position / UIScale, args.Position, args.Position / UIScale - _mouseFocused.GlobalPosition,
args.Position - _mouseFocused.GlobalPixelPosition, args.Alt, args.Control, args.Shift,
args.System);
_doMouseGuiInput(_mouseFocused, guiArgs, (c, ev) => c.MouseUp(ev));
@@ -226,10 +231,12 @@ namespace Robust.Client.UserInterface
var target = _mouseFocused ?? newHovered;
if (target != null)
{
var guiArgs = new GUIMouseMoveEventArgs(mouseMoveEventArgs.Relative, mouseMoveEventArgs.Speed,
target,
mouseMoveEventArgs.ButtonMask, mouseMoveEventArgs.Position,
mouseMoveEventArgs.Position - target.GlobalPosition, mouseMoveEventArgs.Alt,
var guiArgs = new GUIMouseMoveEventArgs(mouseMoveEventArgs.Relative / UIScale,
mouseMoveEventArgs.Speed / UIScale, target,
mouseMoveEventArgs.ButtonMask, mouseMoveEventArgs.Position / UIScale, mouseMoveEventArgs.Position,
mouseMoveEventArgs.Position / UIScale - target.GlobalPosition,
mouseMoveEventArgs.Position - target.GlobalPixelPosition,
mouseMoveEventArgs.Alt,
mouseMoveEventArgs.Control, mouseMoveEventArgs.Shift, mouseMoveEventArgs.System);
_doMouseGuiInput(target, guiArgs, (c, ev) => c.MouseMove(ev));
@@ -246,8 +253,10 @@ namespace Robust.Client.UserInterface
args.Handle();
var guiArgs = new GUIMouseWheelEventArgs(args.WheelDirection, control, Mouse.ButtonMask.None, args.Position,
args.Position - control.GlobalPosition, args.Alt, args.Control, args.Shift, args.System);
var guiArgs = new GUIMouseWheelEventArgs(args.WheelDirection, control, Mouse.ButtonMask.None,
args.Position / UIScale, args.Position,
args.Position / UIScale - control.GlobalPosition, args.Position - control.GlobalPixelPosition, args.Alt,
args.Control, args.Shift, args.System);
_doMouseGuiInput(control, guiArgs, (c, ev) => c.MouseWheel(ev), true);
}
@@ -416,12 +425,13 @@ namespace Robust.Client.UserInterface
{
return;
}
var drawHandle = renderHandle.CreateHandleScreen();
_render(drawHandle, RootControl, Vector2.Zero, Color.White, null);
_render(drawHandle, RootControl, Vector2i.Zero, Color.White, null);
}
private static void _render(DrawingHandleScreen handle, Control control, Vector2 position, Color modulate,
private static void _render(DrawingHandleScreen handle, Control control, Vector2i position, Color modulate,
UIBox2i? scissorBox)
{
if (!control.Visible)
@@ -430,7 +440,7 @@ namespace Robust.Client.UserInterface
}
// Manual clip test with scissor region as optimization.
var controlBox = UIBox2i.FromDimensions((Vector2i)position, (Vector2i)control.Size);
var controlBox = UIBox2i.FromDimensions(position, control.PixelSize);
if (scissorBox != null)
{
@@ -468,10 +478,11 @@ namespace Robust.Client.UserInterface
handle.SetScissor(scissorRegion);
}
control.Draw(handle);
foreach (var child in control.Children)
{
_render(handle, child, position + child.Position.Rounded(), modulate, scissorRegion);
_render(handle, child, position + child.PixelPosition, modulate, scissorRegion);
}
if (clip)
@@ -520,19 +531,19 @@ namespace Robust.Client.UserInterface
{
foreach (var child in control.Children.Reverse())
{
if (!child.Visible || (child.RectClipContent && !child.Rect.Contains(position)))
if (!child.Visible || (child.RectClipContent && !child.PixelRect.Contains((Vector2i) position)))
{
continue;
}
var maybeFoundOnChild = _mouseFindControlAtPos(child, position - child.Position);
var maybeFoundOnChild = _mouseFindControlAtPos(child, position - child.PixelPosition);
if (maybeFoundOnChild != null)
{
return maybeFoundOnChild;
}
}
if (control.MouseFilter != Control.MouseFilterMode.Ignore && control.HasPoint(position))
if (control.MouseFilter != Control.MouseFilterMode.Ignore && control.HasPoint(position / UIScale))
{
return control;
}
@@ -540,7 +551,8 @@ namespace Robust.Client.UserInterface
return null;
}
private void _doMouseGuiInput<T>(Control control, T guiEvent, Action<Control, T> action, bool ignoreStop=false)
private static void _doMouseGuiInput<T>(Control control, T guiEvent, Action<Control, T> action,
bool ignoreStop = false)
where T : GUIMouseEventArgs
{
while (control != null)
@@ -556,6 +568,7 @@ namespace Robust.Client.UserInterface
}
guiEvent.RelativePosition += control.Position;
guiEvent.RelativePixelPosition += control.PixelPosition;
control = control.Parent;
guiEvent.SourceControl = control;
}
@@ -597,5 +610,38 @@ namespace Robust.Client.UserInterface
_tooltip.Position = (_tooltip.Position.X, RootControl.Size.Y - _tooltip.Size.Y);
}
}
void IPostInjectInit.PostInject()
{
_configurationManager.RegisterCVar("display.uiScale", 1f, CVar.ARCHIVE, _uiScaleChanged);
}
private void _uiScaleChanged(float newValue)
{
UIScale = newValue;
if (RootControl == null)
{
return;
}
_propagateUIScaleChanged(RootControl);
_updateRootSize();
}
private static void _propagateUIScaleChanged(Control control)
{
control.UIScaleChanged();
foreach (var child in control.Children)
{
_propagateUIScaleChanged(child);
}
}
private void _updateRootSize()
{
RootControl.Size = _displayManager.ScreenSize / UIScale;
}
}
}