瀏覽代碼

Add popup UI method to Client

tags/v1.4.0
NGnius (Graham) 3 年之前
父節點
當前提交
ea8a9184bc
共有 3 個文件被更改,包括 163 次插入0 次删除
  1. +77
    -0
      GamecraftModdingAPI/App/Client.cs
  2. +59
    -0
      GamecraftModdingAPI/App/ClientAlertTest.cs
  3. +27
    -0
      GamecraftModdingAPI/App/UserPrompts.cs

+ 77
- 0
GamecraftModdingAPI/App/Client.cs 查看文件

@@ -1,8 +1,12 @@
using System;
using System.Reflection;
using HarmonyLib;

using RobocraftX.Services;
using UnityEngine;

using GamecraftModdingAPI.Utility;
using RobocraftX.Common;

namespace GamecraftModdingAPI.App
{
@@ -14,6 +18,12 @@ namespace GamecraftModdingAPI.App
// extensible engine
protected static AppEngine appEngine = new AppEngine();

protected static Func<object> ErrorHandlerInstanceGetter;

protected static Action<object, Error> EnqueueError;

protected static Action<object> HandleErrorClosed;

/// <summary>
/// An event that fires whenever the main menu is loaded.
/// </summary>
@@ -74,9 +84,76 @@ namespace GamecraftModdingAPI.App
get => appEngine.IsInMenu;
}

/// <summary>
/// Open a popup which prompts the user to click a button.
/// This reuses Gamecraft's error dialog popup
/// </summary>
/// <param name="popup">The popup to display. Use an instance of SingleChoicePrompt or DualChoicePrompt.</param>
public void PromptUser(Error popup)
{
// if the stuff wasn't mostly set to internal, this would be written as:
// RobocraftX.Services.ErrorHandler.Instance.EqueueError(error);
object errorHandlerInstance = ErrorHandlerInstanceGetter();
EnqueueError(errorHandlerInstance, popup);
}

// TODO
/*public void CloseCurrentPrompt()
{
// RobocraftX.Services.ErrorHandler.Instance.HandlePopupClosed();
// FIXME: this is a call that is also called when closing, not the actual closing action itself (so it doesn't work)
object errorHandlerInstance = ErrorHandlerInstanceGetter();
HandleErrorClosed(errorHandlerInstance);
}*/

internal static void Init()
{
// this would have been so much simpler if this didn't involve a bunch of internal fields & classes
Type errorHandler = AccessTools.TypeByName("RobocraftX.Services.ErrorHandler");
Type errorHandle = AccessTools.TypeByName("RobocraftX.Services.ErrorHandle");
ErrorHandlerInstanceGetter = (Func<object>) AccessTools.Method("GamecraftModdingAPI.App.Client:GenInstanceGetter")
.MakeGenericMethod(errorHandler)
.Invoke(null, new object[0]);
EnqueueError = (Action<object, Error>) AccessTools.Method("GamecraftModdingAPI.App.Client:GenEnqueueError")
.MakeGenericMethod(errorHandler, errorHandle)
.Invoke(null, new object[0]);
/*HandleErrorClosed = (Action<object>) AccessTools.Method("GamecraftModdingAPI.App.Client:GenHandlePopupClosed")
.MakeGenericMethod(errorHandler)
.Invoke(null, new object[0]);*/
// register engines
MenuEngineManager.AddMenuEngine(appEngine);
}

// Creating delegates once is faster than reflection every time
// Admittedly, this way is more difficult to code and less readable
private static Func<object> GenInstanceGetter<T>()
{
Type errorHandler = AccessTools.TypeByName("RobocraftX.Services.ErrorHandler");
MethodInfo instance = AccessTools.PropertyGetter(errorHandler, "Instance");
Func<T> getterSimple = (Func<T>) Delegate.CreateDelegate(typeof(Func<T>), null, instance);
Func<object> getterCasted = () => (object) getterSimple();
return getterCasted;
}

private static Action<object, Error> GenEnqueueError<T, TRes>()
{
Type errorHandler = AccessTools.TypeByName("RobocraftX.Services.ErrorHandler");
MethodInfo enqueueError = AccessTools.Method(errorHandler, "EnqueueError");
Func<T, Error, TRes> enqueueSimple =
(Func<T, Error, TRes>) Delegate.CreateDelegate(typeof(Func<T, Error, TRes>), enqueueError);
Action<object, Error> enqueueCasted =
(object instance, Error error) => { enqueueSimple((T) instance, error); };
return enqueueCasted;
}

private static Action<object> GenHandlePopupClosed<T>()
{
Type errorHandler = AccessTools.TypeByName("RobocraftX.Services.ErrorHandler");
MethodInfo handlePopupClosed = AccessTools.Method(errorHandler, "HandlePopupClosed");
Action<T> handleSimple =
(Action<T>) Delegate.CreateDelegate(typeof(Action<T>), handlePopupClosed);
Action<object> handleCasted = (object instance) => handleSimple((T) instance);
return handleCasted;
}
}
}

+ 59
- 0
GamecraftModdingAPI/App/ClientAlertTest.cs 查看文件

@@ -0,0 +1,59 @@
using System;
using HarmonyLib;

using RobocraftX.Services;

using GamecraftModdingAPI.Tests;

namespace GamecraftModdingAPI.App
{
#if TEST
/// <summary>
/// Client popups tests.
/// Only available in TEST builds.
/// </summary>
[APITestClass]
public static class ClientAlertTest
{
private static DualChoicePrompt popup2 = null;

private static SingleChoicePrompt popup1 = null;

[APITestStartUp]
public static void StartUp2()
{
popup2 = new DualChoicePrompt("This is a test double-button popup",
"The cake is a lie",
"lmao",
() => { },
"kek",
() => { });
}
[APITestStartUp]
public static void StartUp1()
{
popup1 = new SingleChoicePrompt("The cake is a lie",
"This is a test single-button popup",
"qwertyuiop",
() => { });
}

[APITestCase(TestType.Menu)]
public static void TestPopUp2()
{
Client c = new Client();
c.PromptUser(popup2);
//c.CloseCurrentPrompt();
}
[APITestCase(TestType.Menu)]
public static void TestPopUp1()
{
Client c = new Client();
c.PromptUser(popup1);
//c.CloseCurrentPrompt();
}
}
#endif
}

+ 27
- 0
GamecraftModdingAPI/App/UserPrompts.cs 查看文件

@@ -0,0 +1,27 @@
using System;
using HarmonyLib;

using RobocraftX.Services;

namespace GamecraftModdingAPI.App
{
public class DualChoicePrompt : MultiChoiceError
{
public DualChoicePrompt(string errorMessage, string title, string firstButtonText, Action firstButtonAction, string secondButtonText, Action secondButtonAction) : base(errorMessage, firstButtonText, firstButtonAction, secondButtonText, secondButtonAction)
{
// internal readonly field smh
new Traverse(this).Field<string>("Title").Value = title;
}
}

public class SingleChoicePrompt : SingleChoiceError
{
public SingleChoicePrompt(string errorMessage, string buttonText, Action buttonClickAction) : base(errorMessage, buttonText, buttonClickAction)
{
}

public SingleChoicePrompt(string titleText, string errorMessage, string buttonText, Action buttonClickAction) : base(titleText, errorMessage, buttonText, buttonClickAction)
{
}
}
}

Loading…
取消
儲存