2021-04-23 00:17:46 +02:00
using System ;
using System.Diagnostics ;
using System.Reflection ;
using System.Threading.Tasks ;
using Microsoft.Xna.Framework ;
using Microsoft.Xna.Framework.Input ;
2021-04-23 14:34:59 +02:00
using MLEM.Formatting.Codes ;
2021-04-23 00:17:46 +02:00
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.
2021-11-06 23:42:23 +01:00
/// The MLEM demos' main classes also make use of this functionality: https://github.com/Ellpeck/MLEM/blob/main/Demos.DesktopGL/Program.cs#L8 and https://github.com/Ellpeck/MLEM/blob/main/Demos.Android/Activity1.cs#L33.
2021-04-23 00:17:46 +02:00
/// </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>
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>
2021-04-23 14:34:59 +02:00
/// 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.
2021-04-23 00:17:46 +02:00
/// </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>
2021-10-23 22:05:32 +02:00
/// MlemPlatform.Current = new MlemPlatform.DesktopGl<TextInputEventArgs>((w, c) => w.TextInput += c);
2021-04-23 00:17:46 +02:00
/// </code>
2021-10-23 22:05:32 +02:00
/// </summary>
2021-04-23 00:17:46 +02:00
/// <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>
2021-10-23 22:05:32 +02:00
/// MlemPlatform.Current = new MlemPlatform.Mobile(KeyboardInput.Show, l => this.StartActivity(new Intent(Intent.ActionView, Uri.Parse(l))));
2021-04-23 00:17:46 +02:00
/// </code>
2021-10-23 22:05:32 +02:00
/// </summary>
2021-04-23 00:17:46 +02:00
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>
2021-04-23 14:34:59 +02:00
/// <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 ) {
2021-04-23 00:17:46 +02:00
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 />
2021-12-28 14:56:11 +01:00
public override void AddTextInputListener ( GameWindow window , TextInputCallback callback ) { }
2021-04-23 00:17:46 +02:00
/// <inheritdoc />
public override void OpenLinkOrFile ( string link ) {
2021-04-23 14:34:59 +02:00
this . openLink ? . Invoke ( link ) ;
2021-04-23 00:17:46 +02:00
}
/// <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 />
2021-12-28 14:56:11 +01:00
public override void AddTextInputListener ( GameWindow window , TextInputCallback callback ) { }
2021-04-23 00:17:46 +02:00
/// <inheritdoc />
2021-12-28 14:56:11 +01:00
public override void OpenLinkOrFile ( string link ) { }
2021-04-23 00:17:46 +02:00
}
}
}