Rework chrome.yaml format and panel rendering.

This commit is contained in:
Paul Chote
2019-12-20 23:02:10 +00:00
committed by reaperrr
parent 0b8a367867
commit f3d7bf403e
13 changed files with 2766 additions and 3512 deletions

View File

@@ -9,23 +9,52 @@
*/ */
#endregion #endregion
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using OpenRA.FileSystem; using OpenRA.FileSystem;
using OpenRA.Primitives;
namespace OpenRA.Graphics namespace OpenRA.Graphics
{ {
[Flags]
public enum PanelSides
{
Left = 1,
Top = 2,
Right = 4,
Bottom = 8,
Center = 16,
Edges = Left | Top | Right | Bottom,
All = Edges | Center,
}
public static class PanelSidesExts
{
public static bool HasSide(this PanelSides self, PanelSides m)
{
// PERF: Enum.HasFlag is slower and requires allocations.
return (self & m) == m;
}
}
public static class ChromeProvider public static class ChromeProvider
{ {
struct Collection public class Collection
{ {
public string Src; public readonly string Image = null;
public Dictionary<string, MappedImage> Regions; public readonly int[] PanelRegion = null;
public readonly PanelSides PanelSides = PanelSides.All;
public readonly Dictionary<string, Rectangle> Regions = new Dictionary<string, Rectangle>();
} }
public static IReadOnlyDictionary<string, Collection> Collections { get; private set; }
static Dictionary<string, Collection> collections; static Dictionary<string, Collection> collections;
static Dictionary<string, Sheet> cachedSheets; static Dictionary<string, Sheet> cachedSheets;
static Dictionary<string, Dictionary<string, Sprite>> cachedSprites; static Dictionary<string, Dictionary<string, Sprite>> cachedSprites;
static Dictionary<string, Sprite[]> cachedPanelSprites;
static IReadOnlyFileSystem fileSystem; static IReadOnlyFileSystem fileSystem;
public static void Initialize(ModData modData) public static void Initialize(ModData modData)
@@ -36,12 +65,16 @@ namespace OpenRA.Graphics
collections = new Dictionary<string, Collection>(); collections = new Dictionary<string, Collection>();
cachedSheets = new Dictionary<string, Sheet>(); cachedSheets = new Dictionary<string, Sheet>();
cachedSprites = new Dictionary<string, Dictionary<string, Sprite>>(); cachedSprites = new Dictionary<string, Dictionary<string, Sprite>>();
cachedPanelSprites = new Dictionary<string, Sprite[]>();
Collections = new ReadOnlyDictionary<string, Collection>(collections);
var chrome = MiniYaml.Merge(modData.Manifest.Chrome var chrome = MiniYaml.Merge(modData.Manifest.Chrome
.Select(s => MiniYaml.FromStream(fileSystem.Open(s), s))); .Select(s => MiniYaml.FromStream(fileSystem.Open(s), s)));
foreach (var c in chrome) foreach (var c in chrome)
LoadCollection(c.Key, c.Value); if (!c.Key.StartsWith("^", StringComparison.Ordinal))
LoadCollection(c.Key, c.Value);
} }
public static void Deinitialize() public static void Deinitialize()
@@ -53,37 +86,32 @@ namespace OpenRA.Graphics
collections = null; collections = null;
cachedSheets = null; cachedSheets = null;
cachedSprites = null; cachedSprites = null;
} cachedPanelSprites = null;
public static void Save(string file)
{
var root = new List<MiniYamlNode>();
foreach (var kv in collections)
root.Add(new MiniYamlNode(kv.Key, SaveCollection(kv.Value)));
root.WriteToFile(file);
}
static MiniYaml SaveCollection(Collection collection)
{
var root = new List<MiniYamlNode>();
foreach (var kv in collection.Regions)
root.Add(new MiniYamlNode(kv.Key, kv.Value.Save(collection.Src)));
return new MiniYaml(collection.Src, root);
} }
static void LoadCollection(string name, MiniYaml yaml) static void LoadCollection(string name, MiniYaml yaml)
{ {
if (Game.ModData.LoadScreen != null) if (Game.ModData.LoadScreen != null)
Game.ModData.LoadScreen.Display(); Game.ModData.LoadScreen.Display();
var collection = new Collection()
{
Src = yaml.Value,
Regions = yaml.Nodes.ToDictionary(n => n.Key, n => new MappedImage(yaml.Value, n.Value))
};
collections.Add(name, collection); collections.Add(name, FieldLoader.Load<Collection>(yaml));
}
static Sheet SheetForCollection(Collection c)
{
// Cached sheet
Sheet sheet;
if (cachedSheets.ContainsKey(c.Image))
sheet = cachedSheets[c.Image];
else
{
using (var stream = fileSystem.Open(c.Image))
sheet = new Sheet(SheetType.BGRA, stream);
cachedSheets.Add(c.Image, sheet);
}
return sheet;
} }
public static Sprite GetImage(string collectionName, string imageName) public static Sprite GetImage(string collectionName, string imageName)
@@ -104,33 +132,112 @@ namespace OpenRA.Graphics
return null; return null;
} }
MappedImage mi; Rectangle mi;
if (!collection.Regions.TryGetValue(imageName, out mi)) if (!collection.Regions.TryGetValue(imageName, out mi))
return null; return null;
// Cached sheet
Sheet sheet;
if (cachedSheets.ContainsKey(mi.Src))
sheet = cachedSheets[mi.Src];
else
{
using (var stream = fileSystem.Open(mi.Src))
sheet = new Sheet(SheetType.BGRA, stream);
cachedSheets.Add(mi.Src, sheet);
}
// Cache the sprite // Cache the sprite
var sheet = SheetForCollection(collection);
if (cachedCollection == null) if (cachedCollection == null)
{ {
cachedCollection = new Dictionary<string, Sprite>(); cachedCollection = new Dictionary<string, Sprite>();
cachedSprites.Add(collectionName, cachedCollection); cachedSprites.Add(collectionName, cachedCollection);
} }
var image = mi.GetImage(sheet); var image = new Sprite(sheet, mi, TextureChannel.RGBA);
cachedCollection.Add(imageName, image); cachedCollection.Add(imageName, image);
return image; return image;
} }
public static Sprite[] GetPanelImages(string collectionName)
{
if (string.IsNullOrEmpty(collectionName))
return null;
// Cached sprite
Sprite[] cachedSprites;
if (cachedPanelSprites.TryGetValue(collectionName, out cachedSprites))
return cachedSprites;
Collection collection;
if (!collections.TryGetValue(collectionName, out collection))
{
Log.Write("debug", "Could not find collection '{0}'", collectionName);
return null;
}
Sprite[] sprites;
if (collection.PanelRegion != null)
{
if (collection.PanelRegion.Length != 8)
{
Log.Write("debug", "Collection '{0}' does not define a valid PanelRegion", collectionName);
return null;
}
// Cache the sprites
var sheet = SheetForCollection(collection);
var pr = collection.PanelRegion;
var ps = collection.PanelSides;
var sides = new[]
{
Pair.New(PanelSides.Top | PanelSides.Left, new Rectangle(pr[0], pr[1], pr[2], pr[3])),
Pair.New(PanelSides.Top, new Rectangle(pr[0] + pr[2], pr[1], pr[4], pr[3])),
Pair.New(PanelSides.Top | PanelSides.Right, new Rectangle(pr[0] + pr[2] + pr[4], pr[1], pr[6], pr[3])),
Pair.New(PanelSides.Left, new Rectangle(pr[0], pr[1] + pr[3], pr[2], pr[5])),
Pair.New(PanelSides.Center, new Rectangle(pr[0] + pr[2], pr[1] + pr[3], pr[4], pr[5])),
Pair.New(PanelSides.Right, new Rectangle(pr[0] + pr[2] + pr[4], pr[1] + pr[3], pr[6], pr[5])),
Pair.New(PanelSides.Bottom | PanelSides.Left, new Rectangle(pr[0], pr[1] + pr[3] + pr[5], pr[2], pr[7])),
Pair.New(PanelSides.Bottom, new Rectangle(pr[0] + pr[2], pr[1] + pr[3] + pr[5], pr[4], pr[7])),
Pair.New(PanelSides.Bottom | PanelSides.Right, new Rectangle(pr[0] + pr[2] + pr[4], pr[1] + pr[3] + pr[5], pr[6], pr[7]))
};
sprites = sides.Select(x => ps.HasSide(x.First) ? new Sprite(sheet, x.Second, TextureChannel.RGBA) : null)
.ToArray();
}
else
{
// Support manual definitions for unusual dialog layouts
sprites = new[]
{
GetImage(collectionName, "corner-tl"),
GetImage(collectionName, "border-t"),
GetImage(collectionName, "corner-tr"),
GetImage(collectionName, "border-l"),
GetImage(collectionName, "background"),
GetImage(collectionName, "border-r"),
GetImage(collectionName, "corner-bl"),
GetImage(collectionName, "border-b"),
GetImage(collectionName, "corner-br")
};
}
cachedPanelSprites.Add(collectionName, sprites);
return sprites;
}
public static Size GetMinimumPanelSize(string collectionName)
{
if (string.IsNullOrEmpty(collectionName))
return new Size(0, 0);
Collection collection;
if (!collections.TryGetValue(collectionName, out collection))
{
Log.Write("debug", "Could not find collection '{0}'", collectionName);
return new Size(0, 0);
}
if (collection.PanelRegion == null || collection.PanelRegion.Length != 8)
{
Log.Write("debug", "Collection '{0}' does not define a valid PanelRegion", collectionName);
return new Size(0, 0);
}
var pr = collection.PanelRegion;
return new Size(pr[2] + pr[6], pr[3] + pr[7]);
}
} }
} }

