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