1
0
Fork 0
mirror of https://github.com/Ellpeck/MLEM.git synced 2024-04-27 06:46:59 +02:00
MLEM/MLEM/Misc/MlemPlatform.cs

186 lines
9.4 KiB
C#

using System;
using System.Diagnostics;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using MLEM.Formatting.Codes;
namespace MLEM.Misc {
/// <summary>
/// MlemPlatform is a wrapper around some of MonoGame's platform-dependent behavior to allow for MLEM to stay platform-independent.
/// See <see cref="DesktopGl{T}"/>, <see cref="Mobile"/> and <see cref="None"/> for information on the specific platforms.
/// The MLEM demos' main classes also make use of this functionality: <see href="https://github.com/Ellpeck/MLEM/blob/main/Demos.DesktopGL/Program.cs#L8"/> and <see href="https://github.com/Ellpeck/MLEM/blob/main/Demos.Android/Activity1.cs#L33"/>.
/// </summary>
public abstract class MlemPlatform {
/// <summary>
/// The current MLEM platform
/// Set this value before starting your game if you want to use platform-dependent MLEM features.
/// </summary>
/// <exception cref="InvalidOperationException"></exception>
public static MlemPlatform Current;
/// <summary>
/// Opens the on-screen keyboard if one is required by the platform.
/// Note that, if no on-screen keyboard is required, a null string should be returned.
/// </summary>
/// <param name="title">Title of the dialog box.</param>
/// <param name="description">Description of the dialog box.</param>
/// <param name="defaultText">Default text displayed in the input area.</param>
/// <param name="usePasswordMode">If password mode is enabled, the characters entered are not displayed.</param>
/// <returns>Text entered by the player. Null if back was used.</returns>
public abstract Task<string> OpenOnScreenKeyboard(string title, string description, string defaultText, bool usePasswordMode);
/// <summary>
/// Adds a text input listener to this platform, if supported.
/// The supplied listener will be called whenever a character is input.
/// </summary>
/// <param name="window">The game's window</param>
/// <param name="callback">The callback that should be called whenever a character is pressed</param>
public abstract void AddTextInputListener(GameWindow window, TextInputCallback callback);
/// <summary>
/// A method that should be executed to open a link in the browser or a file explorer.
/// This method is currently used only by MLEM.Ui's implementation of the <see cref="LinkCode"/> formatting code.
/// </summary>
public abstract void OpenLinkOrFile(string link);
/// <summary>
/// Ensures that <see cref="Current"/> is set to a valid <see cref="MlemPlatform"/> value by throwing an <see cref="InvalidOperationException"/> exception if <see cref="Current"/> is null.
/// </summary>
/// <exception cref="InvalidOperationException">If <see cref="Current"/> is null</exception>
public static void EnsureExists() {
if (Current == null)
throw new InvalidOperationException("MlemPlatform was not initialized. For more information, see the MlemPlatform class or https://mlem.ellpeck.de/api/MLEM.Misc.MlemPlatform");
}
/// <summary>
/// A delegate method that can be used for <see cref="MlemPlatform.AddTextInputListener"/>
/// </summary>
/// <param name="sender">The object that sent the event. The <see cref="MlemPlatform"/> used in most cases.</param>
/// <param name="key">The key that was pressed</param>
/// <param name="character">The character that corresponds to that key</param>
public delegate void TextInputCallback(object sender, Keys key, char character);
/// <summary>
/// The MLEM DesktopGL platform.
/// This platform uses the built-in MonoGame TextInput event, which makes this listener work with any keyboard localization natively.
/// This platform is initialized as follows:
/// <code>
/// MlemPlatform.Current = new MlemPlatform.DesktopGl&lt;TextInputEventArgs&gt;((w, c) => w.TextInput += c);
/// </code>
/// </summary>
/// <typeparam name="T"></typeparam>
public class DesktopGl<T> : MlemPlatform {
private FieldInfo key;
private FieldInfo character;
private readonly Action<GameWindow, EventHandler<T>> addListener;
/// <summary>
/// Creates a new DesktopGL-based platform
/// See <see cref="MlemPlatform.DesktopGl{T}"/> class documentation for more detailed information.
/// </summary>
/// <param name="addListener">The function that is used to add a text input listener</param>
public DesktopGl(Action<GameWindow, EventHandler<T>> addListener) {
this.addListener = addListener;
}
/// <inheritdoc />
public override Task<string> OpenOnScreenKeyboard(string title, string description, string defaultText, bool usePasswordMode) {
return Task.FromResult<string>(null);
}
/// <inheritdoc />
public override void AddTextInputListener(GameWindow window, TextInputCallback callback) {
this.addListener(window, (sender, args) => {
if (this.key == null)
this.key = args.GetType().GetField("Key");
if (this.character == null)
this.character = args.GetType().GetField("Character");
callback.Invoke(sender, (Keys) this.key.GetValue(args), (char) this.character.GetValue(args));
});
}
/// <inheritdoc />
public override void OpenLinkOrFile(string link) {
Process.Start(new ProcessStartInfo(link) {UseShellExecute = true});
}
}
/// <summary>
/// The MLEM platform for mobile platforms as well as consoles.
/// This platform opens an on-screen keyboard using the <see cref="Microsoft.Xna.Framework.Input"/> <c>KeyboardInput</c> class on mobile devices.
/// Additionally, it starts a new activity whenever <see cref="OpenLinkOrFile"/> is called.
/// This listener is initialized as follows in the game's <c>Activity</c> class:
/// <code>
/// MlemPlatform.Current = new MlemPlatform.Mobile(KeyboardInput.Show, l =&gt; this.StartActivity(new Intent(Intent.ActionView, Uri.Parse(l))));
/// </code>
/// </summary>
public class Mobile : MlemPlatform {
private readonly OpenOnScreenKeyboardDelegate openOnScreenKeyboard;
private readonly Action<string> openLink;
/// <summary>
/// Creates a new mobile- and console-based platform.
/// See <see cref="MlemPlatform.Mobile"/> class documentation for more detailed information.
/// </summary>
/// <param name="openOnScreenKeyboard">The function that is used to display the on-screen keyboard</param>
/// <param name="openLink">The action that is invoked to open a link in the browser, which is used for <see cref="LinkCode"/></param>
public Mobile(OpenOnScreenKeyboardDelegate openOnScreenKeyboard, Action<string> openLink = null) {
this.openOnScreenKeyboard = openOnScreenKeyboard;
this.openLink = openLink;
}
/// <inheritdoc />
public override Task<string> OpenOnScreenKeyboard(string title, string description, string defaultText, bool usePasswordMode) {
return this.openOnScreenKeyboard(title, description, defaultText, usePasswordMode);
}
/// <inheritdoc />
public override void AddTextInputListener(GameWindow window, TextInputCallback callback) {
}
/// <inheritdoc />
public override void OpenLinkOrFile(string link) {
this.openLink?.Invoke(link);
}
/// <summary>
/// A delegate method used for <see cref="Mobile.OpenOnScreenKeyboard"/>
/// </summary>
/// <param name="title">Title of the dialog box.</param>
/// <param name="description">Description of the dialog box.</param>
/// <param name="defaultText">Default text displayed in the input area.</param>
/// <param name="usePasswordMode">If password mode is enabled, the characters entered are not displayed.</param>
/// <returns>Text entered by the player. Null if back was used.</returns>
public delegate Task<string> OpenOnScreenKeyboardDelegate(string title, string description, string defaultText, bool usePasswordMode);
}
/// <summary>
/// A MLEM platform implementation that does nothing.
/// This can be used if no platform-dependent code is required for the game.
/// </summary>
public class None : MlemPlatform {
/// <inheritdoc />
public override Task<string> OpenOnScreenKeyboard(string title, string description, string defaultText, bool usePasswordMode) {
return Task.FromResult<string>(null);
}
/// <inheritdoc />
public override void AddTextInputListener(GameWindow window, TextInputCallback callback) {
}
/// <inheritdoc />
public override void OpenLinkOrFile(string link) {
}
}
}
}