Skip to content

Commit

Permalink
Added useful extension and util methods from other projects
Browse files Browse the repository at this point in the history
  • Loading branch information
ManlyMarco committed Aug 1, 2019
1 parent baa65a4 commit e4a0511
Show file tree
Hide file tree
Showing 13 changed files with 560 additions and 14 deletions.
10 changes: 9 additions & 1 deletion API/API.projitems
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,17 @@
<Compile Include="$(MSBuildThisFileDirectory)Maker\UI\Sidebar\SidebarSeparator.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Maker\UI\Sidebar\SidebarToggle.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Maker\UI\SubCategoryCreator.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Utilities\CoroutineUtils.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Utilities\HSceneUtils.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Utilities\IMGUIUtils.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Utilities\ReadOnlyDictionary.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Utilities\ResourceUtils.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Utilities\TextureUtils.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Utilities\TextUtils.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Utilities\ThreadingHelper.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Utilities\OpenFileDialog.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Utilities\RecycleBinUtil.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Utilities\Utils.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Utilities\Extensions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Utilities\WindowsStringComparer.cs" />
</ItemGroup>
</Project>
64 changes: 64 additions & 0 deletions API/Utilities/CoroutineUtils.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
using System;
using System.Collections;
using UnityEngine;

namespace KKAPI.Utilities
{
/// <summary>
/// Utility methods for working with coroutines.
/// </summary>
public static class CoroutineUtils
{
/// <summary>
/// Create a coroutine that calls the appendCoroutine after baseCoroutine finishes
/// </summary>
public static IEnumerator AppendCo(this IEnumerator baseCoroutine, IEnumerator appendCoroutine)
{
return ComposeCoroutine(baseCoroutine, appendCoroutine);
}

/// <summary>
/// Create a coroutine that calls the yieldInstruction after baseCoroutine finishes.
/// Useless on its own, append further coroutines to run after this.
/// </summary>
public static IEnumerator AppendCo(this IEnumerator baseCoroutine, YieldInstruction yieldInstruction)
{
return new object[] { baseCoroutine, yieldInstruction }.GetEnumerator();
}

/// <summary>
/// Create a coroutine that calls each of the actions in order after base coroutine finishes.
/// One action is called per frame. First action is called right after the coroutine finishes.
/// </summary>
public static IEnumerator AppendCo(this IEnumerator baseCoroutine, params Action[] actions)
{
return ComposeCoroutine(baseCoroutine, CreateCoroutine(actions));
}

/// <summary>
/// Create a coroutine that calls each of the action delegates on consecutive frames.
/// One action is called per frame. First action is called right away. There is no frame skip after the last action.
/// </summary>
public static IEnumerator CreateCoroutine(params Action[] actions)
{
var first = true;
foreach (var action in actions)
{
if (first)
first = false;
else
yield return null;

action();
}
}

/// <summary>
/// Create a coroutine that calls each of the supplied coroutines in order.
/// </summary>
public static IEnumerator ComposeCoroutine(params IEnumerator[] coroutine)
{
return coroutine.GetEnumerator();
}
}
}
30 changes: 30 additions & 0 deletions API/Utilities/Extensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using UnityEngine;

namespace KKAPI.Utilities
{
/// <summary>
/// General utility extensions that don't fit in other categories.
/// </summary>
public static class Extensions
{
/// <summary>
/// Wrap this dictionary in a read-only wrapper that will prevent any changes to it.
/// Warning: Any reference types inside the dictionary can still be modified.
/// </summary>
public static ReadOnlyDictionary<TKey, TValue> ToReadOnlyDictionary<TKey, TValue>(this IDictionary<TKey, TValue> original)
{
return new ReadOnlyDictionary<TKey, TValue>(original);
}

/// <summary>
/// Mark GameObject of this Component as ignored by AutoTranslator. Prevents AutoTranslator from trying to translate custom UI elements.
/// </summary>
public static void MarkXuaIgnored(this Component target)
{
if (target == null) throw new ArgumentNullException(nameof(target));
target.gameObject.name += "(XUAIGNORE)";
}
}
}
52 changes: 52 additions & 0 deletions API/Utilities/HSceneUtils.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
using System;

