Files
LuaCsForBarotraumaEP/Barotrauma/BarotraumaClient/Source/Screens/SpriteEditorScreen.cs

752 lines
36 KiB
C#

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using Barotrauma.Extensions;
namespace Barotrauma
{
class SpriteEditorScreen : Screen
{
private GUIListBox textureList, spriteList;
private GUIFrame topPanel;
private GUIFrame leftPanel;
private GUIFrame rightPanel;
private GUIFrame bottomPanel;
private GUIFrame backgroundColorPanel;
private GUIFrame topPanelContents;
private GUITextBlock texturePathText;
private GUITextBlock xmlPathText;
private GUIScrollBar zoomBar;
private List<Sprite> selectedSprites = new List<Sprite>();
private List<Sprite> dirtySprites = new List<Sprite>();
private Texture2D selectedTexture;
private Sprite lastSelected;
private Rectangle textureRect;
private float zoom = 1;
private float minZoom = 0.25f;
private float maxZoom;
private int spriteCount;
private bool editBackgroundColor;
private Color backgroundColor = new Color(0.051f, 0.149f, 0.271f, 1.0f);
private readonly Camera cam;
public override Camera Cam
{
get { return cam; }
}
public GUIComponent TopPanel
{
get { return topPanel; }
}
public SpriteEditorScreen()
{
cam = new Camera();
CreateGUIElements();
}
#region Initialization
private void CreateGUIElements()
{
topPanel = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.1f), Frame.RectTransform) { MinSize = new Point(0, 60) }, "GUIFrameTop");
topPanelContents = new GUIFrame(new RectTransform(new Vector2(0.95f, 0.8f), topPanel.RectTransform, Anchor.Center), style: null);
new GUIButton(new RectTransform(new Vector2(0.12f, 0.4f), topPanelContents.RectTransform, Anchor.TopLeft)
{
RelativeOffset = new Vector2(0, 0.1f)
}, "Reload Texture")
{
OnClicked = (button, userData) =>
{
if (!(textureList.SelectedData is Texture2D selectedTexture)) { return false; }
var selected = selectedSprites;
Sprite firstSelected = selected.First();
selected.ForEach(s => s.ReloadTexture());
RefreshLists();
textureList.Select(firstSelected.Texture, autoScroll: false);
selected.ForEachMod(s => spriteList.Select(s, autoScroll: false));
texturePathText.Text = "Textures reloaded from " + firstSelected.FilePath;
texturePathText.TextColor = Color.LightGreen;
return true;
}
};
new GUIButton(new RectTransform(new Vector2(0.12f, 0.4f), topPanelContents.RectTransform, Anchor.BottomLeft)
{
RelativeOffset = new Vector2(0, 0.1f)
}, "Reset Changes")
{
OnClicked = (button, userData) =>
{
if (selectedTexture == null) { return false; }
foreach (Sprite sprite in loadedSprites)
{
if (sprite.Texture != selectedTexture) { continue; }
var element = sprite.SourceElement;
if (element == null) { continue; }
// Not all sprites have a sourcerect defined, in which case we'll want to use the current source rect instead of an empty rect.
sprite.SourceRect = element.GetAttributeRect("sourcerect", sprite.SourceRect);
sprite.RelativeOrigin = element.GetAttributeVector2("origin", new Vector2(0.5f, 0.5f));
}
ResetWidgets();
xmlPathText.Text = "Changes successfully reset";
xmlPathText.TextColor = Color.LightGreen;
return true;
}
};
new GUIButton(new RectTransform(new Vector2(0.12f, 0.4f), topPanelContents.RectTransform, Anchor.TopLeft)
{
RelativeOffset = new Vector2(0.15f, 0.1f)
}, "Save Selected Sprites")
{
OnClicked = (button, userData) =>
{
return SaveSprites(selectedSprites);
}
};
new GUIButton(new RectTransform(new Vector2(0.12f, 0.4f), topPanelContents.RectTransform, Anchor.BottomLeft)
{
RelativeOffset = new Vector2(0.15f, 0.1f)
}, "Save All Sprites")
{
OnClicked = (button, userData) =>
{
return SaveSprites(loadedSprites);
}
};
new GUITextBlock(new RectTransform(new Vector2(0.2f, 0.2f), topPanelContents.RectTransform, Anchor.TopCenter, Pivot.CenterRight) { RelativeOffset = new Vector2(0, 0.3f) }, "Zoom: ");
zoomBar = new GUIScrollBar(new RectTransform(new Vector2(0.2f, 0.35f), topPanelContents.RectTransform, Anchor.TopCenter, Pivot.CenterRight)
{
RelativeOffset = new Vector2(0.05f, 0.3f)
}, barSize: 0.1f)
{
BarScroll = GetBarScrollValue(),
Step = 0.01f,
OnMoved = (scrollBar, value) =>
{
zoom = MathHelper.Lerp(minZoom, maxZoom, value);
viewAreaOffset = Point.Zero;
return true;
}
};
new GUIButton(new RectTransform(new Vector2(0.05f, 0.35f), topPanelContents.RectTransform, Anchor.TopCenter, Pivot.CenterLeft) { RelativeOffset = new Vector2(0.055f, 0.3f) }, "Reset Zoom")
{
OnClicked = (box, data) =>
{
ResetZoom();
return true;
}
};
texturePathText = new GUITextBlock(new RectTransform(new Vector2(0.5f, 0.4f), topPanelContents.RectTransform, Anchor.Center, Pivot.BottomCenter) { RelativeOffset = new Vector2(0.4f, 0) }, "", Color.LightGray);
xmlPathText = new GUITextBlock(new RectTransform(new Vector2(0.5f, 0.4f), topPanelContents.RectTransform, Anchor.Center, Pivot.TopCenter) { RelativeOffset = new Vector2(0.4f, 0) }, "", Color.LightGray);
leftPanel = new GUIFrame(new RectTransform(new Vector2(0.25f, 1.0f - topPanel.RectTransform.RelativeSize.Y), Frame.RectTransform, Anchor.BottomLeft)
{ MinSize = new Point(150, 0) }, style: "GUIFrameLeft");
var paddedLeftPanel = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.95f), leftPanel.RectTransform, Anchor.CenterLeft)
{ RelativeOffset = new Vector2(0.02f, 0.0f) })
{ Stretch = true };
textureList = new GUIListBox(new RectTransform(new Vector2(1.0f, 1.0f), paddedLeftPanel.RectTransform))
{
OnSelected = (listBox, userData) =>
{
var previousTexture = selectedTexture;
selectedTexture = userData as Texture2D;
if (previousTexture != selectedTexture)
{
ResetZoom();
}
foreach (GUIComponent child in spriteList.Content.Children)
{
var textBlock = (GUITextBlock)child;
var sprite = (Sprite)textBlock.UserData;
textBlock.TextColor = new Color(textBlock.TextColor, sprite.Texture == selectedTexture ? 1.0f : 0.4f);
}
if (selectedSprites.None(s => s.Texture == selectedTexture))
{
spriteList.Select(loadedSprites.First(s => s.Texture == selectedTexture), autoScroll: false);
UpdateScrollBar(spriteList);
}
texturePathText.TextColor = Color.LightGray;
topPanelContents.Visible = true;
return true;
}
};
rightPanel = new GUIFrame(new RectTransform(new Vector2(0.25f, 1.0f - topPanel.RectTransform.RelativeSize.Y), Frame.RectTransform, Anchor.BottomRight) { MinSize = new Point(150, 0) }, style: "GUIFrameRight");
var paddedRightPanel = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.95f), rightPanel.RectTransform, Anchor.Center) { RelativeOffset = new Vector2(0.02f, 0.0f) })
{
Stretch = true,
RelativeSpacing = 0.01f
};
spriteList = new GUIListBox(new RectTransform(new Vector2(1.0f, 1.0f), paddedRightPanel.RectTransform))
{
OnSelected = (listBox, userData) =>
{
Sprite sprite = userData as Sprite;
if (sprite == null) return false;
if (selectedSprites.Any(s => s.Texture != selectedTexture))
{
ResetWidgets();
}
if (Widget.EnableMultiSelect)
{
if (selectedSprites.Contains(sprite))
{
selectedSprites.Remove(sprite);
}
else
{
selectedSprites.Add(sprite);
dirtySprites.Add(sprite);
lastSelected = sprite;
}
}
else
{
selectedSprites.Clear();
selectedSprites.Add(sprite);
dirtySprites.Add(sprite);
lastSelected = sprite;
}
if (selectedTexture != sprite.Texture)
{
textureList.Select(sprite.Texture, autoScroll: false);
UpdateScrollBar(textureList);
}
xmlPathText.Text = string.Empty;
foreach (var s in selectedSprites)
{
texturePathText.Text = s.FilePath;
var element = s.SourceElement;
if (element != null)
{
string xmlPath = element.ParseContentPathFromUri();
if (!xmlPathText.Text.Contains(xmlPath))
{
xmlPathText.Text += "\n" + xmlPath;
}
}
}
xmlPathText.TextColor = Color.LightGray;
return true;
}
};
// Background color
bottomPanel = new GUIFrame(new RectTransform(new Vector2(0.5f, 0.05f), Frame.RectTransform, Anchor.BottomCenter), style: null, color: Color.Black * 0.5f);
new GUITickBox(new RectTransform(new Vector2(0.2f, 0.5f), bottomPanel.RectTransform, Anchor.Center), "Edit Background Color")
{
Selected = editBackgroundColor,
OnSelected = box =>
{
editBackgroundColor = box.Selected;
return true;
}
};
backgroundColorPanel = new GUIFrame(new RectTransform(new Point(400, 80), Frame.RectTransform, Anchor.BottomCenter) { RelativeOffset = new Vector2(0, 0.1f) }, style: null, color: Color.Black * 0.4f);
new GUITextBlock(new RectTransform(new Vector2(0.2f, 1), backgroundColorPanel.RectTransform) { MinSize = new Point(80, 26) }, "Background \nColor:", textColor: Color.WhiteSmoke);
var inputArea = new GUILayoutGroup(new RectTransform(new Vector2(0.7f, 1), backgroundColorPanel.RectTransform, Anchor.TopRight)
{
AbsoluteOffset = new Point(20, 0)
}, isHorizontal: true, childAnchor: Anchor.CenterRight)
{
Stretch = true,
RelativeSpacing = 0.01f
};
var fields = new GUIComponent[4];
string[] colorComponentLabels = { "R", "G", "B" };
for (int i = 2; i >= 0; i--)
{
var element = new GUIFrame(new RectTransform(new Vector2(0.2f, 1), inputArea.RectTransform)
{
MinSize = new Point(40, 0),
MaxSize = new Point(100, 50)
}, style: null, color: Color.Black * 0.6f);
var colorLabel = new GUITextBlock(new RectTransform(new Vector2(0.3f, 1), element.RectTransform, Anchor.CenterLeft), colorComponentLabels[i],
font: GUI.SmallFont, textAlignment: Alignment.CenterLeft);
GUINumberInput numberInput = new GUINumberInput(new RectTransform(new Vector2(0.7f, 1), element.RectTransform, Anchor.CenterRight),
GUINumberInput.NumberType.Int)
{
Font = GUI.SmallFont
};
numberInput.MinValueInt = 0;
numberInput.MaxValueInt = 255;
numberInput.Font = GUI.SmallFont;
switch (i)
{
case 0:
colorLabel.TextColor = Color.Red;
numberInput.IntValue = backgroundColor.R;
numberInput.OnValueChanged += (numInput) => backgroundColor.R = (byte)(numInput.IntValue);
break;
case 1:
colorLabel.TextColor = Color.LightGreen;
numberInput.IntValue = backgroundColor.G;
numberInput.OnValueChanged += (numInput) => backgroundColor.G = (byte)(numInput.IntValue);
break;
case 2:
colorLabel.TextColor = Color.DeepSkyBlue;
numberInput.IntValue = backgroundColor.B;
numberInput.OnValueChanged += (numInput) => backgroundColor.B = (byte)(numInput.IntValue);
break;
}
}
}
private HashSet<Sprite> loadedSprites = new HashSet<Sprite>();
private void LoadSprites()
{
loadedSprites.ForEach(s => s.Remove());
loadedSprites.Clear();
//foreach (string filePath in ContentPackage.GetAllContentFiles(GameMain.SelectedPackages))
//{
// XDocument doc = XMLExtensions.TryLoadXml(filePath);
// if (doc != null && doc.Root != null)
// {
// LoadSprites(doc.Root);
// }
//}
foreach (string filePath in Directory.GetFiles("Content/", "*.xml", SearchOption.AllDirectories))
{
XDocument doc = XMLExtensions.TryLoadXml(filePath);
if (doc != null && doc.Root != null)
{
LoadSprites(doc.Root);
}
}
void LoadSprites(XElement element)
{
element.Elements("sprite").ForEach(s => CreateSprite(s));
element.Elements("Sprite").ForEach(s => CreateSprite(s));
element.Elements("backgroundsprite").ForEach(s => CreateSprite(s));
element.Elements("BackgroundSprite").ForEach(s => CreateSprite(s));
element.Elements("brokensprite").ForEach(s => CreateSprite(s));
element.Elements("BrokenSprite").ForEach(s => CreateSprite(s));
element.Elements("containedsprite").ForEach(s => CreateSprite(s));
element.Elements("ContainedSprite").ForEach(s => CreateSprite(s));
element.Elements("inventoryicon").ForEach(s => CreateSprite(s));
element.Elements("InventoryIcon").ForEach(s => CreateSprite(s));
//decorativesprites don't necessarily have textures (can be used to hide/disable other sprites)
element.Elements("decorativesprite").ForEach(s => { if (s.Attribute("texture") != null) CreateSprite(s); });
element.Elements("DecorativeSprite").ForEach(s => { if (s.Attribute("texture") != null) CreateSprite(s); });
element.Elements().ForEach(e => LoadSprites(e));
}
void CreateSprite(XElement element)
{
string spriteFolder = "";
string textureElement = element.GetAttributeString("texture", "");
// TODO: parse and create
if (textureElement.Contains("[GENDER]") || textureElement.Contains("[HEADID]") || textureElement.Contains("[RACE]")) { return; }
if (!textureElement.Contains("/"))
{
spriteFolder = Path.GetDirectoryName(element.ParseContentPathFromUri());
}
// Uncomment if we do multiple passes -> there can be duplicates
//string identifier = Sprite.GetID(element);
//if (loadedSprites.None(s => s.ID == identifier))
//{
// loadedSprites.Add(new Sprite(element, spriteFolder));
//}
loadedSprites.Add(new Sprite(element, spriteFolder));
}
}
private bool SaveSprites(IEnumerable<Sprite> sprites)
{
if (selectedTexture == null) { return false; }
if (sprites.None()) { return false; }
HashSet<XDocument> docsToSave = new HashSet<XDocument>();
foreach (Sprite sprite in sprites)
{
if (sprite.Texture != selectedTexture) { continue; }
var element = sprite.SourceElement;
if (element == null) { continue; }
element.SetAttributeValue("sourcerect", XMLExtensions.RectToString(sprite.SourceRect));
element.SetAttributeValue("origin", XMLExtensions.Vector2ToString(sprite.RelativeOrigin));
docsToSave.Add(element.Document);
}
xmlPathText.Text = "All changes saved to:";
foreach (XDocument doc in docsToSave)
{
string xmlPath = doc.ParseContentPathFromUri();
xmlPathText.Text += "\n" + xmlPath;
doc.Save(xmlPath);
}
xmlPathText.TextColor = Color.LightGreen;
return true;
}
#endregion
#region Public methods
public override void AddToGUIUpdateList()
{
leftPanel.AddToGUIUpdateList();
rightPanel.AddToGUIUpdateList();
topPanel.AddToGUIUpdateList();
bottomPanel.AddToGUIUpdateList();
if (editBackgroundColor)
{
backgroundColorPanel.AddToGUIUpdateList();
}
}
public override void Update(double deltaTime)
{
base.Update(deltaTime);
Widget.EnableMultiSelect = PlayerInput.KeyDown(Keys.LeftControl);
spriteList.SelectMultiple = Widget.EnableMultiSelect;
// Select rects with the mouse
if (Widget.selectedWidgets.None() || Widget.EnableMultiSelect)
{
if (selectedTexture != null)
{
foreach (Sprite sprite in loadedSprites)
{
if (sprite.Texture != selectedTexture) continue;
if (PlayerInput.LeftButtonClicked())
{
var scaledRect = new Rectangle(textureRect.Location + sprite.SourceRect.Location.Multiply(zoom), sprite.SourceRect.Size.Multiply(zoom));
if (scaledRect.Contains(PlayerInput.MousePosition))
{
spriteList.Select(sprite, autoScroll: false);
UpdateScrollBar(spriteList);
UpdateScrollBar(textureList);
}
}
}
}
}
if (GUI.MouseOn == null)
{
if (PlayerInput.ScrollWheelSpeed != 0)
{
zoom = MathHelper.Clamp(zoom + PlayerInput.ScrollWheelSpeed * (float)deltaTime * 0.05f * zoom, minZoom, maxZoom);
zoomBar.BarScroll = GetBarScrollValue();
}
widgets.Values.ForEach(w => w.Update((float)deltaTime));
if (PlayerInput.MidButtonHeld())
{
// "Camera" Pan
Vector2 moveSpeed = PlayerInput.MouseSpeed * (float)deltaTime * 100.0f;
viewAreaOffset += moveSpeed.ToPoint();
}
}
}
public override void Draw(double deltaTime, GraphicsDevice graphics, SpriteBatch spriteBatch)
{
graphics.Clear(backgroundColor);
spriteBatch.Begin(SpriteSortMode.Deferred, rasterizerState: GameMain.ScissorTestEnable, samplerState: SamplerState.PointClamp);
var viewArea = GetViewArea;
if (selectedTexture != null)
{
textureRect = new Rectangle(
(int)(viewArea.Center.X - selectedTexture.Bounds.Width / 2f * zoom),
(int)(viewArea.Center.Y - selectedTexture.Bounds.Height / 2f * zoom),
(int)(selectedTexture.Bounds.Width * zoom),
(int)(selectedTexture.Bounds.Height * zoom));
spriteBatch.Draw(selectedTexture,
viewArea.Center.ToVector2(),
sourceRectangle: null,
color: Color.White,
rotation: 0.0f,
origin: new Vector2(selectedTexture.Bounds.Width / 2.0f, selectedTexture.Bounds.Height / 2.0f),
scale: zoom,
effects: SpriteEffects.None,
layerDepth: 0);
//GUI.DrawRectangle(spriteBatch, viewArea, Color.Green, isFilled: false);
GUI.DrawRectangle(spriteBatch, textureRect, Color.Gray, isFilled: false);
foreach (GUIComponent element in spriteList.Content.Children)
{
Sprite sprite = element.UserData as Sprite;
if (sprite == null) { continue; }
if (sprite.Texture != selectedTexture) continue;
spriteCount++;
Rectangle sourceRect = new Rectangle(
textureRect.X + (int)(sprite.SourceRect.X * zoom),
textureRect.Y + (int)(sprite.SourceRect.Y * zoom),
(int)(sprite.SourceRect.Width * zoom),
(int)(sprite.SourceRect.Height * zoom));
bool isSelected = selectedSprites.Contains(sprite);
GUI.DrawRectangle(spriteBatch, sourceRect, isSelected ? Color.Yellow : Color.Red * 0.5f, thickness: isSelected ? 2 : 1);
string id = sprite.ID;
if (!string.IsNullOrEmpty(id))
{
int widgetSize = 10;
Vector2 GetTopLeft() => sprite.SourceRect.Location.ToVector2();
Vector2 GetTopRight() => new Vector2(GetTopLeft().X + sprite.SourceRect.Width, GetTopLeft().Y);
Vector2 GetBottomRight() => new Vector2(GetTopRight().X, GetTopRight().Y + sprite.SourceRect.Height);
var originWidget = GetWidget($"{id}_origin", sprite, widgetSize, Widget.Shape.Cross, initMethod: w =>
{
w.tooltip = $"Origin: {sprite.RelativeOrigin.FormatDoubleDecimal()}";
w.MouseHeld += dTime =>
{
w.DrawPos = PlayerInput.MousePosition.Clamp(textureRect.Location.ToVector2() + GetTopLeft() * zoom, textureRect.Location.ToVector2() + GetBottomRight() * zoom);
sprite.Origin = (w.DrawPos - textureRect.Location.ToVector2() - sprite.SourceRect.Location.ToVector2() * zoom) / zoom;
w.tooltip = $"Origin: {sprite.RelativeOrigin.FormatDoubleDecimal()}";
};
w.refresh = () =>
w.DrawPos = (textureRect.Location.ToVector2() + (sprite.Origin + sprite.SourceRect.Location.ToVector2()) * zoom)
.Clamp(textureRect.Location.ToVector2() + GetTopLeft() * zoom, textureRect.Location.ToVector2() + GetBottomRight() * zoom);
});
var positionWidget = GetWidget($"{id}_position", sprite, widgetSize, Widget.Shape.Rectangle, initMethod: w =>
{
w.tooltip = $"Position: {sprite.SourceRect.Location}";
w.MouseHeld += dTime =>
{
w.DrawPos = PlayerInput.MousePosition;
sprite.SourceRect = new Rectangle(((w.DrawPos + new Vector2(w.size / 2) - textureRect.Location.ToVector2()) / zoom).ToPoint(), sprite.SourceRect.Size);
if (spriteList.SelectedComponent is GUITextBlock textBox)
{
// TODO: cache the sprite name?
textBox.Text = GetSpriteName(sprite) + " " + sprite.SourceRect;
}
w.tooltip = $"Position: {sprite.SourceRect.Location}";
};
w.refresh = () => w.DrawPos = textureRect.Location.ToVector2() + sprite.SourceRect.Location.ToVector2() * zoom - new Vector2(w.size / 2);
});
var sizeWidget = GetWidget($"{id}_size", sprite, widgetSize, Widget.Shape.Rectangle, initMethod: w =>
{
w.tooltip = $"Size: {sprite.SourceRect.Size}";
w.MouseHeld += dTime =>
{
w.DrawPos = PlayerInput.MousePosition;
sprite.SourceRect = new Rectangle(sprite.SourceRect.Location, ((w.DrawPos - new Vector2(w.size) - positionWidget.DrawPos) / zoom).ToPoint());
// TODO: allow to lock the origin
sprite.RelativeOrigin = sprite.RelativeOrigin;
if (spriteList.SelectedComponent is GUITextBlock textBox)
{
// TODO: cache the sprite name?
textBox.Text = GetSpriteName(sprite) + " " + sprite.SourceRect;
}
w.tooltip = $"Size: {sprite.SourceRect.Size}";
};
w.refresh = () => w.DrawPos = textureRect.Location.ToVector2() + new Vector2(sprite.SourceRect.Right, sprite.SourceRect.Bottom) * zoom + new Vector2(w.size / 2);
});
if (isSelected)
{
positionWidget.Draw(spriteBatch, (float)deltaTime);
sizeWidget.Draw(spriteBatch, (float)deltaTime);
originWidget.Draw(spriteBatch, (float)deltaTime);
}
}
}
}
GUI.Draw(Cam, spriteBatch);
spriteCount = 0;
spriteBatch.End();
}
public override void Select()
{
base.Select();
LoadSprites();
RefreshLists();
// Store the reference, because lastSelected is reassigned when the texture is selected.
Sprite lastSprite = lastSelected;
// Select the last selected texture if any.
// TODO: Does not work if the texture has been disposed. This happens when it's not used by any sprite -> is there a better way to identify the textures? id or something?
if (selectedTexture != null && textureList.Content.Children.Any(c => c.UserData as Texture2D == selectedTexture))
{
textureList.Select(selectedTexture, autoScroll: false);
UpdateScrollBar(textureList);
// Select the last selected sprite if any
if (lastSprite != null && spriteList.Content.Children.FirstOrDefault(c => c.UserData is Sprite s && s.ID == lastSprite.ID)?.UserData is Sprite sprite)
{
spriteList.Select(sprite, autoScroll: false);
UpdateScrollBar(spriteList);
}
}
else
{
spriteList.Select(0, autoScroll: false);
}
}
public override void Deselect()
{
base.Deselect();
loadedSprites.ForEach(s => s.Remove());
loadedSprites.Clear();
ResetWidgets();
// Automatically reload all sprites that have been selected at least once (and thus might have been edited)
var reloadedSprites = new List<Sprite>();
foreach (var sprite in dirtySprites)
{
foreach (var s in Sprite.LoadedSprites)
{
if (s.Texture == sprite.Texture && !reloadedSprites.Contains(s))
{
s.ReloadXML();
reloadedSprites.Add(s);
}
}
}
dirtySprites.Clear();
}
public void SelectSprite(Sprite sprite)
{
ResetWidgets();
textureList.Select(sprite.Texture);
ResetZoom();
selectedSprites.Clear();
selectedSprites.Add(sprite);
}
public void RefreshLists()
{
//selectedTexture = null;
selectedSprites.Clear();
textureList.ClearChildren();
spriteList.ClearChildren();
ResetWidgets();
HashSet<string> textures = new HashSet<string>();
// Create texture list
foreach (Sprite sprite in loadedSprites.OrderBy(s => Path.GetFileNameWithoutExtension(s.FilePath)))
{
//ignore sprites that don't have a file path (e.g. submarine pics)
if (string.IsNullOrEmpty(sprite.FilePath)) continue;
string normalizedFilePath = Path.GetFullPath(sprite.FilePath);
if (!textures.Contains(normalizedFilePath))
{
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), textureList.Content.RectTransform) { MinSize = new Point(0, 20) },
Path.GetFileName(sprite.FilePath))
{
Padding = Vector4.Zero,
ToolTip = sprite.FilePath,
UserData = sprite.Texture
};
textures.Add(normalizedFilePath);
}
}
// Create sprite list
// TODO: allow the user to choose whether to sort by file name or by texture sheet
//foreach (Sprite sprite in loadedSprites.OrderBy(s => GetSpriteName(s)))
foreach (Sprite sprite in loadedSprites.OrderBy(s => s.SourceElement.GetAttributeString("texture", string.Empty)))
{
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), spriteList.Content.RectTransform) { MinSize = new Point(0, 20) }, GetSpriteName(sprite) + " " + sprite.SourceRect)
{
Padding = Vector4.Zero,
UserData = sprite
};
}
topPanelContents.Visible = false;
}
public void ResetZoom()
{
var viewArea = GetViewArea;
float width = viewArea.Width / (float)selectedTexture.Width;
float height = viewArea.Height / (float)selectedTexture.Height;
maxZoom = 10; // TODO: user-definable?
zoom = Math.Min(1, Math.Min(width, height));
zoomBar.BarScroll = GetBarScrollValue();
viewAreaOffset = Point.Zero;
}
#endregion
#region Helpers
private Point viewAreaOffset;
private Rectangle GetViewArea
{
get
{
int margin = 20;
var viewArea = new Rectangle(leftPanel.Rect.Right + margin + viewAreaOffset.X, topPanel.Rect.Bottom + margin + viewAreaOffset.Y, rightPanel.Rect.Left - leftPanel.Rect.Right - margin * 2, Frame.Rect.Height - topPanel.Rect.Height - margin * 2);
return viewArea;
}
}
private float GetBarScrollValue() => MathHelper.Lerp(0, 1, MathUtils.InverseLerp(minZoom, maxZoom, zoom));
private string GetSpriteName(Sprite sprite)
{
var sourceElement = sprite.SourceElement;
if (sourceElement == null) { return string.Empty; }
string name = sprite.Name;
if (string.IsNullOrWhiteSpace(name))
{
name = sourceElement.Parent.GetAttributeString("identifier", string.Empty);
}
if (string.IsNullOrEmpty(name))
{
name = sourceElement.Parent.GetAttributeString("name", string.Empty);
}
return string.IsNullOrEmpty(name) ? Path.GetFileNameWithoutExtension(sprite.FilePath) : name;
}
private void UpdateScrollBar(GUIListBox listBox)
{
var sb = listBox.ScrollBar;
sb.BarScroll = MathHelper.Clamp(MathHelper.Lerp(0, 1, MathUtils.InverseLerp(0, listBox.Content.CountChildren - 1, listBox.SelectedIndex)), sb.MinValue, sb.MaxValue);
}
#endregion
#region Widgets
private Dictionary<string, Widget> widgets = new Dictionary<string, Widget>();
private Widget GetWidget(string id, Sprite sprite, int size = 5, Widget.Shape shape = Widget.Shape.Rectangle, Action<Widget> initMethod = null)
{
if (!widgets.TryGetValue(id, out Widget widget))
{
int selectedSize = (int)Math.Round(size * 1.5f);
widget = new Widget(id, size, shape)
{
data = sprite,
color = Color.Yellow,
secondaryColor = Color.Gray,
tooltipOffset = new Vector2(selectedSize / 2 + 5, -10)
};
widget.PreDraw += (sp, dTime) =>
{
if (!widget.IsControlled)
{
widget.refresh();
}
};
widget.PreUpdate += dTime => widget.Enabled = selectedSprites.Contains(sprite);
widget.PostUpdate += dTime =>
{
widget.inputAreaMargin = widget.IsControlled ? 1000 : 0;
widget.size = widget.IsSelected ? selectedSize : size;
widget.isFilled = widget.IsControlled;
};
widgets.Add(id, widget);
initMethod?.Invoke(widget);
}
return widget;
}
private void ResetWidgets()
{
widgets.Clear();
Widget.selectedWidgets.Clear();
}
#endregion
}
}