#nullable enable
namespace Terminal.Gui;
/// The Color picker.
public class ColorPicker16 : View
{
/// Initializes a new instance of .
public ColorPicker16 () { SetInitialProperties (); }
/// Columns of color boxes
private const int COLS = 8;
/// Rows of color boxes
private const 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.
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 (ICommandContext? commandContext)
{
if (RaiseSelecting (commandContext) == true)
{
return true;
}
if (Cursor.Y < ROWS - 1)
{
SelectedColor += COLS;
}
return true;
}
/// Moves the selected item index to the previous column.
///
private bool MoveLeft (ICommandContext? commandContext)
{
if (RaiseSelecting (commandContext) == true)
{
return true;
}
if (Cursor.X > 0)
{
SelectedColor--;
}
return true;
}
/// Moves the selected item index to the next column.
///
private bool MoveRight (ICommandContext? commandContext)
{
if (RaiseSelecting (commandContext) == true)
{
return true;
}
if (Cursor.X < COLS - 1)
{
SelectedColor++;
}
return true;
}
/// Moves the selected item index to the previous row.
///
private bool MoveUp (ICommandContext? commandContext)
{
if (RaiseSelecting (commandContext) == true)
{
return true;
}
if (Cursor.Y > 0)
{
SelectedColor -= COLS;
}
return true;
}
///
protected override bool OnDrawingContent ()
{
SetAttribute (HasFocus ? GetFocusColor () : 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) =>
{
var set = false;
if (ctx is CommandContext { Binding.MouseEventArgs: { } } mouseCommandContext)
{
Cursor = new (mouseCommandContext.Binding.MouseEventArgs.Position.X / _boxWidth, mouseCommandContext.Binding.MouseEventArgs.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)
{
for (var zoomedY = 0; zoomedY < BoxHeight; zoomedY++)
{
for (var zoomedX = 0; zoomedX < BoxWidth; zoomedX++)
{
AddRune (x * BoxWidth + zoomedX, y * BoxHeight + zoomedY, (Rune)' ');
}
}
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));
}
}