namespace Terminal.Gui; /// The Color picker. public class ColorPicker16 : View { /// Initializes a new instance of . public ColorPicker16 () { SetInitialProperties (); } /// Columns of color boxes private readonly int _cols = 8; /// Rows of color boxes private readonly int _rows = 2; private int _boxHeight = 2; private int _boxWidth = 4; private int _selectColorIndex = (int)Color.Black; /// Height of a color box public int BoxHeight { get => _boxHeight; set { if (_boxHeight != value) { _boxHeight = value; Width = Dim.Auto (minimumContentDim: _boxWidth * _cols); Height = Dim.Auto (minimumContentDim: _boxHeight * _rows); SetContentSize (new (_boxWidth * _cols, _boxHeight * _rows)); SetNeedsLayout (); } } } /// Width of a color box public int BoxWidth { get => _boxWidth; set { if (_boxWidth != value) { _boxWidth = value; Width = Dim.Auto (minimumContentDim: _boxWidth * _cols); Height = Dim.Auto (minimumContentDim: _boxHeight * _rows); SetContentSize (new (_boxWidth * _cols, _boxHeight * _rows)); SetNeedsLayout (); } } } /// Fired when a color is picked. [CanBeNull] public event EventHandler ColorChanged; /// Cursor for the selected color. public Point Cursor { get => new (_selectColorIndex % _cols, _selectColorIndex / _cols); set { int colorIndex = value.Y * _cols + value.X; SelectedColor = (ColorName16)colorIndex; } } /// Moves the selected item index to the next row. /// private bool MoveDown (CommandContext ctx) { if (RaiseSelecting (ctx) == true) { return true; } if (Cursor.Y < _rows - 1) { SelectedColor += _cols; } return true; } /// Moves the selected item index to the previous column. /// private bool MoveLeft (CommandContext ctx) { if (RaiseSelecting (ctx) == true) { return true; } if (Cursor.X > 0) { SelectedColor--; } return true; } /// Moves the selected item index to the next column. /// private bool MoveRight (CommandContext ctx) { if (RaiseSelecting (ctx) == true) { return true; } if (Cursor.X < _cols - 1) { SelectedColor++; } return true; } /// Moves the selected item index to the previous row. /// private bool MoveUp (CommandContext ctx) { if (RaiseSelecting (ctx) == true) { return true; } if (Cursor.Y > 0) { SelectedColor -= _cols; } return true; } /// protected override bool OnDrawingContent (Rectangle viewport) { base.OnDrawingContent (viewport); SetAttribute (HasFocus ? ColorScheme.Focus : GetNormalColor ()); var colorIndex = 0; for (var y = 0; y < Math.Max (2, viewport.Height / BoxHeight); y++) { for (var x = 0; x < Math.Max (8, viewport.Width / BoxWidth); x++) { int foregroundColorIndex = y == 0 ? colorIndex + _cols : colorIndex - _cols; if (foregroundColorIndex > 15 || colorIndex > 15) { continue; } SetAttribute (new ((ColorName16)foregroundColorIndex, (ColorName16)colorIndex)); bool selected = x == Cursor.X && y == Cursor.Y; DrawColorBox (x, y, selected); colorIndex++; } } return true; } /// Selected color. public ColorName16 SelectedColor { get => (ColorName16)_selectColorIndex; set { if (value == (ColorName16)_selectColorIndex) { return; } _selectColorIndex = (int)value; ColorChanged?.Invoke ( this, new (value) ); SetNeedsDraw (); } } /// Add the commands. private void AddCommands () { AddCommand (Command.Left, (ctx) => MoveLeft (ctx)); AddCommand (Command.Right, (ctx) => MoveRight (ctx)); AddCommand (Command.Up, (ctx) => MoveUp (ctx)); AddCommand (Command.Down, (ctx) => MoveDown (ctx)); AddCommand (Command.Select, (ctx) => { bool set = false; if (ctx.Data is MouseEventArgs me) { Cursor = new (me.Position.X / _boxWidth, me.Position.Y / _boxHeight); set = true; } return RaiseAccepting (ctx) == true || set; }); } /// Add the KeyBindings. private void AddKeyBindings () { KeyBindings.Add (Key.CursorLeft, Command.Left); KeyBindings.Add (Key.CursorRight, Command.Right); KeyBindings.Add (Key.CursorUp, Command.Up); KeyBindings.Add (Key.CursorDown, Command.Down); } // TODO: Decouple Cursor from SelectedColor so that mouse press-and-hold can show the color under the cursor. /// Draw a box for one color. /// X location. /// Y location /// private void DrawColorBox (int x, int y, bool selected) { var index = 0; for (var zoomedY = 0; zoomedY < BoxHeight; zoomedY++) { for (var zoomedX = 0; zoomedX < BoxWidth; zoomedX++) { Move (x * BoxWidth + zoomedX, y * BoxHeight + zoomedY); Driver?.AddRune ((Rune)' '); index++; } } if (selected) { DrawFocusRect (new (x * BoxWidth, y * BoxHeight, BoxWidth, BoxHeight)); } } private void DrawFocusRect (Rectangle rect) { var lc = new LineCanvas (); if (rect.Width == 1) { lc.AddLine (rect.Location, rect.Height, Orientation.Vertical, LineStyle.Dotted); } else if (rect.Height == 1) { lc.AddLine (rect.Location, rect.Width, Orientation.Horizontal, LineStyle.Dotted); } else { lc.AddLine (rect.Location, rect.Width, Orientation.Horizontal, LineStyle.Dotted); lc.AddLine ( rect.Location with { Y = rect.Location.Y + rect.Height - 1 }, rect.Width, Orientation.Horizontal, LineStyle.Dotted ); lc.AddLine (rect.Location, rect.Height, Orientation.Vertical, LineStyle.Dotted); lc.AddLine ( rect.Location with { X = rect.Location.X + rect.Width - 1 }, rect.Height, Orientation.Vertical, LineStyle.Dotted ); } foreach (KeyValuePair p in lc.GetMap ()) { AddRune (p.Key.X, p.Key.Y, p.Value); } } private void SetInitialProperties () { HighlightStyle = HighlightStyle.PressedOutside | HighlightStyle.Pressed; CanFocus = true; AddCommands (); AddKeyBindings (); Width = Dim.Auto (minimumContentDim: _boxWidth * _cols); Height = Dim.Auto (minimumContentDim: _boxHeight * _rows); SetContentSize (new (_boxWidth * _cols, _boxHeight * _rows)); } }