mirror of
https://github.com/gui-cs/Terminal.Gui.git
synced 2025-12-26 15:57:56 +01:00
216 lines
6.9 KiB
C#
216 lines
6.9 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Collections.ObjectModel;
|
|
using System.Linq;
|
|
using Terminal.Gui;
|
|
|
|
namespace UICatalog;
|
|
|
|
internal class KeyBindingsDialog : Dialog
|
|
{
|
|
// TODO: Update to use Key instead of KeyCode
|
|
private static readonly Dictionary<Command, KeyCode> CurrentBindings = new ();
|
|
|
|
private readonly ObservableCollection<Command> _commands;
|
|
private readonly ListView _commandsListView;
|
|
private readonly Label _keyLabel;
|
|
|
|
public KeyBindingsDialog ()
|
|
{
|
|
Title = "Keybindings";
|
|
|
|
//Height = 50;
|
|
//Width = 10;
|
|
if (ViewTracker.Instance == null)
|
|
{
|
|
ViewTracker.Initialize ();
|
|
}
|
|
|
|
// known commands that views can support
|
|
_commands = new (Enum.GetValues (typeof (Command)).Cast<Command> ().ToArray ());
|
|
|
|
_commandsListView = new ListView
|
|
{
|
|
Width = Dim.Percent (50),
|
|
Height = Dim.Percent (100) - 1,
|
|
Source = new ListWrapper<Command> (_commands),
|
|
SelectedItem = 0
|
|
};
|
|
|
|
Add (_commandsListView);
|
|
|
|
_keyLabel = new Label { Text = "Key: None", Width = Dim.Fill (), X = Pos.Percent (50), Y = 0 };
|
|
Add (_keyLabel);
|
|
|
|
var btnChange = new Button { X = Pos.Percent (50), Y = 1, Text = "Ch_ange" };
|
|
Add (btnChange);
|
|
btnChange.Accept += RemapKey;
|
|
|
|
var close = new Button { Text = "Ok" };
|
|
|
|
close.Accept += (s, e) =>
|
|
{
|
|
Application.RequestStop ();
|
|
ViewTracker.Instance.StartUsingNewKeyMap (CurrentBindings);
|
|
};
|
|
AddButton (close);
|
|
|
|
var cancel = new Button { Text = "Cancel" };
|
|
cancel.Accept += (s, e) => Application.RequestStop ();
|
|
AddButton (cancel);
|
|
|
|
// Register event handler as the last thing in constructor to prevent early calls
|
|
// before it is even shown (e.g. OnEnter)
|
|
_commandsListView.SelectedItemChanged += CommandsListView_SelectedItemChanged;
|
|
|
|
// Setup to show first ListView entry
|
|
SetTextBoxToShowBinding (_commands.First ());
|
|
}
|
|
|
|
private void CommandsListView_SelectedItemChanged (object sender, ListViewItemEventArgs obj) { SetTextBoxToShowBinding ((Command)obj.Value); }
|
|
|
|
private void RemapKey (object sender, EventArgs e)
|
|
{
|
|
Command cmd = _commands [_commandsListView.SelectedItem];
|
|
KeyCode? key = null;
|
|
|
|
// prompt user to hit a key
|
|
var dlg = new Dialog { Title = "Enter Key" };
|
|
|
|
dlg.KeyDown += (s, k) =>
|
|
{
|
|
key = k.KeyCode;
|
|
Application.RequestStop ();
|
|
};
|
|
Application.Run (dlg);
|
|
dlg.Dispose ();
|
|
|
|
if (key.HasValue)
|
|
{
|
|
CurrentBindings [cmd] = key.Value;
|
|
SetTextBoxToShowBinding (cmd);
|
|
}
|
|
}
|
|
|
|
private void SetTextBoxToShowBinding (Command cmd)
|
|
{
|
|
if (CurrentBindings.ContainsKey (cmd))
|
|
{
|
|
_keyLabel.Text = "Key: " + CurrentBindings [cmd];
|
|
}
|
|
else
|
|
{
|
|
_keyLabel.Text = "Key: None";
|
|
}
|
|
|
|
SetNeedsDisplay ();
|
|
}
|
|
|
|
/// <summary>Tracks views as they are created in UICatalog so that their keybindings can be managed.</summary>
|
|
private class ViewTracker
|
|
{
|
|
/// <summary>All views seen so far and a bool to indicate if we have applied keybindings to them</summary>
|
|
private readonly Dictionary<View, bool> _knownViews = new ();
|
|
|
|
private readonly object _lockKnownViews = new ();
|
|
private Dictionary<Command, KeyCode> _keybindings;
|
|
|
|
private ViewTracker (View top)
|
|
{
|
|
RecordView (top);
|
|
|
|
// Refresh known windows
|
|
Application.AddTimeout (
|
|
TimeSpan.FromMilliseconds (100),
|
|
() =>
|
|
{
|
|
lock (_lockKnownViews)
|
|
{
|
|
RecordView (Application.Top);
|
|
|
|
ApplyKeyBindingsToAllKnownViews ();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
);
|
|
}
|
|
|
|
public static ViewTracker Instance { get; private set; }
|
|
internal static void Initialize () { Instance = new ViewTracker (Application.Top); }
|
|
|
|
internal void StartUsingNewKeyMap (Dictionary<Command, KeyCode> currentBindings)
|
|
{
|
|
lock (_lockKnownViews)
|
|
{
|
|
// change our knowledge of what keys to bind
|
|
_keybindings = currentBindings;
|
|
|
|
// Mark that we have not applied the key bindings yet to any views
|
|
foreach (View view in _knownViews.Keys)
|
|
{
|
|
_knownViews [view] = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void ApplyKeyBindingsToAllKnownViews ()
|
|
{
|
|
if (_keybindings == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Key is the view Value is whether we have already done it
|
|
foreach (KeyValuePair<View, bool> viewDone in _knownViews)
|
|
{
|
|
View view = viewDone.Key;
|
|
bool done = viewDone.Value;
|
|
|
|
if (done)
|
|
{
|
|
// we have already applied keybindings to this view
|
|
continue;
|
|
}
|
|
|
|
HashSet<Command> supported = new (view.GetSupportedCommands ());
|
|
|
|
foreach (KeyValuePair<Command, KeyCode> kvp in _keybindings)
|
|
{
|
|
// if the view supports the keybinding
|
|
if (supported.Contains (kvp.Key))
|
|
{
|
|
// if the key was bound to any other commands clear that
|
|
view.KeyBindings.Remove (kvp.Value);
|
|
view.KeyBindings.Add (kvp.Value, kvp.Key);
|
|
}
|
|
|
|
// mark that we have done this view so don't need to set keybindings again on it
|
|
_knownViews [view] = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void RecordView (View view)
|
|
{
|
|
if (!_knownViews.ContainsKey (view))
|
|
{
|
|
_knownViews.Add (view, false);
|
|
}
|
|
|
|
// may already have subviews that were added to it
|
|
// before we got to it
|
|
foreach (View sub in view.Subviews)
|
|
{
|
|
RecordView (sub);
|
|
}
|
|
|
|
// TODO: BUG: Based on my new understanding of Added event I think this is wrong
|
|
// (and always was wrong). Parents don't get to be told when new views are added
|
|
// to them
|
|
|
|
view.Added += (s, e) => RecordView (e.Child);
|
|
}
|
|
}
|
|
}
|