View File

@@ -1,44 +0,0 @@
#region Copyright & License Information
/*
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System.Collections.Generic;
using OpenRA.Primitives;
namespace OpenRA.Graphics
{
class MappedImage
{
readonly Rectangle rect = Rectangle.Empty;
public readonly string Src;
public MappedImage(string defaultSrc, MiniYaml info)
{
FieldLoader.LoadField(this, "rect", info.Value);
FieldLoader.Load(this, info);
if (Src == null)
Src = defaultSrc;
}
public Sprite GetImage(Sheet s)
{
return new Sprite(s, rect, TextureChannel.RGBA);
}
public MiniYaml Save(string defaultSrc)
{
var root = new List<MiniYamlNode>();
if (defaultSrc != Src)
root.Add(new MiniYamlNode("Src", Src));
return new MiniYaml(FieldSaver.FormatValue(this, GetType().GetField("rect")), root);
}
}
}

View File

@@ -26,8 +26,7 @@ namespace OpenRA.Mods.Cnc
Dictionary<string, string> loadInfo; Dictionary<string, string> loadInfo;
Stopwatch loadTimer = Stopwatch.StartNew(); Stopwatch loadTimer = Stopwatch.StartNew();
Sheet sheet; Sheet sheet;
Sprite borderTop, borderBottom, borderLeft, borderRight, Sprite[] border;
cornerTopLeft, cornerTopRight, cornerBottomLeft, cornerBottomRight;
int loadTick; int loadTick;
float2 nodPos, gdiPos, evaPos; float2 nodPos, gdiPos, evaPos;
Sprite nodLogo, gdiLogo, evaLogo, brightBlock, dimBlock; Sprite nodLogo, gdiLogo, evaLogo, brightBlock, dimBlock;
@@ -51,14 +50,18 @@ namespace OpenRA.Mods.Cnc
var res = r.Resolution; var res = r.Resolution;
bounds = new Rectangle(0, 0, res.Width, res.Height); bounds = new Rectangle(0, 0, res.Width, res.Height);
borderTop = new Sprite(sheet, new Rectangle(161, 128, 62, 33), TextureChannel.RGBA); border = new[]
borderBottom = new Sprite(sheet, new Rectangle(161, 223, 62, 33), TextureChannel.RGBA); {
borderLeft = new Sprite(sheet, new Rectangle(128, 161, 33, 62), TextureChannel.RGBA); new Sprite(sheet, new Rectangle(128, 128, 33, 33), TextureChannel.RGBA),
borderRight = new Sprite(sheet, new Rectangle(223, 161, 33, 62), TextureChannel.RGBA); new Sprite(sheet, new Rectangle(161, 128, 62, 33), TextureChannel.RGBA),
cornerTopLeft = new Sprite(sheet, new Rectangle(128, 128, 33, 33), TextureChannel.RGBA); new Sprite(sheet, new Rectangle(223, 128, 33, 33), TextureChannel.RGBA),
cornerTopRight = new Sprite(sheet, new Rectangle(223, 128, 33, 33), TextureChannel.RGBA); new Sprite(sheet, new Rectangle(128, 161, 33, 62), TextureChannel.RGBA),
cornerBottomLeft = new Sprite(sheet, new Rectangle(128, 223, 33, 33), TextureChannel.RGBA); null,
cornerBottomRight = new Sprite(sheet, new Rectangle(223, 223, 33, 33), TextureChannel.RGBA); new Sprite(sheet, new Rectangle(223, 161, 33, 62), TextureChannel.RGBA),
new Sprite(sheet, new Rectangle(128, 223, 33, 33), TextureChannel.RGBA),
new Sprite(sheet, new Rectangle(161, 223, 62, 33), TextureChannel.RGBA),
new Sprite(sheet, new Rectangle(223, 223, 33, 33), TextureChannel.RGBA)
};
nodLogo = new Sprite(sheet, new Rectangle(0, 256, 256, 256), TextureChannel.RGBA); nodLogo = new Sprite(sheet, new Rectangle(0, 256, 256, 256), TextureChannel.RGBA);
gdiLogo = new Sprite(sheet, new Rectangle(256, 256, 256, 256), TextureChannel.RGBA); gdiLogo = new Sprite(sheet, new Rectangle(256, 256, 256, 256), TextureChannel.RGBA);
@@ -91,10 +94,7 @@ namespace OpenRA.Mods.Cnc
r.RgbaSpriteRenderer.DrawSprite(nodLogo, nodPos); r.RgbaSpriteRenderer.DrawSprite(nodLogo, nodPos);
r.RgbaSpriteRenderer.DrawSprite(evaLogo, evaPos); r.RgbaSpriteRenderer.DrawSprite(evaLogo, evaPos);
WidgetUtils.DrawPanelPartial(bounds, PanelSides.Edges, WidgetUtils.DrawPanel(bounds, border);
borderTop, borderBottom, borderLeft, borderRight,
cornerTopLeft, cornerTopRight, cornerBottomLeft, cornerBottomRight,
null);
var barY = bounds.Height - 78; var barY = bounds.Height - 78;
// The fonts dictionary may change when switching between the mod and content installer // The fonts dictionary may change when switching between the mod and content installer

View File

@@ -0,0 +1,215 @@
#region Copyright & License Information
/*
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using OpenRA.Graphics;
using OpenRA.Mods.Common.Widgets;
namespace OpenRA.Mods.Common.UpdateRules.Rules
{
public class ReformatChromeProvider : UpdateRule
{
public override string Name { get { return "Reformat UI image definitions."; } }
public override string Description
{
get
{
return "The format of the chrome.yaml file defining image regions for the UI has\n" +
"changed to support additional metadata fields. ";
}
}
readonly List<string> overrideLocations = new List<string>();
readonly List<string> panelLocations = new List<string>();
public override IEnumerable<string> AfterUpdate(ModData modData)
{
if (overrideLocations.Any())
yield return "Region-specific image overrides are no longer supported. The following definitions must be replaced:\n" +
UpdateUtils.FormatMessageList(overrideLocations);
if (panelLocations.Any())
yield return "The following definitions appear to be panels, but could not be converted to the new PanelRegion format.\n" +
"You may wish to define PanelRegion/PanelSides manually to reduce duplication:\n" +
UpdateUtils.FormatMessageList(panelLocations);
overrideLocations.Clear();
panelLocations.Clear();
}
readonly string[] edgeKeys =
{
"corner-tl", "corner-tr", "corner-bl", "corner-br",
"border-t", "border-b", "border-l", "border-r"
};
bool ExtractPanelDefinition(MiniYamlNode chromeProviderNode, MiniYamlNode regionsNode)
{
var cNode = regionsNode.LastChildMatching("background");
var hasCenter = cNode != null;
var hasEdges = edgeKeys.Any(k => regionsNode.LastChildMatching(k) != null);
// Not a panel
if (!hasCenter && !hasEdges)
return true;
// Panels may define just the background
if (hasCenter && !hasEdges)
{
var bgRect = cNode.NodeValue<Rectangle>();
chromeProviderNode.AddNode("PanelRegion", new[]
{
bgRect.X, bgRect.Y,
0, 0,
bgRect.Width, bgRect.Height,
0, 0
});
chromeProviderNode.AddNode("PanelSides", PanelSides.Center);
regionsNode.RemoveNode(cNode);
return true;
}
// Panels may define just the edges, or edges plus background
var tlNode = regionsNode.LastChildMatching("corner-tl");
if (tlNode == null)
return false;
var tlRect = tlNode.NodeValue<Rectangle>();
var tNode = regionsNode.LastChildMatching("border-t");
if (tNode == null)
return false;
var tRect = tNode.NodeValue<Rectangle>();
if (tRect.Left != tlRect.Right || tRect.Top != tlRect.Top || tRect.Bottom != tlRect.Bottom)
return false;
var trNode = regionsNode.LastChildMatching("corner-tr");
if (trNode == null)
return false;
var trRect = trNode.NodeValue<Rectangle>();
if (trRect.Left != tRect.Right || trRect.Top != tRect.Top || trRect.Bottom != tRect.Bottom)
return false;
var lNode = regionsNode.LastChildMatching("border-l");
if (lNode == null)
return false;
var lRect = lNode.NodeValue<Rectangle>();
if (lRect.Left != tlRect.Left || lRect.Top != tlRect.Bottom || lRect.Right != tlRect.Right)
return false;
var rNode = regionsNode.LastChildMatching("border-r");
if (rNode == null)
return false;
var rRect = rNode.NodeValue<Rectangle>();
if (rRect.Left != trRect.Left || rRect.Top != trRect.Bottom || rRect.Bottom != lRect.Bottom || rRect.Right != trRect.Right)
return false;
var blNode = regionsNode.LastChildMatching("corner-bl");
if (blNode == null)
return false;
var blRect = blNode.NodeValue<Rectangle>();
if (blRect.Left != lRect.Left || blRect.Top != lRect.Bottom || blRect.Right != lRect.Right)
return false;
var bNode = regionsNode.LastChildMatching("border-b");
if (bNode == null)
return false;
var bRect = bNode.NodeValue<Rectangle>();
if (bRect.Left != blRect.Right || bRect.Top != blRect.Top || bRect.Bottom != blRect.Bottom || bRect.Right != tRect.Right)
return false;
var brNode = regionsNode.LastChildMatching("corner-br");
if (brNode == null)
return false;
var brRect = brNode.NodeValue<Rectangle>();
if (brRect.Left != bRect.Right || brRect.Top != bRect.Top || brRect.Bottom != bRect.Bottom || brRect.Right != rRect.Right)
return false;
// Background definition may be omitted
if (hasCenter)
{
var bgRect = cNode.NodeValue<Rectangle>();
if (bgRect.Left != lRect.Right || bgRect.Top != lRect.Top || bgRect.Bottom != lRect.Bottom || bgRect.Right != tRect.Right)
return false;
}
// Define the short-form panel region
chromeProviderNode.AddNode("PanelRegion", new[]
{
tlRect.X, tlRect.Y,
tlRect.Width, tlRect.Height,
trRect.Left - tlRect.Right, blRect.Top - tlRect.Bottom,
brRect.Width, brRect.Height
});
if (!hasCenter)
chromeProviderNode.AddNode("PanelSides", PanelSides.Edges);
// Remove the now redundant regions
regionsNode.RemoveNode(tlNode);
regionsNode.RemoveNode(tNode);
regionsNode.RemoveNode(trNode);
regionsNode.RemoveNode(lNode);
regionsNode.RemoveNode(rNode);
regionsNode.RemoveNode(blNode);
regionsNode.RemoveNode(bNode);
regionsNode.RemoveNode(brNode);
if (cNode != null)
regionsNode.RemoveNode(cNode);
return true;
}
public override IEnumerable<string> UpdateChromeProviderNode(ModData modData, MiniYamlNode chromeProviderNode)
{
// Migrate image rectangles
var regionsNode = new MiniYamlNode("Regions", "");
foreach (var n in chromeProviderNode.Value.Nodes)
{
if (n.Key == "Inherits")
continue;
// Reformat region as a list
regionsNode.AddNode(n.Key, n.NodeValue<int[]>());
if (n.Value.Nodes.Any())
overrideLocations.Add("{0}.{1} ({2})".F(chromeProviderNode.Key, n.Key, chromeProviderNode.Location.Filename));
}
chromeProviderNode.Value.Nodes.RemoveAll(n => n.Key != "Inherits");
// Migrate image definition
chromeProviderNode.AddNode(new MiniYamlNode("Image", chromeProviderNode.Value.Value));
chromeProviderNode.Value.Value = "";
if (!ExtractPanelDefinition(chromeProviderNode, regionsNode))
panelLocations.Add("{0} ({1})".F(chromeProviderNode.Key, chromeProviderNode.Location.Filename));
if (regionsNode.Value.Nodes.Any())
chromeProviderNode.AddNode(regionsNode);
yield break;
}
}
}

View File

@@ -30,6 +30,7 @@ namespace OpenRA.Mods.Common.UpdateRules
public virtual IEnumerable<string> UpdateWeaponNode(ModData modData, MiniYamlNode weaponNode) { yield break; } public virtual IEnumerable<string> UpdateWeaponNode(ModData modData, MiniYamlNode weaponNode) { yield break; }
public virtual IEnumerable<string> UpdateChromeNode(ModData modData, MiniYamlNode chromeNode) { yield break; } public virtual IEnumerable<string> UpdateChromeNode(ModData modData, MiniYamlNode chromeNode) { yield break; }
public virtual IEnumerable<string> UpdateTilesetNode(ModData modData, MiniYamlNode tilesetNode) { yield break; } public virtual IEnumerable<string> UpdateTilesetNode(ModData modData, MiniYamlNode tilesetNode) { yield break; }
public virtual IEnumerable<string> UpdateChromeProviderNode(ModData modData, MiniYamlNode chromeProviderNode) { yield break; }
public virtual IEnumerable<string> BeforeUpdate(ModData modData) { yield break; } public virtual IEnumerable<string> BeforeUpdate(ModData modData) { yield break; }
public virtual IEnumerable<string> AfterUpdate(ModData modData) { yield break; } public virtual IEnumerable<string> AfterUpdate(ModData modData) { yield break; }

View File

@@ -145,6 +145,7 @@ namespace OpenRA.Mods.Common.UpdateRules
var modWeapons = LoadModYaml(modData, FilterExternalModFiles(modData, modData.Manifest.Weapons, externalFilenames)); var modWeapons = LoadModYaml(modData, FilterExternalModFiles(modData, modData.Manifest.Weapons, externalFilenames));
var modTilesets = LoadModYaml(modData, FilterExternalModFiles(modData, modData.Manifest.TileSets, externalFilenames)); var modTilesets = LoadModYaml(modData, FilterExternalModFiles(modData, modData.Manifest.TileSets, externalFilenames));
var modChromeLayout = LoadModYaml(modData, FilterExternalModFiles(modData, modData.Manifest.ChromeLayout, externalFilenames)); var modChromeLayout = LoadModYaml(modData, FilterExternalModFiles(modData, modData.Manifest.ChromeLayout, externalFilenames));
var modChromeProvider = LoadModYaml(modData, FilterExternalModFiles(modData, modData.Manifest.Chrome, externalFilenames));
// Find and add shared map includes // Find and add shared map includes
foreach (var package in modData.MapCache.EnumerateMapPackagesWithoutCaching()) foreach (var package in modData.MapCache.EnumerateMapPackagesWithoutCaching())
@@ -174,12 +175,14 @@ namespace OpenRA.Mods.Common.UpdateRules
manualSteps.AddRange(ApplyTopLevelTransform(modData, modWeapons, rule.UpdateWeaponNode)); manualSteps.AddRange(ApplyTopLevelTransform(modData, modWeapons, rule.UpdateWeaponNode));
manualSteps.AddRange(ApplyTopLevelTransform(modData, modTilesets, rule.UpdateTilesetNode)); manualSteps.AddRange(ApplyTopLevelTransform(modData, modTilesets, rule.UpdateTilesetNode));
manualSteps.AddRange(ApplyChromeTransform(modData, modChromeLayout, rule.UpdateChromeNode)); manualSteps.AddRange(ApplyChromeTransform(modData, modChromeLayout, rule.UpdateChromeNode));
manualSteps.AddRange(ApplyTopLevelTransform(modData, modChromeProvider, rule.UpdateChromeProviderNode));
manualSteps.AddRange(rule.AfterUpdate(modData)); manualSteps.AddRange(rule.AfterUpdate(modData));
files = modRules.ToList(); files = modRules.ToList();
files.AddRange(modWeapons); files.AddRange(modWeapons);
files.AddRange(modTilesets); files.AddRange(modTilesets);
files.AddRange(modChromeLayout); files.AddRange(modChromeLayout);
files.AddRange(modChromeProvider);
return manualSteps; return manualSteps;
} }

View File

@@ -52,7 +52,7 @@ namespace OpenRA.Mods.Common.Widgets
var percentage = GetPercentage(); var percentage = GetPercentage();
WidgetUtils.DrawPanel(Background, rb); WidgetUtils.DrawPanel(Background, rb);
var minBarWidth = (int)(ChromeProvider.GetImage(Bar, "border-l").Size.X + ChromeProvider.GetImage(Bar, "border-r").Size.X); var minBarWidth = ChromeProvider.GetMinimumPanelSize(Bar).Width;
var maxBarWidth = rb.Width - BarMargin.Width * 2; var maxBarWidth = rb.Width - BarMargin.Width * 2;
var barWidth = wasIndeterminate ? maxBarWidth / 4 : percentage * maxBarWidth / 100; var barWidth = wasIndeterminate ? maxBarWidth / 4 : percentage * maxBarWidth / 100;
barWidth = Math.Max(barWidth, minBarWidth); barWidth = Math.Max(barWidth, minBarWidth);

View File

@@ -40,7 +40,9 @@ namespace OpenRA.Mods.Common.Widgets
public static void DrawPanel(string collection, Rectangle bounds) public static void DrawPanel(string collection, Rectangle bounds)
{ {
DrawPanelPartial(collection, bounds, PanelSides.All); var sprites = ChromeProvider.GetPanelImages(collection);
if (sprites != null)
DrawPanel(bounds, sprites);
} }
public static void FillRectWithSprite(Rectangle r, Sprite s) public static void FillRectWithSprite(Rectangle r, Sprite s)
@@ -89,95 +91,70 @@ namespace OpenRA.Mods.Common.Widgets
Game.Renderer.RgbaColorRenderer.FillEllipse(tl, br, c); Game.Renderer.RgbaColorRenderer.FillEllipse(tl, br, c);
} }
public static int[] GetBorderSizes(string collection)
{
var images = new[] { "border-t", "border-b", "border-l", "border-r" };
var ss = images.Select(i => ChromeProvider.GetImage(collection, i)).ToList();
return new[] { (int)ss[0].Size.Y, (int)ss[1].Size.Y, (int)ss[2].Size.X, (int)ss[3].Size.X };
}
static bool HasFlags(this PanelSides a, PanelSides b)
{
// PERF: Enum.HasFlag is slower and requires allocations.
return (a & b) == b;
}
public static Rectangle InflateBy(this Rectangle rect, int l, int t, int r, int b) public static Rectangle InflateBy(this Rectangle rect, int l, int t, int r, int b)
{ {
return Rectangle.FromLTRB(rect.Left - l, rect.Top - t, return Rectangle.FromLTRB(rect.Left - l, rect.Top - t,
rect.Right + r, rect.Bottom + b); rect.Right + r, rect.Bottom + b);
} }
public static void DrawPanelPartial(string collection, Rectangle bounds, PanelSides ps) /// <summary>
/// Fill a rectangle with sprites defining a panel layout.
/// Draw order is center, borders, corners to allow mods to define fancy border and corner overlays.
/// </summary>
/// <param name="bounds">Rectangle to fill.</param>
/// <param name="sprites">Nine sprites defining the panel: TL, T, TR, L, C, R, BL, B, BR.</param>
public static void DrawPanel(Rectangle bounds, Sprite[] sprites)
{ {
DrawPanelPartial(bounds, ps, if (sprites.Length != 9)
ChromeProvider.GetImage(collection, "border-t"), return;
ChromeProvider.GetImage(collection, "border-b"),
ChromeProvider.GetImage(collection, "border-l"),
ChromeProvider.GetImage(collection, "border-r"),
ChromeProvider.GetImage(collection, "corner-tl"),
ChromeProvider.GetImage(collection, "corner-tr"),
ChromeProvider.GetImage(collection, "corner-bl"),
ChromeProvider.GetImage(collection, "corner-br"),
ChromeProvider.GetImage(collection, "background"));
}
public static void DrawPanelPartial(Rectangle bounds, PanelSides ps, var marginTop = sprites[1] == null ? 0 : (int)sprites[1].Size.Y;
Sprite borderTop, var marginLeft = sprites[3] == null ? 0 : (int)sprites[3].Size.X;
Sprite borderBottom, var marginRight = sprites[5] == null ? 0 : (int)sprites[5].Size.X;
Sprite borderLeft, var marginBottom = sprites[7] == null ? 0 : (int)sprites[7].Size.Y;
Sprite borderRight,
Sprite cornerTopLeft,
Sprite cornerTopRight,
Sprite cornerBottomLeft,
Sprite cornerBottomRight,
Sprite background)
{
var marginLeft = borderLeft == null ? 0 : (int)borderLeft.Size.X;
var marginTop = borderTop == null ? 0 : (int)borderTop.Size.Y;
var marginRight = borderRight == null ? 0 : (int)borderRight.Size.X;
var marginBottom = borderBottom == null ? 0 : (int)borderBottom.Size.Y;
var marginWidth = marginRight + marginLeft; var marginWidth = marginRight + marginLeft;
var marginHeight = marginBottom + marginTop; var marginHeight = marginBottom + marginTop;
// Background // Center
if (ps.HasFlags(PanelSides.Center) && background != null) if (sprites[4] != null)
FillRectWithSprite(new Rectangle(bounds.Left + marginLeft, bounds.Top + marginTop, FillRectWithSprite(new Rectangle(bounds.Left + marginLeft, bounds.Top + marginTop,
bounds.Width - marginWidth, bounds.Height - marginHeight), bounds.Width - marginWidth, bounds.Height - marginHeight), sprites[4]);
background);
// Left border // Left edge
if (ps.HasFlags(PanelSides.Left) && borderLeft != null) if (sprites[3] != null)
FillRectWithSprite(new Rectangle(bounds.Left, bounds.Top + marginTop, FillRectWithSprite(new Rectangle(bounds.Left, bounds.Top + marginTop,
marginLeft, bounds.Height - marginHeight), marginLeft, bounds.Height - marginHeight), sprites[3]);
borderLeft);
// Right border // Right edge
if (ps.HasFlags(PanelSides.Right) && borderRight != null) if (sprites[5] != null)
FillRectWithSprite(new Rectangle(bounds.Right - marginRight, bounds.Top + marginTop, FillRectWithSprite(new Rectangle(bounds.Right - marginRight, bounds.Top + marginTop,
marginLeft, bounds.Height - marginHeight), marginLeft, bounds.Height - marginHeight), sprites[5]);
borderRight);
// Top border // Top edge
if (ps.HasFlags(PanelSides.Top) && borderTop != null) if (sprites[1] != null)
FillRectWithSprite(new Rectangle(bounds.Left + marginLeft, bounds.Top, FillRectWithSprite(new Rectangle(bounds.Left + marginLeft, bounds.Top,
bounds.Width - marginWidth, marginTop), bounds.Width - marginWidth, marginTop), sprites[1]);
borderTop);
// Bottom border // Bottom edge
if (ps.HasFlags(PanelSides.Bottom) && borderBottom != null) if (sprites[7] != null)
FillRectWithSprite(new Rectangle(bounds.Left + marginLeft, bounds.Bottom - marginBottom, FillRectWithSprite(new Rectangle(bounds.Left + marginLeft, bounds.Bottom - marginBottom,
bounds.Width - marginWidth, marginTop), bounds.Width - marginWidth, marginTop), sprites[7]);
borderBottom);
if (ps.HasFlags(PanelSides.Left | PanelSides.Top) && cornerTopLeft != null) // Top-left corner
DrawRGBA(cornerTopLeft, new float2(bounds.Left, bounds.Top)); if (sprites[0] != null)
if (ps.HasFlags(PanelSides.Right | PanelSides.Top) && cornerTopRight != null) DrawRGBA(sprites[0], new float2(bounds.Left, bounds.Top));
DrawRGBA(cornerTopRight, new float2(bounds.Right - cornerTopRight.Size.X, bounds.Top));
if (ps.HasFlags(PanelSides.Left | PanelSides.Bottom) && cornerBottomLeft != null) // Top-right corner
DrawRGBA(cornerBottomLeft, new float2(bounds.Left, bounds.Bottom - cornerBottomLeft.Size.Y)); if (sprites[2] != null)
if (ps.HasFlags(PanelSides.Right | PanelSides.Bottom) && cornerBottomRight != null) DrawRGBA(sprites[2], new float2(bounds.Right - sprites[2].Size.X, bounds.Top));
DrawRGBA(cornerBottomRight, new float2(bounds.Right - cornerBottomRight.Size.X, bounds.Bottom - cornerBottomRight.Size.Y));
// Bottom-left corner
if (sprites[6] != null)
DrawRGBA(sprites[6], new float2(bounds.Left, bounds.Bottom - sprites[6].Size.Y));
// Bottom-right corner
if (sprites[8] != null)
DrawRGBA(sprites[8], new float2(bounds.Right - sprites[8].Size.X, bounds.Bottom - sprites[8].Size.Y));
} }
public static string FormatTime(int ticks, int timestep) public static string FormatTime(int ticks, int timestep)
@@ -315,17 +292,4 @@ namespace OpenRA.Mods.Common.Widgets
return lastOutput; return lastOutput;
} }
} }
[Flags]
public enum PanelSides
{
Left = 1,
Top = 2,
Right = 4,
Bottom = 8,
Center = 16,
Edges = Left | Top | Right | Bottom,
All = Edges | Center,
}
} }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,183 +1,136 @@
panel-header: chrome.png panel-header:
background: 3,515,58,58 Image: chrome.png
border-r: 61,515,3,58 PanelRegion: 0, 512, 3, 3, 58, 58, 3, 3
border-l: 0,515,3,58
border-b: 3,573,58,3
border-t: 3,512,58,3
corner-tl: 0,512,3,3
corner-tr: 61,512,3,3
corner-bl: 0,573,3,3
corner-br: 61,573,3,3
panel-bg: chrome.png panel-bg:
background: 67,515,58,58 Image: chrome.png
border-r: 125,515,3,58 PanelRegion: 64, 512, 3, 3, 58, 58, 3, 3
border-l: 64,515,3,58
border-b: 67,573,58,3
border-t: 67,512,58,3
corner-tl: 64,512,3,3
corner-tr: 125,512,3,3
corner-bl: 64,573,3,3
corner-br: 125,573,3,3
panel-thinborder: chrome.png panel-thinborder:
background: 3,515,58,58 Image: chrome.png
border-r: 61,515,2,58 PanelRegion: 1, 513, 2, 2, 58, 58, 2, 2
border-l: 1,515,2,58
border-b: 3,573,58,2
border-t: 3,513,58,2
corner-tl: 1,513,2,2
corner-tr: 61,513,2,2
corner-bl: 1,573,2,2
corner-br: 61,573,2,2
panel-thinborder-light: chrome.png panel-thinborder-light:
background: 643,515,58,58 Image: chrome.png
border-r: 701,515,2,58 PanelRegion: 641, 513, 2, 2, 58, 58, 2, 2
border-l: 641,515,2,58
border-b: 643,573,58,2
border-t: 643,513,58,2
corner-tl: 641,513,2,2
corner-tr: 701,513,2,2
corner-bl: 641,573,2,2
corner-br: 701,573,2,2
button: chrome.png button:
background: 138,522,44,44 Image: chrome.png
border-r: 182,522,10,44 PanelRegion: 128, 512, 10, 10, 44, 44, 10, 10
border-l: 128,522,10,44
border-b: 138,566,44,10
border-t: 138,512,44,10
corner-tl: 128,512,10,10
corner-tr: 182,512,10,10
corner-bl: 128,566,10,10
corner-br: 182,566,10,10
button-hover: chrome.png button-hover:
background: 202,522,44,44 Image: chrome.png
border-r: 246,522,10,44 PanelRegion: 192, 512, 10, 10, 44, 44, 10, 10
border-l: 192,522,10,44
border-b: 202,566,44,10
border-t: 202,512,44,10
corner-tl: 192,512,10,10
corner-tr: 246,512,10,10
corner-bl: 192,566,10,10
corner-br: 246,566,10,10
button-disabled: chrome.png button-disabled:
Inherits: button Inherits: button
Image: chrome.png
button-pressed: chrome.png button-pressed:
background: 330,522,44,44 Image: chrome.png
border-r: 374,522,10,44 PanelRegion: 320, 512, 10, 10, 44, 44, 10, 10
border-l: 320,522,10,44
border-b: 330,566,44,10
border-t: 330,512,44,10
corner-tl: 320,512,10,10
corner-tr: 374,512,10,10
corner-bl: 320,566,10,10
corner-br: 374,566,10,10
button-highlighted: chrome.png button-highlighted:
background: 266,522,44,44 Image: chrome.png
border-r: 310,522,10,44 PanelRegion: 256, 512, 10, 10, 44, 44, 10, 10
border-l: 256,522,10,44
border-b: 266,566,44,10
border-t: 266,512,44,10
corner-tl: 256,512,10,10
corner-tr: 310,512,10,10
corner-bl: 256,566,10,10
corner-br: 310,566,10,10
button-highlighted-hover: chrome.png button-highlighted-hover:
Inherits: button-pressed Inherits: button-pressed
Image: chrome.png
button-highlighted-pressed: chrome.png button-highlighted-pressed:
Inherits: button-pressed Inherits: button-pressed
Image: chrome.png
button-highlighted-disabled: chrome.png button-highlighted-disabled:
Inherits: button-highlighted Inherits: button-highlighted
Image: chrome.png
button-highlighted-thin: chrome.png button-highlighted-thin:
background: 522,522,44,44 Image: chrome.png
border-r: 566,522,10,44 PanelRegion: 512, 512, 10, 10, 44, 44, 10, 10
border-l: 512,522,10,44
border-b: 522,566,44,10
border-t: 522,512,44,10
corner-tl: 512,512,10,10
corner-tr: 566,512,10,10
corner-bl: 512,566,10,10
corner-br: 566,566,10,10
button-highlighted-thin-hover: chrome.png button-highlighted-thin-hover:
Inherits: button-highlighted-thin-pressed Inherits: button-highlighted-thin-pressed
Image: chrome.png
button-highlighted-thin-pressed: chrome.png button-highlighted-thin-pressed:
background: 586,522,44,44 Image: chrome.png
border-r: 630,522,10,44 PanelRegion: 576, 512, 10, 10, 44, 44, 10, 10
border-l: 576,522,10,44
border-b: 586,566,44,10
border-t: 586,512,44,10
corner-tl: 576,512,10,10
corner-tr: 630,512,10,10
corner-bl: 576,566,10,10
corner-br: 630,566,10,10
button-highlighted-thin-disabled: chrome.png button-highlighted-thin-disabled:
Inherits: button-highlighted-thin Inherits: button-highlighted-thin
Image: chrome.png
progressbar-bg: chrome.png progressbar-bg:
background: 453,565,56,6 Image: chrome.png
border-r: 507,565,5,6 Regions:
border-l: 448,565,5,6 background: 453, 565, 56, 6
border-b: 453,571,54,5 border-r: 507, 565, 5, 6
border-t: 453,560,54,5 border-l: 448, 565, 5, 6
corner-tl: 448,560,5,5 border-b: 453, 571, 54, 5
corner-tr: 507,560,5,5 border-t: 453, 560, 54, 5
corner-bl: 448,571,5,5 corner-tl: 448, 560, 5, 5
corner-br: 507,571,5,5 corner-tr: 507, 560, 5, 5
corner-bl: 448, 571, 5, 5
corner-br: 507, 571, 5, 5
progressbar-thumb: chrome.png progressbar-thumb:
background: 453,549,56,6 Image: chrome.png
border-r: 507,549,5,6 Regions:
border-l: 448,549,5,6 background: 453, 549, 56, 6
border-b: 453,555,54,5 border-r: 507, 549, 5, 6
border-t: 453,544,54,5 border-l: 448, 549, 5, 6
corner-tl: 448,544,5,5 border-b: 453, 555, 54, 5
corner-tr: 507,544,5,5 border-t: 453, 544, 54, 5
corner-bl: 448,555,5,5 corner-tl: 448, 544, 5, 5
corner-br: 507,555,5,5 corner-tr: 507, 544, 5, 5
corner-bl: 448, 555, 5, 5
corner-br: 507, 555, 5, 5
panel-rule: chrome.png panel-rule:
border-t: 64,512,64,2 Image: chrome.png
Regions:
border-t: 64, 512, 64, 2
background: chrome.png background:
background:0,0,1024,480 Image: chrome.png
PanelRegion: 0, 0, 0, 0, 1024, 480, 0, 0
PanelSides: Center
modcontent: chrome.png modcontent:
logo: 0,576,280,128 Image: chrome.png
leftarrow:384,512,20,64 Regions:
rightarrow:404,512,20,64 logo: 0, 576, 280, 128
cdicon: 448,512,20,20 leftarrow: 384, 512, 20, 64
rightarrow: 404, 512, 20, 64
cdicon: 448, 512, 20, 20
scrollpanel-bg: chrome.png scrollpanel-bg:
Inherits: panel-thinborder Inherits: panel-thinborder
Image: chrome.png
scrollpanel-button: chrome.png scrollpanel-button:
Inherits: panel-thinborder Inherits: panel-thinborder
Image: chrome.png
scrollpanel-button-hover: chrome.png scrollpanel-button-hover:
Inherits: panel-thinborder-light Inherits: panel-thinborder-light
Image: chrome.png
scrollpanel-button-disabled: chrome.png scrollpanel-button-disabled:
Inherits: panel-thinborder Inherits: panel-thinborder
Image: chrome.png
scrollpanel-button-pressed: chrome.png scrollpanel-button-pressed:
Inherits: panel-thinborder-light Inherits: panel-thinborder-light
Image: chrome.png
scrollbar: chrome.png scrollbar:
down_arrow: 480,512,16,16 Image: chrome.png
down_pressed: 480,512,16,16 Regions:
up_arrow: 480,528,16,16 down_arrow: 480, 512, 16, 16
up_pressed: 480,528,16,16 down_pressed: 480, 512, 16, 16
up_arrow: 480, 528, 16, 16
up_pressed: 480, 528, 16, 16

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff