Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
153 changes: 60 additions & 93 deletions Examples/UICatalog/Scenarios/Keys.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,87 +15,70 @@ public override void Main ()
ObservableCollection<string> keyDownNotHandledList = [];
ObservableCollection<string> swallowedList = [];

using Window win = new () { Title = GetQuitKeyAndName () };
using Window win = new ();
win.Title = GetQuitKeyAndName ();
win.BorderStyle = LineStyle.None;

Label label = new ()
Shortcut quitShortcut = new ()
{
X = 0,
Y = 0,
Text = "_Type text here:"
// ReSharper disable once AccessToDisposedClosure
Title = "Quit", Key = Application.GetDefaultKey (Command.Quit), BindKeyToApplication = true, Action = app.RequestStop
};

StatusBar statusBar = new ([quitShortcut, new Shortcut { Title = "Disable QuitKey", Action = () => { quitShortcut.Key = Key.Empty; } }]);

app.AddTimeout (TimeSpan.FromMilliseconds (100),
() =>
{
// When the App is initialized, kitty detection is async, so we
// create the shortcut in a timeout.
statusBar.Add (new Shortcut
{
CommandView = new CheckBox
{
Title = "Kitty Keyboard Protocol Enabled",

// ReSharper disable once AccessToDisposedClosure
Value = app.Driver?.KittyKeyboardCapabilities?.IsSupported is true ? CheckState.Checked : CheckState.UnChecked
},
Enabled = false
});

return false;
});

Label label = new () { X = 0, Y = 0, Text = "_Type text here:" };
win.Add (label);

TextField edit = new ()
{
X = Pos.Right (label) + 1,
Y = Pos.Top (label),
Width = Dim.Fill (2),
Height = 1,
};
TextField edit = new () { X = Pos.Right (label) + 1, Y = Pos.Top (label), Width = Dim.Fill (2), Height = 1 };
win.Add (edit);

label = new ()
{
X = 0,
Y = Pos.Bottom (label),
Text = "Last _app.Keyboard.KeyDown:"
};
label = new Label { X = 0, Y = Pos.Bottom (label), Text = "Last _app.Keyboard.KeyDown:" };
win.Add (label);

Label labelAppKeypress = new ()
{
X = Pos.Right (label) + 1,
Y = Pos.Top (label),
Width = Dim.Fill (2)
};
Label labelAppKeypress = new () { X = Pos.Right (label) + 1, Y = Pos.Top (label), Width = Dim.Fill (2) };
win.Add (labelAppKeypress);

app.Keyboard.KeyDown += (_, e) => labelAppKeypress.Text = FormatKeyEvent (e);

label = new ()
{
X = 0,
Y = Pos.Bottom (label),
Text = "Last app.Keyboard._KeyUp:"
};
label = new Label { X = 0, Y = Pos.Bottom (label), Text = "Last app.Keyboard._KeyUp:" };
win.Add (label);

Label labelAppKeyUp = new ()
{
X = Pos.Right (label) + 1,
Y = Pos.Top (label),
Width = Dim.Fill (2)
};
Label labelAppKeyUp = new () { X = Pos.Right (label) + 1, Y = Pos.Top (label), Width = Dim.Fill (2) };
win.Add (labelAppKeyUp);

app.Keyboard.KeyUp += (_, e) => labelAppKeyUp.Text = FormatKeyEvent (e);

label = new ()
{
X = 0,
Y = Pos.Bottom (label),
Text = "_Last TextField.KeyDown:"
};
label = new Label { X = 0, Y = Pos.Bottom (label), Text = "_Last TextField.KeyDown:" };
win.Add (label);

Label lastTextFieldKeyDownLabel = new ()
{
X = Pos.Right (label) + 1,
Y = Pos.Top (label),
Height = 1,
Width = Dim.Fill (2)
};
Label lastTextFieldKeyDownLabel = new () { X = Pos.Right (label) + 1, Y = Pos.Top (label), Height = 1, Width = Dim.Fill (2) };
win.Add (lastTextFieldKeyDownLabel);

edit.KeyDown += (_, e) => lastTextFieldKeyDownLabel.Text = FormatKeyEvent (e);

// Application key event log:
label = new ()
{
X = 0,
Y = Pos.Bottom (label) + 1,
Text = "Application Key Events:"
};
label = new Label { X = 0, Y = Pos.Bottom (label) + 1, Text = "Application Key Events:" };
win.Add (label);
int maxKeyString = Key.CursorRight.WithAlt.WithCtrl.WithShift.ToString ().Length;
int colWidth = maxKeyString + 12; // room for event type + modifier info
Expand All @@ -107,7 +90,7 @@ public override void Main ()
X = 0,
Y = Pos.Bottom (label),
Width = colWidth,
Height = Dim.Fill (),
Height = Dim.Fill (statusBar),
Source = new ListWrapper<string> (keyList)
};
appKeyListView.SchemeName = "Runnable";
Expand All @@ -116,67 +99,48 @@ public override void Main ()
// View key events...
edit.KeyDown += (_, a) => { keyDownList.Add (a.ToString ()); };