namespace KKAPI.Utilities
{
/// <summary>
/// Utility methods for working with H Scenes / main game.
/// </summary>
public static class HSceneUtils
{
#if KK
/// <summary>
/// Get the heroine that is currently in leading position in the h scene.
/// In 3P returns the heroine the cum options affect. Outside of 3P it gets the single heroine.
/// </summary>
public static SaveData.Heroine GetLeadingHeroine(this HFlag hFlag)
{
if (hFlag == null) throw new ArgumentNullException(nameof(hFlag));
return hFlag.lstHeroine[GetLeadingHeroineId(hFlag)];
}

/// <summary>
/// Get the heroine that is currently in leading position in the h scene.
/// In 3P returns the heroine the cum options affect. Outside of 3P it gets the single heroine.
/// </summary>
public static SaveData.Heroine GetLeadingHeroine(this HSprite hSprite)
{
if (hSprite == null) throw new ArgumentNullException(nameof(hSprite));
return GetLeadingHeroine(hSprite.flags);
}

/// <summary>
/// Get ID of the heroine that is currently in leading position in the h scene. 0 is the main heroine, 1 is the "tag along".
/// In 3P returns the heroine the cum options affect. Outside of 3P it gets the single heroine.
/// </summary>
public static int GetLeadingHeroineId(this HFlag hFlag)
{
if (hFlag == null) throw new ArgumentNullException(nameof(hFlag));
return hFlag.mode == HFlag.EMode.houshi3P || hFlag.mode == HFlag.EMode.sonyu3P ? hFlag.nowAnimationInfo.id % 2 : 0;
}

/// <summary>
/// Get ID of the heroine that is currently in leading position in the h scene. 0 is the main heroine, 1 is the "tag along".
/// In 3P returns the heroine the cum options affect. Outside of 3P it gets the single heroine.
/// </summary>
public static int GetLeadingHeroineId(this HSprite hSprite)
{
if (hSprite == null) throw new ArgumentNullException(nameof(hSprite));
return GetLeadingHeroineId(hSprite.flags);
}
#endif
}
}
104 changes: 104 additions & 0 deletions API/Utilities/IMGUIUtils.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
using UnityEngine;

namespace KKAPI.Utilities
{
/// <summary>
/// Utility methods for working with IMGUI / OnGui.
/// </summary>
public static class IMGUIUtils
{
private static Texture2D SolidBoxTex { get; set; }

/// <summary>
/// Draw a gray non-transparent GUI.Box at the specified rect. Use before a window or other controls to get rid of
/// the default transparency and make the GUI easier to read.
/// </summary>
public static void DrawSolidBox(Rect boxRect)
{
if (SolidBoxTex == null)
{
var windowBackground = new Texture2D(1, 1, TextureFormat.ARGB32, false);
windowBackground.SetPixel(0, 0, new Color(0.84f, 0.84f, 0.84f));
windowBackground.Apply();
SolidBoxTex = windowBackground;
}

// It's necessary to make a new GUIStyle here or the texture doesn't show up
GUI.Box(boxRect, GUIContent.none, new GUIStyle { normal = new GUIStyleState { background = SolidBoxTex } });
}

public static void DrawLabelWithOutline(Rect rect, string text, GUIStyle style, Color outColor, Color inColor, float size)
{
var halfSize = size * 0.5F;

var backupGuiColor = GUI.color;
var backupTextColor = style.normal.textColor;

style.normal.textColor = outColor;
GUI.color = outColor;

rect.x -= halfSize;
GUI.Label(rect, text, style);

rect.x += size;
GUI.Label(rect, text, style);

rect.x -= halfSize;
rect.y -= halfSize;
GUI.Label(rect, text, style);

rect.y += size;
GUI.Label(rect, text, style);

rect.y -= halfSize;
style.normal.textColor = inColor;
GUI.color = backupGuiColor;
GUI.Label(rect, text, style);

style.normal.textColor = backupTextColor;
}

public static void DrawLabelWithShadow(Rect rect, GUIContent content, GUIStyle style, Color txtColor, Color shadowColor, Vector2 direction)
{
var backupColor = style.normal.textColor;

style.normal.textColor = shadowColor;
rect.x += direction.x;
rect.y += direction.y;
GUI.Label(rect, content, style);

style.normal.textColor = txtColor;
rect.x -= direction.x;
rect.y -= direction.y;
GUI.Label(rect, content, style);

style.normal.textColor = backupColor;
}

public static void DrawLayoutLabelWithShadow(GUIContent content, GUIStyle style, Color txtColor, Color shadowColor, Vector2 direction, params GUILayoutOption[] options)
{
DrawLabelWithShadow(GUILayoutUtility.GetRect(content, style, options), content, style, txtColor, shadowColor, direction);
}

public static bool DrawButtonWithShadow(Rect r, GUIContent content, GUIStyle style, float shadowAlpha, Vector2 direction)
{
var letters = new GUIStyle(style);
letters.normal.background = null;
letters.hover.background = null;
letters.active.background = null;

var result = GUI.Button(r, content, style);

var color = r.Contains(Event.current.mousePosition) ? letters.hover.textColor : letters.normal.textColor;

DrawLabelWithShadow(r, content, letters, color, new Color(0f, 0f, 0f, shadowAlpha), direction);

return result;
}

public static bool DrawLayoutButtonWithShadow(GUIContent content, GUIStyle style, float shadowAlpha, Vector2 direction, params GUILayoutOption[] options)
{
return DrawButtonWithShadow(GUILayoutUtility.GetRect(content, style, options), content, style, shadowAlpha, direction);
}
}
}
14 changes: 1 addition & 13 deletions API/Utilities/Utils.cs → API/Utilities/ReadOnlyDictionary.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,6 @@

namespace KKAPI.Utilities
{
public static class Extensions
{
/// <summary>
/// Wrap this dictionary in a read-only wrapper that will prevent any changes to it.
/// Warning: Any reference types inside the dictionary can still be modified.
/// </summary>
public static ReadOnlyDictionary<TKey, TValue> ToReadOnlyDictionary<TKey, TValue>(this IDictionary<TKey, TValue> original)
{
return new ReadOnlyDictionary<TKey, TValue>(original);
}
}

/// <summary>
/// Read-only dictionary wrapper. Will protect the base dictionary from being changed.
/// Warning: Any reference types inside the dictionary can still be modified.
Expand Down Expand Up @@ -127,4 +115,4 @@ private static Exception ReadOnlyException()
return new NotSupportedException("This dictionary is read-only");
}
}
}
}
46 changes: 46 additions & 0 deletions API/Utilities/ResourceUtils.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using System;
using System.IO;
using System.Linq;
using System.Reflection;

namespace KKAPI.Utilities
{
/// <summary>
/// Utility methods for working with embedded resources.
/// </summary>
public static class ResourceUtils
{
/// <summary>
/// Read all bytes starting at current position and ending at the end of the stream.
/// </summary>
public static byte[] ReadAllBytes(this Stream input)
{
var buffer = new byte[16 * 1024];
using (var ms = new MemoryStream())
{
int read;
while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
ms.Write(buffer, 0, read);
return ms.ToArray();
}
}

/// <summary>
/// Get a file set as "Embedded Resource" from the assembly that is calling this code, or optionally from a specified assembly.
/// The filename is matched to the end of the resource path, no need to give the full path.
/// If 0 or more than 1 resources match the provided filename, an exception is thrown.
/// For example if you have a file "ProjectRoot\Resources\icon.png" set as "Embedded Resource", you can use this to load it by
/// doing <code>GetEmbeddedResource("icon.png"), assuming that no other embedded files have the same name.</code>
/// </summary>
public static byte[] GetEmbeddedResource(string resourceFileName, Assembly containingAssembly = null)
{
if (containingAssembly == null)
containingAssembly = Assembly.GetCallingAssembly();

var resourceName = containingAssembly.GetManifestResourceNames().Single(str => str.EndsWith(resourceFileName));

using (var stream = containingAssembly.GetManifestResourceStream(resourceName))
return ReadAllBytes(stream ?? throw new InvalidOperationException($"The resource {resourceFileName} was not found"));
}
}
}
20 changes: 20 additions & 0 deletions API/Utilities/TextUtils.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System;
using System.Text.RegularExpressions;

namespace KKAPI.Utilities
{
/// <summary>
/// Utility methods for working with text.
/// </summary>
public static class TextUtils
{
/// <summary>
/// Convert PascalCase to Sentence case.
/// </summary>
public static string PascalCaseToSentenceCase(this string str)
{
if (str == null) throw new ArgumentNullException(nameof(str));
return Regex.Replace(str, "[a-z][A-Z]", m => $"{m.Value[0]} {char.ToLower(m.Value[1])}");
}
}
}
Loading

0 comments on commit e4a0511

Please sign in to comment.