1
0
Files
applications
bindings
components
Comba_Animation
aboutcomponent
acs
beepfp
callite
captcha
chelper
chemtext
cmdline
cmdlinecfg
colorpalette
cryptini
csvdocument
epiktimer
everettrandom
examplecomponent
exctrls
extrasyn
fpexif
fpsound
fpspreadsheet
fractions
freetypepascal
geckoport
gradcontrols
grid_semaphor
industrialstuff
iosdesigner
iphonelazext
jujiboutils
jvcllaz
kcontrols
lazautoupdate
lazbarcodes
lazmapviewer
lclextensions
longtimer
manualdock
mbColorLib
examples
BAxisColorPicker.pas
BColorPicker.pas
CColorPicker.pas
CIEAColorPicker.pas
CIEBColorPicker.pas
CIELColorPicker.pas
GAxisColorPicker.pas
GColorPicker.pas
HColorPicker.pas
HRingPicker.pas
HSCirclePicker.pas
HSColorPicker.pas
HSLColorPicker.pas
HSLRingPicker.pas
HTMLColors.pas
HexaColorPicker.pas
KColorPicker.pas
LVColorPicker.pas
MColorPicker.pas
OfficeMoreColorsDialog.lfm
OfficeMoreColorsDialog.pas
PalUtils.pas
PickCursor.res
RAxisColorPicker.pas
RColorPicker.pas
RGBCIEUtils.pas
RGBCMYKUtils.pas
RGBHSLUtils.pas
RGBHSVUtils.pas
Readme.rtf
SColorPicker.pas
SLColorPicker.pas
SLHColorPicker.pas
Scanlines.pas
ScreenWin.lfm
ScreenWin.pas
SelPropUtils.pas
XPLibIntegration.txt
YColorPicker.pas
clean.bat
clear history.bat
mbBasicPicker.pas
mbColorLibD10.dpk
mbColorLibD5.dpk
mbColorLibD7.dpk
mbColorLibD9.dpk
mbColorList.pas
mbColorPalette.pas
mbColorPickerControl.pas
mbColorPreview.pas
mbColorTree.pas
mbDeskPickerButton.pas
mbOfficeColorDialog.pas
mbReg.lrs
mbReg.pas
mbTrackBarPicker.pas
mbcolorconv.pas
mbcolorliblaz.lpk
mbutils.pas
mxs.inc
readme.txt
mplayer
multithreadprocs
nvidia-widgets
onguard
orpheus
playsoundpackage
poweredby
powerpdf
rgbgraphics
richmemo
richview
rtfview
rx
scrolltext
smnetgradient
spktoolbar
splashabout
svn
systools
tdi
thtmlport
tparadoxdataset
tvplanit
xdev_toolkit
zlibar
zmsql
examples
image_sources
lclbindings
wst
lazarus-ccr/components/mbColorLib/HexaColorPicker.pas
wp_xxyyzz abdec8801e mbColorLib: Less hints and warnings.
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@8129 8e941d3f-bd1b-0410-a28a-d453659cc2b4
2021-10-27 17:26:55 +00:00

1427 lines
41 KiB
ObjectPascal

