ColorPicker.cs 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. using System;
  2. using System.Reflection;
  3. using NStack;
  4. using static Terminal.Gui.SpinnerStyle;
  5. namespace Terminal.Gui {
  6. /// <summary>
  7. /// Event arguments for the <see cref="Color"/> events.
  8. /// </summary>
  9. public class ColorEventArgs : EventArgs {
  10. /// <summary>
  11. /// Initializes a new instance of <see cref="ColorEventArgs"/>
  12. /// </summary>
  13. public ColorEventArgs ()
  14. {
  15. }
  16. /// <summary>
  17. /// The new Thickness.
  18. /// </summary>
  19. public Color Color { get; set; }
  20. /// <summary>
  21. /// The previous Thickness.
  22. /// </summary>
  23. public Color PreviousColor { get; set; }
  24. }
  25. /// <summary>
  26. /// The <see cref="ColorPicker"/> <see cref="View"/> Color picker.
  27. /// </summary>
  28. public class ColorPicker : View {
  29. private int _selectColorIndex = (int)Color.Black;
  30. /// <summary>
  31. /// Columns of color boxes
  32. /// </summary>
  33. private int _cols = 8;
  34. /// <summary>
  35. /// Rows of color boxes
  36. /// </summary>
  37. private int _rows = 2;
  38. /// <summary>
  39. /// Width of a color box
  40. /// </summary>
  41. public int BoxWidth {
  42. get => _boxWidth;
  43. set {
  44. if (_boxWidth != value) {
  45. _boxWidth = value;
  46. SetNeedsLayout ();
  47. }
  48. }
  49. }
  50. private int _boxWidth = 4;
  51. /// <summary>
  52. /// Height of a color box
  53. /// </summary>
  54. public int BoxHeight {
  55. get => _boxHeight;
  56. set {
  57. if (_boxHeight != value) {
  58. _boxHeight = value;
  59. SetNeedsLayout ();
  60. }
  61. }
  62. }
  63. private int _boxHeight = 2;
  64. // Cursor runes.
  65. private static readonly Rune [] _cursorRunes = new Rune []
  66. {
  67. 0x250C, 0x2500, 0x2500, 0x2510,
  68. 0x2514, 0x2500, 0x2500, 0x2518
  69. };
  70. /// <summary>
  71. /// Cursor for the selected color.
  72. /// </summary>
  73. public Point Cursor {
  74. get {
  75. return new Point (_selectColorIndex % _cols, _selectColorIndex / _cols);
  76. }
  77. set {
  78. var colorIndex = value.Y * _cols + value.X;
  79. SelectedColor = (Color)colorIndex;
  80. }
  81. }
  82. /// <summary>
  83. /// Fired when a color is picked.
  84. /// </summary>
  85. public event EventHandler<ColorEventArgs> ColorChanged;
  86. /// <summary>
  87. /// Selected color.
  88. /// </summary>
  89. public Color SelectedColor {
  90. get {
  91. return (Color)_selectColorIndex;
  92. }
  93. set {
  94. Color prev = (Color)_selectColorIndex;
  95. _selectColorIndex = (int)value;
  96. ColorChanged?.Invoke (this, new ColorEventArgs () {
  97. PreviousColor = prev,
  98. Color = value,
  99. });
  100. SetNeedsDisplay ();
  101. }
  102. }
  103. /// <summary>
  104. /// Initializes a new instance of <see cref="ColorPicker"/>.
  105. /// </summary>
  106. public ColorPicker ()
  107. {
  108. SetInitialProperties ();
  109. }
  110. private void SetInitialProperties ()
  111. {
  112. CanFocus = true;
  113. AddCommands ();
  114. AddKeyBindings ();
  115. LayoutStarted += (o, a) => {
  116. Bounds = new Rect (Bounds.Location, new Size (_cols * BoxWidth, _rows * BoxHeight));
  117. };
  118. }
  119. /// <summary>
  120. /// Add the commands.
  121. /// </summary>
  122. private void AddCommands ()
  123. {
  124. AddCommand (Command.Left, () => MoveLeft ());
  125. AddCommand (Command.Right, () => MoveRight ());
  126. AddCommand (Command.LineUp, () => MoveUp ());
  127. AddCommand (Command.LineDown, () => MoveDown ());
  128. }
  129. /// <summary>
  130. /// Add the KeyBindinds.
  131. /// </summary>
  132. private void AddKeyBindings ()
  133. {
  134. AddKeyBinding (Key.CursorLeft, Command.Left);
  135. AddKeyBinding (Key.CursorRight, Command.Right);
  136. AddKeyBinding (Key.CursorUp, Command.LineUp);
  137. AddKeyBinding (Key.CursorDown, Command.LineDown);
  138. }
  139. ///<inheritdoc/>
  140. public override void Redraw (Rect bounds)
  141. {
  142. base.Redraw (bounds);
  143. Driver.SetAttribute (HasFocus ? ColorScheme.Focus : GetNormalColor ());
  144. var colorIndex = 0;
  145. for (var y = 0; y < (Bounds.Height / BoxHeight); y++) {
  146. for (var x = 0; x < (Bounds.Width / BoxWidth); x++) {
  147. var foregroundColorIndex = y == 0 ? colorIndex + _cols : colorIndex - _cols;
  148. Driver.SetAttribute (Driver.MakeAttribute ((Color)foregroundColorIndex, (Color)colorIndex));
  149. var selected = x == Cursor.X && y == Cursor.Y;
  150. DrawColorBox (x, y, selected);
  151. colorIndex++;
  152. }
  153. }
  154. }
  155. /// <summary>
  156. /// Draw a box for one color.
  157. /// </summary>
  158. /// <param name="x">X location.</param>
  159. /// <param name="y">Y location</param>
  160. /// <param name="selected"></param>
  161. private void DrawColorBox (int x, int y, bool selected)
  162. {
  163. var index = 0;
  164. for (var zoomedY = 0; zoomedY < BoxHeight; zoomedY++) {
  165. for (var zoomedX = 0; zoomedX < BoxWidth; zoomedX++) {
  166. Move (x * BoxWidth + zoomedX, y * BoxHeight + zoomedY);
  167. Driver.AddRune (' ');
  168. index++;
  169. }
  170. }
  171. if (selected) {
  172. DrawFocusRect (new Rect (x * BoxWidth, y * BoxHeight, BoxWidth, BoxHeight));
  173. }
  174. }
  175. private void DrawFocusRect (Rect rect)
  176. {
  177. var lc = new LineCanvas ();
  178. if (rect.Width == 1) {
  179. lc.AddLine (rect.Location, rect.Height, Orientation.Vertical, LineStyle.Dotted);
  180. } else if (rect.Height == 1) {
  181. lc.AddLine (rect.Location, rect.Width, Orientation.Horizontal, LineStyle.Dotted);
  182. } else {
  183. lc.AddLine (rect.Location, rect.Width, Orientation.Horizontal, LineStyle.Dotted);
  184. lc.AddLine (new Point (rect.Location.X, rect.Location.Y + rect.Height - 1), rect.Width, Orientation.Horizontal, LineStyle.Dotted);
  185. lc.AddLine (rect.Location, rect.Height, Orientation.Vertical, LineStyle.Dotted);
  186. lc.AddLine (new Point (rect.Location.X + rect.Width - 1, rect.Location.Y), rect.Height, Orientation.Vertical, LineStyle.Dotted);
  187. }
  188. foreach (var p in lc.GetMap ()) {
  189. AddRune (p.Key.X, p.Key.Y, p.Value);
  190. }
  191. }
  192. /// <summary>
  193. /// Moves the selected item index to the previous column.
  194. /// </summary>
  195. /// <returns></returns>
  196. public virtual bool MoveLeft ()
  197. {
  198. if (Cursor.X > 0) SelectedColor--;
  199. return true;
  200. }
  201. /// <summary>
  202. /// Moves the selected item index to the next column.
  203. /// </summary>
  204. /// <returns></returns>
  205. public virtual bool MoveRight ()
  206. {
  207. if (Cursor.X < _cols - 1) SelectedColor++;
  208. return true;
  209. }
  210. /// <summary>
  211. /// Moves the selected item index to the previous row.
  212. /// </summary>
  213. /// <returns></returns>
  214. public virtual bool MoveUp ()
  215. {
  216. if (Cursor.Y > 0) SelectedColor -= _cols;
  217. return true;
  218. }
  219. /// <summary>
  220. /// Moves the selected item index to the next row.
  221. /// </summary>
  222. /// <returns></returns>
  223. public virtual bool MoveDown ()
  224. {
  225. if (Cursor.Y < _rows - 1) SelectedColor += _cols;
  226. return true;
  227. }
  228. ///<inheritdoc/>
  229. public override bool ProcessKey (KeyEvent kb)
  230. {
  231. var result = InvokeKeybindings (kb);
  232. if (result != null)
  233. return (bool)result;
  234. return false;
  235. }
  236. ///<inheritdoc/>
  237. public override bool MouseEvent (MouseEvent me)
  238. {
  239. if (!me.Flags.HasFlag (MouseFlags.Button1Clicked) || !CanFocus) {
  240. return false;
  241. }
  242. SetFocus ();
  243. Cursor = new Point ((me.X - GetFramesThickness ().Left) / _boxWidth, (me.Y - GetFramesThickness ().Top) / _boxHeight);
  244. return true;
  245. }
  246. }
  247. }