mirror of
https://github.com/gui-cs/Terminal.Gui.git
synced 2025-12-26 15:57:56 +01:00
Merge branch 'copilot/restructure-scenarios-standalone' of https://github.com/gui-cs/Terminal.Gui into copilot/restructure-scenarios-standalone
This commit is contained in:
@@ -6,36 +6,22 @@
|
||||
|
||||
using Terminal.Gui.App;
|
||||
using Terminal.Gui.Configuration;
|
||||
using Terminal.Gui.Examples;
|
||||
using Terminal.Gui.ViewBase;
|
||||
using Terminal.Gui.Views;
|
||||
|
||||
[assembly: ExampleMetadata ("Simple Example", "A basic login form demonstrating Terminal.Gui fundamentals")]
|
||||
[assembly: ExampleCategory ("Getting Started")]
|
||||
[assembly: ExampleDemoKeyStrokes (KeyStrokes = ["a", "d", "m", "i", "n", "Tab", "p", "a", "s", "s", "w", "o", "r", "d", "Enter"], DelayMs = 500, Order = 1)]
|
||||
[assembly: ExampleDemoKeyStrokes (KeyStrokes = ["Enter"], DelayMs = 500, Order = 2)]
|
||||
[assembly: ExampleDemoKeyStrokes (KeyStrokes = ["Esc"], DelayMs = 100, Order = 3)]
|
||||
// Example metadata
|
||||
[assembly: Terminal.Gui.Examples.ExampleMetadata ("Simple Example", "A basic login form demonstrating Terminal.Gui fundamentals")]
|
||||
[assembly: Terminal.Gui.Examples.ExampleCategory ("Getting Started")]
|
||||
[assembly: Terminal.Gui.Examples.ExampleDemoKeyStrokes (KeyStrokes = ["SetDelay:500", "a", "d", "m", "i", "n", "Tab", "p", "a", "s", "s", "w", "o", "r", "d", "Enter"], Order = 1)]
|
||||
[assembly: Terminal.Gui.Examples.ExampleDemoKeyStrokes (KeyStrokes = ["SetDelay:500", "Enter"], Order = 2)]
|
||||
[assembly: Terminal.Gui.Examples.ExampleDemoKeyStrokes (KeyStrokes = ["SetDelay:100", "Esc"], Order = 3)]
|
||||
|
||||
// Override the default configuration for the application to use the Light theme
|
||||
ConfigurationManager.RuntimeConfig = """{ "Theme": "Light" }""";
|
||||
ConfigurationManager.Enable (ConfigLocations.All);
|
||||
|
||||
// Check for test context to determine driver
|
||||
string? contextJson = Environment.GetEnvironmentVariable (ExampleContext.ENVIRONMENT_VARIABLE_NAME);
|
||||
string? driverName = null;
|
||||
|
||||
if (!string.IsNullOrEmpty (contextJson))
|
||||
{
|
||||
ExampleContext? context = ExampleContext.FromJson (contextJson);
|
||||
driverName = context?.DriverName;
|
||||
}
|
||||
|
||||
IApplication app = Application.Create ();
|
||||
|
||||
// Setup automatic key injection for testing
|
||||
ExampleContextInjector.SetupAutomaticInjection (app);
|
||||
|
||||
app.Init (driverName);
|
||||
IApplication app = Application.Create (example: true);
|
||||
app.Init ();
|
||||
app.Run<ExampleWindow> ();
|
||||
|
||||
// Dispose the app to clean up and enable Console.WriteLine below
|
||||
|
||||
@@ -2,11 +2,35 @@
|
||||
// Example Runner - Demonstrates discovering and running all examples using the example infrastructure
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Terminal.Gui.Configuration;
|
||||
using Terminal.Gui.Examples;
|
||||
|
||||
[assembly: ExampleMetadata ("Example Runner", "Discovers and runs all examples sequentially")]
|
||||
[assembly: ExampleCategory ("Infrastructure")]
|
||||
|
||||
// Parse command line arguments
|
||||
bool useFakeDriver = args.Contains ("--fake-driver") || args.Contains ("-f");
|
||||
int timeout = 5000; // Default timeout in milliseconds
|
||||
|
||||
for (var i = 0; i < args.Length; i++)
|
||||
{
|
||||
if ((args [i] == "--timeout" || args [i] == "-t") && i + 1 < args.Length)
|
||||
{
|
||||
if (int.TryParse (args [i + 1], out int parsedTimeout))
|
||||
{
|
||||
timeout = parsedTimeout;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Configure ForceDriver via ConfigurationManager if requested
|
||||
if (useFakeDriver)
|
||||
{
|
||||
Console.WriteLine ("Using FakeDriver (forced via ConfigurationManager)\n");
|
||||
ConfigurationManager.RuntimeConfig = """{ "ForceDriver": "FakeDriver" }""";
|
||||
ConfigurationManager.Enable (ConfigLocations.All);
|
||||
}
|
||||
|
||||
// Discover examples from the Examples directory
|
||||
string? assemblyDir = Path.GetDirectoryName (System.Reflection.Assembly.GetExecutingAssembly ().Location);
|
||||
|
||||
@@ -63,12 +87,13 @@ foreach (ExampleInfo example in examples)
|
||||
Console.Write ($"Running: {example.Name,-40} ");
|
||||
|
||||
// Create context for running the example
|
||||
// Note: When running with example mode, the demo keys from attributes will be used
|
||||
// We don't need to inject additional keys via the context
|
||||
ExampleContext context = new ()
|
||||
{
|
||||
KeysToInject = example.DemoKeyStrokes.OrderBy (ks => ks.Order)
|
||||
.SelectMany (ks => ks.KeyStrokes)
|
||||
.ToList (),
|
||||
TimeoutMs = 5000,
|
||||
DriverName = useFakeDriver ? "FakeDriver" : null,
|
||||
KeysToInject = [], // Empty - let example mode handle keys from attributes
|
||||
TimeoutMs = timeout,
|
||||
Mode = ExecutionMode.InProcess
|
||||
};
|
||||
|
||||
@@ -101,4 +126,9 @@ foreach (ExampleInfo example in examples)
|
||||
|
||||
Console.WriteLine ($"\n=== Summary: {successCount} passed, {failCount} failed ===");
|
||||
|
||||
if (useFakeDriver)
|
||||
{
|
||||
Console.WriteLine ("\nNote: Tests run with FakeDriver. Some examples may timeout if they don't respond to Esc key.");
|
||||
}
|
||||
|
||||
return failCount == 0 ? 0 : 1;
|
||||
|
||||
@@ -3,34 +3,20 @@
|
||||
|
||||
using Terminal.Gui.App;
|
||||
using Terminal.Gui.Drawing;
|
||||
using Terminal.Gui.Examples;
|
||||
using Terminal.Gui.ViewBase;
|
||||
using Terminal.Gui.Views;
|
||||
|
||||
[assembly: ExampleMetadata ("Fluent API Example", "Demonstrates the fluent IApplication API with IRunnable pattern")]
|
||||
[assembly: ExampleCategory ("API Patterns")]
|
||||
[assembly: ExampleCategory ("Controls")]
|
||||
[assembly: ExampleDemoKeyStrokes (KeyStrokes = ["CursorDown", "CursorDown", "CursorRight", "Enter"], Order = 1)]
|
||||
[assembly: ExampleDemoKeyStrokes (KeyStrokes = ["Esc"], DelayMs = 100, Order = 2)]
|
||||
// Example metadata
|
||||
[assembly: Terminal.Gui.Examples.ExampleMetadata ("Fluent API Example", "Demonstrates the fluent IApplication API with IRunnable pattern")]
|
||||
[assembly: Terminal.Gui.Examples.ExampleCategory ("API Patterns")]
|
||||
[assembly: Terminal.Gui.Examples.ExampleCategory ("Controls")]
|
||||
[assembly: Terminal.Gui.Examples.ExampleDemoKeyStrokes (KeyStrokes = ["CursorDown", "CursorDown", "CursorRight", "Enter"], Order = 1)]
|
||||
[assembly: Terminal.Gui.Examples.ExampleDemoKeyStrokes (KeyStrokes = ["SetDelay:100", "Esc"], Order = 2)]
|
||||
|
||||
|
||||
// Check for test context to determine driver
|
||||
string? contextJson = Environment.GetEnvironmentVariable (ExampleContext.ENVIRONMENT_VARIABLE_NAME);
|
||||
string? driverName = null;
|
||||
|
||||
if (!string.IsNullOrEmpty (contextJson))
|
||||
{
|
||||
ExampleContext? context = ExampleContext.FromJson (contextJson);
|
||||
driverName = context?.DriverName;
|
||||
}
|
||||
|
||||
IApplication? app = Application.Create ()
|
||||
.Init (driverName)
|
||||
IApplication? app = Application.Create (example: true)
|
||||
.Init ()
|
||||
.Run<ColorPickerView> ();
|
||||
|
||||
// Setup automatic key injection for testing
|
||||
ExampleContextInjector.SetupAutomaticInjection (app);
|
||||
|
||||
// Run the application with fluent API - automatically creates, runs, and disposes the runnable
|
||||
Color? result = app.GetResult () as Color?;
|
||||
|
||||
|
||||
@@ -3,35 +3,21 @@
|
||||
|
||||
using Terminal.Gui.App;
|
||||
using Terminal.Gui.Drawing;
|
||||
using Terminal.Gui.Examples;
|
||||
using Terminal.Gui.ViewBase;
|
||||
using Terminal.Gui.Views;
|
||||
|
||||
[assembly: ExampleMetadata ("Runnable Wrapper Example", "Shows how to wrap any View to make it runnable without implementing IRunnable")]
|
||||
[assembly: ExampleCategory ("API Patterns")]
|
||||
[assembly: ExampleCategory ("Views")]
|
||||
[assembly: ExampleDemoKeyStrokes (KeyStrokes = ["t", "e", "s", "t", "Esc"], Order = 1)]
|
||||
[assembly: ExampleDemoKeyStrokes (KeyStrokes = ["Enter", "Esc"], DelayMs = 100, Order = 2)]
|
||||
[assembly: ExampleDemoKeyStrokes (KeyStrokes = ["Enter", "Esc"], DelayMs = 100, Order = 3)]
|
||||
[assembly: ExampleDemoKeyStrokes (KeyStrokes = ["Enter", "Esc"], DelayMs = 100, Order = 4)]
|
||||
[assembly: ExampleDemoKeyStrokes (KeyStrokes = ["Enter", "Esc"], DelayMs = 100, Order = 5)]
|
||||
// Example metadata
|
||||
[assembly: Terminal.Gui.Examples.ExampleMetadata ("Runnable Wrapper Example", "Shows how to wrap any View to make it runnable without implementing IRunnable")]
|
||||
[assembly: Terminal.Gui.Examples.ExampleCategory ("API Patterns")]
|
||||
[assembly: Terminal.Gui.Examples.ExampleCategory ("Views")]
|
||||
[assembly: Terminal.Gui.Examples.ExampleDemoKeyStrokes (KeyStrokes = ["SetDelay:200", "t", "e", "s", "t", "Esc"], Order = 1)]
|
||||
[assembly: Terminal.Gui.Examples.ExampleDemoKeyStrokes (KeyStrokes = ["SetDelay:200", "Enter", "Esc"], Order = 2)]
|
||||
[assembly: Terminal.Gui.Examples.ExampleDemoKeyStrokes (KeyStrokes = ["SetDelay:200", "Enter", "Esc"], Order = 3)]
|
||||
[assembly: Terminal.Gui.Examples.ExampleDemoKeyStrokes (KeyStrokes = ["SetDelay:200", "Enter", "Esc"], Order = 4)]
|
||||
[assembly: Terminal.Gui.Examples.ExampleDemoKeyStrokes (KeyStrokes = ["SetDelay:200", "Enter", "Esc"], Order = 5)]
|
||||
|
||||
// Check for test context to determine driver
|
||||
string? contextJson = Environment.GetEnvironmentVariable (ExampleContext.ENVIRONMENT_VARIABLE_NAME);
|
||||
string? driverName = null;
|
||||
|
||||
if (!string.IsNullOrEmpty (contextJson))
|
||||
{
|
||||
ExampleContext? context = ExampleContext.FromJson (contextJson);
|
||||
driverName = context?.DriverName;
|
||||
}
|
||||
|
||||
IApplication app = Application.Create ();
|
||||
|
||||
// Setup automatic key injection for testing
|
||||
ExampleContextInjector.SetupAutomaticInjection (app);
|
||||
|
||||
app.Init (driverName);
|
||||
IApplication app = Application.Create (example: true);
|
||||
app.Init ();
|
||||
|
||||
// Example 1: Use extension method with result extraction
|
||||
var textField = new TextField { Width = 40, Text = "Default text" };
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Reflection;
|
||||
@@ -10,6 +11,11 @@ namespace Terminal.Gui.App;
|
||||
|
||||
public static partial class Application // Lifecycle (Init/Shutdown)
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the observable collection of all application instances.
|
||||
/// External observers can subscribe to this collection to monitor application lifecycle.
|
||||
/// </summary>
|
||||
public static ObservableCollection<IApplication> Apps { get; } = [];
|
||||
/// <summary>
|
||||
/// Gets the singleton <see cref="IApplication"/> instance used by the legacy static Application model.
|
||||
/// </summary>
|
||||
@@ -29,6 +35,10 @@ public static partial class Application // Lifecycle (Init/Shutdown)
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="IApplication"/> instance.
|
||||
/// </summary>
|
||||
/// <param name="example">
|
||||
/// If <see langword="true"/>, the application will run in example mode where metadata is collected
|
||||
/// and demo keys are automatically sent when the first TopRunnable is modal.
|
||||
/// </param>
|
||||
/// <remarks>
|
||||
/// The recommended pattern is for developers to call <c>Application.Create()</c> and then use the returned
|
||||
/// <see cref="IApplication"/> instance for all subsequent application operations.
|
||||
@@ -37,12 +47,15 @@ public static partial class Application // Lifecycle (Init/Shutdown)
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// Thrown if the legacy static Application model has already been used in this process.
|
||||
/// </exception>
|
||||
public static IApplication Create ()
|
||||
public static IApplication Create (bool example = false)
|
||||
{
|
||||
//Debug.Fail ("Application.Create() called");
|
||||
ApplicationImpl.MarkInstanceBasedModelUsed ();
|
||||
|
||||
return new ApplicationImpl ();
|
||||
ApplicationImpl app = new () { IsExample = example };
|
||||
Apps.Add (app);
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IApplication.Init"/>
|
||||
|
||||
@@ -11,6 +11,9 @@ internal partial class ApplicationImpl
|
||||
/// <inheritdoc/>
|
||||
public bool Initialized { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsExample { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public event EventHandler<EventArgs<bool>>? InitializedChanged;
|
||||
|
||||
@@ -93,6 +96,12 @@ internal partial class ApplicationImpl
|
||||
RaiseInitializedChanged (this, new (true));
|
||||
SubscribeDriverEvents ();
|
||||
|
||||
// Setup example mode if requested
|
||||
if (IsExample)
|
||||
{
|
||||
SetupExampleMode ();
|
||||
}
|
||||
|
||||
SynchronizationContext.SetSynchronizationContext (new ());
|
||||
MainThreadId = Thread.CurrentThread.ManagedThreadId;
|
||||
|
||||
@@ -381,4 +390,151 @@ internal partial class ApplicationImpl
|
||||
Application.Force16ColorsChanged -= OnForce16ColorsChanged;
|
||||
Application.ForceDriverChanged -= OnForceDriverChanged;
|
||||
}
|
||||
|
||||
#region Example Mode
|
||||
|
||||
private bool _exampleModeDemoKeysSent;
|
||||
|
||||
/// <summary>
|
||||
/// Sets up example mode functionality - collecting metadata and sending demo keys
|
||||
/// when the first TopRunnable becomes modal.
|
||||
/// </summary>
|
||||
private void SetupExampleMode ()
|
||||
{
|
||||
// Subscribe to SessionBegun to monitor when runnables start
|
||||
SessionBegun += OnSessionBegunForExample;
|
||||
}
|
||||
|
||||
private void OnSessionBegunForExample (object? sender, SessionTokenEventArgs e)
|
||||
{
|
||||
// Only send demo keys once
|
||||
if (_exampleModeDemoKeysSent)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Subscribe to IsModalChanged event on the TopRunnable
|
||||
if (TopRunnable is { })
|
||||
{
|
||||
TopRunnable.IsModalChanged += OnIsModalChangedForExample;
|
||||
|
||||
// Check if already modal - if so, send keys immediately
|
||||
if (TopRunnable.IsModal)
|
||||
{
|
||||
_exampleModeDemoKeysSent = true;
|
||||
TopRunnable.IsModalChanged -= OnIsModalChangedForExample;
|
||||
SendDemoKeys ();
|
||||
}
|
||||
}
|
||||
|
||||
// Unsubscribe from SessionBegun - we only need to set up the modal listener once
|
||||
SessionBegun -= OnSessionBegunForExample;
|
||||
}
|
||||
|
||||
private void OnIsModalChangedForExample (object? sender, EventArgs<bool> e)
|
||||
{
|
||||
// Only send demo keys once, when a runnable becomes modal (not when it stops being modal)
|
||||
if (_exampleModeDemoKeysSent || !e.Value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Mark that we've sent the keys
|
||||
_exampleModeDemoKeysSent = true;
|
||||
|
||||
// Unsubscribe - we only need to do this once
|
||||
if (TopRunnable is { })
|
||||
{
|
||||
TopRunnable.IsModalChanged -= OnIsModalChangedForExample;
|
||||
}
|
||||
|
||||
// Send demo keys from assembly attributes
|
||||
SendDemoKeys ();
|
||||
}
|
||||
|
||||
private void SendDemoKeys ()
|
||||
{
|
||||
// Get the entry assembly to read example metadata
|
||||
var assembly = System.Reflection.Assembly.GetEntryAssembly ();
|
||||
|
||||
if (assembly is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Look for ExampleDemoKeyStrokesAttribute
|
||||
var demoKeyAttributes = assembly.GetCustomAttributes (typeof (Terminal.Gui.Examples.ExampleDemoKeyStrokesAttribute), false)
|
||||
.OfType<Terminal.Gui.Examples.ExampleDemoKeyStrokesAttribute> ()
|
||||
.ToList ();
|
||||
|
||||
if (!demoKeyAttributes.Any ())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Sort by Order and collect all keystrokes
|
||||
var sortedSequences = demoKeyAttributes.OrderBy<Terminal.Gui.Examples.ExampleDemoKeyStrokesAttribute, int> (a => a.Order);
|
||||
|
||||
// Send keys asynchronously to avoid blocking the UI thread
|
||||
Task.Run (async () =>
|
||||
{
|
||||
// Default delay between keys is 100ms
|
||||
int currentDelay = 100;
|
||||
|
||||
foreach (var attr in sortedSequences)
|
||||
{
|
||||
// Handle KeyStrokes array
|
||||
if (attr.KeyStrokes is { Length: > 0 })
|
||||
{
|
||||
foreach (string keyStr in attr.KeyStrokes)
|
||||
{
|
||||
// Check for SetDelay command
|
||||
if (keyStr.StartsWith ("SetDelay:", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
string delayValue = keyStr.Substring ("SetDelay:".Length);
|
||||
|
||||
if (int.TryParse (delayValue, out int newDelay))
|
||||
{
|
||||
currentDelay = newDelay;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Regular key
|
||||
if (Input.Key.TryParse (keyStr, out Input.Key? key) && key is { })
|
||||
{
|
||||
// Apply delay before sending key
|
||||
if (currentDelay > 0)
|
||||
{
|
||||
await Task.Delay (currentDelay);
|
||||
}
|
||||
|
||||
Keyboard?.RaiseKeyDownEvent (key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle RepeatKey
|
||||
if (!string.IsNullOrEmpty (attr.RepeatKey))
|
||||
{
|
||||
if (Input.Key.TryParse (attr.RepeatKey, out Input.Key? key) && key is { })
|
||||
{
|
||||
for (var i = 0; i < attr.RepeatCount; i++)
|
||||
{
|
||||
// Apply delay before sending key
|
||||
if (currentDelay > 0)
|
||||
{
|
||||
await Task.Delay (currentDelay);
|
||||
}
|
||||
|
||||
Keyboard?.RaiseKeyDownEvent (key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#endregion Example Mode
|
||||
}
|
||||
|
||||
@@ -86,6 +86,12 @@ public interface IApplication : IDisposable
|
||||
/// <summary>Gets or sets whether the application has been initialized.</summary>
|
||||
bool Initialized { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this application is running in example mode.
|
||||
/// When <see langword="true"/>, metadata is collected and demo keys are automatically sent.
|
||||
/// </summary>
|
||||
bool IsExample { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// INTERNAL: Resets the state of this instance. Called by Dispose.
|
||||
/// </summary>
|
||||
|
||||
@@ -7,14 +7,10 @@ public class DemoKeyStrokeSequence
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the array of keystroke names to inject.
|
||||
/// Can include special "SetDelay:nnn" commands to change the delay between keys.
|
||||
/// </summary>
|
||||
public string [] KeyStrokes { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the delay in milliseconds before injecting these keystrokes.
|
||||
/// </summary>
|
||||
public int DelayMs { get; set; } = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the order in which this sequence should be executed.
|
||||
/// </summary>
|
||||
|
||||
@@ -9,11 +9,15 @@ namespace Terminal.Gui.Examples;
|
||||
/// Multiple instances of this attribute can be applied to a single assembly to define a sequence
|
||||
/// of keystroke injections. The <see cref="Order"/> property controls the execution sequence.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Keystrokes can include special "SetDelay:nnn" entries to change the delay between subsequent keys.
|
||||
/// The default delay is 100ms. For example: KeyStrokes = ["SetDelay:500", "Enter", "SetDelay:100", "Tab"]
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// [assembly: ExampleDemoKeyStrokes(RepeatKey = "CursorDown", RepeatCount = 5, Order = 1, DelayMs = 100)]
|
||||
/// [assembly: ExampleDemoKeyStrokes(KeyStrokes = new[] { "Enter" }, Order = 2, DelayMs = 200)]
|
||||
/// [assembly: ExampleDemoKeyStrokes(RepeatKey = "CursorDown", RepeatCount = 5, Order = 1)]
|
||||
/// [assembly: ExampleDemoKeyStrokes(KeyStrokes = ["SetDelay:500", "Enter", "SetDelay:100", "Esc"], Order = 2)]
|
||||
/// </code>
|
||||
/// </example>
|
||||
[AttributeUsage (AttributeTargets.Assembly, AllowMultiple = true)]
|
||||
@@ -21,7 +25,8 @@ public class ExampleDemoKeyStrokesAttribute : System.Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets an array of keystroke names to inject.
|
||||
/// Each string should be a valid key name that can be parsed by <see cref="Input.Key.TryParse"/>.
|
||||
/// Each string should be a valid key name that can be parsed by <see cref="Input.Key.TryParse"/>,
|
||||
/// or a special "SetDelay:nnn" command to change the delay between subsequent keys.
|
||||
/// </summary>
|
||||
public string []? KeyStrokes { get; set; }
|
||||
|
||||
@@ -37,11 +42,6 @@ public class ExampleDemoKeyStrokesAttribute : System.Attribute
|
||||
/// </summary>
|
||||
public int RepeatCount { get; set; } = 1;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the delay in milliseconds before injecting these keystrokes.
|
||||
/// </summary>
|
||||
public int DelayMs { get; set; } = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the order in which this keystroke sequence should be executed
|
||||
/// relative to other <see cref="ExampleDemoKeyStrokesAttribute"/> instances.
|
||||
|
||||
@@ -110,7 +110,6 @@ public static class ExampleDiscovery
|
||||
new ()
|
||||
{
|
||||
KeyStrokes = keys.ToArray (),
|
||||
DelayMs = attr.DelayMs,
|
||||
Order = attr.Order
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user