unit HexaColorPicker;
{$IFDEF FPC}
{$MODE DELPHI}
{$ENDIF}
interface
//{$I mxs.inc}
uses
LCLIntf, LCLType, LMessages, SysUtils, Classes, Controls, Graphics,
Forms, Themes, Math,
HTMLColors, mbBasicPicker;
const
CustomCell = -2;
NoCell = -1;
type
TMarker = (smArrow, smRect);
TCombEntry = record
Position: TPoint;
Color: COLORREF;
TabIndex: integer;
end;
TCombArray = array of TCombEntry;
TFloatPoint = record
X, Y: Extended;
end;
TRGBrec = record
Red, Green, Blue: Single;
end;
TSelectionMode = (smNone, smColor, smBW, smRamp);
THexaColorPicker = class(TmbBasicPicker)
private
FIncrement: integer;
FSelectedCombIndex: integer;
mX, mY: integer;
FHintFormat: string;
FUnderCursor: TColor;
//FOnChange,
FOnIntensityChange: TNotifyEvent;
FCurrentColor: TColor;
FSelectedIndex: Integer;
FColorCombRect, FBWCombRect, FSliderRect, FCustomColorRect: TRect;
FCombSize, FLevels: Integer;
FBWCombs, FColorCombs: TCombArray;
FCombCorners: array[0..5] of TFloatPoint;
FCenterColor: TRGBrec;
FCenterIntensity: Single;
FSliderWidth: integer;
FCustomIndex: Integer; // If FSelectedIndex contains CustomCell then this index shows
// which index in the custom area has been selected.
// Positive values indicate the color comb and negative values
// indicate the B&W combs (complement). This value is offset with
// 1 to use index 0 to show no selection.
FRadius: Integer;
FSelectionMode: TSelectionMode;
FSliderVisible: boolean;
FMarker: TMarker;
FNewArrowStyle: boolean;
FIntensityText: string;
procedure CalculateCombLayout;
procedure ChangeIntensity(increase: boolean);
procedure DrawAll;
procedure DrawComb(ACanvas: TCanvas; X, Y, Size: Integer);
procedure DrawCombControls(ACanvas: TCanvas);
procedure EndSelection;
procedure EnumerateCombs;
function FindBWArea(X, Y: Integer): Integer;
function FindColorArea(X, Y: Integer): Integer;
function GetIntensity: integer;
function GetNextCombIndex(i: integer): integer;
function GetPreviousCombIndex(i: integer): integer;
procedure HandleCustomColors(var Message: TLMMouse);
function HandleBWArea(const Message: TLMMouse): Boolean;
function HandleColorComb(const Message: TLMMouse): Boolean;
function HandleSlider(const Message: TLMMouse): Boolean;
procedure Initialize;
function PtInComb(Comb: TCombEntry; P: TPoint; Scale: Integer): Boolean;
procedure SetIntensity(v: integer);
procedure SetNewArrowStyle(Value: boolean);
procedure SetMarker(Value: TMarker);
procedure SetRadius(r: integer);
procedure SetSliderVisible(Value: boolean);
procedure SetSliderWidth(w: integer);
function SelectAvailableColor(Color: TColor): boolean;
procedure SelectColor(Color: TColor);
protected
function DoMouseWheel(Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint): Boolean; override;
procedure KeyDown(var Key: Word; Shift: TShiftState); override;
procedure Paint; override;
procedure Resize; override;
procedure SetSelectedColor(Value: TColor); override;
procedure CMHintShow(var Message: TLMessage); message CM_HINTSHOW;
procedure WMLButtonDown(var Message: TLMLButtonDown); message LM_LBUTTONDOWN;
procedure WMLButtonUp(var Message: TLMLButtonUp); message LM_LBUTTONUP;
procedure WMMouseMove(var Message: TLMMouseMove); message LM_MOUSEMOVE;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
function GetColorAtPoint(X, Y: integer): TColor; override;
function GetColorUnderCursor: TColor; override;
function GetHexColorUnderCursor: string; override;
function GetHexColorAtPoint(X, Y: integer): string;
function GetSelectedCombIndex: integer;
procedure SelectCombIndex(i: integer);
property ColorUnderCursor: TColor read GetColorUnderCursor;
published
property Align;
property Anchors;
property HintFormat: string read FHintFormat write FHintFormat;
property Intensity: integer read GetIntensity write SetIntensity default 100;
property IntensityIncrement: integer read FIncrement write FIncrement default 1;
property IntensityText: string read FIntensityText write FIntensityText;
property NewArrowStyle: boolean read FNewArrowStyle write SetNewArrowStyle default false;
property SelectedColor: TColor read FCurrentColor write SetSelectedColor default clBlack;
property SliderVisible: boolean read FSliderVisible write SetSliderVisible default true;
property SliderWidth: integer read FSliderWidth write SetSliderWidth default 12;
property SliderMarker: TMarker read FMarker write SetMarker default smArrow;
property ShowHint default true;
property TabStop default true;
property Visible;
property Enabled;
property PopupMenu;
property TabOrder;
property Color;
property ParentColor;
property DragCursor;
property DragMode;
property DragKind;
property Constraints;
property OnChange; //: TNotifyEvent read FOnChange write FOnChange;
property OnIntensityChange: TNotifyEvent read FOnIntensityChange write FOnIntensityChange;
property OnDblClick;
property OnContextPopup;
property OnMouseDown;
property OnMouseMove;
property OnMouseUp;
property OnMouseWheel;
property OnMouseWheelUp;
property OnMouseWheelDown;
property OnKeyDown;
property OnKeyPress;
property OnKeyUp;
property OnDragDrop;
property OnDragOver;
property OnEndDrag;
property OnEnter;
property OnExit;
property OnResize;
property OnStartDrag;
end;
const
DefCenterColor: TRGBrec = (Red: 1; Green: 1; Blue: 1); // White
DefColors: array[0..5] of TRGBrec = (
(Red: 1; Green: 0; Blue: 1), // Magenta
(Red: 1; Green: 0; Blue: 0), // Red
(Red: 1; Green: 1; Blue: 0), // Yellow
(Red: 0; Green: 1; Blue: 0), // Green
(Red: 0; Green: 1; Blue: 1), // Cyan
(Red: 0; Green: 0; Blue: 1) // Blue
);
DefCenter: TFloatPoint = (X: 0; Y: 0);
implementation
uses
PalUtils, mbUtils;
{ THexaColorPicker }
constructor THexaColorPicker.Create(AOwner: TComponent);
begin
inherited;
//ControlStyle := ControlStyle - [csAcceptsControls] + [csOpaque];
FRadius := 90;
FSliderWidth := 12;
DoubleBuffered := true;
SetInitialBounds(0, 0, 204, 204);
Constraints.MinHeight := 85;
Constraints.MinWidth := 93;
TabStop := true;
FSelectedCombIndex := 0;
FHintFormat := 'RGB(%r, %g, %b)'#13'Hex: #%hex';
ShowHint := True;
FSliderVisible := true;
FMarker := smArrow;
FNewArrowStyle := false;
Initialize;
DrawAll;
FIntensityText := 'Intensity';
{
MaxHue := 360;
MaxLum := 255;
MaxSat := 255;
}
end;
destructor THexaColorPicker.Destroy;
begin
FBWCombs := nil;
FColorCombs := nil;
// FBufferBmp.Free; is already destroyed by ancestor TmbBasicPicker
inherited;
end;
procedure THexaColorPicker.ChangeIntensity(increase: boolean);
var
i: integer;
begin
i := round(FCenterIntensity * 100);
if increase then
begin
Inc(i, FIncrement);
if i > 100 then i := 100;
SetIntensity(i);
end
else
begin
Dec(i, FIncrement);
if i < 0 then i := 0;
SetIntensity(i);
end;
end;
function THexaColorPicker.DoMouseWheel(Shift: TShiftState; WheelDelta: Integer;
MousePos: TPoint): Boolean;
begin
Result := inherited DoMouseWheel(Shift, WheelDelta, MousePos);
if not Result then
begin
Result := True;
ChangeIntensity(WheelDelta > 0);
end;
end;
procedure THexaColorPicker.DrawComb(ACanvas: TCanvas; X, Y: Integer; Size: Integer);
var
I: Integer;
P: array[0..5] of TPoint;
begin
for I := 0 to 5 do
begin
P[I].X := Round(FCombCorners[I].X * Size + X);
P[I].Y := Round(FCombCorners[I].Y * Size + Y);
end;
ACanvas.Polygon(P);
end;
procedure THexaColorPicker.DrawCombControls(ACanvas: TCanvas);
var
I, Index: Integer;
XOffs, YOffs, Count: Integer;
OffScreen: TBitmap;
R: TRect;
begin
OffScreen := TBitmap.Create;
try
OffScreen.Width := Width;
OffScreen.Height := HeightOfRect(FColorCombRect) + HeightOfRect(FBWCombRect);
//Parent background
if Color = clDefault then
begin
Offscreen.Transparent := true;
Offscreen.TransparentColor := clForm;
Offscreen.Canvas.Brush.Color := clForm
end else
OffScreen.Canvas.Brush.Color := Color;
OffScreen.Canvas.FillRect(OffScreen.Canvas.ClipRect);
with OffScreen.Canvas do
begin
Pen.Style := psClear;
// draw color combs from FColorCombs array
XOffs := FRadius + FColorCombRect.Left;
YOffs := FRadius + FColorCombRect.Top;
// draw the combs
for I := 0 to High(FColorCombs) do
begin
Brush.Color := FColorCombs[I].Color;
Pen.Mode := pmCopy; // the pen is set here so there are no gaps between the combs
Pen.Style := psSolid;
Pen.Color := FColorCombs[I].Color;
DrawComb(OffScreen.Canvas, FColorCombs[I].Position.X + XOffs, FColorCombs[I].Position.Y + YOffs, FCombSize);
end;
// mark selected comb
if FCustomIndex > 0 then
begin
Index := FCustomIndex - 1;
FSelectedCombIndex := index;
Pen.Style := psSolid;
{
Pen.Mode := pmXOR;
Pen.Color := clWhite;
}
Pen.Color := HighContrastColor(FColorCombs[Index].Color);
Pen.Width := 2;
Brush.Style := bsClear;
DrawComb(OffScreen.Canvas, FColorCombs[Index].Position.X + XOffs, FColorCombs[Index].Position.Y + YOffs, FCombSize);
Pen.Style := psClear;
Pen.Mode := pmCopy;
Pen.Width := 1;
end;
// draw white-to-black combs
XOffs := FColorCombRect.Left;
YOffs := FColorCombRect.Bottom - 4;
// brush is automatically reset to bsSolid
for I := 0 to High(FBWCombs) do
begin
Pen.Mode := pmCopy; // the pen is set here so there are no gaps between the combs
Pen.Style := psSolid;
Pen.Color := FBWCombs[I].Color;
Brush.Color := FBWCombs[I].Color;
if I in [0, High(FBWCombs)] then
begin
if Pen.Color = clWhite then // "white" needs a border if background is white as well
Pen.Color := clGray;
DrawComb(OffScreen.Canvas, FBWCombs[I].Position.X + XOffs, FBWCombs[I].Position.Y + YOffs, 2 * FCombSize)
end else
DrawComb(OffScreen.Canvas, FBWCombs[I].Position.X + XOffs, FBWCombs[I].Position.Y + YOffs, FCombSize);
end;
// mark selected comb
if FCustomIndex < 0 then
begin
Index := -(FCustomIndex + 1);
if index < 0 then
FSelectedCombIndex := Index
else
FSelectedCombIndex := -index;
Pen.Style := psSolid;
{
Pen.Mode := pmXOR;
Pen.Color := clWhite;
}
Pen.Mode := pmCopy;
Pen.Color := HighContrastColor(FBWCombs[Index].Color);
Pen.Width := 2;
Brush.Style := bsClear;
if Index in [0, High(FBWCombs)] then
begin
if Index = High(FBWCombs) then begin
Pen.Color := rgb(254, 254, 254); //clWhite;
Pen.Mode := pmXOR;
end;
if ((FColorCombs[0].Color = Cardinal(clWhite)) and (Index = 0)) or
((FColorCombs[0].Color = Cardinal(clBlack)) and (Index = High(FBWCombs)))
then
DrawComb(OffScreen.Canvas, FRadius + FColorCombRect.Left, FRadius + FColorCombRect.Top, FCombSize); // mark white or black center
DrawComb(OffScreen.Canvas, FBWCombs[Index].Position.X + XOffs, FBWCombs[Index].Position.Y + YOffs, 2 * FCombSize);
end
else
DrawComb(OffScreen.Canvas, FBWCombs[Index].Position.X + XOffs, FBWCombs[Index].Position.Y + YOffs, FCombSize);
Pen.Style := psClear;
Pen.Mode := pmCopy;
Pen.Width := 1;
end;
// Slider
if FSliderVisible then
begin
// center-color trackbar
R := FSliderRect;
R.Right := R.Left + FSliderWidth;
Pen.Style := psSolid;
GradientFill(R, clWhite, clBlack, gdVertical);
// draw marker
Count := FSliderRect.Bottom - FSliderRect.Top - 1;
XOffs := FSliderRect.Left + FSliderWidth + 1;
YOffs := FSliderRect.Top + Round(Count * (1 - FCenterIntensity));;
Pen.Color := clBlack;
case FMarker of
smArrow:
begin
if not FNewArrowStyle then
begin
Brush.Color := clBlack;
Polygon([
Point(XOffs, YOffs),
Point(XOffs + 6, YOffs - 4),
Point(XOffs + 6, YOffs + 4)
])
end
else
begin
Brush.Color := clWhite;
Pen.Color := clBtnShadow;
Polygon([
Point(XOffs, YOffs),
Point(XOffs + 4, YOffs - 4),
Point(XOffs + 6, YOffs - 4),
Point(XOffs + 7, YOffs - 3),
Point(XOffs + 7, YOffs + 3),
Point(XOffs + 6, YOffs + 4),
Point(XOffs + 4, YOffs + 4)]);
end;
end;
smRect:
begin
Brush.Style := bsClear;
Pen.Mode := pmNot;
Rectangle(XOffs - FSliderWidth - 4, YOffs - 3, XOffs + 2, YOffs + 3);
Pen.Mode := pmCopy;
Brush.Style := bsSolid;
end;
end; // case
Pen.Style := psClear;
end;
end;
ACanvas.Draw(0, 0, OffScreen);
finally
Offscreen.Free;
end;
EnumerateCombs;
end;
// Looks for a comb at position (X, Y) in the black&white area.
// Result is -1 if nothing could be found else the index of the particular comb
// into FBWCombs.
function THexaColorPicker.FindBWArea(X, Y: Integer): Integer;
var
I, Scale: Integer;
Pt: TPoint;
begin
Result := -1;
Pt := Point(X - FBWCombRect.Left, Y - FBWCombRect.Top);
for I := 0 to High(FBWCombs) do
begin
if I in [0, High(FBWCombs)] then
Scale := FCombSize
else
Scale := FCombSize div 2;
if PtInComb(FBWCombs[I], Pt, Scale) then
begin
Result := I;
Break;
end;
end;
end;
// Looks for a comb at position (X, Y) in the custom color area.
// Result is -1 if nothing could be found else the index of the particular comb
// into FColorCombs.
function THexaColorPicker.FindColorArea(X, Y: Integer): Integer;
var
I: Integer;
Pt: TPoint;
begin
Result := -1;
Pt := Point(X - (FRadius + FColorCombRect.Left), Y - (FRadius + FColorCombRect.Top));
for I := 0 to High(FColorCombs) do
begin
if PtInComb(FColorCombs[I], Pt, FCombSize div 2) then
begin
Result := I;
Break;
end;
end;
end;
function THexaColorPicker.GetIntensity: integer;
begin
Result := round(FCenterIntensity * 100);
end;
function THexaColorPicker.GetNextCombIndex(i: integer): integer;
begin
if i = 127 then
Result := -1
else
if i = -15 then
Result := 1
else
if i > 0 then
Result := i + 1
else
Result := i - 1;
end;
function THexaColorPicker.GetPreviousCombIndex(i: integer): integer;
begin
if i = 1 then
Result := -15
else
if i = -1 then
Result := 127
else
if i > 0 then
Result := i - 1
else
Result := i + 1;
end;
function THexaColorPicker.GetSelectedCombIndex: integer;
begin
if FSelectedCombIndex < 0 then
Result := FBWCombs[-FSelectedCombIndex].TabIndex
else
Result := FColorCombs[FSelectedCombIndex].TabIndex;
end;
// determines whether the mouse position is within the B&W comb area and acts accordingly
function THexaColorPicker.HandleBWArea(const Message: TLMMouse): Boolean;
var
Index: Integer;
Shift: TShiftState;
begin
Result := PtInRect(FBWCombRect, Point(Message.XPos, Message.YPos)) and (FSelectionMode in [smNone, smBW]);
if Result then
begin
Shift := KeysToShiftState(Message.Keys);
if ssLeft in Shift then
begin
FSelectionMode := smBW;
Index := FindBWArea(Message.XPos, Message.YPos);
if Index > -1 then
begin
// remove selection comb if it was previously in color comb
if FCustomIndex > 0 then InvalidateRect(Handle, @FColorCombRect, False);
if FCustomIndex <> -(Index + 1) then
begin
FCustomIndex := -(Index + 1);
InvalidateRect(Handle, @FBWCombRect, False);
InvalidateRect(Handle, @FCustomColorRect, False);
EndSelection;
end;
end
else
Result := False;
end;
end;
end;
// determines whether the mouse position is within the color comb area and acts accordingly
function THexaColorPicker.HandleColorComb(const Message: TLMMouse): Boolean;
var
Index: Integer;
Shift: TShiftState;
begin
Result := PtInRect(FColorCombRect, Point(Message.XPos, Message.YPos)) and (FSelectionMode in [smNone, smColor]);
if Result then
begin
Shift := KeysToShiftState(Message.Keys);
if ssLeft in Shift then
begin
FSelectionMode := smColor;
Index := FindColorArea(Message.XPos, Message.YPos);
if Index > -1 then
begin
// remove selection comb if it was previously in b&w comb
if FCustomIndex < 0 then InvalidateRect(Handle, @FBWCombRect, False);
if FCustomIndex <> (Index + 1) then
begin
FCustomIndex := Index + 1;
InvalidateRect(Handle, @FColorCombRect, False);
InvalidateRect(Handle, @FCustomColorRect, False);
EndSelection;
end;
end
else
Result := False;
end;
end;
end;
procedure THexaColorPicker.HandleCustomColors(
var Message: {$IFDEF FPC}TLMMouse{$ELSE}TWMMouse{$ENDIF});
begin
if not HandleSlider(Message) then
if not HandleBWArea(Message) then
HandleColorComb(Message);
end;
// determines whether the mouse position is within the slider area and acts accordingly
function THexaColorPicker.HandleSlider(const Message: TLMMouse): Boolean;
var
Shift: TShiftState;
dY: Integer;
R: TRect;
begin
if not FSliderVisible then
begin
Result := false;
Exit;
end;
Result :=
(PtInRect(FSliderRect, Point(Message.XPos, Message.YPos))
and (FSelectionMode = smNone))
or
((Message.XPos >= FSliderRect.Left) and (Message.XPos <= FSliderRect.Right)
and (FSelectionMode = smRamp));
if Result then
begin
Shift := KeysToShiftState(Message.Keys);
if ssLeft in Shift then
begin
FSelectionMode := smRamp;
dY := FSliderRect.Bottom - FSliderRect.Top;
FCenterIntensity := 1 - (Message.YPos - FSliderRect.Top) / dY;
if FCenterIntensity < 0 then FCenterIntensity := 0;
if FCenterIntensity > 1 then FCenterIntensity := 1;
FCenterColor.Red := DefCenterColor.Red * FCenterIntensity;
FCenterColor.Green := DefCenterColor.Green * FCenterIntensity;
FCenterColor.Blue := DefCenterColor.Blue * FCenterIntensity;
R := FSliderRect;
Dec(R.Top, 3);
Inc(R.Bottom, 3);
Inc(R.Left, 10);
InvalidateRect(Handle, @R, False);
FColorCombs := nil;
InvalidateRect(Handle, @FColorCombRect, False);
InvalidateRect(Handle, @FCustomColorRect, False);
CalculateCombLayout;
EndSelection;
if Assigned(FOnIntensityChange) then
FOnIntensityChange(Self);
end;
end;
end;
procedure THexaColorPicker.Initialize;
var
I: Integer;
begin
FSelectedIndex := NoCell;
for I := 0 to 5 do
begin
FCombCorners[I].X := 0.5 * cos(Pi * (90 - I * 60) / 180);
FCombCorners[I].Y := 0.5 * sin(Pi * (90 - I * 60) / 180);
end;
FLevels := 7;
FCombSize := Round(FRadius / (FLevels - 1));
FCenterColor := DefCenterColor;
FIncrement := 1;
FCenterIntensity := 1;
end;
procedure THexaColorPicker.KeyDown(var Key: Word; Shift: TShiftState);
var
eraseKey: Boolean;
begin
eraseKey := true;
if ssCtrl in Shift then
case Key of
VK_LEFT: SetSelectedColor(clWhite);
VK_RIGHT: SetSelectedColor(clBlack);
VK_UP: if FSliderVisible then SetIntensity(100);
VK_DOWN: if FSliderVisible then SetIntensity(0);
else
eraseKey := false;
end
else
case Key of
VK_LEFT: SelectCombIndex(GetPreviousCombIndex(GetSelectedCombIndex));
VK_RIGHT: SelectCombIndex(GetNextCombIndex(GetSelectedCombIndex));
VK_UP: if FSliderVisible then ChangeIntensity(true);
VK_DOWN: if FSliderVisible then ChangeIntensity(false);
else
eraseKey := false;
end;
if eraseKey then
Key := 0;
inherited;
end;
procedure THexaColorPicker.Paint;
begin
PaintParentBack(Canvas);
if FColorCombs = nil then
CalculateCombLayout;
DrawCombControls(Canvas);
end;
function THexaColorPicker.PtInComb(Comb: TCombEntry; P: TPoint; Scale: Integer): Boolean;
begin
Result := (Sqr(Comb.Position.X - P.X) + Sqr(Comb.Position.Y - P.Y)) <= Scale * Scale;
end;
procedure THexaColorPicker.DrawAll;
var
WinTop: integer;
begin
WinTop := - FRadius div 8; // use 10 instead of 8 if the top has been cut
FCombSize := Round(1 + FRadius / (FLevels - 1));
FColorCombRect := Rect(0, WinTop, 2 * FRadius, 2 * FRadius + WinTop);
FBWCombRect := Rect(
FColorCombRect.Left,
FColorCombRect.Bottom - 4,
Round(17 * FCombSize * cos(Pi / 6) / 2) {%H-}+ 6 * FCombSize,
FColorCombRect.Bottom + 2 * FCombSize
);
if FSliderVisible then
FSliderRect := Rect(FColorCombRect.Right, FCombSize, FColorCombRect.Right + 10 + FSliderWidth, FColorCombRect.Bottom - FCombSize)
// FSliderRect := Rect(FColorCombRect.Right, FColorCombRect.Top, FColorCombRect.Right + 10 + FSliderWidth, FColorCombRect.Bottom)
else
FSliderRect := Rect(-1, -1, -1, -1);
end;
// fills arrays with centers and colors for the custom color and black & white combs,
// these arrays are used to quickly draw the combx and do hit tests
function RGBFromFloat(Color: TRGBrec): COLORREF;
begin
Result := RGB(Round(255 * Color.Red), Round(255 * Color.Green), Round(255 * Color.Blue));
end;
{function TRGBrecFromTColor(Color: TColor): TRGBrec;
begin
Result.Red := GetRValue(Color)/255;
Result.Green := GetGValue(Color)/255;
Result.Blue := GetBValue(Color)/255;
end;}
procedure THexaColorPicker.CalculateCombLayout;
function GrayFromIntensity(Intensity: Byte): COLORREF;
begin
Result := RGB(Intensity, Intensity, Intensity);
end;
var
I, J, Level, CurrentIndex, CombCount: Cardinal;
CurrentColor: TRGBrec;
CurrentPos: TFloatPoint;
Scale: Extended;
// triangle vars
Pos1, Pos2, dPos1, dPos2, dPos: TFloatPoint;
Color1, Color2, dColor1, dColor2, dColor: TRGBrec;
begin
// this ensures the radius and comb size is set correctly
// HandleNeeded;
if FLevels < 1 then FLevels := 1;
// To draw perfectly aligned combs we split the final comb into six triangles (sextants)
// and calculate each separately. The center comb is stored as first entry in the array
// and will not considered twice (as with the other shared combs too).
//
// The way used here for calculation of the layout seems a bit complicated, but works
// correctly for all cases (even if the comb corners are rotated).
// initialization
CurrentIndex := 0;
CurrentColor := FCenterColor;
// number of combs can be calculated by:
// 1 level: 1 comb (the center)
// 2 levels: 1 comb + 6 combs
// 3 levels: 1 comb + 1 * 6 combs + 2 * 6 combs
// n levels: 1 combs + 1 * 6 combs + 2 * 6 combs + .. + (n-1) * 6 combs
// this equals to 1 + 6 * (1 + 2 + 3 + .. + (n-1)), by using Gauss' famous formula we get:
// Count = 1 + 6 * (((n-1) * n) / 2)
// Because there's always an even number involved (either n or n-1) we can use an integer div
// instead of a float div here...
CombCount := 1 + 6 * (((FLevels - 1) * FLevels) div 2);
SetLength(FColorCombs, CombCount);
// store center values
FColorCombs[CurrentIndex].Position := Point(0, 0);
FColorCombs[CurrentIndex].Color := RGBFromFloat(CurrentColor);
Inc(CurrentIndex);
// go out off here if there are not further levels to draw
if FLevels < 2 then Exit;
// now go for each sextant, the generic corners have been calculated already at creation
// time for a comb with diameter 1
// ------
// /\ 1 /\
// / \ / \
// / 2 \/ 0 \
// -----------
// \ 3 /\ 5 /
// \ / \ /
// \/ 4 \/
// ------
for I := 0 to 5 do
begin
// initialize triangle corner values
//
// center (always at 0,0)
// /\
// dPos1 / \ dPos2
// dColor1 / \ dColor2
// / dPos \
// /--------\ (span)
// / dColor \
// /____________\
// comb corner 1 comb corner 2
//
// Pos1, Pos2, Color1, Color2 are running terms for both sides of the triangle
// incremented by dPos1/2 and dColor1/2.
// dPos and dColor are used to interpolate a span between the values just mentioned.
//
// The small combs are actually oriented with corner 0 at top (i.e. mirrored at y = x,
// compared with the values in FCombCorners), we can achieve that by simply exchanging
// X and Y values.
Scale := 2 * FRadius * cos(Pi / 6);
Pos1.X := FCombCorners[I].Y * Scale;
Pos1.Y := FCombCorners[I].X * Scale;
Color1 := DefColors[I];
if I = 5 then
begin
Pos2.X := FCombCorners[0].Y * Scale;
Pos2.Y := FCombCorners[0].X * Scale;
Color2 := DefColors[0];
end
else
begin
Pos2.X := FCombCorners[I + 1].Y * Scale;
Pos2.Y := FCombCorners[I + 1].X * Scale;
Color2 := DefColors[I + 1];
end;
dPos1.X := Pos1.X / (FLevels - 1);
dPos1.Y := Pos1.Y / (FLevels - 1);
dPos2.X := Pos2.X / (FLevels - 1);
dPos2.Y := Pos2.Y / (FLevels - 1);
dColor1.Red := (Color1.Red - FCenterColor.Red) / (FLevels - 1);
dColor1.Green := (Color1.Green - FCenterColor.Green) / (FLevels - 1);
dColor1.Blue := (Color1.Blue - FCenterColor.Blue) / (FLevels - 1);
dColor2.Red := (Color2.Red - FCenterColor.Red) / (FLevels - 1);
dColor2.Green := (Color2.Green - FCenterColor.Green) / (FLevels - 1);
dColor2.Blue := (Color2.Blue - FCenterColor.Blue) / (FLevels - 1);
Pos1 := DefCenter;
Pos2 := DefCenter;
Color1 := FCenterColor;
Color2 := FCenterColor;
// Now that we have finished the initialization for this step we'll go
// through a loop for each level to calculate the spans.
// We can ignore level 0 (as this is the center we already have determined) as well
// as the last step of each span (as this is the start value in the next triangle and will
// be calculated there). We have, though, take them into the calculation of the running terms.
for Level := 0 to FLevels - 1 do
begin
if Level > 0 then
begin
// initialize span values
dPos.X := (Pos2.X - Pos1.X) / Level;
dPos.Y := (Pos2.Y - Pos1.Y) / Level;
dColor.Red := (Color2.Red - Color1.Red) / Level;
dColor.Green := (Color2.Green - Color1.Green) / Level;
dColor.Blue := (Color2.Blue - Color1.Blue) / Level;
CurrentPos := Pos1;
CurrentColor := Color1;
for J := 0 to Level - 1 do
begin
// store current values in the array
FColorCombs[CurrentIndex].Position.X := Round(CurrentPos.X);
FColorCombs[CurrentIndex].Position.Y := Round(CurrentPos.Y);
FColorCombs[CurrentIndex].Color := RGBFromFloat(CurrentColor);
Inc(CurrentIndex);
// advance in span
CurrentPos.X := CurrentPos.X + dPos.X;
CurrentPos.Y := CurrentPos.Y + dPos.Y;
CurrentColor.Red := CurrentColor.Red + dColor.Red;
CurrentColor.Green := CurrentColor.Green + dColor.Green;
CurrentColor.Blue := CurrentColor.Blue + dColor.Blue;
end;
end;
// advance running terms
Pos1.X := Pos1.X + dPos1.X;
Pos1.Y := Pos1.Y + dPos1.Y;
Pos2.X := Pos2.X + dPos2.X;
Pos2.Y := Pos2.Y + dPos2.Y;
Color1.Red := Color1.Red + dColor1.Red;
Color1.Green := Color1.Green + dColor1.Green;
Color1.Blue := Color1.Blue + dColor1.Blue;
Color2.Red := Color2.Red + dColor2.Red;
Color2.Green := Color2.Green + dColor2.Green;
Color2.Blue := Color2.Blue + dColor2.Blue;
end;
end;
// second step is to build a list for the black & white area
// 17 entries from pure white to pure black
// the first and last are implicitely of double comb size
SetLength(FBWCombs, 17);
CurrentIndex := 0;
FBWCombs[CurrentIndex].Color := GrayFromIntensity(255);
FBWCombs[CurrentIndex].Position := Point(FCombSize, FCombSize);
Inc(CurrentIndex);
CurrentPos.X := 3 * FCombSize;
CurrentPos.Y := 3 * (FCombSize div 4);
dPos.X := Round(FCombSize * cos(Pi / 6) / 2);
dPos.Y := Round(FCombSize * (1 + sin(Pi / 6)) / 2);
for I := 0 to 14 do
begin
FBWCombs[CurrentIndex].Color := GrayFromIntensity((16 - CurrentIndex) * 15);
if Odd(I) then
FBWCombs[CurrentIndex].Position := Point(Round(CurrentPos.X + I * dPos.X), Round(CurrentPos.Y + dPos.Y))
else
FBWCombs[CurrentIndex].Position := Point(Round(CurrentPos.X + I * dPos.X), Round(CurrentPos.Y));
Inc(CurrentIndex);
end;
FBWCombs[CurrentIndex].Color := 0;
FBWCombs[CurrentIndex].Position := Point(Round(CurrentPos.X + 16 * dPos.X + FCombSize), FCombSize);
EnumerateCombs;
end;
// determine hint message and out-of-hint rect
procedure THexaColorPicker.CMHintShow(
var Message: {$IFDEF FPC}TLMessage{$ELSE}TMessage{$ENDIF} );
var
Index: Integer;
Colors: TCombArray;
cp: TPoint;
begin
Colors := nil;
if (GetColorUnderCursor <> clNone) or PtInRect(FSliderRect, Point(mX, mY)) then
with TCMHintShow(Message) do
begin
if not ShowHint then
Message.Result := 1
else
begin
with HintInfo^ do
begin
// show that we want a hint
Result := 0;
cp := CursorPos;
ReshowTimeout := 0; //1;
HideTimeout := 5000;
HintInfo^.CursorRect := Rect(cp.X, cp.Y, cp.X+1, cp.Y+1);
if PtInRect(FSliderRect, cp) and FSliderVisible then
begin
// in case of the intensity slider we show the current intensity
HintStr := FIntensityText + Format(': %d%%', [Round(100 * FCenterIntensity)]);
HintPos := ClientToScreen(Point(FSliderRect.Right, CursorPos.Y - 8));
end
else
begin
Index := -1;
if PtInRect(FBWCombRect, Point(CursorPos.X, CursorPos.Y)) then
begin
// considering black&white area...
if csLButtonDown in ControlState then
Index := -(FCustomIndex + 1)
else
Index := FindBWArea(CursorPos.X, CursorPos.Y);
Colors := FBWCombs;
end
else
if PtInRect(FColorCombRect, Point(CursorPos.X, CursorPos.Y)) then
begin
// considering color comb area...
if csLButtonDown in ControlState then
Index := FCustomIndex - 1
else
Index := FindColorArea(CursorPos.X, CursorPos.Y);
Colors := FColorCombs;
end;
if (Index > -1) and (Colors <> nil) then
HintStr := FormatHint(FHintFormat, Colors[Index].Color);
end;
end;
end;
end;
end;
procedure THexaColorPicker.SetSelectedColor(Value: TColor);
begin
FCurrentColor := Value;
SelectColor(Value);
Invalidate;
end;
procedure THexaColorPicker.EndSelection;
begin
if FCustomIndex < 0 then
SetSelectedColor(FBWCombs[-(FCustomIndex + 1)].Color)
else
if FCustomIndex > 0 then
SetSelectedColor(FColorCombs[FCustomIndex - 1].Color)
else
SetSelectedColor(clNone);
end;
function THexaColorPicker.GetColorUnderCursor: TColor;
begin
Result := FUnderCursor;
end;
function THexaColorPicker.GetColorAtPoint(X, Y: integer): TColor;
var
Index: Integer;
Colors: TCombArray;
begin
Colors := nil;
Index := -1;
if PtInRect(FBWCombRect, Point(X, Y)) then
begin
Index := FindBWArea(X, Y);
Colors := FBWCombs;
end
else
if PtInRect(FColorCombRect, Point(X, Y)) then
begin
Index := FindColorArea(X, Y);
Colors := FColorCombs;
end;
if (Index > -1) and (Colors <> nil) then
Result := Colors[Index].Color
else
Result := clNone;
end;
function THexaColorPicker.GetHexColorUnderCursor: string;
begin
Result := ColorToHex(GetColorUnderCursor);
end;
function THexaColorPicker.GetHexColorAtPoint(X, Y: integer): string;
begin
Result := ColorToHex(GetColorAtPoint(X, Y));
end;
procedure THexaColorPicker.EnumerateCombs;
var
i, k: integer;
begin
k := 0;
if FBWCombs <> nil then
for i := 1 to High(FBWCombs) do
begin
case i of
// b & w comb indices
1: k := -1;
2: k := -9;
3: k := -2;
4: k := -10;
5: k := -3;
6: k := -11;
7: k := -4;
8: k := -12;
9: k := -5;
10: k := -13;
11: k := -6;
12: k := -14;
13: k := -7;
14: k := -15;
15: k := -8;
// big black comb index (match center comb)
16: K := 64;
end;
FBWCombs[i].TabIndex := k;
end;
if FColorCombs <> nil then
for i := 0 to High(FColorCombs) do
begin
case i of
// center comb index
0: k := 64;
// color comb indices
1: k := 65;
2: k := 66;
3: k := 78;
4: k := 67;
5: k := 79;
6: k := 90;
7: k := 68;
8: k := 80;
9: k := 91;
10: k := 101;
11: k := 69;
12: k := 81;
13: k := 92;
14: k := 102;
15: k := 111;
16: k := 70;
17: k := 82;
18: k := 93;
19: k := 103;
20: k := 112;
21: k := 120;
22: k := 77;
23: k := 89;
24: k := 88;
25: k := 100;
26: k := 99;
27: k := 98;
28: k := 110;
29: k := 109;
30: k := 108;
31: k := 107;
32: k := 119;
33: k := 118;
34: k := 117;
35: k := 116;
36: k := 115;
37: k := 127;
38: k := 126;
39: k := 125;
40: k := 124;
41: k := 123;
42: k := 122;
43: k := 76;
44: k := 87;
45: k := 75;
46: k := 97;
47: k := 86;
48: k := 74;
49: k := 106;
50: k := 96;
51: k := 85;
52: k := 73;
53: k := 114;
54: k := 105;
55: k := 95;
56: k := 84;
57: k := 72;
58: k := 121;
59: k := 113;
60: k := 104;
61: k := 94;
62: k := 83;
63: k := 71;
64: k := 63;
65: k := 62;
66: k := 50;
67: k := 61;
68: k := 49;
69: k := 38;
70: k := 60;
71: k := 48;
72: k := 37;
73: k := 27;
74: k := 59;
75: k := 47;
76: k := 36;
77: k := 26;
78: k := 17;
79: k := 58;
80: k := 46;
81: k := 35;
82: k := 25;
83: k := 16;
84: k := 8;
85: k := 51;
86: k := 39;
87: k := 40;
88: k := 28;
89: k := 29;
90: k := 30;
91: k := 18;
92: k := 19;
93: k := 20;
94: k := 21;
95: k := 9;
96: k := 10;
97: k := 11;
98: k := 12;
99: k := 13;
100: k := 1;
101: k := 2;
102: k := 3;
103: k := 4;
104: k := 5;
105: k := 6;
106: k := 52;
107: k := 41;
108: k := 53;
109: k := 31;
110: k := 42;
111: k := 54;
112: k := 22;
113: k := 32;
114: k := 43;
115: k := 55;
116: k := 14;
117: k := 23;
118: k := 33;
119: k := 44;
120: k := 56;
121: k := 7;
122: k := 15;
123: k := 24;
124: k := 34;
125: k := 45;
126: k := 57;
end;
FColorCombs[i].TabIndex := k;
end;
end;
procedure THexaColorPicker.SelectCombIndex(i: integer);
var
j: integer;
begin
if i > 0 then
begin
if FColorCombs <> nil then
for j := 0 to High(FColorCombs) do
begin
if FColorCombs[j].TabIndex = i then
begin
SetSelectedColor(FColorCombs[j].Color);
Break;
end;
end;
end
else
if FBWCombs <> nil then
for j := 1 to High(FBWCombs) - 1 do
begin
if FBWCombs[j].TabIndex = i then
begin
SetSelectedColor(FBWCombs[j].Color);
Break;
end;
end;
end;
procedure THexaColorPicker.Resize;
var
rw, rh: integer;
begin
if (Width >= 93) and (Height >= 85) then
begin
if FSliderVisible then
rw := Round((Width - 10 - FSliderWidth)/2)
else
rw := Round(Width/2 - 5);
rh := Round((24/53)*(Height - 6));
SetRadius(Min(rw, rh));
end;
inherited;
end;
function THexaColorPicker.SelectAvailableColor(Color: TColor): boolean;
var
I: integer;
C: COLORREF;
found: Boolean;
begin
found := False;
Result := false;
C := ColorToRGB(Color);
if FColorCombs = nil then CalculateCombLayout;
FCustomIndex := 0;
FSelectedIndex := NoCell;
for I := 0 to High(FBWCombs) do
if FBWCombs[I].Color = C then
begin
FSelectedIndex := CustomCell;
FCustomIndex := -(I + 1);
found := True;
Result := true;
Break;
end;
if not found then
for I := 0 to High(FColorCombs) do
if FColorCombs[I].Color = C then
begin
FSelectedIndex := CustomCell;
FCustomIndex := I + 1;
Result := true;
Break;
end;
end;
procedure THexaColorPicker.SelectColor(Color: TColor);
begin
SelectAvailableColor(Color);
Invalidate;
if Assigned(OnChange) then OnChange(Self);
end;
procedure THexaColorPicker.SetIntensity(v: integer);
var
R: TRect;
begin
FCenterIntensity := EnsureRange(v/100, 0, 1);
FCenterColor.Red := DefCenterColor.Red * FCenterIntensity;
FCenterColor.Green := DefCenterColor.Green * FCenterIntensity;
FCenterColor.Blue := DefCenterColor.Blue * FCenterIntensity;
R := FSliderRect;
Dec(R.Top, 3);
Inc(R.Bottom, 3);
Inc(R.Left, 10);
InvalidateRect(Handle, @R, False);
FColorCombs := nil;
InvalidateRect(Handle, @FColorCombRect, False);
InvalidateRect(Handle, @FCustomColorRect, False);
CalculateCombLayout;
EndSelection;
if Assigned(FOnIntensityChange) then
FOnIntensityChange(Self);
end;
procedure THexaColorPicker.SetMarker(Value: TMarker);
begin
if FMarker <> Value then
begin
FMarker := Value;
DrawAll;
CalculateCombLayout;
Invalidate;
end;
end;
procedure THexaColorPicker.SetNewArrowStyle(Value: boolean);
begin
if FNewArrowStyle <> Value then
begin
FNewArrowStyle := Value;
DrawAll;
CalculateCombLayout;
Invalidate;
end;
end;
procedure THexaColorPicker.SetRadius(r: integer);
begin
if Parent = nil then
exit;
FRadius := r;
DrawAll;
CalculateCombLayout;
Invalidate;
end;
procedure THexaColorPicker.SetSliderVisible(Value: boolean);
begin
if FSliderVisible <> Value then
begin
FSliderVisible := Value;
DrawAll;
CalculateCombLayout;
Invalidate;
end;
end;
procedure THexaColorPicker.SetSliderWidth(w: integer);
begin
if (FSliderWidth <> w) and FSliderVisible then
begin
FSliderWidth := w;
DrawAll;
Width := FSliderRect.Right + 2;
CalculateCombLayout;
Invalidate;
end;
end;
procedure THexaColorPicker.WMLButtonDown(
var Message: {$IFDEF FPC}TLMLButtonDown{$ELSE}TWMLButtonDown{$ENDIF} );
begin
inherited;
SetFocus; // needed so the key events work
if PtInRect(ClientRect, Point(Message.XPos, Message.YPos)) then
HandleCustomColors(Message);
end;
procedure THexaColorPicker.WMLButtonUp(
var Message: {$IFDEF FPC}TLMLButtonUp{$ELSE}TWMLButtonUp{$ENDIF} );
var
LastMode: TSelectionMode;
begin
inherited;
LastMode := FSelectionMode;
FSelectionMode := smNone;
if (FSelectedIndex = CustomCell) and (FCustomIndex <> 0) then
begin
if ((FSelectedIndex = CustomCell) and (LastMode in [smColor, smBW])) or
(FSelectedIndex <> NoCell) and (FSelectedIndex <> CustomCell)
then
EndSelection
end;
end;
procedure THexaColorPicker.WMMouseMove(
var Message: {$IFDEF FPC}TLMMouseMove{$ELSE}TWMMouseMove{$ENDIF} );
var
Shift: TShiftState;
Index: Integer;
Colors: TCombArray;
begin
inherited;
mX := Message.XPos;
mY := Message.YPos;
//get color under cursor
Colors := nil;
FUnderCursor := clNone;
if PtInRect(FBWCombRect, Point(Message.XPos, Message.YPos)) then
begin
Index := FindBWArea(Message.XPos, Message.YPos);
Colors := FBWCombs;
if (Index > -1) and (Colors <> nil) then
FUnderCursor := Colors[Index].Color;
end
else
if PtInRect(FColorCombRect, Point(Message.XPos, Message.YPos)) then
begin
Index := FindColorArea(Message.XPos, Message.YPos);
Colors := FColorCombs;
if (Index > -1) and (Colors <> nil) then
FUnderCursor := Colors[Index].Color;
end
else
FUnderCursor := clNone;
// further process message
Shift := KeysToShiftState(Message.Keys);
if ssLeft in Shift then
HandleCustomColors(Message);
end;
end.