lazarus-ccr/components/colorpalette/colorpalette.pas

1346 lines
41 KiB
ObjectPascal

{
/***************************************************************************
ColorPalette.pas
***************************************************************************/
*****************************************************************************
* *
* See the file COPYING.modifiedLGPL, included in this distribution, *
* for details about the copyright. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
* *
*****************************************************************************
Author: Tom Gregorovic (_tom_@centrum.cz)
Abstract:
Color palette grid with custom palette support.
The OnColorPick event is fired when user picks a color.
The LoadPalette procedure loads custom palette.
Custom palette example:
$COLS 8
# sets count of palette grid columns
0,0,0
# inserts color r,g,b [optional color name]
255,255,255 Pure white
$NONE
# inserts empty palette grid cell
$BLENDWB 128,128,128 3
# creates color gradient white -> color -> black with specified steps
}
unit ColorPalette;
{$ifdef fpc}
{$mode objfpc}{$H+}
{$endif}
interface
uses
Classes, SysUtils, LResources, LCLVersion, Controls, Forms, Graphics, Math,
LCLType;
type
TPickMode = (
pmDefault, // Select color at mouse-down, ColorPick event at mouse-up if at same pos
pmImmediate, // Select color and ColorPick event at mouse-down
pmContinuous // Select color at mouse-down and mouse-move, ColorPick event at mouse-up
);
TPaletteKind = (pkStandardPalette, pkExtendedPalette, pkSystemPalette,
pkStandardAndSystemPalette, pkExtendedAndSystemPalette,
pkGradientPalette, pkHTMLPalette, pkWebSafePalette);
TPaletteSelectionKind = (pskNone, pskThin, pskThinInverted, pskThick, pskThickInverted);
TPaletteItem = (
piColors, piColumnCount, piFlipped,
piButtonBorder, piButtonSize, piButtonDistance,
piSelKind, piSelColor
);
TPaletteItems = set of TPaletteItem;
const
piAll = [piColors, piColumnCount, piFlipped,
piButtonBorder, piButtonSize, piButtonDistance,
piSelKind, piSelColor
];
type
{ TCustomColorPalette }
TColorMouseEvent = procedure (Sender: TObject; AColor: TColor; Shift: TShiftState) of object;
TColorPaletteHintEvent = procedure (Sender: TObject; AColor: TColor; var AText: String) of object;
TCustomColorPalette = class(TGraphicControl)
private
FSizeToLastCol: Integer;
FButtonHeight: Integer;
FButtonWidth: Integer;
FButtonBorderColor: TColor;
FButtonDistance: Integer;
FCols: Integer;
FOnColorMouseMove: TColorMouseEvent;
FOnColorPick: TColorMouseEvent;
FOnGetHintText: TColorPaletteHintEvent;
FRows: Integer;
FColors: TStringList;
FPickedIndex: Integer;
FPickMode: TPickMode;
FMousePt: TPoint;
FMouseIndex: Integer;
FPrevMouseIndex: Integer;
FStoredShift: TShiftState;
FShowColorHint: Boolean;
FSelectionColor: TColor;
FSelectionKind: TPaletteSelectionKind;
FSavedHint: String;
FPaletteKind: TPaletteKind;
FGradientSteps: Byte;
FUseSpacers: Boolean;
FMargin: Integer;
FFlipped: Boolean;
function GetColorCount: Integer;
function GetColors(AIndex: Integer): TColor;
function GetColorNames(AIndex: Integer): String;
function GetMouseColor: TColor;
function GetPickedColor: TColor;
procedure SetButtonBorderColor(const AValue: TColor);
procedure SetButtonDistance(const AValue: Integer);
procedure SetButtonHeight(const AValue: Integer);
procedure SetButtonWidth(const AValue: Integer);
procedure SetColorNames(AIndex: Integer; const AValue: String);
procedure SetColors(AIndex: Integer; const AValue: TColor);
procedure SetCols(AValue: Integer);
procedure SetFlipped(AValue: Boolean);
procedure SetGradientSteps(AValue: Byte);
procedure SetPaletteKind(AValue: TPaletteKind);
procedure SetPickedIndex(AValue: Integer);
procedure SetSelectionColor(AValue: TColor);
procedure SetSelectionKind(AValue: TPaletteSelectionKind);
procedure SetUseSpacers(AValue: Boolean);
protected
procedure BlendWBColor(AColor: TColor; Steps: Integer);
procedure ColorPick(AIndex: Integer; Shift: TShiftState); virtual;
procedure ColorMouseMove(AColor: TColor; Shift: TShiftState); virtual;
procedure DoAddColor(AColor: TColor; AColorName: String = ''); virtual;
{$IF LCL_FULLVERSION >= 1080000}
procedure DoAutoAdjustLayout(const AMode: TLayoutAdjustmentPolicy;
const AXProportion, AYProportion: Double); override;
{$ENDIF}
procedure DoColorPick(AColor: TColor; AShift: TShiftState); virtual;
procedure DoDeleteColor(AIndex: Integer); virtual;
procedure DoInsertColor(AIndex: Integer; AColor: TColor; AColorName: String = ''); virtual;
function GetCellHeight: Integer; inline;
function GetCellWidth: Integer; inline;
function GetColorIndex(X,Y: Integer): Integer;
function GetHintText(AIndex: Integer): String; virtual;
procedure Loaded; override;
procedure MouseDown(Button: TMouseButton; Shift:TShiftState; X, Y:Integer); override;
procedure MouseEnter; override;
procedure MouseLeave; override;
procedure MouseMove(Shift:TShiftState; X, Y:Integer); override;
procedure MouseUp(Button: TMouseButton; Shift:TShiftState; X, Y:Integer); override;
procedure UpdateSize; virtual;
property ButtonBorderColor: TColor read FButtonBorderColor write SetButtonBorderColor default clBlack;
property ButtonDistance: Integer read FButtonDistance write SetButtonDistance default 0;
property ButtonWidth: Integer read FButtonWidth write SetButtonWidth;
property ButtonHeight: Integer read FButtonHeight write SetButtonHeight;
property ColumnCount: Integer read FCols write SetCols;
property Flipped: Boolean read FFlipped write SetFlipped default false;
property GradientSteps: Byte read FGradientSteps write SetGradientSteps default 3;
property PaletteKind: TPaletteKind read FPaletteKind write SetPaletteKind default pkStandardPalette;
property PickedIndex: Integer read FPickedIndex write SetPickedIndex default -1;
property PickMode: TPickMode read FPickMode write FPickMode default pmImmediate;
property SelectionColor: TColor read FSelectionColor write SetSelectionColor default clBlack;
property SelectionKind: TPaletteSelectionKind read FSelectionKind write SetSelectionKind default pskNone;
property ShowColorHint: Boolean read FShowColorHint write FShowColorHint default true;
property UseSpacers: Boolean read FUseSpacers write SetUseSpacers default true;
property OnColorPick: TColorMouseEvent read FOnColorPick write FOnColorPick;
property OnColorMouseMove: TColorMouseEvent read FOnColorMouseMove write FOnColorMouseMove; deprecated 'Use OnMouseMove() and MouseColor';
property OnGetHintText: TColorPaletteHintEvent read FOnGetHintText write FOnGetHintText;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure Paint; override;
procedure AddColor(AColor: TColor; AColorName: String = '');
procedure ClearColors;
procedure DeleteColor(AIndex: Integer);
procedure InsertColor(AIndex: Integer; AColor: TColor; AColorName: String = '');
procedure LoadPalette(const FileName: String;
AItems: TPaletteItems = [piColors, piColumnCount]);
procedure SavePalette(const FileName: String);
property Colors[Index: Integer]: TColor read GetColors write SetColors;
property ColorNames[Index: Integer]: String read GetColorNames write SetColorNames;
property ColorCount: Integer read GetColorCount;
property MouseIndex: Integer read FMouseIndex;
property MouseColor: TColor read GetMouseColor;
property PickedColor: TColor read GetPickedColor;
property Height stored False;
property Width stored False;
end;
TColorPalette = class(TCustomColorPalette)
published
// inherited from TCustomColorPalette
property ButtonBorderColor;
property ButtonDistance;
property ButtonHeight;
property ButtonWidth;
property ColumnCount;
property Flipped;
property GradientSteps;
property PaletteKind;
property PickedIndex;
property PickMode;
property SelectionColor;
property SelectionKind;
property ShowColorHint;
property UseSpacers;
property OnColorMouseMove;
property OnColorPick;
property OnGetHintText;
// inherited from TCustomColorPalette's ancestors
property Align;
property Anchors;
property BorderSpacing;
property Color default clNone;
property Constraints;
property DragCursor;
property DragKind;
property DragMode;
property Enabled;
property Hint;
property ParentColor;
property ParentShowHint;
property PopupMenu;
property ShowHint;
property Visible;
property OnChangeBounds;
property OnClick;
property OnDblClick;
property OnMouseDown;
property OnMouseMove;
property OnMouseUp;
property OnMouseEnter;
property OnMouseLeave;
property OnResize;
end;
procedure Register;
implementation
{$R colorpalette.res}
uses
LCLIntf;
const
SELKIND_NAMES: Array[TPaletteSelectionKind] of String = (
'NONE', 'THIN', 'THIN-INV', 'THICK', 'THICK-INV'
);
procedure Register;
begin
RegisterComponents('Misc', [TColorPalette]);
end;
{ TCustomColorPalette }
constructor TCustomColorPalette.Create(AOwner: TComponent);
begin
inherited;
ControlStyle := ControlStyle + [csFixedWidth, csFixedHeight];
Color := clNone;
FColors := TStringList.Create;
FButtonBorderColor := clBlack;
FButtonDistance := 0;
FMargin := 1;
FButtonHeight := 12;
FButtonWidth := 12;
FPrevMouseIndex := -1;
FMouseIndex := -1;
FPickMode := pmImmediate;
FShowColorHint := true;
FGradientSteps := 3;
FUseSpacers := true;
FCols := 8;
SetPaletteKind(pkStandardPalette);
UpdateSize;
end;
destructor TCustomColorPalette.Destroy;
begin
FColors.Free;
inherited;
end;
procedure TCustomColorPalette.AddColor(AColor: TColor; AColorName: String = '');
begin
DoAddColor(AColor, AColorName);
UpdateSize;
Invalidate;
end;
procedure TCustomColorPalette.BlendWBColor(AColor: TColor; Steps: Integer);
var
I: Integer;
R, G, B, NR, NG, NB: Byte;
begin
RedGreenBlue(AColor, R, G, B);
for I := 1 to Steps do
begin
NR := Round((R * I + 255 * (Steps + 1 - I)) / (Steps + 1));
NG := Round((G * I + 255 * (Steps + 1 - I)) / (Steps + 1));
NB := Round((B * I + 255 * (Steps + 1 - I)) / (Steps + 1));
DoAddColor(RGBToColor(NR, NG, NB));
end;
DoAddColor(AColor);
for I := Steps downto 1 do
begin
NR := Round(R * I / (Steps + 1));
NG := Round(G * I / (Steps + 1));
NB := Round(B * I / (Steps + 1));
DoAddColor(RGBToColor(NR, NG, NB));
end;
end;
procedure TCustomColorPalette.ClearColors;
begin
FColors.Clear;
end;
procedure TCustomColorPalette.ColorPick(AIndex: Integer; Shift: TShiftState);
var
c: TColor;
begin
FPickedIndex := AIndex;
c := GetColors(AIndex);
DoColorPick(c, Shift);
Invalidate;
end;
procedure TCustomColorPalette.ColorMouseMove(AColor: TColor; Shift: TShiftState);
begin
if Assigned(FOnColorMouseMove) then
FOnColorMouseMove(Self, AColor, Shift);
end;
procedure TCustomColorPalette.DeleteColor(AIndex: Integer);
begin
DoDeleteColor(AIndex);
UpdateSize;
Invalidate;
end;
procedure TCustomColorPalette.DoAddColor(AColor: TColor; AColorName: String = '');
begin
FColors.AddObject(AColorName, TObject(PtrInt(AColor)));
end;
{$IF LCL_FULLVERSION >= 1080000}
procedure TCustomColorPalette.DoAutoAdjustLayout(
const AMode: TLayoutAdjustmentPolicy; const AXProportion, AYProportion: Double);
begin
inherited DoAutoAdjustLayout(AMode, AXProportion, AYProportion);
if AMode = lapAutoAdjustForDPI then
begin
FButtonWidth := Round(FButtonWidth * AXProportion);
FButtonHeight := Round(FButtonHeight * AYProportion);
FButtonDistance := Round(FButtonDistance * AXProportion);
UpdateSize;
Invalidate;
end;
end;
{$ENDIF}
procedure TCustomColorPalette.DoColorPick(AColor: TColor; AShift: TShiftState);
begin
if Assigned(FOnColorPick) then
FOnColorPick(Self, AColor, AShift);
end;
procedure TCustomColorPalette.DoDeleteColor(AIndex: Integer);
begin
if (AIndex < 0) or (AIndex >= FColors.Count) then
exit;
FColors.Delete(AIndex);
end;
procedure TCustomColorPalette.DoInsertColor(AIndex: Integer; AColor: TColor;
AColorName: String = '');
begin
FColors.InsertObject(AIndex, AColorName, TObject(PtrInt(AColor)));
end;
function TCustomColorPalette.GetColorCount: Integer;
begin
Result := FColors.Count;
end;
function TCustomColorPalette.GetColorNames(AIndex: Integer): String;
begin
if (AIndex >= 0) and (AIndex < FColors.Count) then
begin
Result := FColors.Strings[AIndex];
if Result = '' then
begin
Result := ColorToString(GetColors(AIndex));
if FUseSpacers and (Result = ColorToString(clNone)) then
Result := '';
end;
end else
Result := '';
end;
function TCustomColorPalette.GetMouseColor: TColor;
begin
Result := GetColors(FMouseIndex);
end;
function TCustomColorPalette.GetColors(AIndex: Integer): TColor;
begin
if (AIndex >= 0) and (AIndex < FColors.Count) then
Result := TColor(PtrUInt(FColors.Objects[AIndex]))
else
Result := clNone;
end;
// Distance between top edge of a cell to the top edge of the next one
function TCustomColorPalette.GetCellHeight: Integer;
begin
Result := FButtonHeight + FButtonDistance;
end;
// Distance between left edge of a cell to the left edge of the next one
function TCustomColorPalette.GetCellWidth: Integer;
begin
Result := FButtonWidth + FButtonDistance;
end;
function TCustomColorPalette.GetColorIndex(X,Y: Integer): Integer;
var
W, H: Integer;
begin
Result := -1;
if FFlipped then
begin
if (Y < 0) or (Y >= FSizeToLastCol-1) then exit;
end else
if (X < 0) or (X >= FSizeToLastCol-1) then exit;
W := GetCellWidth;
H := GetCellHeight;
dec(X, FMargin);
dec(Y, FMargin);
if (FButtonDistance = 0) and (FButtonBorderColor <> clNone) then
begin
dec(W);
dec(H);
if FFlipped then
Result := Y div H + X div W * FCols else
Result := X div W + Y div H * FCols;
end else
begin
if FFlipped then
begin
Result := Y div H + X div W * FCols;
// Do not consider the space between the buttons
if (Y mod H > FButtonWidth) or (X mod W > FButtonWidth) then
Result := -1;
end else
begin
Result := X div W + Y div H * FCols;
// Do not consider the space between the buttons
if (X mod W > FButtonWidth) or (Y mod H > FButtonWidth) then
Result := -1
end;
end;
if (Result >= FColors.Count) or (Result < 0) then
Result := -1;
end;
function TCustomColorPalette.GetHintText(AIndex: Integer): string;
const
INDENT = '* ';
MASK = '%0:s' + LineEnding + '%4:sRed: %1:d' + LineEnding + '%4:sGreen: %2:d' + LineEnding + '%4:sBlue: %3:d';
var
C: TColor;
begin
C := GetColors(AIndex);
if C = clNone then
begin
if FUseSpacers then
Result := ''
else
Result := 'None'
end else
begin
Result := GetColorNames(AIndex);
if (Result <> '') and (Result[1] = 'c') and (Result[2] = 'l') then
Delete(Result, 1, 2);
Result := Format(MASK, [
Result, Red(C), Green(C), Blue(C), INDENT]
);
end;
if Assigned(FOnGetHintText) then
FOnGetHintText(Self, C, Result);
end;
function TCustomColorPalette.GetPickedColor: TColor;
begin
Result := GetColors(FPickedIndex);
end;
procedure TCustomColorPalette.InsertColor(AIndex: Integer; AColor: TColor;
AColorName: String = '');
begin
DoInsertColor(AIndex, AColor, AColorName);
end;
procedure TCustomColorPalette.Loaded;
begin
inherited;
UpdateSize;
end;
procedure TCustomColorPalette.LoadPalette(const FileName: String;
AItems: TPaletteItems = [piColors, piColumnCount]);
var
F: TextFile;
Line: String;
ucline: String;
C: TColor;
clrName: String;
p, steps: Integer;
sk: TPaletteSelectionKind;
procedure ParseColor(S: String; out AColor: TColor; out Steps: Integer;
out ColorName: String);
var
counter: Integer;
tmp: String;
P: PChar;
R,G,B: Integer;
begin
R := 0;
G := 0;
B := 0;
Steps := 0;
ColorName := '';
tmp := '';
P := PChar(S);
counter := 0;
// Skip leading spaces
while (P^ = ' ') do inc(P);
while P^ <> #0 do begin
case P^ of
' ': begin
if counter = 2 then begin
B := StrToIntDef(tmp, B);
inc(counter);
tmp := '';
while P^ = ' ' do inc(P);
end else
if counter > 2 then
begin
tmp := tmp + ' ';
inc(P);
end;
end;
',': begin
case counter of
0: R := StrToIntDef(tmp, R);
1: G := StrToIntDef(tmp, G);
end;
inc(counter);
tmp := '';
inc(P);
while P^ = ' ' do inc(P);
end;
else tmp := tmp + P^;
inc(P);
end;
end;
if tmp <> '' then
case counter of
0: R := StrToIntDef(tmp, R);
1: G := StrToIntDef(tmp, B);
2: B := StrToIntDef(tmp, B);
else
if not TryStrToInt(tmp, Steps) then ColorName := tmp;
end;
AColor := RGBToColor(Max(0, Min(R, 255)), Max(0, Min(G, 255)), Max(0, Min(B, 255)));
end;
begin
if not FileExists(FileName) then
raise Exception.Create(Format('[TCustomColorPalette.LoadPalette] File not found: %s', [FileName]));
AssignFile(F, FileName);
try
Reset(F);
FColors.Clear;
FCols := 1;
while not EOF(F) do
begin
ReadLn(F, Line);
Line := Trim(Line);
if Length(Line) < 2 then Continue;
if Line[1] = '#' then
Continue;
// Allow '#' as comment within line
p := pos('#', Line);
if p > 0 then
Line := TrimRight(Copy(Line, 1, p-1));
// Parse data lines
ucLine := Uppercase(Line);
if ucLine[1] = '$' then
begin
if Copy(ucLine, 2, 4) = 'NONE' then
DoAddColor(clNone)
else
if (Copy(ucLine, 2, 4) = 'COLS') and (piColumnCount in AItems) then
FCols := StrToIntDef(Copy(Line, 6, MaxInt), FCols)
else
if (Copy(ucLine, 2, 7) = 'BTNDIST') and (piButtonDistance in AItems) then
FButtonDistance := StrToIntDef(Copy(Line, 9, MaxInt), FButtonDistance)
else
if (Copy(ucLine, 2, 8) = 'BTNWIDTH') and (piButtonSize in AItems) then
FButtonWidth := StrToIntDef(Copy(Line, 10, MaxInt), FButtonWidth)
else
if (Copy(ucLine, 2, 9) = 'BTNHEIGHT') and (piButtonSize in AItems) then
FButtonHeight := StrToIntDef(Copy(Line, 11, MaxInt), FButtonHeight)
else
if (Copy(ucLine, 2, 9) = 'BTNBORDER') and (piButtonBorder in AItems) then
begin
Delete(Line, 1, 11);
ParseColor(Line, C, steps, clrName);
FButtonBorderColor := C;
end else
if (Copy(ucLine, 2, 7) = 'FLIPPED') and (piFlipped in AItems) then
begin
Delete(ucLine, 1, 9);
case ucLine of
'TRUE' : FFlipped := true;
'FALSE': FFlipped := false;
end;
end else
if (Copy(ucLine, 2, 7) = 'SELKIND') and (piSelKind in AItems) then
begin
Delete(ucLine, 1, 9);
for sk in TPaletteSelectionKind do
if ucLine = SELKIND_NAMES[sk] then
begin
FSelectionKind := sk;
break;
end;
end else
if (Copy(ucLine, 2, 8) = 'SELCOLOR') and (piSelColor in AItems) then
begin
Delete(Line, 1, 10);
ParseColor(Line, C, steps, clrName);
FSelectionColor := C;
end else
if (Copy(ucLine, 2, 7) = 'BLENDWB') and (piColors in AItems) then
begin
Delete(Line, 1, 8);
ParseColor(Line, C, steps, clrName);
BlendWBColor(C, steps);
end;
end
else
if (Pos(',', Line) > 0) and (piColors in AItems) then
begin
ParseColor(Line, C, steps, clrName);
DoAddColor(C, clrName);
end;
end;
finally
Close(F);
end;
UpdateSize;
PickedIndex := -1;
end;
procedure TCustomColorPalette.MouseDown(Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
inherited;
FMousePt.X := X;
FMousePt.Y := Y;
FMouseIndex := GetColorIndex(X, Y);
FPrevMouseIndex := FMouseIndex;
if FMouseIndex < 0 then
Exit;
if (FMouseIndex < FColors.Count) then
begin
FStoredShift := Shift; // store for usage by pmDefault at MouseUp
if FPickMode <> pmDefault then
ColorPick(FMouseIndex, Shift);
end;
end;
procedure TCustomColorPalette.MouseEnter;
begin
FSavedHint := Hint;
inherited;
end;
procedure TCustomColorPalette.MouseLeave;
begin
inherited;
Hint := FSavedHint;
FMouseIndex := -1;
if Assigned(OnMouseMove) then
OnMouseMove(self, GetKeyShiftState, FMousePt.X, FMousePt.Y);
// ColorMouseMove(MouseColor, []);
end;
procedure TCustomColorPalette.MouseMove(Shift: TShiftState; X, Y: Integer);
var
C: TColor;
begin
FMouseIndex := GetColorIndex(X, Y);
C := GetColors(FMouseIndex);
ColorMouseMove(C, Shift);
if ShowHint and FShowColorHint then
begin
Hint := GetHintText(FMouseIndex);
if FMouseIndex <> FPrevMouseIndex then
Application.ActivateHint(ClientToScreen(Point(X, Y)));
end;
if (FMouseIndex >= 0) and (FMouseIndex < FColors.Count) and
([ssLeft, ssRight, ssMiddle] * Shift <> []) and
(FMouseIndex <> FPrevMouseIndex) and
(FUseSpacers or (C <> clNone)) and
(FPickMode = pmContinuous)
then
ColorPick(FMouseIndex, Shift);
FPrevMouseIndex := FMouseIndex;
inherited;
end;
procedure TCustomColorPalette.MouseUp(Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
case FPickMode of
pmDefault:
if (FMousePt.X = X) and (FMousePt.Y = Y) then
ColorPick(FMouseIndex, FStoredShift);
pmImmediate, pmContinuous:
begin
FMouseIndex := GetColorIndex(X, Y);
if (FMouseIndex >= 0) and (FMouseIndex < FColors.Count) and
(FMouseIndex <> FPrevMouseIndex) then
begin
ColorPick(FMouseIndex, Shift);
end;
end;
end;
FPrevMouseIndex := -1;
inherited;
end;
procedure TCustomColorPalette.Paint;
procedure PaintBox(x1, y1, x2, y2: Integer; c: TColor);
begin
if FUseSpacers and (c = clNone) then
exit;
// Fill interior
Canvas.Pen.Color := FButtonBorderColor;
Canvas.Pen.Width := 1;
Canvas.Pen.Style := psSolid;
if c = clNone then
begin
if Canvas.Pen.Color = clNone then
Canvas.Pen.Color := FButtonBorderColor;
Canvas.Line(x1, y1, x2, y2);
Canvas.Line(x1, y2, x2, y1);
Canvas.Brush.Style := bsClear;
Canvas.Rectangle(x1, y1, x2, y2);
end else
begin
Canvas.Brush.Color := c;
if (FButtonBorderColor = clNone) then
Canvas.FillRect(x1, y1, x2, y2) else
Canvas.Rectangle(x1, y1, x2, y2);
end;
end;
var
I, X, Y: Integer;
Rsel: TRect;
max: Integer;
begin
// Paint background color
if Color <> clNone then begin
Canvas.Brush.Color := Color;
Canvas.Brush.Style := bsSolid;
Canvas.FillRect(0, 0, Width, Height);
end;
Canvas.Pen.Endcap := pecFlat;
// Paint color boxes
X := FMargin;
Y := FMargin;
max := FSizeToLastCol - FMargin;
// max := IfThen(FFlipped, Height, Width) - FMargin;
if (FButtonDistance = 0) and (FButtonBordercolor <> clNone) then
dec(max);
for I := 0 to pred(FColors.Count) do
begin
if I = FPickedIndex then // Selected rect of box with selected color
Rsel := Bounds(X, Y, FButtonWidth, FButtonHeight);
PaintBox(X, Y, X + FButtonWidth, Y + FButtonHeight, GetColors(I));
if FFlipped then
begin
inc(Y, GetCellHeight);
if (FButtonDistance = 0) and (FButtonBorderColor <> clNone) then dec(Y);
if Y >= max then
begin
inc(X, GetCellWidth);
if (FButtonDistance = 0) and (FButtonBorderColor <> clNone) then dec(X);
Y := FMargin;
end;
end else
begin
inc(X, GetCellWidth);
if (FButtonDistance = 0) and (FButtonBorderColor <> clNone) then dec(X);
if X >= max then
begin
inc(Y, GetCellHeight);
if (FButtonDistance = 0) and (FButtonBorderColor <> clNone) then dec(Y);
X := FMargin;
end;
end;
end;
// Paint selection
if FSelectionKind <> pskNone then
begin
Canvas.Brush.Style := bsClear;
Canvas.Pen.Style := psSolid;
case FSelectionKind of
pskThin, pskThinInverted :
begin
Canvas.Pen.Width := 1;
if FButtonDistance > 2 then InflateRect(Rsel, 2, 2);
end;
pskThick, pskThickInverted:
begin
Canvas.Pen.Width := 3;
end;
end;
case FSelectionKind of
pskThin, pskThick:
Canvas.Pen.Color := FSelectionColor;
pskThinInverted, pskThickInverted:
begin
Canvas.Pen.Color := InvertColor(GetPickedColor);
if (FSelectionKind = pskThinInverted) and (Canvas.Pen.Color = FButtonBorderColor) then
Canvas.Pen.Color := FSelectionColor;
end;
end;
Canvas.Rectangle(Rsel);
end;
end;
procedure TCustomColorPalette.SavePalette(const Filename: String);
var
i: Integer;
L: TStringList;
clr: TColor;
clrName: String;
r,g,b: Byte;
begin
L := TStringList.Create;
try
L.Add('# PROPERTIES');
L.Add(Format('$BTNBORDER %d, %d, %d', [Red(FButtonBorderColor), Green(FButtonBorderColor), Blue(FButtonBorderColor)]));
L.ADd(Format('$BTNWIDTH %d', [FButtonWidth]));
L.Add(Format('$BTNHEIGHT %d', [FButtonHeight]));
L.Add(Format('$BTNDIST %d', [FButtonDistance]));
L.Add(Format('$SELKIND %s', [SELKIND_NAMES[FSelectionKind]]));
L.Add(Format('$SELCOLOR %d, %d, %d', [Red(FSelectionColor), Green(FSelectionColor), Blue(FSelectionColor)]));
L.Add(Format('$FLIPPED %s', [BoolToStr(FFlipped, 'TRUE', 'FALSE')]));
L.Add(Format('$COLS %d', [FCols]));
L.Add('');
L.Add('# COLORS');
for i:=0 to FColors.Count-1 do begin
clr := Colors[i];
if clr = clNone then
L.Add('$NONE')
else begin
RedGreenBlue(clr, r,g,b);
clrName := ColorNames[i];
if clrName = '' then
L.Add(Format('%d, %d, %d',[r, g, b]))
else
L.Add(Format('%d, %d, %d %s', [r, g, b, clrName]));
end;
end;
L.SaveToFile(FileName);
finally
L.Free;
end;
end;
procedure TCustomColorPalette.SetButtonBorderColor(const AValue: TColor);
begin
if FButtonBorderColor = AValue then exit;
FButtonBorderColor := AValue;
UpdateSize;
Invalidate;
end;
procedure TCustomColorPalette.SetButtonDistance(const AValue: Integer);
begin
if FButtonDistance = AValue then exit;
FButtonDistance := AValue;
UpdateSize;
Invalidate;
end;
procedure TCustomColorPalette.SetButtonHeight(const AValue: Integer);
begin
if FButtonHeight = AValue then Exit;
FButtonHeight := AValue;
if FButtonHeight < 1 then FButtonHeight := 1;
UpdateSize;
end;
procedure TCustomColorPalette.SetButtonWidth(const AValue: Integer);
begin
if FButtonWidth = AValue then Exit;
FButtonWidth := AValue;
if FButtonWidth < 1 then FButtonWidth := 1;
UpdateSize;
end;
procedure TCustomColorPalette.SetColorNames(AIndex: Integer; const AValue: String);
begin
FColors.Strings[AIndex] := AValue;
end;
procedure TCustomColorPalette.SetColors(AIndex: Integer; const AValue: TColor);
begin
FColors.Objects[AIndex] := TObject(PtrInt(AValue));
Invalidate;
end;
{ Setter for the property ColumnCount.
WARNING: If Flipped is true then this property is reinterpreted as the number
of ROWS! }
procedure TCustomColorPalette.SetCols(AValue: Integer);
begin
if AValue = FCols then
exit;
FCols := AValue;
UpdateSize;
Invalidate;
end;
{ Setter for the property Flipped.
WARNING: If Flipped is true then the property ColumnCount is reinterpreted
as the number of ROWS! }
procedure TCustomColorPalette.SetFlipped(AValue: Boolean);
begin
if FFlipped = AValue then exit;
FFlipped := AValue;
UpdateSize;
Invalidate;
end;
procedure TCustomColorPalette.SetGradientSteps(AValue: Byte);
begin
if FGradientSteps = AValue then
exit;
FGradientSteps := AValue;
if FPaletteKind = pkGradientPalette then
begin
FColors.Clear;
SetPaletteKind(FPaletteKind);
end;
end;
procedure TCustomColorPalette.SetPaletteKind(AValue: TPaletteKind);
const
STEPS: array[0..4] of byte = (0, 64, 128, 192, 255);
// Number of columns for each built-in palette, for a decent layout.
COLCOUNT: array[TPaletteKind] of Integer = (
8, // StandardPalette = 16 standard colors
4, // ExtendedPalette = 16 standard colors + 4 extra colors
5, // SystemPalette = 25 system colors
8, // StandardAndSystemPalette = 16 standard + 25 system colors = 41 colors
5, // ExtendedAndSystemPalette = 16 std + 4 extra + 25 system colors = 45 colors
-1, // Gradient palette - color count depends on PaletteStep
10, // HTML palette
6 // Websafe palette
);
var
i, n: Integer;
r,g,b: Integer;
begin
if (FPaletteKind = AValue) and (FColors.Count > 0) then
exit;
FPaletteKind := AValue;
FColors.Clear;
if FPaletteKind in [pkStandardPalette, pkExtendedPalette,
pkStandardAndSystemPalette, pkExtendedAndSystemPalette] then
begin
DoAddColor(clBlack); // 16
DoAddColor(clGray);
DoAddColor(clMaroon);
DoAddColor(clOlive);
DoAddColor(clGreen);
DoAddColor(clTeal);
DoAddColor(clNavy);
DoAddColor(clPurple);
DoAddColor(clWhite);
DoAddColor(clSilver);
DoAddColor(clRed);
DoAddColor(clYellow);
DoAddColor(clLime);
DoAddColor(clAqua);
DoAddColor(clBlue);
DoAddColor(clFuchsia);
end;
if FPaletteKind in [pkExtendedPalette, pkExtendedAndSystemPalette] then
begin
DoAddColor(clMoneyGreen); // 4
DoAddColor(clSkyBlue);
DoAddColor(clCream);
DoAddColor(clMedGray);
end;
if FPaletteKind in [pkSystemPalette, pkStandardAndSystemPalette, pkExtendedAndSystemPalette] then
begin
DoAddColor(clScrollBar); // 25
DoAddColor(clBackground);
DoAddColor(clActiveCaption);
DoAddColor(clInactiveCaption);
DoAddColor(clMenu);
DoAddColor(clWindow);
DoAddColor(clWindowFrame);
DoAddColor(clMenuText);
DoAddColor(clWindowText);
DoAddColor(clCaptionText);
DoAddColor(clActiveBorder);
DoAddColor(clInactiveBorder);
DoAddColor(clAppWorkspace);
DoAddColor(clHighlight);
DoAddColor(clHighlightText);
DoAddColor(clBtnFace);
DoAddColor(clBtnShadow);
DoAddColor(clGrayText);
DoAddColor(clBtnText);
DoAddColor(clInactiveCaptionText);
DoAddColor(clBtnHighlight);
DoAddColor(cl3DDkShadow);
DoAddColor(cl3DLight);
DoAddColor(clInfoText);
DoAddColor(clInfoBk);
end;
if FPaletteKind = pkGradientPalette then
begin
n := FGradientSteps;
for i:= Low(STEPS) to High(STEPS)-1 do BlendWBColor((RGBToColor(255, STEPS[i], 0)), n);
for i:= High(STEPS) downto Low(STEPS)+1 do BlendWBColor((RGBToColor(STEPS[i], 255, 0)), n);
for i:= Low(STEPS) to High(STEPS)-1 do BlendWBColor((RGBToColor(0, 255, STEPS[i])), n);
for i:= High(STEPS) downto Low(STEPS)+1 do BlendWBColor((RGBToColor(0, STEPS[i], 255)), n);
for i:= Low(STEPS) to High(STEPS)-1 do BlendWBColor((RGBToColor(STEPS[i], 0, 255)), n);
for i:= Low(STEPS) downto High(STEPS) do BlendWBColor((RGBToColor(0, 255, STEPS[i])), n);
SetCols(n*2 + 1);
end;
if FPaletteKind = pkHTMLPalette then
// https://en.wikipedia.org/wiki/Web_colors#X11_color_names
begin
// White_colors
DoAddColor(RGBToColor(255,255,255), 'White');
DoAddColor(RGBToColor(255,250,250), 'Snow');
DoAddColor(RGBToColor(240,255,240), 'Honeydew');
DoAddColor(RGBToColor(245,255,250), 'MintCream');
DoAddColor(RGBToColor(240,255,255), 'Azure');
DoAddColor(RGBToColor(240,248,255), 'AliceBlue');
DoAddColor(RGBToColor(248,248,255), 'GhostWhite');
DoAddColor(RGBToColor(245,245,245), 'WhiteSmoke');
DoAddColor(RGBToColor(255,245,238), 'Seashell');
DoAddColor(RGBToColor(245,245,220), 'Beige');
DoAddColor(RGBToColor(253,245,230), 'OldLace');
DoAddColor(RGBToColor(255,250,240), 'FloralWhite');
DoAddColor(RGBToColor(255,255,240), 'Ivory');
DoAddColor(RGBToColor(250,235,215), 'AntiqueWhite');
DoAddColor(RGBToColor(250,240,230), 'Linen');
DoAddColor(RGBToColor(255,240,245), 'LavenderBlush');
DoAddColor(RGBToColor(255,228,225), 'MistyRose');
// Pink_colors
DoAddColor(RGBToColor(255,192,203), 'Pink');
DoAddColor(RGBToColor(255,182,193), 'LightPink');
DoAddColor(RGBToColor(255,105,180), 'HotPink');
DoAddColor(RGBToColor(255, 20,147), 'DeepPink');
DoAddColor(RGBToColor(219,112,147), 'PaleVioletRed');
DoAddColor(RGBToColor(199, 21,133), 'MediumVioletRed');
// Red_colors
DoAddColor(RGBToColor(255,160,122), 'LightSalmon');
DoAddColor(RGBToColor(250,128,114), 'Salmon');
DoAddColor(RGBToColor(233,150,122), 'DarkSalmon');
DoAddColor(RGBToColor(240,128,128), 'LightCoral');
DoAddColor(RGBToColor(205, 92, 92), 'IndianRed');
DoAddColor(RGBToColor(220, 20, 60), 'Crimson');
DoAddColor(RGBToColor(178, 34, 34), 'FireBrick');
DoAddColor(RGBToColor(139, 0, 0), 'DarkRed');
DoAddColor(RGBToColor(255, 0, 0), 'Red');
// Orange_colors
DoAddColor(RGBToColor(255, 69, 0), 'OrangeRed');
DoAddColor(RGBToColor(255, 99, 71), 'Tomato');
DoAddColor(RGBToColor(255,127, 80), 'Coral');
DoAddColor(RGBToColor(255,140, 0), 'DarkOrange');
DoAddColor(RGBToColor(255,165, 0), 'Orange');
// Yellow_colors
DoAddColor(RGBToColor(255,255, 0), 'Yellow');
DoAddColor(RGBToColor(255,255,224), 'LightYellow');
DoAddColor(RGBToColor(255,250,205), 'LemonChiffon');
DoAddColor(RGBToColor(250,250,210), 'LightGoldenrodYellow');
DoAddColor(RGBToColor(255,239,213), 'PapayaWhip');
DoAddColor(RGBToColor(255,228,181), 'Moccasin');
DoAddColor(RGBToColor(255,218,185), 'PeachPuff');
DoAddColor(RGBToColor(238,232,170), 'PaleGoldenrod');
DoAddColor(RGBToColor(240,230,140), 'Khaki');
DoAddColor(RGBToColor(189,183,107), 'DarkKhaki');
DoAddColor(RGBToColor(255,215, 0), 'Gold');
// Brown_colors
DoAddColor(RGBToColor(255,248,220), 'Cornsilk');
DoAddColor(RGBToColor(255,235,205), 'BlanchedAlmond');
DoAddColor(RGBToColor(255,228,196), 'Bisque');
DoAddColor(RGBToColor(255,222,173), 'NavajoWhite');
DoAddColor(RGBToColor(245,222,179), 'Wheat');
DoAddColor(RGBToColor(222,184,135), 'BurlyWood');
DoAddColor(RGBToColor(210,180,140), 'Tan');
DoAddColor(RGBToColor(188,143,143), 'RosyBrown');
DoAddColor(RGBToColor(244,164, 96), 'SandyBrown');
DoAddColor(RGBToColor(218,165, 32), 'Goldenrod');
DoAddColor(RGBToColor(184,134, 11), 'DarkGoldenrod');
DoAddColor(RGBToColor(205,133, 63), 'Peru');
DoAddColor(RGBToColor(210,105, 30), 'Chocolate');
DoAddColor(RGBToColor(139, 69, 19), 'SaddleBrown');
DoAddColor(RGBToColor(160, 82, 45), 'Sienna');
DoAddColor(RGBToColor(165, 42, 42), 'Brown');
DoAddColor(RGBToColor(128, 0, 0), 'Maroon');
// Green_colors
DoAddColor(RGBToColor( 85,107, 47), 'DarkOliveGreen');
DoAddColor(RGBToColor(128,128, 0), 'Olive');
DoAddColor(RGBToColor(107,142, 35), 'OliveDrab');
DoAddColor(RGBToColor(154,205, 50), 'YellowGreen');
DoAddColor(RGBToColor( 50,205, 50), 'LimeGreen');
DoAddColor(RGBToColor( 0,255, 0), 'Lime');
DoAddColor(RGBToColor(124,252, 0), 'LawnGreen');
DoAddColor(RGBToColor(127,255, 0), 'Chartreuse');
DoAddColor(RGBToColor(173,255, 47), 'GreenYellow');
DoAddColor(RGBToColor( 0,255,127), 'SpringGreen');
DoAddColor(RGBToColor( 0,250,154), 'MediumSpringGreen');
DoAddColor(RGBToColor(144,238,144), 'LightGreen');
DoAddColor(RGBToColor(152,251,152), 'PaleGreen');
DoAddColor(RGBToColor(143,188,143), 'DarkSeaGreen');
DoAddColor(RGBToColor( 60,179,113), 'MediumSeaGreen');
DoAddColor(RGBToColor( 46,139, 87), 'SeaGreen');
DoAddColor(RGBToColor( 34,139, 34), 'ForestGreen');
DoAddColor(RGBToColor( 0,128, 0), 'Green');
DoAddColor(RGBToColor( 0,100, 0), 'DarkGreen');
// Cyan_colors
DoAddColor(RGBToColor(102,205,170), 'MediumAquamarine');
DoAddColor(RGBToColor( 0,255,255), 'Aqua');
// DoAddColor(RGBToColor( 0,255,255), 'Cyan');
DoAddColor(RGBToColor(224,255,255), 'LightCyan');
DoAddColor(RGBToColor(175,238,238), 'PaleTurquoise');
DoAddColor(RGBToColor(127,255,212), 'Aquamarine');
DoAddColor(RGBToColor( 64,224,208), 'Turquoise');
DoAddColor(RGBToColor( 72,209,204), 'MediumTurquoise');
DoAddColor(RGBToColor( 0,206,209), 'DarkTurquoise');
DoAddColor(RGBToColor( 32,178,170), 'LightSeaGreen');
DoAddColor(RGBToColor( 95,158,160), 'CadetBlue');
DoAddColor(RGBToColor( 0,139,139), 'DarkCyan');
DoAddColor(RGBToColor( 0,128,128), 'Teal');
// Blue_colors
DoAddColor(RGBToColor(176,196,222), 'LightSteelBlue');
DoAddColor(RGBToColor(176,224,230), 'PowderBlue');
DoAddColor(RGBToColor(173,216,230), 'LightBlue');
DoAddColor(RGBToColor(135,206,235), 'SkyBlue');
DoAddColor(RGBToColor(135,206,250), 'LightSkyBlue');
DoAddColor(RGBToColor( 0,191,255), 'DeepSkyBlue');
DoAddColor(RGBToColor( 30,144,255), 'DodgerBlue');
DoAddColor(RGBToColor(100,149,237), 'CornflowerBlue');
DoAddColor(RGBToColor( 70,130,180), 'SteelBlue');
DoAddColor(RGBToColor( 65,105,225), 'RoyalBlue');
DoAddColor(RGBToColor( 0, 0,255), 'Blue');
DoAddColor(RGBToColor( 0, 0,205), 'MediumBlue');
DoAddColor(RGBToColor( 0, 0,139), 'DarkBlue');
DoAddColor(RGBToColor( 0, 0,128), 'Navy');
DoAddColor(RGBToColor( 25, 25,112), 'MidnightBlue');
// Purple/Violet/Magenta colors
DoAddColor(RGBToColor(230,230,250), 'Lavender');
DoAddColor(RGBToColor(216,191,216), 'Thistle');
DoAddColor(RGBToColor(221,160,221), 'Plum');
DoAddColor(RGBToColor(238,130,238), 'Violet');
DoAddColor(RGBToColor(218,112,214), 'Orchid');
DoAddColor(RGBToColor(255, 0,255), 'Fuchsia');
DoAddColor(RGBToColor(255, 0,255), 'Magenta');
DoAddColor(RGBToColor(186, 85,211), 'MediumOrchid');
DoAddColor(RGBToColor(147,112,219), 'MediumPurple');
DoAddColor(RGBToColor(138, 43,226), 'BlueViolet');
DoAddColor(RGBToColor(148, 0,211), 'DarkViolet');
DoAddColor(RGBToColor(153, 50,204), 'DarkOrchid');
DoAddColor(RGBToColor(139, 0,139), 'DarkMagenta');
DoAddColor(RGBToColor(128, 0,128), 'Purple');
DoAddColor(RGBToColor( 75, 0,130), 'Indigo');
DoAddColor(RGBToColor( 72, 61,139), 'DarkSlateBlue');
DoAddColor(RGBToColor(102, 51,153), 'RebeccaPurple');
DoAddColor(RGBToColor(106, 90,205), 'SlateBlue');
DoAddColor(RGBToColor(123,104,238), 'MediumSlateBlue');
// Gray/Black_colors
DoAddColor(RGBToColor(220,220,220), 'Gainsboro');
DoAddColor(RGBToColor(211,211,211), 'LightGrey');
DoAddColor(RGBToColor(192,192,192), 'Silver');
DoAddColor(RGBToColor(169,169,169), 'DarkGray');
DoAddColor(RGBToColor(128,128,128), 'Gray');
DoAddColor(RGBToColor(105,105,105), 'DimGray');
DoAddColor(RGBToColor(119,136,153), 'LightSlateGray');
DoAddColor(RGBToColor(112,128,144), 'SlateGray');
DoAddColor(RGBToColor( 47, 79, 79), 'DarkSlateGray');
DoAddColor(RGBToColor( 0, 0, 0), 'Black');
end;
if FPaletteKind = pkWebSafePalette then
begin
// https://en.wikipedia.org/wiki/Web_colors
for g := 0 to 5 do
for b:= 0 to 5 do
for r:=0 to 5 do
DoAddColor(RGBToColor(r*$33, g*$33, b*$33));
end;
if FPaletteKind <> pkGradientPalette then
SetCols(COLCOUNT[FPaletteKind]);
end;
procedure TCustomColorPalette.SetPickedIndex(AValue: Integer);
begin
if FPickedIndex = AValue then exit;
if (AValue >= 0) and (AValue < FColors.Count) then
FPickedIndex := AValue else
FPickedIndex := -1;
Invalidate;
end;
procedure TCustomColorPalette.SetSelectionColor(AValue: TColor);
begin
if FSelectionColor = AValue then exit;
FSelectionColor := AValue;
Invalidate;
end;
procedure TCustomColorPalette.SetSelectionKind(AValue: TPaletteSelectionKind);
begin
if FSelectionKind = AValue then exit;
FSelectionKind := AValue;
UpdateSize;
Invalidate;
end;
procedure TCustomColorPalette.SetUseSpacers(AValue: Boolean);
begin
if FUseSpacers = AValue then exit;
FUseSpacers := AValue;
Invalidate;
end;
procedure TCustomColorPalette.UpdateSize;
var
d, dx, dy: Integer;
begin
if (FCols = 0) or (FColors.Count = 0) then
FRows := 0 else
FRows := Ceil(FColors.Count / FCols);
if FButtonDistance = 0 then
FMargin := 1 else
FMargin := FButtonDistance div 2 + FButtonDistance mod 2;
dx := GetCellWidth;
dy := GetCellHeight;
d := FButtonDistance;
if (FButtonDistance = 0) and (FButtonBorderColor <> clNone) then
begin
dec(dx);
dec(dy);
d := 0;
end;
if FFlipped then // Rows and columns are interchanged here !!!
begin
FSizeToLastCol := FCols * dy - d + 2*FMargin;
SetBounds(Left, Top, FRows * dx - d + 2*FMargin, FCols * dy - d + 2*FMargin);
end else
begin
FSizeToLastCol := FCols * dx - d + 2*FMargin;
SetBounds(Left, Top, FCols * dx - d + 2*FMargin, FRows * dy - d + 2*FMargin);
end;
end;
end.