edit.KeyDownNotHandled += (_, a) =>
{
keyDownNotHandledList.Add ($"{a}");
};
edit.KeyDownNotHandled += (_, a) => { keyDownNotHandledList.Add ($"{a}"); };

// KeyDown
label = new ()
{
X = Pos.Right (appKeyListView) + 1,
Y = Pos.Top (label),
Text = "TextView Key Down:"
};
label = new Label { X = Pos.Right (appKeyListView) + 1, Y = Pos.Top (label), Text = "TextView Key Down:" };
win.Add (label);

ListView onKeyDownListView = new ()
{
X = Pos.Left (label),
Y = Pos.Bottom (label),
Width = maxKeyString,
Height = Dim.Fill (),
Height = Dim.Fill (statusBar),
Source = new ListWrapper<string> (keyDownList)
};
appKeyListView.SchemeName = "Runnable";
win.Add (onKeyDownListView);

// KeyDownNotHandled
label = new ()
{
X = Pos.Right (onKeyDownListView) + 1,
Y = Pos.Top (label),
Text = "TextView KeyDownNotHandled:"
};
label = new Label { X = Pos.Right (onKeyDownListView) + 1, Y = Pos.Top (label), Text = "TextView KeyDownNotHandled:" };
win.Add (label);

ListView onKeyDownNotHandledListView = new ()
{
X = Pos.Left (label),
Y = Pos.Bottom (label),
Width = maxKeyString,
Height = Dim.Fill (),
Height = Dim.Fill (statusBar),
Source = new ListWrapper<string> (keyDownNotHandledList)
};
appKeyListView.SchemeName = "Runnable";
win.Add (onKeyDownNotHandledListView);


// Swallowed
label = new ()
{
X = Pos.Right (onKeyDownNotHandledListView) + 1,
Y = Pos.Top (label),
Text = "Swallowed:"
};
label = new Label { X = Pos.Right (onKeyDownNotHandledListView) + 1, Y = Pos.Top (label), Text = "Swallowed:" };
win.Add (label);

ListView onSwallowedListView = new ()
{
X = Pos.Left (label),
Y = Pos.Bottom (label),
Width = maxKeyString,
Height = Dim.Fill (),
Height = Dim.Fill (statusBar),
Source = new ListWrapper<string> (swallowedList)
};
appKeyListView.SchemeName = "Runnable";
Expand All @@ -187,15 +151,18 @@ public override void Main ()
app.Keyboard.KeyDown += (_, a) => KeyDownPressUp (a, "Down");
app.Keyboard.KeyUp += (_, a) => KeyDownPressUp (a, "Up");

win.Add (statusBar);
app.Run (win);

return;

void KeyDownPressUp (Key args, string upDown)
{
string msg = $"Key{upDown,-7}: {FormatKeyEvent (args)}";
var msg = $"Key{upDown,-7}: {FormatKeyEvent (args)}";
keyList.Add (msg);
appKeyListView.MoveDown ();
onKeyDownNotHandledListView.MoveDown ();
}

app.Run (win);
}

