namespace Terminal.Gui; /// Event arguments for the events. public class ColorEventArgs : EventArgs { /// Initializes a new instance of public ColorEventArgs () { } /// The new Thickness. public Color Color { get; set; } /// The previous Thickness. public Color PreviousColor { get; set; } } /// The Color picker. public class ColorPicker : View { /// 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; /// Initializes a new instance of . public ColorPicker () { SetInitialProperties (); } private void SetInitialProperties () { HighlightStyle = Gui.HighlightStyle.PressedOutside | Gui.HighlightStyle.Pressed; CanFocus = true; AddCommands (); AddKeyBindings (); Width = Dim.Auto (minimumContentDim: _boxWidth * _cols); Height = Dim.Auto (minimumContentDim: _boxHeight * _rows); SetContentSize (new (_boxWidth * _cols, _boxHeight * _rows)); MouseClick += ColorPicker_MouseClick; } // TODO: Decouple Cursor from SelectedColor so that mouse press-and-hold can show the color under the cursor. private void ColorPicker_MouseClick (object sender, MouseEventEventArgs me) { // if (CanFocus) { Cursor = new Point (me.MouseEvent.Position.X / _boxWidth, me.MouseEvent.Position.Y / _boxHeight); SetFocus (); me.Handled = true; } } /// 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 (); } } } /// Cursor for the selected color. public Point Cursor { get => new (_selectColorIndex % _cols, _selectColorIndex / _cols); set { int colorIndex = value.Y * _cols + value.X; SelectedColor = (ColorName)colorIndex; } } /// Selected color. public ColorName SelectedColor { get => (ColorName)_selectColorIndex; set { if (value == (ColorName)_selectColorIndex) { return; } var prev = (ColorName)_selectColorIndex; _selectColorIndex = (int)value; ColorChanged?.Invoke ( this, new ColorEventArgs { PreviousColor = new Color (prev), Color = new Color (value) } ); SetNeedsDisplay (); } } /// Fired when a color is picked. public event EventHandler ColorChanged; /// Moves the selected item index to the previous column. /// public virtual bool MoveLeft () { if (Cursor.X > 0) { SelectedColor--; } return true; } /// Moves the selected item index to the next column. /// public virtual bool MoveRight () { if (Cursor.X < _cols - 1) { SelectedColor++; } return true; } /// Moves the selected item index to the previous row. /// public virtual bool MoveUp () { if (Cursor.Y > 0) { SelectedColor -= _cols; } return true; } /// Moves the selected item index to the next row. /// public virtual bool MoveDown () { if (Cursor.Y < _rows - 1) { SelectedColor += _cols; } return true; } /// public override void OnDrawContent (Rectangle viewport) { base.OnDrawContent (viewport); Driver.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; Driver.SetAttribute (new Attribute ((ColorName)foregroundColorIndex, (ColorName)colorIndex)); bool selected = x == Cursor.X && y == Cursor.Y; DrawColorBox (x, y, selected); colorIndex++; } } } /// Add the commands. private void AddCommands () { AddCommand (Command.Left, () => MoveLeft ()); AddCommand (Command.Right, () => MoveRight ()); AddCommand (Command.LineUp, () => MoveUp ()); AddCommand (Command.LineDown, () => MoveDown ()); } /// Add the KeyBindinds. private void AddKeyBindings () { KeyBindings.Add (Key.CursorLeft, Command.Left); KeyBindings.Add (Key.CursorRight, Command.Right); KeyBindings.Add (Key.CursorUp, Command.LineUp); KeyBindings.Add (Key.CursorDown, Command.LineDown); } /// 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); } } }