KeyBindingsDialog.cs 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Threading.Tasks;
  6. using Terminal.Gui;
  7. namespace UICatalog {
  8. class KeyBindingsDialog : Dialog {
  9. static Dictionary<Command,Key> CurrentBindings = new Dictionary<Command,Key>();
  10. private Command[] commands;
  11. private ListView commandsListView;
  12. private Label keyLabel;
  13. /// <summary>
  14. /// Tracks views as they are created in UICatalog so that their keybindings can
  15. /// be managed.
  16. /// </summary>
  17. private class ViewTracker {
  18. public static ViewTracker Instance;
  19. /// <summary>
  20. /// All views seen so far and a bool to indicate if we have applied keybindings to them
  21. /// </summary>
  22. Dictionary<View, bool> knownViews = new Dictionary<View, bool> ();
  23. private object lockKnownViews = new object ();
  24. private Dictionary<Command, Key> keybindings;
  25. public ViewTracker (View top)
  26. {
  27. RecordView (top);
  28. // Refresh known windows
  29. Application.MainLoop.AddTimeout (TimeSpan.FromMilliseconds (100), (m) => {
  30. lock (lockKnownViews) {
  31. RecordView (Application.Top);
  32. ApplyKeyBindingsToAllKnownViews ();
  33. }
  34. return true;
  35. });
  36. }
  37. private void RecordView (View view)
  38. {
  39. if (!knownViews.ContainsKey (view)) {
  40. knownViews.Add (view, false);
  41. }
  42. // may already have subviews that were added to it
  43. // before we got to it
  44. foreach (var sub in view.Subviews) {
  45. RecordView (sub);
  46. }
  47. // TODO: BUG: Based on my new understanding of Added event I think this is wrong
  48. // (and always was wrong). Parents don't get to be told when new views are added
  49. // to them
  50. view.Added += (s,e)=>RecordView(e.Child);
  51. }
  52. internal static void Initialize ()
  53. {
  54. Instance = new ViewTracker (Application.Top);
  55. }
  56. internal void StartUsingNewKeyMap (Dictionary<Command, Key> currentBindings)
  57. {
  58. lock (lockKnownViews) {
  59. // change our knowledge of what keys to bind
  60. this.keybindings = currentBindings;
  61. // Mark that we have not applied the key bindings yet to any views
  62. foreach (var view in knownViews.Keys) {
  63. knownViews [view] = false;
  64. }
  65. }
  66. }
  67. private void ApplyKeyBindingsToAllKnownViews ()
  68. {
  69. if(keybindings == null) {
  70. return;
  71. }
  72. // Key is the view Value is whether we have already done it
  73. foreach (var viewDone in knownViews) {
  74. var view = viewDone.Key;
  75. var done = viewDone.Value;
  76. if (done) {
  77. // we have already applied keybindings to this view
  78. continue;
  79. }
  80. var supported = new HashSet<Command>(view.GetSupportedCommands ());
  81. foreach (var kvp in keybindings) {
  82. // if the view supports the keybinding
  83. if(supported.Contains(kvp.Key))
  84. {
  85. // if the key was bound to any other commands clear that
  86. view.ClearKeybinding (kvp.Key);
  87. view.AddKeyBinding (kvp.Value,kvp.Key);
  88. }
  89. // mark that we have done this view so don't need to set keybindings again on it
  90. knownViews [view] = true;
  91. }
  92. }
  93. }
  94. }
  95. public KeyBindingsDialog () : base()
  96. {
  97. Title = "Keybindings";
  98. Height = 50;
  99. Width = 10;
  100. if (ViewTracker.Instance == null) {
  101. ViewTracker.Initialize ();
  102. }
  103. // known commands that views can support
  104. commands = Enum.GetValues (typeof (Command)).Cast<Command>().ToArray();
  105. commandsListView = new ListView (commands) {
  106. Width = Dim.Percent (50),
  107. Height = Dim.Percent (100) - 1,
  108. };
  109. Add (commandsListView);
  110. keyLabel = new Label () {
  111. Text = "Key: None",
  112. Width = Dim.Fill(),
  113. X = Pos.Percent(50),
  114. Y = 0
  115. };
  116. Add (keyLabel);
  117. var btnChange = new Button ("Ch_ange") {
  118. X = Pos.Percent (50),
  119. Y = 1,
  120. };
  121. Add (btnChange);
  122. btnChange.Clicked += RemapKey;
  123. var close = new Button ("Ok");
  124. close.Clicked += (s,e) => {
  125. Application.RequestStop ();
  126. ViewTracker.Instance.StartUsingNewKeyMap (CurrentBindings);
  127. };
  128. AddButton (close);
  129. var cancel = new Button ("Cancel");
  130. cancel.Clicked += (s,e)=>Application.RequestStop();
  131. AddButton (cancel);
  132. // Register event handler as the last thing in constructor to prevent early calls
  133. // before it is even shown (e.g. OnEnter)
  134. commandsListView.SelectedItemChanged += CommandsListView_SelectedItemChanged;
  135. // Setup to show first ListView entry
  136. SetTextBoxToShowBinding (commands.First());
  137. }
  138. private void RemapKey (object sender, EventArgs e)
  139. {
  140. var cmd = commands [commandsListView.SelectedItem];
  141. Key? key = null;
  142. // prompt user to hit a key
  143. var dlg = new Dialog () { Title = "Enter Key" };
  144. dlg.KeyPress += (s, k) => {
  145. key = k.KeyEvent.Key;
  146. Application.RequestStop ();
  147. };
  148. Application.Run (dlg);
  149. if(key.HasValue) {
  150. CurrentBindings [cmd] = key.Value;
  151. SetTextBoxToShowBinding (cmd);
  152. }
  153. }
  154. private void SetTextBoxToShowBinding (Command cmd)
  155. {
  156. if (CurrentBindings.ContainsKey (cmd)) {
  157. keyLabel.Text = "Key: " + CurrentBindings [cmd].ToString ();
  158. } else {
  159. keyLabel.Text = "Key: None";
  160. }
  161. SetNeedsDisplay ();
  162. }
  163. private void CommandsListView_SelectedItemChanged (object sender, ListViewItemEventArgs obj)
  164. {
  165. SetTextBoxToShowBinding ((Command)obj.Value);
  166. }
  167. }
  168. }