diff --git a/RecrownedAthenaeum/UI/Skin/SkinManager.cs b/RecrownedAthenaeum/UI/Skin/SkinManager.cs index 74585f7..03a1de3 100644 --- a/RecrownedAthenaeum/UI/Skin/SkinManager.cs +++ b/RecrownedAthenaeum/UI/Skin/SkinManager.cs @@ -4,21 +4,56 @@ using Newtonsoft.Json; using RecrownedAthenaeum.Pipeline; using RecrownedAthenaeum.Data; using RecrownedAthenaeum.SpecialTypes; -using RecrownedAthenaeum.UI.Skin.Definitions; using System; using System.Collections.Generic; using System.IO; +using System.Threading; namespace RecrownedAthenaeum.UI.Skin { + /// + /// Called when the skin manager has completed a async action. + /// + /// The completed action. + public delegate void AsyncComplete(SkinManager.Action actionCompleted); + /// /// Manages reference to default and loading of custom skins. /// 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; + + /// + /// The list of paths for all found skins by . May be null. + /// + public volatile List skinPaths; + + /// + /// The event that is called when a state changes. + /// + public event AsyncComplete AsyncCompleteEvent; + + /// + /// The various possible states a skin manager could be in. + /// + public enum Action + { + /// + /// After a search has completed. + /// + SEARCH, + /// + /// Having the skin generated to be in a useable state. + /// + GENERATE + } /// /// the skin that favors the selected skin, but still has a fallback to the default skin in case anything is missing. @@ -40,15 +75,13 @@ namespace RecrownedAthenaeum.UI.Skin /// public string skinsDirectory; - List skinPaths = new List(); - /// - /// Updates list of skins by searching recursively in the set . + /// Performs a recursive asynchronous search of the directory given in a path set by . /// - public void SearchForSkins() + public void SearchSkinDirectory() { - skinPaths.Clear(); - skinPaths = RecursiveSkinSearch(skinsDirectory); + action = Action.SEARCH; + AttemptAsync(); } /// @@ -66,13 +99,49 @@ namespace RecrownedAthenaeum.UI.Skin } /// - /// Generate skin from skin data. + /// Generates a skin asynchronously. /// - /// The skin data to generate skin from. + /// Graphics device to use for texture creation. + /// The data to generate from. /// The path pointing to the file with the extension "". - /// The graphics device to generate the texture. - /// The skin generated from the skin data. - public Skin GenerateSkinFromData(SkinData skinData, string path, GraphicsDevice graphicsDevice) + public void GenerateSkin(GraphicsDevice graphicsDevice, SkinData skinData, string path) + { + action = Action.GENERATE; + this.graphicsDevice = graphicsDevice; + this.selectedSkinPath = path; + this.skinDataToUse = skinData; + + 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.GENERATE: + SelectedSkin = GenerateSkinFromData(skinDataToUse, selectedSkinPath, graphicsDevice); + OnAsyncComplete(action); + break; + } + } + + private void SearchForSkins() + { + skinPaths.Clear(); + skinPaths = RecursiveSkinSearch(skinsDirectory); + } + + private Skin GenerateSkinFromData(SkinData skinData, string path, GraphicsDevice graphicsDevice) { TextureAtlasDataReader textureAtlasDataReader = new TextureAtlasDataReader(); FileInfo[] skinFiles = Directory.GetParent(path).GetFiles(); @@ -96,7 +165,8 @@ namespace RecrownedAthenaeum.UI.Skin { cursorTexture = Texture2D.FromStream(graphicsDevice, stream); } - } else + } + else { cursorTexture = textureAtlas[skinData.cursorTextureName].AsTexture2D(graphicsDevice); } @@ -137,5 +207,9 @@ namespace RecrownedAthenaeum.UI.Skin return skins; } + private void OnAsyncComplete(Action newState) + { + AsyncCompleteEvent?.Invoke(newState); + } } }