Replaced color usage with OpenTK's implementation.
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
using RecrownedAthenaeum.Graphics.UI.Modular.Modules.Interactive;
|
||||
|
||||
namespace RecrownedAthenaeum.Graphics.UI.SkinSystem.Definitions
|
||||
{
|
||||
/// <summary>
|
||||
/// Skin definition for a button.
|
||||
/// </summary>
|
||||
public class ButtonSkinDefinition : SkinDefinitionData
|
||||
{
|
||||
/// <summary>
|
||||
/// Names for the regions in the texture atlas respectively.
|
||||
/// </summary>
|
||||
public string upRegion, downRegion, disabledRegion, selectedRegion;
|
||||
|
||||
/// <summary>
|
||||
/// Constructs the definition with minimum requirements.
|
||||
/// </summary>
|
||||
/// <param name="downRegion">Name of region specifying the texture shown for when the button is pressed down.</param>
|
||||
/// <param name="upRegion">Name of region specifying the texture shown for when the button is not pressed.</param>
|
||||
public ButtonSkinDefinition(string downRegion, string upRegion)
|
||||
{
|
||||
UIModuleType = typeof(Button);
|
||||
this.downRegion = downRegion;
|
||||
this.upRegion = upRegion;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
|
||||
namespace RecrownedAthenaeum.Graphics.UI.SkinSystem.Definitions
|
||||
{
|
||||
/// <summary>
|
||||
/// A definition containing the data for the skin system. Needs to follow data transfer object model.
|
||||
/// </summary>
|
||||
public abstract class SkinDefinitionData
|
||||
{
|
||||
/// <summary>
|
||||
/// The full name of the UI module this definition defines.
|
||||
/// </summary>
|
||||
public string uiModuleTypeFullName;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the module type by setting <see cref="uiModuleTypeFullName"/>.
|
||||
/// </summary>
|
||||
public Type UIModuleType { set { uiModuleTypeFullName = value.FullName; } }
|
||||
}
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
using RecrownedAthenaeum.Graphics.UI.Modular.Modules.Interactive;
|
||||
|
||||
namespace RecrownedAthenaeum.Graphics.UI.SkinSystem.Definitions
|
||||
{
|
||||
/// <summary>
|
||||
/// Definition for a text button for a skin theme.
|
||||
/// </summary>
|
||||
public class TextButtonSkinDefinition : ButtonSkinDefinition
|
||||
{
|
||||
/// <summary>
|
||||
/// Name of color from the skin to use for the font.
|
||||
/// </summary>
|
||||
public string fontColor;
|
||||
|
||||
/// <summary>
|
||||
/// Creates this definition with the most minimal requirements.
|
||||
/// </summary>
|
||||
/// <param name="downRegion">Texture region from skin that represents when the button is pressed down.</param>
|
||||
/// <param name="upRegion">The texture region that represents when the button is not pressed.</param>
|
||||
public TextButtonSkinDefinition(string downRegion, string upRegion) : base(downRegion, upRegion)
|
||||
{
|
||||
UIModuleType = typeof(TextButton);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,32 @@
|
||||
using RecrownedAthenaeum.Graphics.UI.Modular.Modules;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace RecrownedAthenaeum.Graphics.UI.SkinSystem.Definitions
|
||||
{
|
||||
/// <summary>
|
||||
/// Skin definition of a scroll module.
|
||||
/// </summary>
|
||||
public class UIScrollableSkinDefinition : SkinDefinitionData
|
||||
{
|
||||
/// <summary>
|
||||
/// Name of the region that specifies the texture needed.
|
||||
/// </summary>
|
||||
public string horizontalBar, verticalBar, horizontalBarTrack, verticalBarTrack, background;
|
||||
|
||||
/// <summary>
|
||||
/// Instantiates the definition with the minimum requirements.
|
||||
/// </summary>
|
||||
/// <param name="horizontalBar">Name of the region used by the skin that defines what the horizontal scroll bar looks like.</param>
|
||||
/// <param name="verticalBar">Name of the region used by the skin that defines what the vertical scroll bar looks like.</param>
|
||||
public UIScrollableSkinDefinition(string horizontalBar, string verticalBar)
|
||||
{
|
||||
this.horizontalBar = horizontalBar;
|
||||
this.verticalBar = verticalBar;
|
||||
UIModuleType = typeof(UIScrollable);
|
||||
}
|
||||
}
|
||||
}
|
54
RecrownedAthenaeum/Graphics/UI/SkinSystem/ISkin.cs
Normal file
54
RecrownedAthenaeum/Graphics/UI/SkinSystem/ISkin.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
using RecrownedAthenaeum.Graphics.Render;
|
||||
using RecrownedAthenaeum.Types;
|
||||
using RecrownedAthenaeum.Graphics.UI.SkinSystem.Definitions;
|
||||
using OpenTK.Graphics;
|
||||
using OpenTK;
|
||||
|
||||
namespace RecrownedAthenaeum.Graphics.UI.SkinSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// The output requirements of a skin. This allows for very customized skin systems if needed.
|
||||
/// </summary>
|
||||
public interface ISkin
|
||||
{
|
||||
/// <summary>
|
||||
/// The texture for the cursor.
|
||||
/// </summary>
|
||||
Texture2D CursorTexture { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Draws a region from the texture atlas.
|
||||
/// </summary>
|
||||
/// <param name="regionName">Region to draw.</param>
|
||||
/// <param name="color">The color to tint the region.</param>
|
||||
/// <param name="batch">The batch to use.</param>
|
||||
/// <param name="destination">The destination to draw to.</param>
|
||||
/// <param name="rotation">The rotation to use in radians.</param>
|
||||
/// <param name="origin">The origin for the rotation.</param>
|
||||
void Draw(string regionName, string color, ConsistentSpriteBatch batch, Rectangle destination, float rotation = 0, Vector2 origin = default(Vector2));
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="Color"/> with given name of defined color;
|
||||
/// Should use value "default" if <paramref name="name"/> is null.
|
||||
/// </summary>
|
||||
/// <param name="name">Name of defined color.</param>
|
||||
/// <returns>The defined color based on the name given.</returns>
|
||||
Color4 GetColor(string name = null);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="TextureAtlas.Region"/> with given name of region.
|
||||
/// </summary>
|
||||
/// <param name="name">Name of region.</param>
|
||||
/// <param name="required">Whether or not the region is required. If true, it will throw an error if the region does not exist while if false, will return null if the region does not exist.</param>
|
||||
/// <returns>The region corresponding to the name and may return null depending on if the region exists, and is required.</returns>
|
||||
TextureAtlas.Region GetTextureAtlasRegion(string name, bool required = false);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the proper definition for the given parameters or throws exception in the case the requested definition does not exist.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Convenience to cast to the needed definition type.</typeparam>
|
||||
/// <param name="definitionName">The name of the definition. Default is null and will be replaced with "default" for name.</param>
|
||||
/// <returns>The definition cast to T.</returns>
|
||||
T ObtainDefinition<T>(string definitionName = null) where T : SkinDefinitionData;
|
||||
}
|
||||
}
|
88
RecrownedAthenaeum/Graphics/UI/SkinSystem/MergedSkin.cs
Normal file
88
RecrownedAthenaeum/Graphics/UI/SkinSystem/MergedSkin.cs
Normal file
@@ -0,0 +1,88 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using RecrownedAthenaeum.Graphics.Render;
|
||||
using RecrownedAthenaeum.Types;
|
||||
using RecrownedAthenaeum.Graphics.UI.SkinSystem.Definitions;
|
||||
using OpenTK.Graphics;
|
||||
using OpenTK;
|
||||
|
||||
namespace RecrownedAthenaeum.Graphics.UI.SkinSystem
|
||||
{
|
||||
internal class MergedSkin : ISkin
|
||||
{
|
||||
/// <summary>
|
||||
/// The skin to try to use first.
|
||||
/// </summary>
|
||||
public ISkin mainSkin;
|
||||
|
||||
/// <summary>
|
||||
/// The fallback skin.
|
||||
/// </summary>
|
||||
public ISkin alternateSkin;
|
||||
|
||||
public Texture2D CursorTexture
|
||||
{
|
||||
get
|
||||
{
|
||||
if (mainSkin.CursorTexture != null) {
|
||||
return mainSkin.CursorTexture;
|
||||
} else {
|
||||
return alternateSkin.CursorTexture;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Draw(string regionName, string color, ConsistentSpriteBatch batch, Rectangle destination, float rotation = 0, Vector2 origin = default(Vector2))
|
||||
{
|
||||
try
|
||||
{
|
||||
mainSkin.Draw(regionName, color, batch, destination, rotation, origin);
|
||||
} catch (KeyNotFoundException)
|
||||
{
|
||||
alternateSkin.Draw(regionName, color, batch, destination, rotation, origin);
|
||||
} catch (NullReferenceException)
|
||||
{
|
||||
alternateSkin.Draw(regionName, color, batch, destination, rotation, origin);
|
||||
}
|
||||
}
|
||||
|
||||
public Color4 GetColor(string name)
|
||||
{
|
||||
try
|
||||
{
|
||||
return mainSkin.GetColor(name);
|
||||
} catch (KeyNotFoundException)
|
||||
{
|
||||
return alternateSkin.GetColor(name);
|
||||
}
|
||||
catch (NullReferenceException)
|
||||
{
|
||||
return alternateSkin.GetColor(name);
|
||||
}
|
||||
}
|
||||
|
||||
public TextureAtlas.Region GetTextureAtlasRegion(string name, bool required = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
return mainSkin.GetTextureAtlasRegion(name);
|
||||
}
|
||||
catch (NullReferenceException)
|
||||
{
|
||||
return alternateSkin.GetTextureAtlasRegion(name, required);
|
||||
}
|
||||
}
|
||||
|
||||
public T ObtainDefinition<T>(string definitionName = null) where T : SkinDefinitionData
|
||||
{
|
||||
try
|
||||
{
|
||||
return mainSkin.ObtainDefinition<T>(definitionName);
|
||||
}
|
||||
catch (NullReferenceException)
|
||||
{
|
||||
return alternateSkin.ObtainDefinition<T>(definitionName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
183
RecrownedAthenaeum/Graphics/UI/SkinSystem/Skin.cs
Normal file
183
RecrownedAthenaeum/Graphics/UI/SkinSystem/Skin.cs
Normal file
@@ -0,0 +1,183 @@
|
||||
using RecrownedAthenaeum.Graphics.Render;
|
||||
using RecrownedAthenaeum.Types;
|
||||
using RecrownedAthenaeum.Graphics.UI.SkinSystem.Definitions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using OpenTK.Graphics;
|
||||
using OpenTK;
|
||||
|
||||
namespace RecrownedAthenaeum.Graphics.UI.SkinSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// A skin is used to group a theme which can then be applied to the UI via the use of modules.
|
||||
/// </summary>
|
||||
public class Skin : IDisposable, ISkin
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether or not this skin is completed being built and thus ready to use.
|
||||
/// </summary>
|
||||
public bool Laminated { get; private set; }
|
||||
private bool disposed;
|
||||
|
||||
private TextureAtlas textureAtlas;
|
||||
|
||||
Dictionary<string, Color4> colors;
|
||||
readonly Dictionary<string, string> definitionOfType;
|
||||
readonly Dictionary<string, Dictionary<string, SkinDefinitionData>> definitions;
|
||||
|
||||
/// <summary>
|
||||
/// The texture for the cursor.
|
||||
/// </summary>
|
||||
public virtual Texture2D CursorTexture { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a basic unfilled skin.
|
||||
/// </summary>
|
||||
/// <param name="textureAtlas">The texture atlas to use for this skin.</param>
|
||||
/// <param name="cursorTexture">The texture the cursor will be.</param>
|
||||
public Skin(TextureAtlas textureAtlas, Texture2D cursorTexture)
|
||||
{
|
||||
this.textureAtlas = textureAtlas;
|
||||
this.CursorTexture = cursorTexture;
|
||||
colors = new Dictionary<string, Color4>();
|
||||
definitionOfType = new Dictionary<string, string>();
|
||||
definitions = new Dictionary<string, Dictionary<string, SkinDefinitionData>>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="TextureAtlas.Region"/> with given name of region. Null values acceptable. Will return null if parameter is null.
|
||||
/// </summary>
|
||||
/// <param name="name">Name of region.</param>
|
||||
/// <param name="required">Whether or not this texture is mandatory for the module to work. If true, will throw error on failing to retrieve.</param>
|
||||
/// <returns>The region corresponding to the name or null if the requested region doesn't exist.</returns>
|
||||
public TextureAtlas.Region GetTextureAtlasRegion(string name, bool required = false)
|
||||
{
|
||||
if (!required && (name == null || !textureAtlas.ContainsRegion(name)))
|
||||
{
|
||||
return null;
|
||||
} else
|
||||
{
|
||||
return textureAtlas[name];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="Color"/> with given name of defined color;
|
||||
/// </summary>
|
||||
/// <param name="name">Name of defined color. Will use "default" if null. Default value is null.</param>
|
||||
/// <returns>The defined color based on the name given.</returns>
|
||||
public Color4 GetColor(string name = null)
|
||||
{
|
||||
if (name == null) name = "default";
|
||||
return colors[name];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws a region from the texture atlas.
|
||||
/// </summary>
|
||||
/// <param name="regionName">Region to draw.</param>
|
||||
/// <param name="color">The color to tint the region.</param>
|
||||
/// <param name="batch">The batch to use.</param>
|
||||
/// <param name="destination">The destination to draw to.</param>
|
||||
/// <param name="rotation">The rotation to use in radians.</param>
|
||||
/// <param name="origin">The origin for the rotation.</param>
|
||||
public void Draw(string regionName, string color, ConsistentSpriteBatch batch, Rectangle destination, float rotation = 0, Vector2 origin = default(Vector2))
|
||||
{
|
||||
if (disposed) throw new ObjectDisposedException(GetType().Name);
|
||||
textureAtlas.Draw(regionName, batch, destination, colors[color], rotation, origin);
|
||||
}
|
||||
|
||||
private SkinDefinitionData ObtainDefinition(string typeFullName, string definitionName)
|
||||
{
|
||||
if (disposed) throw new ObjectDisposedException(GetType().Name);
|
||||
if (!Laminated) throw new InvalidOperationException("Skin has yet to be laminated yet.");
|
||||
if (definitionName == null) definitionName = "default";
|
||||
if (!definitions.ContainsKey(typeFullName)) throw new KeyNotFoundException("Could not find any skin definition defining type \"" + typeFullName + "\"");
|
||||
if (!definitions[typeFullName].ContainsKey(definitionName)) throw new KeyNotFoundException("Could not find skin definition defining type \"" + typeFullName + "\" with name \"" + definitionName + "\"");
|
||||
return definitions[typeFullName][definitionName];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the proper definition for the given parameters or throws exception in the case the requested definition does not exist.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Convenience to cast to the needed definition type.</typeparam>
|
||||
/// <param name="definitionName">The name of the definition.</param>
|
||||
/// <returns>The definition cast to T.</returns>
|
||||
public T ObtainDefinition<T>(string definitionName = null) where T : SkinDefinitionData
|
||||
{
|
||||
return (T)ObtainDefinition(definitionOfType[typeof(T).FullName], definitionName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the definition.
|
||||
/// </summary>
|
||||
/// <param name="definitionName">The name of the definition. Default (if left blank) name is "default".</param>
|
||||
/// <param name="skinDefinition">The definition itself.</param>
|
||||
public void AddDefinition(SkinDefinitionData skinDefinition, string definitionName = null)
|
||||
{
|
||||
if (disposed) throw new ObjectDisposedException(GetType().Name);
|
||||
if (Laminated) throw new InvalidOperationException("This skin has been laminated and cannot be edited.");
|
||||
if (definitionName == null) definitionName = "default";
|
||||
if (!definitions.ContainsKey(skinDefinition.uiModuleTypeFullName))
|
||||
{
|
||||
definitionOfType.Add(skinDefinition.GetType().FullName, skinDefinition.uiModuleTypeFullName);
|
||||
definitions.Add(skinDefinition.uiModuleTypeFullName, new Dictionary<string, SkinDefinitionData>());
|
||||
}
|
||||
else if (definitions[skinDefinition.uiModuleTypeFullName].ContainsKey(definitionName)) throw new ArgumentException("Type of definition with that name already exists!");
|
||||
|
||||
definitions[skinDefinition.uiModuleTypeFullName].Add(definitionName, skinDefinition);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds color to skin.
|
||||
/// </summary>
|
||||
/// <param name="name"></param>
|
||||
/// <param name="color"></param>
|
||||
public void AddColor(string name, Color4 color)
|
||||
{
|
||||
if (Laminated) throw new InvalidOperationException("This skin has been laminated and cannot be edited.");
|
||||
colors.Add(name, color);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Laminates the skin. Making sure no more additions are done and sets the skin to be ready for use.
|
||||
/// Needs to be called before any use of skin. Building skin needs to be done before lamination.
|
||||
/// </summary>
|
||||
public void Laminate()
|
||||
{
|
||||
Laminated = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes <see cref="textureAtlas"/> and the <see cref="Texture2D"/> holding the cursor texture.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (disposed) throw new ObjectDisposedException(GetType().Name);
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overridable dispose function.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true when it's a user call to dispose.</param>
|
||||
public virtual void Dispose(bool disposing)
|
||||
{
|
||||
disposed = true;
|
||||
if (disposing && !disposed)
|
||||
{
|
||||
textureAtlas.Dispose();
|
||||
CursorTexture.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destructor. Calls the dispose with false.
|
||||
/// </summary>
|
||||
~Skin()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
}
|
||||
}
|
239
RecrownedAthenaeum/Graphics/UI/SkinSystem/SkinManager.cs
Normal file
239
RecrownedAthenaeum/Graphics/UI/SkinSystem/SkinManager.cs
Normal file
@@ -0,0 +1,239 @@
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using Newtonsoft.Json;
|
||||
using RecrownedAthenaeum.Data;
|
||||
using RecrownedAthenaeum.Types;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
|
||||
namespace RecrownedAthenaeum.Graphics.UI.SkinSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// Called when the skin manager has completed a async action.
|
||||
/// </summary>
|
||||
/// <param name="actionCompleted">The completed action.</param>
|
||||
public delegate void AsyncComplete(SkinManager.Action actionCompleted);
|
||||
|
||||
/// <summary>
|
||||
/// Manages reference to default and loading of custom skins.
|
||||
/// </summary>
|
||||
public class SkinManager
|
||||
{
|
||||
private const string EXTENSION = ".rbskin";
|
||||
private readonly MergedSkin mergedSkin = new MergedSkin();
|
||||
private Thread thread;
|
||||
private Action action;
|
||||
private GraphicsDevice graphicsDevice;
|
||||
private string selectedSkinPath;
|
||||
private SkinData skinDataToUse;
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not the skin manager is set up with a <see cref="BaseSkin"/> and <see cref="loadedSkin"/>.
|
||||
/// </summary>
|
||||
public bool MergingSkins { get { return (loadedSkin != null && SkinUseable); } }
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not this manager has been set up with at least a base skin.
|
||||
/// </summary>
|
||||
public bool SkinUseable { get { return BaseSkin == null; } }
|
||||
|
||||
/// <summary>
|
||||
/// The list of paths for all found skins by <see cref="SearchSkinDirectory"/>. May be null.
|
||||
/// </summary>
|
||||
public volatile List<string> skinPaths;
|
||||
|
||||
/// <summary>
|
||||
/// The event that is called when a state changes.
|
||||
/// </summary>
|
||||
public event AsyncComplete AsyncCompleteEvent;
|
||||
|
||||
/// <summary>
|
||||
/// The various possible states a skin manager could be in.
|
||||
/// </summary>
|
||||
public enum Action
|
||||
{
|
||||
/// <summary>
|
||||
/// After a search has completed.
|
||||
/// </summary>
|
||||
SEARCH,
|
||||
/// <summary>
|
||||
/// Having the skin generated to be in a useable state.
|
||||
/// </summary>
|
||||
LOAD
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// the skin that favors the selected skin, but still has a fallback to the default skin in case anything is missing.
|
||||
/// </summary>
|
||||
public ISkin Skin { get { return mergedSkin; } }
|
||||
|
||||
/// <summary>
|
||||
/// The user loaded skin resulted from asynchronous <see cref="LoadSkin(string, GraphicsDevice)"/>.
|
||||
/// </summary>
|
||||
public ISkin loadedSkin { get { return mergedSkin.mainSkin; } private set { mergedSkin.mainSkin = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// The default skin in case the selected skin doesn't cover a specific definition or color.
|
||||
/// </summary>
|
||||
public ISkin BaseSkin { get { return mergedSkin.alternateSkin; } set { mergedSkin.alternateSkin = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// The directory that contains the skins.
|
||||
/// </summary>
|
||||
public string skinsDirectory;
|
||||
|
||||
/// <summary>
|
||||
/// Performs a recursive asynchronous search of the directory given in a path set by <see cref="skinsDirectory"/>.
|
||||
/// </summary>
|
||||
public void SearchSkinDirectory()
|
||||
{
|
||||
action = Action.SEARCH;
|
||||
AttemptAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads skin data if extension is valid.
|
||||
/// </summary>
|
||||
/// <param name="path">the path to load from.</param>
|
||||
/// <returns>A <see cref="SkinData"/> that holds all the information and some metadata for the loaded skin.</returns>
|
||||
public SkinData ReadSkinData(string path)
|
||||
{
|
||||
if (path.ToLower().EndsWith(EXTENSION))
|
||||
{
|
||||
return JsonConvert.DeserializeObject<SkinData>(File.ReadAllText(path));
|
||||
}
|
||||
throw new ArgumentException("The path given does not point to a file with the required extension \"" + EXTENSION + "\" rather, has \"" + Path.GetExtension(path) + "\".");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// loads a skin asynchronously to the <see cref="loadedSkin"/>.
|
||||
/// </summary>
|
||||
/// <param name="path">The path pointing to the file with the extension "<see cref="EXTENSION"/>".</param>
|
||||
/// <param name="graphicsDevice">Graphics device to use for texture creation.</param>
|
||||
public void LoadSkin(string path, GraphicsDevice graphicsDevice)
|
||||
{
|
||||
action = Action.LOAD;
|
||||
this.graphicsDevice = graphicsDevice ?? throw new ArgumentNullException("Requires graphics device to create textures.");
|
||||
selectedSkinPath = path ?? throw new ArgumentNullException("Requires path to find textures.");
|
||||
skinDataToUse = ReadSkinData(path);
|
||||
|
||||
AttemptAsync();
|
||||
}
|
||||
|
||||
private void AttemptAsync()
|
||||
{
|
||||
if (thread != null && thread.IsAlive) throw new InvalidOperationException("Already performing task: " + action);
|
||||
thread = new Thread(ThreadStart);
|
||||
}
|
||||
|
||||
private void ThreadStart()
|
||||
{
|
||||
switch (action)
|
||||
{
|
||||
case Action.SEARCH:
|
||||
SearchForSkins();
|
||||
OnAsyncComplete(action);
|
||||
break;
|
||||
case Action.LOAD:
|
||||
loadedSkin = LoadSkinFromData(skinDataToUse, selectedSkinPath, graphicsDevice);
|
||||
OnAsyncComplete(action);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void SearchForSkins()
|
||||
{
|
||||
skinPaths.Clear();
|
||||
skinPaths = RecursiveSkinSearch(skinsDirectory);
|
||||
}
|
||||
|
||||
private Skin LoadSkinFromData(SkinData skinData, string path, GraphicsDevice graphicsDevice)
|
||||
{
|
||||
TextureAtlasDataReader textureAtlasDataReader = new TextureAtlasDataReader();
|
||||
FileInfo[] skinFiles = Directory.GetParent(path).GetFiles();
|
||||
Dictionary<string, string> filePath = new Dictionary<string, string>();
|
||||
for (int i = 0; i < skinFiles.Length; i++)
|
||||
{
|
||||
filePath.Add(skinFiles[i].Name, skinFiles[i].FullName);
|
||||
}
|
||||
TextureAtlasDataReader tatlasDataReader = new TextureAtlasDataReader();
|
||||
TextureAtlasData atlasData;
|
||||
atlasData = JsonConvert.DeserializeObject<TextureAtlasData>(File.ReadAllText(filePath[skinData.nameOfTextureAtlas]));
|
||||
Texture2D atlasTexture;
|
||||
using (FileStream stream = new FileStream(filePath[atlasData.textureName], FileMode.Open))
|
||||
{
|
||||
atlasTexture = Texture2D.FromStream(graphicsDevice, stream);
|
||||
Vector4[] data = new Vector4[atlasTexture.Width * atlasTexture.Height];
|
||||
atlasTexture.GetData(data);
|
||||
for (int i = 0; i < data.Length; i++)
|
||||
{
|
||||
Color4Ext.FromNonPremultiplied(ref data[i]);
|
||||
}
|
||||
atlasTexture.SetData(data);
|
||||
}
|
||||
TextureAtlas.Region[] regions = textureAtlasDataReader.GenerateAtlasRegionsFromData(atlasData, atlasTexture);
|
||||
TextureAtlas textureAtlas = new TextureAtlas(atlasTexture, regions);
|
||||
Texture2D cursorTexture;
|
||||
if (Path.HasExtension(skinData.cursorTextureName) && filePath.ContainsKey(skinData.cursorTextureName))
|
||||
{
|
||||
using (FileStream stream = new FileStream(filePath[skinData.cursorTextureName], FileMode.Open))
|
||||
{
|
||||
cursorTexture = Texture2D.FromStream(graphicsDevice, stream);
|
||||
Vector4[] data = new Vector4[cursorTexture.Width * cursorTexture.Height];
|
||||
atlasTexture.GetData(data);
|
||||
for (int i = 0; i < data.Length; i++)
|
||||
{
|
||||
Color4Ext.FromNonPremultiplied(ref data[i]);
|
||||
}
|
||||
cursorTexture.SetData(data);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cursorTexture = textureAtlas[skinData.cursorTextureName].AsTexture2D(graphicsDevice);
|
||||
}
|
||||
Skin skin = new Skin(new TextureAtlas(atlasTexture, regions), cursorTexture);
|
||||
|
||||
for (int i = 0; i < skinData.colors.Length; i++)
|
||||
{
|
||||
SkinData.ColorData colorData = skinData.colors[i];
|
||||
skin.AddColor(colorData.name, new Color4(colorData.r, colorData.g, colorData.b, colorData.a));
|
||||
}
|
||||
|
||||
for (int i = 0; i < skinData.definitions.Length; i++)
|
||||
{
|
||||
SkinData.DefinitionData definitionData = skinData.definitions[i];
|
||||
skin.AddDefinition(definitionData.skin, definitionData.name);
|
||||
}
|
||||
|
||||
return skin;
|
||||
}
|
||||
|
||||
private List<string> RecursiveSkinSearch(string path)
|
||||
{
|
||||
string[] files = Directory.GetFiles(path);
|
||||
string[] folders = Directory.GetDirectories(path);
|
||||
List<string> skins = new List<string>();
|
||||
for (int i = 0; i < files.Length; i++)
|
||||
{
|
||||
if (files[i].ToLower().EndsWith(EXTENSION))
|
||||
{
|
||||
skins.Add(files[i]);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < folders.Length; i++)
|
||||
{
|
||||
skins.AddRange(RecursiveSkinSearch(folders[i]));
|
||||
}
|
||||
|
||||
return skins;
|
||||
}
|
||||
|
||||
private void OnAsyncComplete(Action newState)
|
||||
{
|
||||
AsyncCompleteEvent?.Invoke(newState);
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user