/// <summary>
Expand All @@ -204,14 +171,14 @@ void KeyDownPressUp (Key args, string upDown)
private static string FormatKeyEvent (Key key)
{
string eventType = key.EventType switch
{
KeyEventType.Press => "press",
KeyEventType.Repeat => "repeat",
KeyEventType.Release => "release",
_ => "?"
};

string text = $"{key} ({eventType})";
{
KeyEventType.Press => "press",
KeyEventType.Repeat => "repeat",
KeyEventType.Release => "release",
_ => "?"
};

var text = $"{key} ({eventType})";

if (key.IsModifierOnly)
{
Expand Down
18 changes: 17 additions & 1 deletion Examples/UICatalog/UICatalogRunnable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,23 @@ protected override void OnIsModalChanged (bool newIsModal)

_disableMouseCb?.Value = App.Mouse.IsMouseDisabled ? CheckState.Checked : CheckState.UnChecked;

_shVersion?.Title = $"{RuntimeEnvironment.OperatingSystem} {RuntimeEnvironment.OperatingSystemVersion}, {App?.Driver?.GetVersionInfo ()}";
string? driverName = App.Driver?.GetName ();

_shVersion?.Title = string.IsNullOrEmpty (driverName)
? $"{RuntimeEnvironment.OperatingSystem} {RuntimeEnvironment.OperatingSystemVersion}"
: $"{RuntimeEnvironment.OperatingSystem} {RuntimeEnvironment.OperatingSystemVersion}, {driverName}";

App.AddTimeout (TimeSpan.FromMilliseconds (100),
() =>
{
// Kitty detection is async, so we update the shortcut in a timeout.
if (App.Driver?.KittyKeyboardCapabilities?.IsSupported is true)
{
_shVersion?.Title += " (kitty)";
}

return false;
});

if (string.IsNullOrEmpty ((string?)Result))
{
Expand Down
63 changes: 17 additions & 46 deletions Terminal.Gui/App/MainLoop/ApplicationMainLoop.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,22 +20,11 @@ namespace Terminal.Gui.App;
/// <typeparam name="TInputRecord">Type of raw input events, e.g. <see cref="ConsoleKeyInfo"/> for .NET driver</typeparam>
public class ApplicationMainLoop<TInputRecord> : IApplicationMainLoop<TInputRecord> where TInputRecord : struct
{
private ITimedEvents? _timedEvents;
private ConcurrentQueue<TInputRecord>? _inputQueue;
private IInputProcessor? _inputProcessor;
private IOutput? _output;
private AnsiRequestScheduler? _ansiRequestScheduler;
private ISizeMonitor? _sizeMonitor;

/// <inheritdoc/>
public IApplication? App { get; private set; }

/// <inheritdoc/>
public ITimedEvents TimedEvents
{
get => _timedEvents ?? throw new NotInitializedException (nameof (TimedEvents));
private set => _timedEvents = value;
}
public ITimedEvents TimedEvents { get => field ?? throw new NotInitializedException (nameof (TimedEvents)); private set; }

// TODO: follow above pattern for others too

Expand All @@ -44,42 +33,22 @@ public ITimedEvents TimedEvents
/// thread by a <see cref="IInput{T}"/>. Is drained as part of each
/// <see cref="Iteration"/> on the main loop thread.
/// </summary>
public ConcurrentQueue<TInputRecord> InputQueue
{
get => _inputQueue ?? throw new NotInitializedException (nameof (InputQueue));
private set => _inputQueue = value;
}
public ConcurrentQueue<TInputRecord> InputQueue { get => field ?? throw new NotInitializedException (nameof (InputQueue)); private set; }

/// <inheritdoc/>
public IInputProcessor InputProcessor
{
get => _inputProcessor ?? throw new NotInitializedException (nameof (InputProcessor));
private set => _inputProcessor = value;
}
public IInputProcessor InputProcessor { get => field ?? throw new NotInitializedException (nameof (InputProcessor)); private set; }

/// <inheritdoc/>
public IOutputBuffer OutputBuffer { get; } = new OutputBufferImpl ();

/// <inheritdoc/>
public IOutput Output
{
get => _output ?? throw new NotInitializedException (nameof (Output));
private set => _output = value;
}
public IOutput Output { get => field ?? throw new NotInitializedException (nameof (Output)); private set; }

/// <inheritdoc/>
public AnsiRequestScheduler AnsiRequestScheduler
{
get => _ansiRequestScheduler ?? throw new NotInitializedException (nameof (AnsiRequestScheduler));
private set => _ansiRequestScheduler = value;
}
public AnsiRequestScheduler AnsiRequestScheduler { get => field ?? throw new NotInitializedException (nameof (AnsiRequestScheduler)); private set; }

/// <inheritdoc/>
public ISizeMonitor SizeMonitor
{
get => _sizeMonitor ?? throw new NotInitializedException (nameof (SizeMonitor));
private set => _sizeMonitor = value;
}
public ISizeMonitor SizeMonitor { get => field ?? throw new NotInitializedException (nameof (SizeMonitor)); private set; }

/// <summary>
/// Initializes the class with the provided subcomponents
Expand All @@ -90,14 +59,12 @@ public ISizeMonitor SizeMonitor
/// <param name="consoleOutput"></param>
/// <param name="componentFactory"></param>
/// <param name="app"></param>
public void Initialize (
ITimedEvents timedEvents,
ConcurrentQueue<TInputRecord> inputBuffer,
IInputProcessor inputProcessor,
IOutput consoleOutput,
IComponentFactory<TInputRecord> componentFactory,
IApplication? app
)
public void Initialize (ITimedEvents timedEvents,
ConcurrentQueue<TInputRecord> inputBuffer,
IInputProcessor inputProcessor,
IOutput consoleOutput,
IComponentFactory<TInputRecord> componentFactory,
IApplication? app)
{
App = app;
InputQueue = inputBuffer;
Expand Down Expand Up @@ -137,11 +104,15 @@ internal void IterationImpl ()
// Pull any input events from the input queue and process them
InputProcessor.ProcessQueue ();

// Run any queued ANSI requests that previously could not be sent
// (e.g. throttled duplicate request sent too soon after an earlier one).
AnsiRequestScheduler.RunSchedule (App?.Driver);

// Check for any size changes; this will cause SizeChanged events
SizeMonitor.Poll ();

// Layout and draw any views that need it
App?.LayoutAndDraw (false);
App?.LayoutAndDraw ();

// Update the cursor
App?.Navigation?.UpdateCursor ();
Expand Down
Loading
Loading