using System; using Robust.Shared.Maths; using Robust.Shared.Utility; namespace Robust.Client.Graphics.Drawing { /// /// Style box based on a 9-patch texture. /// public class StyleBoxTexture : StyleBox { public StyleBoxTexture() { } /// /// Clones a stylebox so it can be separately modified. /// public StyleBoxTexture(StyleBoxTexture copy) : base(copy) { PatchMarginTop = copy.PatchMarginTop; PatchMarginLeft = copy.PatchMarginLeft; PatchMarginBottom = copy.PatchMarginBottom; PatchMarginRight = copy.PatchMarginRight; ExpandMarginLeft = copy.ExpandMarginLeft; ExpandMarginTop = copy.ExpandMarginTop; ExpandMarginBottom = copy.ExpandMarginBottom; ExpandMarginRight = copy.ExpandMarginRight; Texture = copy.Texture; Modulate = copy.Modulate; } public float ExpandMarginLeft { get; set; } public float ExpandMarginTop { get; set; } public float ExpandMarginBottom { get; set; } public float ExpandMarginRight { get; set; } public StretchMode Mode { get; set; } = StretchMode.Stretch; private float _patchMarginLeft; public float PatchMarginLeft { get => _patchMarginLeft; set { if (value < 0) { throw new ArgumentOutOfRangeException(nameof(value), value, "Value cannot be less than zero."); } _patchMarginLeft = value; } } private float _patchMarginRight; public float PatchMarginRight { get => _patchMarginRight; set { if (value < 0) { throw new ArgumentOutOfRangeException(nameof(value), value, "Value cannot be less than zero."); } _patchMarginRight = value; } } private float _patchMarginTop; public float PatchMarginTop { get => _patchMarginTop; set { if (value < 0) { throw new ArgumentOutOfRangeException(nameof(value), value, "Value cannot be less than zero."); } _patchMarginTop = value; } } private float _patchMarginBottom; public float PatchMarginBottom { get => _patchMarginBottom; set { if (value < 0) { throw new ArgumentOutOfRangeException(nameof(value), value, "Value cannot be less than zero."); } _patchMarginBottom = value; } } public Color Modulate { get; set; } = Color.White; public Texture Texture { get; set; } [Obsolete("Use SetPatchMargin")] public void SetMargin(Margin margin, float value) { SetPatchMargin(margin, value); } public void SetPatchMargin(Margin margin, float value) { if ((margin & Margin.Top) != 0) { PatchMarginTop = value; } if ((margin & Margin.Bottom) != 0) { PatchMarginBottom = value; } if ((margin & Margin.Right) != 0) { PatchMarginRight = value; } if ((margin & Margin.Left) != 0) { PatchMarginLeft = value; } } public void SetExpandMargin(Margin margin, float value) { if ((margin & Margin.Top) != 0) { ExpandMarginTop = value; } if ((margin & Margin.Bottom) != 0) { ExpandMarginBottom = value; } if ((margin & Margin.Right) != 0) { ExpandMarginRight = value; } if ((margin & Margin.Left) != 0) { ExpandMarginLeft = value; } } protected override void DoDraw(DrawingHandleScreen handle, UIBox2 box) { box = new UIBox2( box.Left - ExpandMarginLeft, box.Top - ExpandMarginTop, box.Right + ExpandMarginRight, box.Bottom + ExpandMarginBottom); if (PatchMarginLeft > 0) { if (PatchMarginTop > 0) { // Draw top left var topLeftBox = new UIBox2(0, 0, PatchMarginLeft, PatchMarginTop) .Translated(box.TopLeft); handle.DrawTextureRectRegion(Texture, topLeftBox, new UIBox2(0, 0, PatchMarginLeft, PatchMarginTop), Modulate); } { // Draw left var leftBox = new UIBox2(0, PatchMarginTop, PatchMarginLeft, box.Height - PatchMarginBottom) .Translated(box.TopLeft); DrawStretchingArea(handle, leftBox, new UIBox2(0, PatchMarginTop, PatchMarginLeft, Texture.Height - PatchMarginBottom)); } if (PatchMarginBottom > 0) { // Draw bottom left var bottomLeftBox = new UIBox2(0, box.Height - PatchMarginBottom, PatchMarginLeft, box.Height) .Translated(box.TopLeft); handle.DrawTextureRectRegion(Texture, bottomLeftBox, new UIBox2(0, Texture.Height - PatchMarginBottom, PatchMarginLeft, Texture.Height), Modulate); } } if (PatchMarginRight > 0) { if (PatchMarginTop > 0) { // Draw top right var topRightBox = new UIBox2(box.Width - PatchMarginRight, 0, box.Width, PatchMarginTop) .Translated(box.TopLeft); handle.DrawTextureRectRegion(Texture, topRightBox, new UIBox2(Texture.Width - PatchMarginRight, 0, Texture.Width, PatchMarginTop), Modulate); } { // Draw right var rightBox = new UIBox2(box.Width - PatchMarginRight, PatchMarginTop, box.Width, box.Height - PatchMarginBottom) .Translated(box.TopLeft); DrawStretchingArea(handle, rightBox, new UIBox2(Texture.Width - PatchMarginRight, PatchMarginTop, Texture.Width, Texture.Height - PatchMarginBottom)); } if (PatchMarginBottom > 0) { // Draw bottom right var bottomRightBox = new UIBox2(box.Width - PatchMarginRight, box.Height - PatchMarginBottom, box.Width, box.Height) .Translated(box.TopLeft); handle.DrawTextureRectRegion(Texture, bottomRightBox, new UIBox2(Texture.Width - PatchMarginRight, Texture.Height - PatchMarginBottom, Texture.Width, Texture.Height), Modulate); } } if (PatchMarginTop > 0) { // Draw top var topBox = new UIBox2(PatchMarginLeft, 0, box.Width - PatchMarginRight, PatchMarginTop) .Translated(box.TopLeft); DrawStretchingArea(handle, topBox, new UIBox2(PatchMarginLeft, 0, Texture.Width - PatchMarginRight, PatchMarginTop)); } if (PatchMarginBottom > 0) { // Draw bottom var bottomBox = new UIBox2(PatchMarginLeft, box.Height - PatchMarginBottom, box.Width - PatchMarginRight, box.Height) .Translated(box.TopLeft); DrawStretchingArea(handle, bottomBox, new UIBox2(PatchMarginLeft, Texture.Height - PatchMarginBottom, Texture.Width - PatchMarginRight, Texture.Height)); } // Draw center { var centerBox = new UIBox2(PatchMarginLeft, PatchMarginTop, box.Width - PatchMarginRight, box.Height - PatchMarginBottom).Translated(box.TopLeft); DrawStretchingArea(handle, centerBox, new UIBox2(PatchMarginLeft, PatchMarginTop, Texture.Width - PatchMarginRight, Texture.Height - PatchMarginBottom)); } } private void DrawStretchingArea(DrawingHandleScreen handle, UIBox2 area, UIBox2 texCoords) { if (Mode == StretchMode.Stretch) { handle.DrawTextureRectRegion(Texture, area, texCoords, Modulate); return; } DebugTools.Assert(Mode == StretchMode.Tile); // TODO: this is an insanely expensive way to do tiling, seriously. // This should 100% be implemented in a shader instead. var texWidth = texCoords.Width; var texHeight = texCoords.Height; for (var x = area.Left; area.Right - x > 0; x += texWidth) { for (var y = area.Top; area.Bottom - y > 0; y += texHeight) { var w = Math.Min(area.Right - x, texWidth); var h = Math.Min(area.Bottom - y, texHeight); handle.DrawTextureRectRegion( Texture, UIBox2.FromDimensions(x, y, w, h), UIBox2.FromDimensions(texCoords.Left, texCoords.Top, w, h), Modulate); } } } protected override float GetDefaultContentMargin(Margin margin) { switch (margin) { case Margin.Top: return PatchMarginTop; case Margin.Bottom: return PatchMarginBottom; case Margin.Right: return PatchMarginRight; case Margin.Left: return PatchMarginLeft; default: throw new ArgumentOutOfRangeException(nameof(margin), margin, null); } } /// /// Specifies how to stretch the sides and center of the style box. /// public enum StretchMode { Stretch, Tile, } } }