mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-09-02 18:17:09 +02:00
416 lines
13 KiB
C#
416 lines
13 KiB
C#
using System;
|
|
using System.Numerics;
|
|
using JetBrains.Annotations;
|
|
using Robust.Shared.Maths;
|
|
|
|
namespace Robust.Client.Graphics
|
|
{
|
|
/// <summary>
|
|
/// This is for drawing modestly fancy boxes using minimal code.
|
|
/// </summary>
|
|
[PublicAPI]
|
|
public abstract class StyleBox
|
|
{
|
|
private float? _contentMarginTopOverride;
|
|
private float? _contentMarginLeftOverride;
|
|
private float? _contentMarginBottomOverride;
|
|
private float? _contentMarginRightOverride;
|
|
|
|
private float _paddingLeft;
|
|
private float _paddingBottom;
|
|
private float _paddingRight;
|
|
private float _paddingTop;
|
|
|
|
protected StyleBox()
|
|
{
|
|
}
|
|
|
|
protected StyleBox(StyleBox other)
|
|
{
|
|
_contentMarginBottomOverride = other._contentMarginBottomOverride;
|
|
_contentMarginTopOverride = other._contentMarginTopOverride;
|
|
_contentMarginLeftOverride = other._contentMarginLeftOverride;
|
|
_contentMarginRightOverride = other._contentMarginRightOverride;
|
|
|
|
_paddingLeft = other._paddingLeft;
|
|
_paddingBottom = other._paddingBottom;
|
|
_paddingRight = other._paddingRight;
|
|
_paddingTop = other._paddingTop;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Minimum size, in virtual pixels.
|
|
/// </summary>
|
|
public Vector2 MinimumSize =>
|
|
new(GetContentMargin(Margin.Left) + GetContentMargin(Margin.Right),
|
|
GetContentMargin(Margin.Top) + GetContentMargin(Margin.Bottom));
|
|
|
|
/// <summary>
|
|
/// Left content margin, in virtual pixels.
|
|
/// </summary>
|
|
public float? ContentMarginLeftOverride
|
|
{
|
|
get => _contentMarginLeftOverride;
|
|
set
|
|
{
|
|
if (value < 0)
|
|
{
|
|
throw new ArgumentOutOfRangeException(nameof(value), value, "Value cannot be less than zero.");
|
|
}
|
|
|
|
_contentMarginLeftOverride = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Top content margin, in virtual pixels.
|
|
/// </summary>
|
|
public float? ContentMarginTopOverride
|
|
{
|
|
get => _contentMarginTopOverride;
|
|
set
|
|
{
|
|
if (value < 0)
|
|
{
|
|
throw new ArgumentOutOfRangeException(nameof(value), value, "Value cannot be less than zero.");
|
|
}
|
|
|
|
_contentMarginTopOverride = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Right content margin, in virtual pixels.
|
|
/// </summary>
|
|
public float? ContentMarginRightOverride
|
|
{
|
|
get => _contentMarginRightOverride;
|
|
set
|
|
{
|
|
if (value < 0)
|
|
{
|
|
throw new ArgumentOutOfRangeException(nameof(value), value, "Value cannot be less than zero.");
|
|
}
|
|
|
|
_contentMarginRightOverride = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bottom content margin, in virtual pixels.
|
|
/// </summary>
|
|
public float? ContentMarginBottomOverride
|
|
{
|
|
get => _contentMarginBottomOverride;
|
|
set
|
|
{
|
|
if (value < 0)
|
|
{
|
|
throw new ArgumentOutOfRangeException(nameof(value), value, "Value cannot be less than zero.");
|
|
}
|
|
|
|
_contentMarginBottomOverride = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Left padding, in virtual pixels.
|
|
/// </summary>
|
|
public float PaddingLeft
|
|
{
|
|
get => _paddingLeft;
|
|
set
|
|
{
|
|
if (value < 0)
|
|
{
|
|
throw new ArgumentOutOfRangeException(nameof(value), value, "Value cannot be less than zero.");
|
|
}
|
|
|
|
_paddingLeft = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bottom padding, in virtual pixels.
|
|
/// </summary>
|
|
public float PaddingBottom
|
|
{
|
|
get => _paddingBottom;
|
|
set
|
|
{
|
|
if (value < 0)
|
|
{
|
|
throw new ArgumentOutOfRangeException(nameof(value), value, "Value cannot be less than zero.");
|
|
}
|
|
|
|
_paddingBottom = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Right padding, in virtual pixels.
|
|
/// </summary>
|
|
public float PaddingRight
|
|
{
|
|
get => _paddingRight;
|
|
set
|
|
{
|
|
if (value < 0)
|
|
{
|
|
throw new ArgumentOutOfRangeException(nameof(value), value, "Value cannot be less than zero.");
|
|
}
|
|
|
|
_paddingRight = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Top padding, in virtual pixels.
|
|
/// </summary>
|
|
public float PaddingTop
|
|
{
|
|
get => _paddingTop;
|
|
set
|
|
{
|
|
if (value < 0)
|
|
{
|
|
throw new ArgumentOutOfRangeException(nameof(value), value, "Value cannot be less than zero.");
|
|
}
|
|
|
|
_paddingTop = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Padding, in virtual pixels.
|
|
/// </summary>
|
|
public Thickness Padding
|
|
{
|
|
set
|
|
{
|
|
PaddingLeft = value.Left;
|
|
PaddingTop = value.Top;
|
|
PaddingRight = value.Right;
|
|
PaddingBottom = value.Bottom;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Draw this style box to the screen at the specified coordinates. This is using physical pixels, not virtual pixels.
|
|
/// </summary>
|
|
/// <param name="handle"></param>
|
|
/// <param name="box"></param>
|
|
public void Draw(DrawingHandleScreen handle, UIBox2 box, float uiScale)
|
|
{
|
|
var left = box.Left + PaddingLeft;
|
|
var top = box.Top + PaddingTop;
|
|
var right = MathF.Max(left, box.Right - PaddingRight);
|
|
var bottom = MathF.Max(top, box.Bottom - PaddingBottom);
|
|
box = new UIBox2(left, top, right, bottom);
|
|
|
|
DoDraw(handle, box, uiScale);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the offset from a margin of the box to where content should actually be drawn. This is in virtual pixels.
|
|
/// </summary>
|
|
/// <exception cref="ArgumentException">
|
|
/// Thrown if <paramref name="margin"/> is a compound is not a single margin flag.
|
|
/// </exception>
|
|
public float GetContentMargin(Margin margin)
|
|
{
|
|
float? marginData;
|
|
switch (margin)
|
|
{
|
|
case Margin.Top:
|
|
marginData = ContentMarginTopOverride;
|
|
break;
|
|
case Margin.Bottom:
|
|
marginData = ContentMarginBottomOverride;
|
|
break;
|
|
case Margin.Right:
|
|
marginData = ContentMarginRightOverride;
|
|
break;
|
|
case Margin.Left:
|
|
marginData = ContentMarginLeftOverride;
|
|
break;
|
|
default:
|
|
throw new ArgumentException("Margin must be a single margin flag.", nameof(margin));
|
|
}
|
|
|
|
var contentMargin = marginData ?? GetDefaultContentMargin(margin);
|
|
return contentMargin + GetPadding(margin);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the padding. This is in virtual pixels.
|
|
/// </summary>
|
|
public float GetPadding(Margin margin)
|
|
{
|
|
switch (margin)
|
|
{
|
|
case Margin.Top:
|
|
return PaddingTop;
|
|
case Margin.Bottom:
|
|
return PaddingBottom;
|
|
case Margin.Right:
|
|
return PaddingRight;
|
|
case Margin.Left:
|
|
return PaddingLeft;
|
|
default:
|
|
throw new ArgumentException("Margin must be a single margin flag.", nameof(margin));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the offsets of the content region of this box when drawn from the given position. Input and
|
|
/// output positions are in real pixels, though virtual pixels can also be used if the ui scale is set to 1.
|
|
/// </summary>
|
|
public Vector2 GetContentOffset(Vector2 basePosition, float uiScale)
|
|
{
|
|
return basePosition + uiScale * new Vector2(GetContentMargin(Margin.Left), GetContentMargin(Margin.Top));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the box considered the "contents" of this style box, when drawn at a specific size. Input and output
|
|
/// boxes are in virtual pixels, though virtual pixels can also be used if the ui scale is set to 1.
|
|
/// </summary>
|
|
public UIBox2 GetContentBox(UIBox2 baseBox, float uiScale)
|
|
{
|
|
var left = baseBox.Left + GetContentMargin(Margin.Left) * uiScale;
|
|
var top = baseBox.Top + GetContentMargin(Margin.Top) * uiScale;
|
|
var right = baseBox.Right - GetContentMargin(Margin.Right) * uiScale;
|
|
var bottom = baseBox.Bottom - GetContentMargin(Margin.Bottom) * uiScale;
|
|
|
|
right = MathF.Max(left, right);
|
|
bottom = MathF.Max(top, bottom);
|
|
|
|
return new UIBox2(left, top, right, bottom);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the draw box, positioned at <paramref name="position"/>,
|
|
/// that envelops a box with the given dimensions perfectly given this box's content margins.
|
|
/// Input and output values are in physical pixels, though virtual pixels can also be used if the ui scale
|
|
/// is set to 1.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// It's basically a reverse <see cref="GetContentBox"/>.
|
|
/// </remarks>
|
|
/// <param name="position">The position at which the new box should be drawn.</param>
|
|
/// <param name="dimensions">The dimensions of the content box inside this new box.</param>
|
|
/// <param name="uiScale">Scales the content margin border size</param>
|
|
/// <returns>
|
|
/// A box that, when ran through <see cref="GetContentBox"/>,
|
|
/// has a content box of size <paramref name="dimensions"/>
|
|
/// </returns>
|
|
public UIBox2 GetEnvelopBox(Vector2 position, Vector2 dimensions, float uiScale)
|
|
{
|
|
return UIBox2.FromDimensions(position, dimensions + MinimumSize * uiScale);
|
|
}
|
|
|
|
public void SetContentMarginOverride(Margin margin, float value)
|
|
{
|
|
if ((margin & Margin.Left) != 0)
|
|
{
|
|
ContentMarginLeftOverride = value;
|
|
}
|
|
|
|
if ((margin & Margin.Top) != 0)
|
|
{
|
|
ContentMarginTopOverride = value;
|
|
}
|
|
|
|
if ((margin & Margin.Right) != 0)
|
|
{
|
|
ContentMarginRightOverride = value;
|
|
}
|
|
|
|
if ((margin & Margin.Bottom) != 0)
|
|
{
|
|
ContentMarginBottomOverride = value;
|
|
}
|
|
}
|
|
|
|
public void SetPadding(Margin margin, float value)
|
|
{
|
|
if ((margin & Margin.Left) != 0)
|
|
{
|
|
PaddingLeft = value;
|
|
}
|
|
|
|
if ((margin & Margin.Top) != 0)
|
|
{
|
|
PaddingTop = value;
|
|
}
|
|
|
|
if ((margin & Margin.Right) != 0)
|
|
{
|
|
PaddingRight = value;
|
|
}
|
|
|
|
if ((margin & Margin.Bottom) != 0)
|
|
{
|
|
PaddingBottom = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Draw the style box in the given UI Box.
|
|
/// </summary>
|
|
/// <param name="handle"></param>
|
|
/// <param name="box">The area to draw in, in physical pixels</param>
|
|
/// <param name="uiScale">The UI scale to use.</param>
|
|
protected abstract void DoDraw(DrawingHandleScreen handle, UIBox2 box, float uiScale);
|
|
|
|
protected virtual float GetDefaultContentMargin(Margin margin)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Describes margins of a style box.
|
|
/// </summary>
|
|
[Flags]
|
|
public enum Margin : byte
|
|
{
|
|
None = 0,
|
|
|
|
/// <summary>
|
|
/// The top margin.
|
|
/// </summary>
|
|
Top = 1,
|
|
|
|
/// <summary>
|
|
/// The bottom margin.
|
|
/// </summary>
|
|
Bottom = 2,
|
|
|
|
/// <summary>
|
|
/// The right margin.
|
|
/// </summary>
|
|
Right = 4,
|
|
|
|
/// <summary>
|
|
/// The left margin.
|
|
/// </summary>
|
|
Left = 8,
|
|
|
|
/// <summary>
|
|
/// All margins.
|
|
/// </summary>
|
|
All = Top | Bottom | Right | Left,
|
|
|
|
/// <summary>
|
|
/// The vertical margins.
|
|
/// </summary>
|
|
Vertical = Top | Bottom,
|
|
|
|
/// <summary>
|
|
/// The horizontal margins.
|
|
/// </summary>
|
|
Horizontal = Left | Right,
|
|
}
|
|
}
|
|
}
|