◆◆◆求一个用颜色下拉列表选颜色的控件(类似ColorBo
有什么现成的控件吗?推荐一个,谢谢!
2楼: 不会吧????逼我自己写啊,老大!难道没有吗?
如果自己写的话我想参考下拉菜单和ColorBox,在下拉菜单上画颜色,不知道思路对不? 如进销存excel表
3楼: 自己画菜单喽~把颜色都画到一个imagelist里,然后通过菜单的imageindex来显示,至于名字,就是菜单的caption,自己写就行了
4楼: 谁写了现成的了,给我一个谢谢了。
5楼: 看一下代码。
应当可以继承ColorBox,修改颜色为中文描述。
6楼: 我看了代码了,就是应为嫌麻烦才来问问有没有现成的控件的!哪位好心的给我一个啊!
企业管理软件版7楼: 就是windows写字板中选择字体颜色的那种下拉颜色菜单。难道真的没人写过吗?不会吧!回个话行不??
8楼: unit ColorPickerButton;
// This unit contains a special speed button which can be used to let the user select
// a specific color. The control does not use the standard Windows color dialog, but
// a popup window very similar to the one in Office97, which has been improved a lot
// to support the task of picking one color out of millions. Included is also the
// ability to pick one of the predefined system colors (e.g. clBtnFace).
// Note: The layout is somewhat optimized to look pretty with the predefined box size
// of 18 pixels (the size of one little button in the predefined color area) and
// the number of color comb levels. It is easily possible to change this, but
// if you want to do so then you have probably to make some additional
// changes to the overall layout.
//
// TColorPickerButton works only with D4 and BCB!
// (BCB check by Josue Andrade Gomes gomesj@bsi.com.br)
//
// (c) 1999, written by Dipl. Ing. Mike Lischke (public@lischke-online.de)
// All rights reserved. This unit is freeware and may be used in any software
// product (free or commercial) under the condition that I''m given proper credit
// (Titel, Name and eMail address in the documentation or the About box of the
// product this source code is used in).
// Portions copyright by Borland. The implementation of the speed button has been
// taken from Delphi sources.
//
// 22-JUN-99 ml: a few improvements for the overall layout (mainly indicator rectangle
// does now draw in four different styles and considers the layout
// property of the button (changed to version 1.2, BCB compliance is
// now proved by Josue Andrade Gomes)
// 18-JUN-99 ml: message redirection bug removed (caused an AV under some circumstances)
// and accelerator key handling bug removed (wrong flag for EndSelection)
// (changed to version 1.1)
// 16-JUN-99 ml: initial release
interface
uses Windows, Messages, SysUtils, Classes, Controls, Forms, Graphics, StdCtrls,
ExtCtrls, CommCtrl;
const // constants used in OnHint and internally to indicate a specific cell
DefaultCell = -3;
CustomCell = -2;
NoCell = -1;
type
TButtonLayout = (blGlyphLeft, blGlyphRight, blGlyphTop, blGlyphBottom);
TButtonState = (bsUp, bsDisabled, bsDown, bsExclusive);
TButtonStyle = (bsAutoDetect, bsWin31, bsNew);
TNumGlyphs = 1..4;
TIndicatorBorder = (ibNone, ibFlat, ibSunken, ibRaised);
THintEvent = procedure(Sender: TObject; Cell: Integer; var Hint: String) of object;
TDropChangingEvent = procedure(Sender: TObject; var Allowed: Boolean) of object;
TColorPickerButton = class(TGraphicControl)
private
FGroupIndex: Integer;
FGlyph: Pointer;
FDown: Boolean;
FDragging: Boolean;
FAllowAllUp: Boolean;
FLayout: TButtonLayout;
FSpacing: Integer;
FMargin: Integer;
FFlat: Boolean;
FMouseInControl: Boolean;
FTransparent: Boolean;
FIndicatorBorder: TIndicatorBorder;
FDropDownArrowColor: TColor;
FDropDownWidth: Integer;
FDropDownZone: Boolean;
FDroppedDown: Boolean;
FSelectionColor: TColor;
FState: TButtonState;
FColorPopup: TWinControl;
FPopupWnd: HWND;
FOnChange,
FOnDefaultSelect,
FOnDropChanged: TNotifyEvent;
FOnDropChanging: TDropChangingEvent;
FOnHint: THintEvent;
procedure GlyphChanged(Sender: TObject);
procedure UpdateExclusive;
function GetGlyph: TBitmap;
procedure SetDropDownArrowColor(Value: TColor);
procedure SetDropDownWidth(Value: integer);
procedure SetGlyph(Value: TBitmap);
function GetNumGlyphs: TNumGlyphs;
procedure SetNumGlyphs(Value: TNumGlyphs);
procedure SetDown(Value: Boolean);
procedure SetFlat(Value: Boolean);
procedure SetAllowAllUp(Value: Boolean);
procedure SetGroupIndex(Value: Integer);
procedure SetLayout(Value: TButtonLayout);
procedure SetSpacing(Value: Integer);
procedure SetMargin(Value: Integer);
procedure UpdateTracking;
procedure CMEnabledChanged(var Message: TMessage); message CM_ENABLEDCHANGED;
procedure CMButtonPressed(var Message: TMessage); message CM_BUTTONPRESSED;
procedure CMDialogChar(var Message: TCMDialogChar); message CM_DIALOGCHAR;
procedure CMFontChanged(var Message: TMessage); message CM_FONTCHANGED;
procedure CMTextChanged(var Message: TMessage); message CM_TEXTCHANGED;
procedure CMSysColorChange(var Message: TMessage); message CM_SYSCOLORCHANGE;
procedure CMMouseEnter(var Message: TMessage); message CM_MOUSEENTER;
procedure CMMouseLeave(var Message: TMessage); message CM_MOUSELEAVE;
procedure WMLButtonDblClk(var Message: TWMLButtonDown); message WM_LBUTTONDBLCLK;
procedure DrawButtonSeperatorUp(Canvas: TCanvas);
procedure DrawButtonSeperatorDown(Canvas: TCanvas);
procedure DrawTriangle(Canvas: TCanvas; Top, Left, Width: Integer);
procedure SetDroppedDown(const Value: Boolean);
procedure SetSelectionColor(const Value: TColor);
procedure PopupWndProc(var Msg: TMessage);
function GetCustomText: String;
procedure SetCustomText(const Value: String);
function GetDefaultText: String;
procedure SetDefaultText(const Value: String);
procedure SetShowSystemColors(const Value: Boolean);
function GetShowSystemColors: Boolean;
procedure SetTransparent(const Value: Boolean);
procedure SetIndicatorBorder(const Value: TIndicatorBorder);
function GetPopupSpacing: Integer;
procedure SetPopupSpacing(const Value: Integer);
protected
procedure DoDefaultEvent; virtual;
procedure ActionChange(Sender: TObject; CheckDefaults: Boolean); override;
function GetPalette: HPALETTE; override;
procedure Loaded; override;
procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
procedure MouseMove(Shift: TShiftState; X, Y: Integer); override;
procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
procedure Paint; override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure Click; override;
property DroppedDown: Boolean read FDroppedDown write SetDroppedDown;
published
property Action;
property AllowAllUp: Boolean read FAllowAllUp write SetAllowAllUp default False;
property Anchors;
property BiDiMode;
property Caption;
property Constraints;
property CustomText: String read GetCustomText write SetCustomText;
property DefaultText: String read GetDefaultText write SetDefaultText;
property Down: Boolean read FDown write SetDown default False;
property DropDownArrowColor: TColor read FDropDownArrowColor write SetDropDownArrowColor default clBlack;
property DropDownWidth: integer read FDropDownWidth write SetDropDownWidth default 15;
property Enabled;
property Flat: Boolean read FFlat write SetFlat default False;
property Font;
property Glyph: TBitmap read GetGlyph write SetGlyph;
property GroupIndex: Integer read FGroupIndex write SetGroupIndex default 0;
property IndicatorBorder: TIndicatorBorder read FIndicatorBorder write SetIndicatorBorder default ibFlat;
property Layout: TButtonLayout read FLayout write SetLayout default blGlyphLeft;
property Margin: Integer read FMargin write SetMargin default -1;
property NumGlyphs: TNumGlyphs read GetNumGlyphs write SetNumGlyphs default 1;
property ParentBiDiMode;
property ParentFont;
property ParentShowHint;
property PopupSpacing: Integer read GetPopupSpacing write SetPopupSpacing;
property SelectionColor: TColor read FSelectionColor write SetSelectionColor default clBlack;
property ShowHint;
property ShowSystemColors: Boolean read GetShowSystemColors write SetShowSystemColors;
property Spacing: Integer read FSpacing write SetSpacing default 4;
property Transparent: Boolean read FTransparent write SetTransparent default True;
property Visible;
property OnChange: TNotifyEvent read FOnChange write FOnChange;
property OnClick;
property OnDblClick;
property OnDefaultSelect: TNotifyEvent read FOnDefaultSelect write FOnDefaultSelect;
property OnDropChanged: TNotifyEvent read FOnDropChanged write FOnDropChanged;
property OnDropChanging: TDropChangingEvent read FOnDropChanging write FOnDropChanging;
property OnHint: THintEvent read FOnHint write FOnHint;
property OnMouseDown;
property OnMouseMove;
property OnMouseUp;
end;
procedure Register;
//-----------------------------------------------------------------------------
implementation
uses ActnList, ImgList;
const DRAW_BUTTON_UP = 8208;
DRAW_BUTTON_DOWN = 8720;
type TColorEntry = record
Name: PChar;
case Boolean of
True: (R, G, B, reserved: Byte);
False: (Color: COLORREF);
end;
const DefaultColorCount = 40;
// these colors are the same as used in Office 97/2000
DefaultColors : array[0..DefaultColorCount - 1] of TColorEntry = (
(Name: ''Black''; Color: $000000),
(Name: ''Brown''; Color: $003399),
(Name: ''Olive Green''; Color: $003333),
(Name: ''Dark Green''; Color: $003300),
(Name: ''Dark Teal''; Color: $663300),
(Name: ''Dark blue''; Color: $800000),
(Name: ''Indigo''; Color: $993333),
(Name: ''Gray-80%''; Color: $333333),
(Name: ''Dark Red''; Color: $000080),
(Name: ''Orange''; Color: $0066FF),
(Name: ''Dark Yellow''; Color: $008080),
(Name: ''Green''; Color: $008000),
(Name: ''Teal''; Color: $808000),
(Name: ''Blue''; Color: $FF0000),
(Name: ''Blue-Gray''; Color: $996666),
(Name: ''Gray-50%''; Color: $808080),
(Name: ''Red''; Color: $0000FF),
(Name: ''Light Orange''; Color: $0099FF),
(Name: ''Lime''; Color: $00CC99),
(Name: ''Sea Green''; Color: $669933),
(Name: ''Aqua''; Color: $CCCC33),
(Name: ''Light Blue''; Color: $FF6633),
(Name: ''Violet''; Color: $800080),
(Name: ''Grey-40%''; Color: $969696),
(Name: ''Pink''; Color: $FF00FF),
(Name: ''Gold''; Color: $00CCFF),
(Name: ''Yellow''; Color: $00FFFF),
(Name: ''Bright Green''; Color: $00FF00),
(Name: ''Turquoise''; Color: $FFFF00),
(Name: ''Sky Blue''; Color: $FFCC00),
(Name: ''Plum''; Color: $663399),
(Name: ''Gray-25%''; Color: $C0C0C0),
(Name: ''Rose''; Color: $CC99FF),
(Name: ''Tan''; Color: $99CCFF),
(Name: ''Light Yellow''; Color: $99FFFF),
(Name: ''Light Green''; Color: $CCFFCC),
(Name: ''Light Turquoise''; Color: $FFFFCC),
(Name: ''Pale Blue''; Color: $FFCC99),
(Name: ''Lavender''; Color: $FF99CC),
(Name: ''White''; Color: $FFFFFF)
);
SysColorCount = 25;
SysColors : array[0..SysColorCount - 1] of TColorEntry = (
(Name: ''system color: scroll bar''; Color: COLORREF(clScrollBar)),
(Name: ''system color: background''; Color: COLORREF(clBackground)),
(Name: ''system color: active caption''; Color: COLORREF(clActiveCaption)),
(Name: ''system color: inactive caption''; Color: COLORREF(clInactiveCaption)),
(Name: ''system color: menu''; Color: COLORREF(clMenu)),
(Name: ''system color: window''; Color: COLORREF(clWindow)),
(Name: ''system color: window frame''; Color: COLORREF(clWindowFrame)),
(Name: ''system color: menu text''; Color: COLORREF(clMenuText)),
(Name: ''system color: window text''; Color: COLORREF(clWindowText)),
(Name: ''system color: caption text''; Color: COLORREF(clCaptionText)),
(Name: ''system color: active border''; Color: COLORREF(clActiveBorder)),
(Name: ''system color: inactive border''; Color: COLORREF(clInactiveBorder)),
(Name: ''system color: application workspace''; Color: COLORREF(clAppWorkSpace)),
(Name: ''system color: highlight''; Color: COLORREF(clHighlight)),
(Name: ''system color: highlight text''; Color: COLORREF(clHighlightText)),
(Name: ''system color: button face''; Color: COLORREF(clBtnFace)),
(Name: ''system color: button shadow''; Color: COLORREF(clBtnShadow)),
(Name: ''system color: gray text''; Color: COLORREF(clGrayText)),
(Name: ''system color: button text''; Color: COLORREF(clBtnText)),
(Name: ''system color: inactive caption text''; Color: COLORREF(clInactiveCaptionText)),
(Name: ''system color: button highlight''; Color: COLORREF(clBtnHighlight)),
(Name: ''system color: 3D dark shadow''; Color: COLORREF(cl3DDkShadow)),
(Name: ''system color: 3D light''; Color: COLORREF(cl3DLight)),
(Name: ''system color: info text''; Color: COLORREF(clInfoText)),
(Name: ''system color: info background''; Color: COLORREF(clInfoBk))
);
type
TGlyphList = class(TImageList)
private
FUsed: TBits;
FCount: Integer;
function AllocateIndex: Integer;
public
constructor CreateSize(AWidth, AHeight: Integer);
destructor Destroy; override;
function AddMasked(Image: TBitmap; MaskColor: TColor): Integer;
procedure Delete(Index: Integer);
property Count: Integer read FCount;
end;
TGlyphCache = class
private
FGlyphLists: TList;
public
constructor Create;
destructor Destroy; override;
function GetList(AWidth, AHeight: Integer): TGlyphList;
procedure ReturnList(List: TGlyphList);
function Empty: Boolean;
end;
TButtonGlyph = class
private
FOriginal: TBitmap;
FGlyphList: TGlyphList;
FIndexes: array[TButtonState] of Integer;
FTransparentColor: TColor;
FNumGlyphs: TNumGlyphs;
FOnChange: TNotifyEvent;
procedure GlyphChanged(Sender: TObject);
procedure SetGlyph(Value: TBitmap);
procedure SetNumGlyphs(Value: TNumGlyphs);
procedure Invalidate;
function CreateButtonGlyph(State: TButtonState): Integer;
procedure DrawButtonGlyph(Canvas: TCanvas; const GlyphPos: TPoint;
State: TButtonState; Transparent: Boolean);
procedure DrawButtonText(Canvas: TCanvas; const Caption: string;
TextBounds: TRect; State: TButtonState; BiDiFlags: Longint);
procedure CalcButtonLayout(Canvas: TCanvas; const Client: TRect;
const Offset: TPoint; const Caption: string; Layout: TButtonLayout;
Margin, Spacing: Integer; var GlyphPos: TPoint; var TextBounds: TRect;
const DropDownWidth: Integer; BiDiFlags: Longint);
public
constructor Create;
destructor Destroy; override;
function Draw(Canvas: TCanvas; const Client: TRect; const Offset: TPoint;
const Caption: string; Layout: TButtonLayout; Margin, Spacing: Integer;
State: TButtonState; Transparent: Boolean;
const DropDownWidth: Integer; BiDiFlags: Longint): TRect;
property Glyph: TBitmap read FOriginal write SetGlyph;
property NumGlyphs: TNumGlyphs read FNumGlyphs write SetNumGlyphs;
property OnChange: TNotifyEvent read FOnChange write FOnChange;
end;
TCombEntry = record
Position: TPoint;
Color: COLORREF;
end;
TCombArray = array of TCombEntry;
TFloatPoint = record
X, Y: Extended;
end;
TRGB = record
Red, Green, Blue: Single;
end;
TSelectionMode = (smNone, smColor, smBW, smRamp);
TColorPopup = class(TWinControl)
private
FDefaultText,
FCustomText: String;
FCurrentColor: TCOlor;
FCanvas: TCanvas;
FMargin,
FSpacing,
FColumnCount,
FRowCount,
FSysRowCount,
FBoxSize: Integer;
FSelectedIndex,
FHoverIndex: Integer;
FWindowRect,
FCustomTextRect,
FDefaultTextRect,
FColorCombRect,
FBWCombRect,
FSliderRect,
FCustomColorRect: TRect;
FShowSysColors: Boolean;
// custom color picking
FCombSize,
FLevels: Integer;
FBWCombs,
FColorCombs: TCombArray;
FCombCorners: array[0..5] of TFloatPoint;
FCenterColor: TRGB;
FCenterIntensity: Single; // scale factor for the center color
FCustomIndex, // If FSelectedIndex contains CustomCell then this index shows
// which index in the custom area has been selected.
// Positive values indicate the color comb and negativ 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; // indicates where the user has clicked
// with the mouse to restrict draw selection
procedure SelectColor(Color: TColor);
procedure ChangeHoverSelection(Index: Integer);
procedure DrawCell(Index: Integer);
procedure InvalidateCell(Index: Integer);
procedure EndSelection(Cancel: Boolean);
function GetCellRect(Index: Integer; var Rect: TRect): Boolean;
function GetColumn(Index: Integer): Integer;
function GetIndex(Row, Col: Integer): Integer;
function GetRow(Index: Integer): Integer;
procedure Initialise;
procedure AdjustWindow;
procedure SetSpacing(Value: Integer);
procedure SetSelectedColor(const Value: TColor);
procedure CMHintShow(var Message: TMessage); message CM_HINTSHOW;
procedure CNKeyDown(var Message: TWMKeyDown); message CN_KEYDOWN;
procedure CNSysKeyDown(var Message: TWMChar); message CN_SYSKEYDOWN;
procedure WMActivateApp(var Message: TWMActivateApp); message WM_ACTIVATEAPP;
procedure WMLButtonDown(var Message: TWMLButtonDown); message WM_LBUTTONDOWN;
procedure WMKillFocus(var Message: TWMKillFocus); message WM_KILLFOCUS;
procedure WMLButtonUp(var Message: TWMLButtonUp); message WM_LBUTTONUP;
procedure WMMouseMove(var Message: TWMMouseMove); message WM_MOUSEMOVE;
procedure WMPaint(var Message: TWMPaint); message WM_PAINT;
function SelectionFromPoint(P: TPoint): Integer;
procedure DrawCombControls;
procedure DrawComb(Canvas: TCanvas; X, Y, Size: Integer);
function HandleBWArea(const Message: TWMMouse): Boolean;
function HandleColorComb(const Message: TWMMouse): Boolean;
function HandleSlider(const Message: TWMMouse): Boolean;
function PtInComb(Comb: TCombEntry; P: TPoint; Scale: Integer): Boolean;
procedure HandleCustomColors(var Message: TWMMouse);
function GetHint(Cell: Integer): String;
function FindBWArea(X, Y: Integer): Integer;
function FindColorArea(X, Y: Integer): Integer;
procedure DrawSeparator(Left, Top, Right: Integer);
procedure ChangeSelection(NewSelection: Integer);
protected
procedure CalculateCombLayout;
procedure CreateParams(var Params: TCreateParams); override;
procedure CreateWnd; override;
procedure ShowPopupAligned;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
property SelectedColor: TColor read FCurrentColor write SetSelectedColor;
property Spacing: Integer read FSpacing write SetSpacing;
end;
const DefCenterColor: TRGB =(Red: 1; Green: 1; Blue: 1); // White
DefColors: array[0..5] of TRGB = (
(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);
var GlyphCache: TGlyphCache;
ButtonCount: Integer;
//----------------- TGlyphList ------------------------------------------------
constructor TGlyphList.CreateSize(AWidth, AHeight: Integer);
begin
inherited CreateSize(AWidth, AHeight);
FUsed := TBits.Create;
end;
//-----------------------------------------------------------------------------
destructor TGlyphList.Destroy;
begin
FUsed.Free;
inherited Destroy;
end;
//-----------------------------------------------------------------------------
function TGlyphList.AllocateIndex: Integer;
begin
Result := FUsed.OpenBit;
if Result >= FUsed.Size then
begin
Result := inherited Add(nil, nil);
FUsed.Size := Result + 1;
end;
FUsed[Result] := True;
end;
//-----------------------------------------------------------------------------
function TGlyphList.AddMasked(Image: TBitmap; MaskColor: TColor): Integer;
begin
Result := AllocateIndex;
ReplaceMasked(Result, Image, MaskColor);
Inc(FCount);
end;
//-----------------------------------------------------------------------------
procedure TGlyphList.Delete(Index: Integer);
begin
if FUsed[Index] then
begin
Dec(FCount);
FUsed[Index] := False;
end;
end;
//----------------- TGlyphCache -----------------------------------------------
constructor TGlyphCache.Create;
begin
inherited Create;
FGlyphLists := TList.Create;
end;
//-----------------------------------------------------------------------------
destructor TGlyphCache.Destroy;
begin
FGlyphLists.Free;
inherited Destroy;
end;
//-----------------------------------------------------------------------------
function TGlyphCache.GetList(AWidth, AHeight: Integer): TGlyphList;
var I: Integer;
begin
for I := FGlyphLists.Count - 1 downto 0 do
begin
Result := FGlyphLists[I];
with Result do
if (AWidth = Width) and (AHeight = Height) then Exit;
end;
Result := TGlyphList.CreateSize(AWidth, AHeight);
FGlyphLists.Add(Result);
end;
//-----------------------------------------------------------------------------
procedure TGlyphCache.ReturnList(List: TGlyphList);
begin
if List = nil then Exit;
if List.Count = 0 then
begin
FGlyphLists.Remove(List);
List.Free;
end;
end;
//-----------------------------------------------------------------------------
function TGlyphCache.Empty: Boolean;
begin
Result := FGlyphLists.Count = 0;
end;
//----------------- TButtonGlyph ----------------------------------------------
constructor TButtonGlyph.Create;
var I: TButtonState;
begin
inherited Create;
FOriginal := TBitmap.Create;
FOriginal.OnChange := GlyphChanged;
FTransparentColor := clOlive;
FNumGlyphs := 1;
for I := Low(I) to High(I) do FIndexes[I] := -1;
if GlyphCache = nil then GlyphCache := TGlyphCache.Create;
end;
//-----------------------------------------------------------------------------
destructor TButtonGlyph.Destroy;
begin
FOriginal.Free;
Invalidate;
if Assigned(GlyphCache) and GlyphCache.Empty then
begin
GlyphCache.Free;
GlyphCache := nil;
end;
inherited Destroy;
end;
//-----------------------------------------------------------------------------
procedure TButtonGlyph.Invalidate;
var I: TButtonState;
begin
for I := Low(I) to High(I) do
begin
if FIndexes[I] <> -1 then FGlyphList.Delete(FIndexes[I]);
FIndexes[I] := -1;
end;
GlyphCache.ReturnList(FGlyphList);
FGlyphList := nil;
end;
//-----------------------------------------------------------------------------
procedure TButtonGlyph.GlyphChanged(Sender: TObject);
begin
if Sender = FOriginal then
begin
FTransparentColor := FOriginal.TransparentColor;
Invalidate;
if Assigned(FOnChange) then FOnChange(Self);
end;
end;
//-----------------------------------------------------------------------------
procedure TButtonGlyph.SetGlyph(Value: TBitmap);
var Glyphs: Integer;
begin
Invalidate;
FOriginal.Assign(Value);
if (Value <> nil) and (Value.Height > 0) then
begin
FTransparentColor := Value.TransparentColor;
if Value.Width mod Value.Height = 0 then
begin
Glyphs := Value.Width div Value.Height;
if Glyphs > 4 then Glyphs := 1;
SetNumGlyphs(Glyphs);
end;
end;
end;
//-----------------------------------------------------------------------------
procedure TButtonGlyph.SetNumGlyphs(Value: TNumGlyphs);
begin
if (Value <> FNumGlyphs) and (Value > 0) then
begin
Invalidate;
FNumGlyphs := Value;
GlyphChanged(Glyph);
end;
end;
//-----------------------------------------------------------------------------
function TButtonGlyph.CreateButtonGlyph(State: TButtonState): Integer;
const ROP_DSPDxax = $00E20746;
var TmpImage, DDB, MonoBmp: TBitmap;
IWidth, IHeight: Integer;
IRect, ORect: TRect;
I: TButtonState;
DestDC: HDC;
begin
if (State = bsDown) and (NumGlyphs < 3) then State := bsUp;
Result := FIndexes[State];
if Result <> -1 then Exit;
if (FOriginal.Width or FOriginal.Height) = 0 then Exit;
IWidth := FOriginal.Width div FNumGlyphs;
IHeight := FOriginal.Height;
if FGlyphList = nil then
begin
if GlyphCache = nil then GlyphCache := TGlyphCache.Create;
FGlyphList := GlyphCache.GetList(IWidth, IHeight);
end;
TmpImage := TBitmap.Create;
try
TmpImage.Width := IWidth;
TmpImage.Height := IHeight;
IRect := Rect(0, 0, IWidth, IHeight);
TmpImage.Canvas.Brush.Color := clBtnFace;
TmpImage.Palette := CopyPalette(FOriginal.Palette);
I := State;
if Ord(I) >= NumGlyphs then I := bsUp;
ORect := Rect(Ord(I) * IWidth, 0, (Ord(I) + 1) * IWidth, IHeight);
case State of
bsUp, bsDown,
bsExclusive:
begin
TmpImage.Canvas.CopyRect(IRect, FOriginal.Canvas, ORect);
if FOriginal.TransparentMode = tmFixed then
FIndexes[State] := FGlyphList.AddMasked(TmpImage, FTransparentColor)
else
FIndexes[State] := FGlyphList.AddMasked(TmpImage, clDefault);
end;
bsDisabled:
begin
MonoBmp := nil;
DDB := nil;
try
MonoBmp := TBitmap.Create;
DDB := TBitmap.Create;
DDB.Assign(FOriginal);
DDB.HandleType := bmDDB;
if NumGlyphs > 1 then
with TmpImage.Canvas do
begin
// Change white & gray to clBtnHighlight and clBtnShadow
CopyRect(IRect, DDB.Canvas, ORect);
MonoBmp.Monochrome := True;
MonoBmp.Width := IWidth;
MonoBmp.Height := IHeight;
// Convert white to clBtnHighlight
DDB.Canvas.Brush.Color := clWhite;
MonoBmp.Canvas.CopyRect(IRect, DDB.Canvas, ORect);
Brush.Color := clBtnHighlight;
DestDC := Handle;
SetTextColor(DestDC, clBlack);
SetBkColor(DestDC, clWhite);
BitBlt(DestDC, 0, 0, IWidth, IHeight, MonoBmp.Canvas.Handle, 0, 0, ROP_DSPDxax);
// Convert gray to clBtnShadow
DDB.Canvas.Brush.Color := clGray;
MonoBmp.Canvas.CopyRect(IRect, DDB.Canvas, ORect);
Brush.Color := clBtnShadow;
DestDC := Handle;
SetTextColor(DestDC, clBlack);
SetBkColor(DestDC, clWhite);
BitBlt(DestDC, 0, 0, IWidth, IHeight, MonoBmp.Canvas.Handle, 0, 0, ROP_DSPDxax);
// Convert transparent color to clBtnFace
DDB.Canvas.Brush.Color := ColorToRGB(FTransparentColor);
MonoBmp.Canvas.CopyRect(IRect, DDB.Canvas, ORect);
Brush.Color := clBtnFace;
DestDC := Handle;
SetTextColor(DestDC, clBlack);
SetBkColor(DestDC, clWhite);
BitBlt(DestDC, 0, 0, IWidth, IHeight, MonoBmp.Canvas.Handle, 0, 0, ROP_DSPDxax);
end
else
begin
// Create a disabled version
with MonoBmp do
begin
Assign(FOriginal);
HandleType := bmDDB;
Canvas.Brush.Color := clBlack;
Width := IWidth;
if Monochrome then
begin
Canvas.Font.Color := clWhite;
Monochrome := False;
Canvas.Brush.Color := clWhite;
end;
Monochrome := True;
end;
with TmpImage.Canvas do
begin
Brush.Color := clBtnFace;
FillRect(IRect);
Brush.Color := clBtnHighlight;
SetTextColor(Handle, clBlack);
SetBkColor(Handle, clWhite);
BitBlt(Handle, 1, 1, IWidth, IHeight, MonoBmp.Canvas.Handle, 0, 0, ROP_DSPDxax);
Brush.Color := clBtnShadow;
SetTextColor(Handle, clBlack);
SetBkColor(Handle, clWhite);
BitBlt(Handle, 0, 0, IWidth, IHeight, MonoBmp.Canvas.Handle, 0, 0, ROP_DSPDxax);
end;
end;
finally
DDB.Free;
MonoBmp.Free;
end;
FIndexes[State] := FGlyphList.AddMasked(TmpImage, clDefault);
end;
end;
finally
TmpImage.Free;
end;
Result := FIndexes[State];
FOriginal.Dormant;
end;
//-----------------------------------------------------------------------------
procedure TButtonGlyph.DrawButtonGlyph(Canvas: TCanvas; const GlyphPos: TPoint;
State: TButtonState; Transparent: Boolean);
var Index: Integer;
begin
if Assigned(FOriginal) then
begin
if (FOriginal.Width = 0) or (FOriginal.Height = 0) then Exit;
Index := CreateButtonGlyph(State);
with GlyphPos do
if Transparent or (State = bsExclusive) then
ImageList_DrawEx(FGlyphList.Handle, Index, Canvas.Handle, X, Y, 0, 0, clNone, clNone, ILD_Transparent)
else
ImageList_DrawEx(FGlyphList.Handle, Index, Canvas.Handle, X, Y, 0, 0, ColorToRGB(clBtnFace), clNone, ILD_Normal);
end;
end;
//-----------------------------------------------------------------------------
procedure TButtonGlyph.DrawButtonText(Canvas: TCanvas; const Caption: string;
TextBounds: TRect; State: TButtonState;
BiDiFlags: Longint);
begin
with Canvas do
begin
Brush.Style := bsClear;
if State = bsDisabled then
begin
OffsetRect(TextBounds, 1, 1);
Font.Color := clBtnHighlight;
DrawText(Handle, PChar(Caption), Length(Caption), TextBounds, DT_CENTER or DT_VCENTER or BiDiFlags);
OffsetRect(TextBounds, -1, -1);
Font.Color := clBtnShadow;
DrawText(Handle, PChar(Caption), Length(Caption), TextBounds, DT_CENTER or DT_VCENTER or BiDiFlags);
end
else
DrawText(Handle, PChar(Caption), Length(Caption), TextBounds, DT_CENTER or DT_VCENTER or BiDiFlags);
end;
end;
//-----------------------------------------------------------------------------
procedure TButtonGlyph.CalcButtonLayout(Canvas: TCanvas; const Client: TRect;
const Offset: TPoint; const Caption: string; Layout: TButtonLayout; Margin,
Spacing: Integer; var GlyphPos: TPoint; var TextBounds: TRect;
const DropDownWidth: Integer; BiDiFlags: Longint);
var TextPos: TPoint;
ClientSize,
GlyphSize,
TextSize: TPoint;
TotalSize: TPoint;
begin
if (BiDiFlags and DT_RIGHT) = DT_RIGHT then
if Layout = blGlyphLeft then Layout := blGlyphRight
else
if Layout = blGlyphRight then Layout := blGlyphLeft;
// calculate the item sizes
ClientSize := Point(Client.Right - Client.Left - DropDownWidth, Client.Bottom - Client.Top);
if FOriginal <> nil then GlyphSize := Point(FOriginal.Width div FNumGlyphs, FOriginal.Height)
else GlyphSize := Point(0, 0);
if Length(Caption) > 0 then
begin
TextBounds := Rect(0, 0, Client.Right - Client.Left, 0);
DrawText(Canvas.Handle, PChar(Caption), Length(Caption), TextBounds, DT_CALCRECT or BiDiFlags);
TextSize := Point(TextBounds.Right - TextBounds.Left, TextBounds.Bottom - TextBounds.Top);
end
else
begin
TextBounds := Rect(0, 0, 0, 0);
TextSize := Point(0,0);
end;
// If the layout has the glyph on the right or the left, then both the
// text and the glyph are centered vertically. If the glyph is on the top
// or the bottom, then both the text and the glyph are centered horizontally.
if Layout in [blGlyphLeft, blGlyphRight] then
begin
GlyphPos.Y := (ClientSize.Y - GlyphSize.Y + 1) div 2;
TextPos.Y := (ClientSize.Y - TextSize.Y + 1) div 2;
end
else
begin
GlyphPos.X := (ClientSize.X - GlyphSize.X + 1) div 2;
TextPos.X := (ClientSize.X - TextSize.X + 1) div 2;
end;
// if there is no text or no bitmap, then Spacing is irrelevant
if (TextSize.X = 0) or (GlyphSize.X = 0) then Spacing := 0;
// adjust Margin and Spacing
if Margin = -1 then
begin
if Spacing = -1 then
begin
TotalSize := Point(GlyphSize.X + TextSize.X, GlyphSize.Y + TextSize.Y);
if Layout in [blGlyphLeft, blGlyphRight] then Margin := (ClientSize.X - TotalSize.X) div 3
else Margin := (ClientSize.Y - TotalSize.Y) div 3;
Spacing := Margin;
end
else
begin
TotalSize := Point(GlyphSize.X + Spacing + TextSize.X, GlyphSize.Y + Spacing + TextSize.Y);
if Layout in [blGlyphLeft, blGlyphRight] then Margin := (ClientSize.X - TotalSize.X + 1) div 2
else Margin := (ClientSize.Y - TotalSize.Y + 1) div 2;
end;
end
else
begin
if Spacing = -1 then
begin
TotalSize := Point(ClientSize.X - (Margin + GlyphSize.X), ClientSize.Y - (Margin + GlyphSize.Y));
if Layout in [blGlyphLeft, blGlyphRight] then Spacing := (TotalSize.X - TextSize.X) div 2
else Spacing := (TotalSize.Y - TextSize.Y) div 2;
end;
end;
case Layout of
blGlyphLeft:
begin
GlyphPos.X := Margin;
TextPos.X := GlyphPos.X + GlyphSize.X + Spacing;
end;
blGlyphRight:
begin
GlyphPos.X := ClientSize.X - Margin - GlyphSize.X;
TextPos.X := GlyphPos.X - Spacing - TextSize.X;
end;
blGlyphTop:
begin
GlyphPos.Y := Margin;
TextPos.Y := GlyphPos.Y + GlyphSize.Y + Spacing;
end;
blGlyphBottom:
begin
GlyphPos.Y := ClientSize.Y - Margin - GlyphSize.Y;
TextPos.Y := GlyphPos.Y - Spacing - TextSize.Y;
end;
end;
// fixup the result variables
with GlyphPos do
begin
Inc(X, Client.Left + Offset.X);
Inc(Y, Client.Top + Offset.Y);
end;
OffsetRect(TextBounds, TextPos.X + Client.Left + Offset.X, TextPos.Y + Client.Top + Offset.X);
end;
//-----------------------------------------------------------------------------
function TButtonGlyph.Draw(Canvas: TCanvas; const Client: TRect;
const Offset: TPoint; const Caption: string; Layout: TButtonLayout;
Margin, Spacing: Integer; State: TButtonState; Transparent: Boolean;
const DropDownWidth: Integer; BiDiFlags: Longint): TRect;
var GlyphPos: TPoint;
R: TRect;
begin
CalcButtonLayout(Canvas, Client, Offset, Caption, Layout, Margin, Spacing, GlyphPos, R, DropDownWidth, BidiFlags);
DrawButtonGlyph(Canvas, GlyphPos, State, Transparent);
DrawButtonText(Canvas, Caption, R, State, BiDiFlags);
// return a rectangle wherein the color indicator can be drawn
if Caption = '''' then
begin
Result := Client;
Dec(Result.Right, DropDownWidth + 2);
InflateRect(Result, -2, -2);
// consider glyph if no text is to be painted (else it is already taken into account)
if Assigned(FOriginal) and (FOriginal.Width > 0) and (FOriginal.Height > 0) then
case Layout of
blGlyphLeft:
begin
Result.Left := GlyphPos.X + FOriginal.Width + 4;
Result.Top := GlyphPos.Y;
Result.Bottom := GlyphPos.Y + FOriginal.Height;
end;
blGlyphRight:
begin
Result.Right := GlyphPos.X - 4;
Result.Top := GlyphPos.Y;
Result.Bottom := GlyphPos.Y + FOriginal.Height;
end;
blGlyphTop:
Result.Top := GlyphPos.Y + FOriginal.Height + 4;
blGlyphBottom:
Result.Bottom := GlyphPos.Y - 4;
end;
end
else
begin
// consider caption
Result := Rect(R.Left, R.Bottom, R.Right, R.Bottom + 6);
if (Result.Bottom + 2) > Client.Bottom then Result.Bottom := Client.Bottom - 2;
end;
end;
//----------------- TColorPopup ------------------------------------------------
constructor TColorPopup.Create(AOwner: TComponent);
begin
inherited;
ControlStyle := ControlStyle - [csAcceptsControls] + [csOpaque];
FCanvas := TCanvas.Create;
Color := clBtnFace;
ShowHint := True;
Initialise;
end;
//------------------------------------------------------------------------------
procedure TColorPopup.Initialise;
var I: Integer;
begin
FBoxSize := 18;
FMargin := GetSystemMetrics(SM_CXEDGE);
FSpacing := 8;
FHoverIndex := NoCell;
FSelectedIndex := NoCell;
// init comb caclulation
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;
FRadius := 66;
FLevels := 7;
FCombSize := Trunc(FRadius / (FLevels - 1));
FCenterColor := DefCenterColor;
FCenterIntensity := 1;
end;
//------------------------------------------------------------------------------
destructor TColorPopup.Destroy;
begin
FBWCombs := nil;
FColorCombs := nil;
FCanvas.Free;
inherited;
end;
//------------------------------------------------------------------------------
procedure TColorPopup.CNSysKeyDown(var Message: TWMKeyDown);
// handles accelerator keys
begin
with Message do
begin
if (Length(FDefaultText) > 0) and IsAccel(CharCode, FDefaultText) then
begin
ChangeSelection(DefaultCell);
EndSelection(False);
Result := 1;
end
else
if (FSelectedIndex <> CustomCell) and
(Length(FCustomText) > 0) and
IsAccel(CharCode, FCustomText) then
begin
ChangeSelection(CustomCell);
Result := 1;
end
else inherited;
end;
end;
//------------------------------------------------------------------------------
procedure TColorPopup.CNKeyDown(var Message: TWMKeyDown);
// if an arrow key is pressed, then move the selection
var Row,
MaxRow,
Column: Integer;
begin
inherited;
if FHoverIndex <> NoCell then
begin
Row := GetRow(FHoverIndex);
Column := GetColumn(FHoverIndex);
end
else
begin
Row := GetRow(FSelectedIndex);
Column := GetColumn(FSelectedIndex);
end;
if FShowSysColors then MaxRow := DefaultColorCount + SysColorCount - 1
else MaxRow := DefaultColorCount - 1;
case Message.CharCode of
VK_DOWN:
begin
if Row = DefaultCell then
begin
Row := 0;
Column := 0;
end
else
if Row = CustomCell then
begin
if Length(FDefaultText) > 0 then
begin
Row := DefaultCell;
Column := Row;
end
else
begin
Row := 0;
Column := 0;
end;
end
else
begin
Inc(Row);
if GetIndex(Row, Column) < 0 then
begin
if Length(FCustomText) > 0 then
begin
Row := CustomCell;
Column := Row;
end
else
begin
if Length(FDefaultText) > 0 then
begin
Row := DefaultCell;
Column := Row;
end
else
begin
Row := 0;
Column := 0;
end;
end;
end;
end;
ChangeHoverSelection(GetIndex(Row, Column));
Message.Result := 1;
end;
VK_UP:
begin
if Row = DefaultCell then
begin
if Length(FCustomText) > 0 then
begin
Row := CustomCell;
Column := Row;
end
else
begin
Row := GetRow(MaxRow);
Column := GetColumn(MaxRow);
end
end
else
if Row = CustomCell then
begin
Row := GetRow(MaxRow);
Column := GetColumn(MaxRow);
end
else
if Row > 0 then Dec(Row)
else
begin
if Length(FDefaultText) > 0 then
begin
Row := DefaultCell;
Column := Row;
end
else
if Length(FCustomText) > 0 then
begin
Row := CustomCell;
Column := Row;
end
else
begin
Row := GetRow(MaxRow);
Column := GetColumn(MaxRow);
end;
end;
ChangeHoverSelection(GetIndex(Row, Column));
Message.Result := 1;
end;
VK_RIGHT:
begin
if Row = DefaultCell then
begin
Row := 0;
Column := 0;
end
else
if Row = CustomCell then
begin
if Length(FDefaultText) > 0 then
begin
Row := DefaultCell;
Column := Row;
end
else
begin
Row := 0;
Column := 0;
end;
end
else
if Column < FColumnCount - 1 then Inc(Column)
else
begin
Column := 0;
Inc(Row);
end;
if GetIndex(Row, Column) = NoCell then
begin
if Length(FCustomText) > 0 then
begin
Row := CustomCell;
Column := Row;
end
else
if Length(FDefaultText) > 0 then
begin
Row := DefaultCell;
Column := Row;
end
else
begin
Row := 0;
Column := 0;
end;
end;
ChangeHoverSelection(GetIndex(row, Column));
Message.Result := 1;
end;
VK_LEFT:
begin
if Row = DefaultCell then
begin
if Length(FCustomText) > 0 then
begin
Row := CustomCell;
Column := Row;
end
else
begin
Row := GetRow(MaxRow);
Column := GetColumn(MaxRow);
end;
end
else
if Row = CustomCell then
begin
Row := GetRow(MaxRow);
Column := GetColumn(MaxRow);
end
else
if Column > 0 then Dec(Column)
else
begin
if Row > 0 then
begin
Dec(Row);
Column := FColumnCount - 1;
end
else
begin
if Length(FDefaultText) > 0 then
begin
Row := DefaultCell;
Column := Row;
end
else
if Length(FCustomText) > 0 then
begin
Row := CustomCell;
Column := Row;
end
else
begin
Row := GetRow(MaxRow);
Column := GetColumn(MaxRow);
end;
end;
end;
ChangeHoverSelection(GetIndex(Row, Column));
Message.Result := 1;
end;
VK_ESCAPE:
begin
EndSelection(True);
Message.Result := 1;
end;
VK_RETURN,
VK_SPACE:
begin
// this case can only occur if there was no click on the window
// hence the hover index is the new color
FSelectedIndex := FHoverIndex;
EndSelection(False);
Message.Result := 1;
end;
end;
end;
//------------------------------------------------------------------------------
procedure TColorPopup.DrawSeparator(Left, Top, Right: Integer);
var R: TRect;
begin
R := Rect(Left, Top, Right, Top);
DrawEdge(FCanvas.Handle, R, EDGE_ETCHED, BF_TOP);
end;
//------------------------------------------------------------------------------
procedure TColorPopup.DrawCell(Index: Integer);
var R, MarkRect: TRect;
CellColor: TColor;
begin
// for the custom text area
if (Length(FCustomText) > 0) and (Index = CustomCell) then
begin
// the extent of the actual text button
R := FCustomTextRect;
// fill background
FCanvas.Brush.Color := clBtnFace;
FCanvas.FillRect(R);
with FCustomTextRect do DrawSeparator(Left, Top - 2 * FMargin, Right);
InflateRect(R, -1, 0);
// fill background
if (FSelectedIndex = Index) and (FHoverIndex <> Index) then FCanvas.Brush.Color := clBtnHighlight
else FCanvas.Brush.Color := clBtnFace;
FCanvas.FillRect(R);
// draw button
if (FSelectedIndex = Index) or
((FHoverIndex = Index) and (csLButtonDown in ControlState)) then DrawEdge(FCanvas.Handle, R, BDR_SUNKENOUTER, BF_RECT)
else
if FHoverIndex = Index then DrawEdge(FCanvas.Handle, R, BDR_RAISEDINNER, BF_RECT);
// draw custom text
DrawText(FCanvas.Handle, PChar(FCustomText), Length(FCustomText), R, DT_CENTER or DT_VCENTER or DT_SINGLELINE);
// draw preview color rectangle
if FCustomIndex = 0 then
begin
FCanvas.Brush.Color := clBtnShadow;
FCanvas.FrameRect(FCustomColorRect);
end
else
begin
FCanvas.Pen.Color := clGray;
if FCustomIndex > 0 then FCanvas.Brush.Color := FColorCombs[FCustomIndex - 1].Color
else FCanvas.Brush.Color := FBWCombs[- (FCustomIndex + 1)].Color;
with FCustomColorRect do
FCanvas.Rectangle(Left, Top, Right, Bottom);
end;
end
else
// for the default text area
if (Length(FDefaultText) > 0) and (Index = DefaultCell) then
begin
R := FDefaultTextRect;
// Fill background
FCanvas.Brush.Color := clBtnFace;
FCanvas.FillRect(R);
InflateRect(R, -1, -1);
// fill background
if (FSelectedIndex = Index) and (FHoverIndex <> Index) then FCanvas.Brush.Color := clBtnHighlight
else FCanvas.Brush.Color := clBtnFace;
FCanvas.FillRect(R);
// draw button
if (FSelectedIndex = Index) or
((FHoverIndex = Index) and (csLButtonDown in ControlState)) then DrawEdge(FCanvas.Handle, R, BDR_SUNKENOUTER, BF_RECT)
else
if FHoverIndex = Index then DrawEdge(FCanvas.Handle, R, BDR_RAISEDINNER, BF_RECT);
// draw small rectangle
with MarkRect do
begin
MarkRect := R;
InflateRect(MarkRect, -FMargin - 1, -FMargin - 1);
FCanvas.Brush.Color := clBtnShadow;
FCanvas.FrameRect(MarkRect);
end;
// draw default text
SetBkMode(FCanvas.Handle, TRANSPARENT);
DrawText(FCanvas.Handle, PChar(FDefaultText), Length(FDefaultText), R, DT_CENTER or DT_VCENTER or DT_SINGLELINE);
end
else
begin
if GetCellRect(Index, R) then
begin
if Index < DefaultColorCount then CellColor := TColor(DefaultColors[Index].Color)
else CellColor := TColor(SysColors[Index - DefaultColorCount].Color);
FCanvas.Pen.Color := clGray;
// fill background
if (FSelectedIndex = Index) and (FHoverIndex <> Index) then FCanvas.Brush.Color := clBtnHighlight
else FCanvas.Brush.Color := clBtnFace;
FCanvas.FillRect(R);
// draw button
if (FSelectedIndex = Index) or
((FHoverIndex = Index) and (csLButtonDown in ControlState)) then DrawEdge(FCanvas.Handle, R, BDR_SUNKENOUTER, BF_RECT)
else
if FHoverIndex = Index then DrawEdge(FCanvas.Handle, R, BDR_RAISEDINNER, BF_RECT);
FCanvas.Brush.Color := CellColor;
// draw the cell colour
InflateRect(R, -(FMargin + 1), -(FMargin + 1));
FCanvas.Rectangle(R.Left, R.Top, R.Right, R.Bottom);
end;
end;
end;
//------------------------------------------------------------------------------
procedure TColorPopup.DrawComb(Canvas: TCanvas; X, Y: Integer; Size: Integer);
// draws one single comb at position X, Y and with size Size
// fill color must already be set on call
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;
Canvas.Polygon(P);
end;
//------------------------------------------------------------------------------
procedure TColorPopup.DrawCombControls;
var I, Index: Integer;
XOffs, YOffs,
Count: Integer;
dColor: Single;
OffScreen: TBitmap;
{$ifdef DEBUG}
R: TRect;
{$endif}
begin
// to make the painting (and selecting) flicker free we use an offscreen
// bitmap here
OffScreen := TBitmap.Create;
try
OffScreen.Width := Width;
OffScreen.Height := FColorCombRect.Bottom - FColorCombRect.Top +
FBWCombRect.Bottom - FBWCombRect.Top + 2 * FMargin;
with OffScreen.Canvas do
begin
Brush.Color := clBtnFace;
FillRect(ClipRect);
Pen.Style := psClear;
// draw color comb from FColorCombs array
XOffs := FRadius + FColorCombRect.Left;
YOffs := FRadius;
// draw the combs
for I := 0 to High(FColorCombs) do
begin
Brush.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;
Pen.Style := psSolid;
Pen.Mode := pmXOR;
Pen.Color := clWhite;
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 - FColorCombRect.Top - 4;
// brush is automatically reset to bsSolid
for I := 0 to High(FBWCombs) do
begin
Brush.Color := FBWCombs[I].Color;
if I in [0, High(FBWCombs)]
then DrawComb(OffScreen.Canvas, FBWCombs[I].Position.X + XOffs, FBWCombs[I].Position.Y + YOffs, 2 * FCombSize)
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);
Pen.Style := psSolid;
Pen.Mode := pmXOR;
Pen.Color := clWhite;
Pen.Width := 2;
Brush.Style := bsClear;
if Index in [0, High(FBWCombs)]
then DrawComb(OffScreen.Canvas, FBWCombs[Index].Position.X + XOffs, FBWCombs[Index].Position.Y + YOffs, 2 * FCombSize)
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;
// center-color trackbar
XOffs := FSliderRect.Left;
YOffs := FSliderRect.Top - FColorCombRect.Top;
Count := FSliderRect.Bottom - FSliderRect.Top - 1;
dColor := 255 / Count;
Pen.Style := psSolid;
// b&w ramp
for I := 0 to Count do
begin
Pen.Color := RGB(Round((Count - I) * dColor),
Round((Count - I) * dColor),
Round((Count - I) * dColor));
MoveTo(XOffs, YOffs + I);
LineTo(XOffs + 10, YOffs + I);
end;
// marker
Inc(XOffs, 11);
Inc(YOffs, Round(Count * (1 - FCenterIntensity)));
Brush.Color := clBlack;
Polygon([Point(XOffs, YOffs), Point(XOffs + 5, YOffs - 3), Point(XOffs + 5, YOffs + 3)]);
{$ifdef DEBUG}
Brush.Color := clRed;
R := FColorCombRect;
OffsetRect(R, 0, - FColorCombRect.Top);
FrameRect(R);
R := FBWCombRect;
OffsetRect(R, 0, - FColorCombRect.Top);
FrameRect(R);
R := FSliderRect;
OffsetRect(R, 0, - FColorCombRect.Top);
FrameRect(R);
{$endif}
Pen.Style := psClear;
end;
// finally put the drawing on the screen
FCanvas.Draw(0, FColorCombRect.Top, OffScreen);
finally
Offscreen.Free;
end;
end;
//------------------------------------------------------------------------------
procedure TColorPopup.WMPaint(var Message: TWMPaint);
var PS: TPaintStruct;
I: Cardinal;
R: TRect;
SeparatorTop: Integer;
begin
if Message.DC = 0 then FCanvas.Handle := BeginPaint(Handle, PS)
else FCanvas.Handle := Message.DC;
try
// use system default font for popup text
FCanvas.Font.Handle := GetStockObject(DEFAULT_GUI_FONT);
if FColorCombs = nil then CalculateCombLayout;
// default area text
if Length(FDefaultText) > 0 then DrawCell(DefaultCell);
// Draw colour cells
for I := 0 to DefaultColorCount - 1 do DrawCell(I);
if FShowSysColors then
begin
SeparatorTop := FRowCount * FBoxSize + FMargin;
if Length(FDefaultText) > 0 then Inc(SeparatorTop, FDefaultTextRect.Bottom);
with FCustomTextRect do DrawSeparator(FMargin + FSpacing, SeparatorTop, Width - FMargin - FSpacing);
for I := 0 to SysColorCount - 1 do DrawCell(I + DefaultColorCount);
end;
// Draw custom text
if Length(FCustomText) > 0 then DrawCell(CustomCell);
if FSelectedIndex = CustomCell then DrawCombControls;
// draw raised window edge (ex-window style WS_EX_WINDOWEDGE is supposed to do this,
// but for some reason doesn''t paint it)
R := ClientRect;
DrawEdge(FCanvas.Handle, R, EDGE_RAISED, BF_RECT);
finally
FCanvas.Font.Handle := 0; // a stock object never needs to be freed
FCanvas.Handle := 0;
if Message.DC = 0 then EndPaint(Handle, PS);
end;
end;
//------------------------------------------------------------------------------
function TColorPopup.SelectionFromPoint(P: TPoint): Integer;
// determines the button at the given position
begin
Result := NoCell;
// first check we aren''t in text box
if (Length(FCustomText) > 0) and PtInRect(FCustomTextRect, P) then Result := CustomCell
else
if (Length(FDefaultText) > 0) and PtInRect(FDefaultTextRect, P) then Result := DefaultCell
else
begin
// take into account text box
if Length(FDefaultText) > 0 then Dec(P.Y, FDefaultTextRect.Bottom - FDefaultTextRect.Top);
// Get the row and column
if P.X > FSpacing then
begin
Dec(P.X, FSpacing);
// take the margin into account, 2 * FMargin is too small while 3 * FMargin
// is correct, but looks a bit strange (the arrow corner is so small, it isn''t
// really recognized by the eye) hence I took 2.5 * FMargin
Dec(P.Y, 5 * FMargin div 2);
if (P.X >= 0) and (P.Y >= 0) then
begin
// consider system colors
if FShowSysColors and ((P.Y div FBoxSize) >= FRowCount) then
begin
// here we know the point is out of the default color area, so
// take the separator line between default and system colors into account
Dec(P.Y, 3 * FMargin);
// if we now are back in the default area then the point was originally
// between both areas and we have therefore to reject a hit
if (P.Y div FBoxSize) < FRowCount then Exit;
end;
Result := GetIndex(P.Y div FBoxSize, P.X div FBoxSize);
end;
end;
end;
end;
//------------------------------------------------------------------------------
function TColorPopup.HandleSlider(const Message: TWMMouse): Boolean;
// determines whether the mouse position is within the slider area (result is then True
// else False) and acts accordingly
var Shift: TShiftState;
dY: Integer;
R: TRect;
begin
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;
// left mouse button pressed -> change the intensity of the center color comb
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);
UpdateWindow(Handle);
end;
end;
end;
//------------------------------------------------------------------------------
function TColorPopup.PtInComb(Comb: TCombEntry; P: TPoint; Scale: Integer): Boolean;
// simplyfied "PointInPolygon" test, we know a comb is "nearly" a circle...
begin
Result := (Sqr(Comb.Position.X - P.X) + Sqr(Comb.Position.Y - P.Y)) <= (Scale * Scale);
end;
//------------------------------------------------------------------------------
function TColorPopup.FindBWArea(X, Y: Integer): Integer;
// 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.
var I: Integer;
Pt: TPoint;
Scale: Integer;
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;
//------------------------------------------------------------------------------
function TColorPopup.HandleBWArea(const Message: TWMMouse): Boolean;
// determines whether the mouse position is within the B&W comb area (result is then True
// else False) and acts accordingly
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);
UpdateWindow(Handle);
end;
end
else Result := False;
end;
end;
end;
//------------------------------------------------------------------------------
function TColorPopup.FindColorArea(X, Y: Integer): Integer;
// 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.
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 TColorPopup.HandleColorComb(const Message: TWMMouse): Boolean;
// determines whether the mouse position is within the color comb area (result is then True
// else False) and acts accordingly
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);
UpdateWindow(Handle);
end;
end
else Result := False;
end;
end;
end;
//------------------------------------------------------------------------------
procedure TColorPopup.HandleCustomColors(var Message: TWMMouse);
begin
if not HandleSlider(Message) then
if not HandleBWArea(Message) then
if not HandleColorComb(Message) then
begin
// user has clicked somewhere else, so remove last custom selection
if FCustomIndex > 0 then InvalidateRect(Handle, @FColorCombRect, False)
else
if FCustomIndex < 0 then InvalidateRect(Handle, @FBWCombRect, False);
InvalidateRect(Handle, @FCustomColorRect, False);
FCustomIndex := 0;
UpdateWindow(Handle);
end;
end;
//------------------------------------------------------------------------------
procedure TColorPopup.WMMouseMove(var Message: TWMMouseMove);
var NewSelection: Integer;
begin
inherited;
// determine new hover index
NewSelection := SelectionFromPoint(Point(Message.XPos, Message.YPos));
if NewSelection <> FHoverIndex then ChangeHoverSelection(NewSelection);
if (NewSelection = -1) and
PtInRect(ClientRect, Point(Message.XPos, Message.YPos)) and
(csLButtonDown in ControlState) then HandleCustomColors(Message);
end;
//------------------------------------------------------------------------------
procedure TColorPopup.WMLButtonDown(var Message: TWMLButtonDown);
begin
inherited;
if PtInRect(ClientRect, Point(Message.XPos, Message.YPos)) then
begin
if FHoverIndex <> NoCell then
begin
InvalidateCell(FHoverIndex);
UpdateWindow(Handle);
end;
if FHoverIndex = -1 then HandleCustomColors(Message);
end
else EndSelection(True); // hide popup window if the user has clicked elsewhere
end;
//------------------------------------------------------------------------------
procedure TColorPopup.ShowPopupAligned;
var Pt: TPoint;
Parent: TColorPickerButton;
ParentTop: Integer;
R: TRect;
H: Integer;
begin
HandleNeeded;
if FSelectedIndex = CustomCell then
begin
// make room for the custem color picking area
R := Rect(FWindowRect.Left, FWindowRect.Bottom - 3, FWindowRect.Right, FWindowRect.Bottom);
H := FBWCombRect.Bottom + 2 * FMargin;
end
else
begin
// hide the custem color picking area
R := Rect(FWindowRect.Left, FWindowRect.Bottom - 3, FWindowRect.Right, FWindowRect.Bottom);
H := FWindowRect.Bottom;
end;
// to ensure the window frame is drawn correctly we invalidate the lower bound explicitely
InvalidateRect(Handle, @R, True);
// Make sure the window is still entirely visible and aligned.
// There''s no VCL parent window as this popup is a child of the desktop,
// but we have the owner and get the parent from this.
Parent := TColorPickerButton(Owner);
Pt := Parent.Parent.ClientToScreen(Point(Parent.Left - 1, Parent.Top + Parent.Height));
if (Pt.y + H) > Screen.Height then Pt.y := Screen.Height - H;
ParentTop := Parent.Parent.ClientToScreen(Point(Parent.Left, Parent.Top)).y;
if Pt.y < ParentTop then Pt.y := ParentTop - H;
if (Pt.x + Width) > Screen.Width then Pt.x := Screen.Width - Width;
if Pt.x < 0 then Pt.x := 0;
SetWindowPos(Handle, HWND_TOPMOST, Pt.X, Pt.Y, FWindowRect.Right, H, SWP_SHOWWINDOW);
end;
//------------------------------------------------------------------------------
procedure TColorPopup.ChangeSelection(NewSelection: Integer);
begin
if NewSelection <> NoCell then
begin
if FSelectedIndex <> NoCell then InvalidateCell(FSelectedIndex);
FSelectedIndex := NewSelection;
if FSelectedIndex <> NoCell then InvalidateCell(FSelectedIndex);
if FSelectedIndex = CustomCell then ShowPopupAligned;
end;
end;
//------------------------------------------------------------------------------
procedure TColorPopup.WMLButtonUp(var Message: TWMLButtonUp);
var NewSelection: Integer;
LastMode: TSelectionMode;
begin
inherited;
// determine new selection index
NewSelection := SelectionFromPoint(Point(Message.XPos, Message.YPos));
LastMode := FSelectionMode;
FSelectionMode := smNone;
if (NewSelection <> NoCell) or
((FSelectedIndex = CustomCell) and (FCustomIndex <> 0)) then
begin
ChangeSelection(NewSelection);
if ((FSelectedIndex = CustomCell) and (LastMode in [smColor, smBW])) or
(FSelectedIndex <> NoCell) and
(FSelectedIndex <> CustomCell) then EndSelection(False)
else SetCapture(TColorPickerButton(Owner).FPopupWnd);
end
else
// we need to restore the mouse capturing, else the utility window will loose it
// (safety feature of Windows?)
SetCapture(TColorPickerButton(Owner).FPopupWnd);
end;
//------------------------------------------------------------------------------
function TColorPopup.GetIndex(Row, Col: Integer): Integer;
begin
Result := NoCell;
if ((Row = CustomCell) or (Col = CustomCell)) and
(Length(FCustomText) > 0) then Result := CustomCell
else
if ((Row = DefaultCell) or (Col = DefaultCell)) and
(Length(FDefaultText) > 0) then Result := DefaultCell
else
if (Col in [0..FColumnCount - 1]) and (Row >= 0) then
begin
if Row < FRowCount then
begin
Result := Row * FColumnCount + Col;
// consider not fully filled last row
if Result >= DefaultColorCount then Result := NoCell;
end
else
if FShowSysColors then
begin
Dec(Row, FRowCount);
if Row < FSysRowCount then
begin
Result := Row * FColumnCount + Col;
// consider not fully filled last row
if Result >= SysColorCount then Result := NoCell
else Inc(Result, DefaultColorCount);
end;
end;
end;
end;
//------------------------------------------------------------------------------
function TColorPopup.GetRow(Index: Integer): Integer;
begin
if (Index = CustomCell) and (Length(FCustomText) > 0) then Result := CustomCell
else
if (Index = DefaultCell) and (Length(FDefaultText) > 0 ) then Result := DefaultCell
else Result := Index div FColumnCount;
end;
//------------------------------------------------------------------------------
function TColorPopup.GetColumn(Index: Integer): Integer;
begin
if (Index = CustomCell) and (Length(FCustomText) > 0) then Result := CustomCell
else
if (Index = DefaultCell) and (Length(FDefaultText) > 0 ) then Result := DefaultCell
else Result := Index mod FColumnCount;
end;
//------------------------------------------------------------------------------
procedure TColorPopup.SelectColor(Color: TColor);
// looks up the given color in our lists and sets the proper indices
var I: Integer;
C: COLORREF;
found: Boolean;
begin
found := False;
// handle special colors first
if Color = clNone then FSelectedIndex := NoCell
else
if Color = clDefault then FSelectedIndex := DefaultCell
else
begin
// if the incoming color is one of the predefined colors (clBtnFace etc.) and
// system colors are active then start looking in the system color list
if FShowSysColors and (Color < 0) then
begin
for I := 0 to SysColorCount - 1 do
if TColor(SysColors[I].Color) = Color then
begin
FSelectedIndex := I + DefaultColorCount;
found := True;
Break;
end;
end;
if not found then
begin
C := ColorToRGB(Color);
for I := 0 to DefaultColorCount - 1 do
// only Borland knows why the result of ColorToRGB is Longint not COLORREF,
// in order to make the compiler quiet I need a Longint cast here
if ColorToRGB(DefaultColors[I].Color) = Longint(C) then
begin
FSelectedIndex := I;
found := True;
Break;
end;
// look in the system colors if not already done yet
if not found and FShowSysColors and (Color >= 0) then
begin
for I := 0 to SysColorCount - 1 do
begin
if ColorToRGB(TColor(SysColors[I].Color)) = Longint(C) then
begin
FSelectedIndex := I + DefaultColorCount;
found := True;
Break;
end;
end;
end;
if not found then
begin
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;
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;
Break;
end;
end;
end;
end;
end;
//------------------------------------------------------------------------------
function TColorPopup.GetCellRect(Index: Integer; var Rect: TRect): Boolean;
// gets the dimensions of the colour cell given by Index
begin
Result := False;
if Index = CustomCell then
begin
Rect := FCustomTextRect;
Result := True;
end
else
if Index = DefaultCell then
begin
Rect := FDefaultTextRect;
Result := True;
end
else
if Index >= 0 then
begin
Rect.Left := GetColumn(Index) * FBoxSize + FMargin + FSpacing;
Rect.Top := GetRow(Index) * FBoxSize + 2 * FMargin;
// move everything down if we are displaying a default text area
if Length(FDefaultText) > 0 then Inc(Rect.Top, FDefaultTextRect.Bottom - 2 * FMargin);
// move everything further down if we consider syscolors
if Index >= DefaultColorCount then Inc(Rect.Top, 3 * FMargin);
Rect.Right := Rect.Left + FBoxSize;
Rect.Bottom := Rect.Top + FBoxSize;
Result := True;
end;
end;
//------------------------------------------------------------------------------
procedure TColorPopup.AdjustWindow;
// works out an appropriate size and position of this window
var TextSize,
DefaultSize: TSize;
DC: HDC;
WHeight: Integer;
begin
// If we are showing a custom or default text area, get the font and text size.
if (Length(FCustomText) > 0) or (Length(FDefaultText) > 0) then
begin
DC := GetDC(Handle);
FCanvas.Handle := DC;
FCanvas.Font.Handle := GetStockObject(DEFAULT_GUI_FONT);
try
// Get the size of the custom text (if there IS custom text)
TextSize.cx := 0;
TextSize.cy := 0;
if Length(FCustomText) > 0 then TextSize := FCanvas.TextExtent(FCustomText);
// Get the size of the default text (if there IS default text)
if Length(FDefaultText) > 0 then
begin
DefaultSize := FCanvas.TextExtent(FDefaultText);
if DefaultSize.cx > TextSize.cx then TextSize.cx := DefaultSize.cx;
if DefaultSize.cy > TextSize.cy then TextSize.cy := DefaultSize.cy;
end;
Inc(TextSize.cx, 2 * FMargin);
Inc(TextSize.cy, 4 * FMargin + 2);
finally
FCanvas.Font.Handle := 0;
FCanvas.Handle := 0;
ReleaseDC(Handle, DC);
end;
end;
// Get the number of columns and rows
FColumnCount := 8;
FRowCount := DefaultColorCount div FColumnCount;
if (DefaultColorCount mod FColumnCount) <> 0 then Inc(FRowCount);
FWindowRect := Rect(0, 0,
FColumnCount * FBoxSize + 2 * FMargin + 2 * FSpacing,
FRowCount * FBoxSize + 4 * FMargin);
FRadius := Trunc(7 * (FColumnCount * FBoxSize) / 16);
FCombSize := Round(0.5 + FRadius / (FLevels - 1));
// if default text, then expand window if necessary, and set text width as
// window width
if Length(FDefaultText) > 0 then
begin
if TextSize.cx > (FWindowRect.Right - FWindowRect.Left) then FWindowRect.Right := FWindowRect.Left + TextSize.cx;
TextSize.cx := FWindowRect.Right - FWindowRect.Left - 2 * FMargin;
// work out the text area
FDefaultTextRect := Rect(FMargin + FSpacing, 2 * FMargin, FMargin -FSpacing + TextSize.cx, 2 * FMargin + TextSize.cy);
Inc(FWindowRect.Bottom, FDefaultTextRect.Bottom - FDefaultTextRect.Top + 2 * FMargin);
end;
if FShowSysColors then
begin
FSysRowCount := SysColorCount div FColumnCount;
if (SysColorCount mod FColumnCount) <> 0 then Inc(FSysRowCount);
Inc(FWindowRect.Bottom, FSysRowCount * FBoxSize + 2 * FMargin);
end;
// if custom text, then expand window if necessary, and set text width as
// window width
if Length(FCustomText) > 0 then
begin
if TextSize.cx > (FWindowRect.Right - FWindowRect.Left) then FWindowRect.Right := FWindowRect.Left + TextSize.cx;
TextSize.cx := FWindowRect.Right - FWindowRect.Left - 2 * FMargin;
// work out the text area
WHeight := FWindowRect.Bottom - FWindowRect.Top;
FCustomTextRect := Rect(FMargin + FSpacing,
WHeight,
FMargin - FSpacing + TextSize.cx,
WHeight + TextSize.cy);
// precalculate also the small preview box for custom color selection for fast updates
FCustomColorRect := Rect(0, 0, FBoxSize, FBoxSize);
InflateRect(FCustomColorRect, -(FMargin + 1), -(FMargin + 1));
OffsetRect(FCustomColorRect,
FCustomTextRect.Right - FBoxSize - FMargin,
FCustomTextRect.Top + (FCustomTextRect.Bottom - FCustomTextRect.Top - FCustomColorRect.Bottom - FMargin - 1) div 2);
Inc(FWindowRect.Bottom, FCustomTextRect.Bottom - FCustomTextRect.Top + 2 * FMargin);
end;
// work out custom color choice area (color combs) (FWindowRect covers only the always visible part)
FColorCombRect := Rect(FMargin + FSpacing,
FWindowRect.Bottom,
FMargin + FSpacing + 2 * FRadius,
FWindowRect.Bottom + 2 * FRadius);
// work out custom color choice area (b&w combs)
FBWCombRect := Rect(FColorCombRect.Left,
FColorCombRect.Bottom - 4,
Round(17 * FCombSize * cos(Pi / 6) / 2) + 6 * FCombSize,
FColorCombRect.Bottom + 2 * FCombSize);
// work out slider area
FSliderRect := Rect(FColorCombRect.Right,
FColorCombRect.Top + FCombSize,
FColorCombRect.Right + 20,
FColorCombRect.Bottom - FCombSize);
// set the window size
with FWindowRect do SetBounds(Left, Top, Right - Left, Bottom - Top);
end;
//------------------------------------------------------------------------------
procedure TColorPopup.ChangeHoverSelection(Index: Integer);
begin
if not FShowSysColors and (Index >= DefaultColorCount) or
(Index >= (DefaultColorCount + SysColorCount)) then Index := NoCell;
// remove old hover selection
InvalidateCell(FHoverIndex);
FHoverIndex := Index;
InvalidateCell(FHoverIndex);
UpdateWindow(Handle);
end;
//------------------------------------------------------------------------------
procedure TColorPopup.EndSelection(Cancel: Boolean);
begin
with Owner as TColorPickerButton do
begin
if not Cancel then
begin
if FSelectedIndex > -1 then
if FSelectedIndex < DefaultColorCount then SelectionColor := TColor(DefaultColors[FSelectedIndex].Color)
else SelectionColor := TColor(SysColors[FSelectedIndex - DefaultColorCount].Color)
else
if FSelectedIndex = CustomCell then
begin
if FCustomIndex < 0 then SelectionColor := FBWCombs[-(FCustomIndex + 1)].Color
else
if FCustomIndex > 0 then SelectionColor := FColorCombs[FCustomIndex - 1].Color;
end
else DoDefaultEvent;
end;
DroppedDown := False;
end;
end;
//------------------------------------------------------------------------------
procedure TColorPopup.WMKillFocus(var Message: TWMKillFocus);
begin
inherited;
(Owner as TColorPickerButton).DroppedDown := False;
end;
//------------------------------------------------------------------------------
procedure TColorPopup.CalculateCombLayout;
// 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
//--------------- local functions -----------------------
function RGBFromFloat(Color: TRGB): COLORREF;
begin
Result := RGB(Round(255 * Color.Red), Round(255 * Color.Green), Round(255 * Color.Blue));
end;
//-------------------------------------------------------
function GrayFromIntensity(Intensity: Byte): COLORREF;
begin
Result := RGB(Intensity, Intensity, Intensity);
end;
//--------------- end local functions -------------------
var CurrentIndex: Cardinal;
CurrentColor: TRGB;
CurrentPos: TFloatPoint;
CombCount: Cardinal;
I, J,
Level: Cardinal;
Scale: Extended;
// triangle vars
Pos1, Pos2: TFloatPoint;
dPos1, dPos2: TFloatPoint;
Color1, Color2: TRGB;
dColor1, dColor2: TRGB;
dPos: TFloatPoint;
dColor: TRGB;
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);
end;
//-----------------------------------------------------------------------------
procedure TColorPopup.CreateParams(var Params: TCreateParams);
begin
inherited CreateParams(Params);
with Params do
begin
WndParent := GetDesktopWindow;
Style := WS_CLIPSIBLINGS or WS_CHILD;
ExStyle := WS_EX_TOPMOST or WS_EX_TOOLWINDOW;
WindowClass.Style := CS_DBLCLKS or CS_SAVEBITS;
end;
end;
//------------------------------------------------------------------------------
procedure TColorPopup.CreateWnd;
begin
inherited;
AdjustWindow;
end;
//------------------------------------------------------------------------------
procedure TColorPopup.SetSpacing(Value: Integer);
begin
if Value < 0 then Value := 0;
if FSpacing <> Value then
begin
FSpacing := Value;
Invalidate;
end;
end;
//------------------------------------------------------------------------------
procedure TColorPopup.InvalidateCell(Index: Integer);
var R: TRect;
begin
if GetCellRect(Index, R) then InvalidateRect(Handle, @R, False);
end;
//------------------------------------------------------------------------------
function TColorPopup.GetHint(Cell: Integer): String;
begin
Result := '''';
if Assigned(TColorPickerButton(Owner).FOnHint) then TColorPickerButton(Owner).FOnHint(Owner, Cell, Result);
end;
//------------------------------------------------------------------------------
procedure TColorPopup.CMHintShow(var Message: TMessage);
// determine hint message (tooltip) and out-of-hint rect
var Index: Integer;
r, g, b: Byte;
Colors: TCombArray;
begin
Colors := nil;
with TCMHintShow(Message) do
begin
if not TColorPickerButton(Owner).ShowHint then Message.Result := 1
else
begin
with HintInfo^ do
begin
// show that we want a hint
Result := 0;
// predefined colors always get their names as tooltip
if FHoverIndex >= 0 then
begin
GetCellRect(FHoverIndex, CursorRect);
if FHoverIndex < DefaultColorCount then HintStr := DefaultColors[FHoverIndex].Name
else HintStr := SysColors[FHoverIndex - DefaultColorCount].Name;
end
else
// both special cells get their hint either from the application by
// means of the OnHint event or the hint string of the owner control
if (FHoverIndex = DefaultCell) or
(FHoverIndex = CustomCell) then
begin
HintStr := GetHint(FHoverIndex);
if HintStr = '''' then HintStr := TColorPickerButton(Owner).Hint
else
begin
// if the application supplied a hint by event then deflate the cursor rect
// to the belonging button
if FHoverIndex = DefaultCell then CursorRect := FDefaultTextRect
else CursorRect := FCustomTextRect;
end;
end
else
begin
// well, mouse is not hovering over one of the buttons, now check for
// the ramp and the custom color areas
if PtInRect(FSliderRect, Point(CursorPos.X, CursorPos.Y)) then
begin
// in case of the intensity slider we show the current intensity
HintStr := Format(''Intensity: %d%%'', [Round(100 * FCenterIntensity)]);
CursorRect := Rect(FSliderRect.Left, CursorPos.Y - 2,
FSliderRect.Right, CursorPos.Y + 2);
HintPos := ClientToScreen(Point(FSliderRect.Right, CursorPos.Y - 8));
HideTimeout := 5000;
CursorRect := Rect(FSliderRect.Left, CursorPos.Y,
FSliderRect.Right, CursorPos.Y);
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
begin
with Colors[Index] do
begin
r := GetRValue(Color);
g := GetGValue(Color);
b := GetBValue(Color);
end;
HintStr := Format(''red: %d, green: %d, blue: %d'', [r, g, b]);
HideTimeout := 5000;
end
else HintStr := GetHint(NoCell);
// make the hint follow the mouse
CursorRect := Rect(CursorPos.X, CursorPos.Y,
CursorPos.X, CursorPos.Y);
end;
end;
end;
end;
end;
end;
//------------------------------------------------------------------------------
procedure TColorPopup.SetSelectedColor(const Value: TColor);
begin
FCurrentColor := Value;
SelectColor(Value);
end;
//----------------- TColorPickerButton ------------------------------------------
constructor TColorPickerButton.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FSelectionColor := clBlack;
FColorPopup := TColorPopup.Create(Self);
// park the window somewhere it can''t be seen
FColorPopup.Left := -1000;
FPopupWnd := AllocateHWnd(PopupWndProc);
FGlyph := TButtonGlyph.Create;
TButtonGlyph(FGlyph).OnChange := GlyphChanged;
SetBounds(0, 0, 45, 22);
FDropDownWidth := 15;
ControlStyle := [csCaptureMouse, csDoubleClicks];
ParentFont := True;
Color := clBtnFace;
FSpacing := 4;
FMargin := -1;
FLayout := blGlyphLeft;
FTransparent := True;
FIndicatorBorder := ibFlat;
Inc(ButtonCount);
end;
//-----------------------------------------------------------------------------
destructor TColorPickerButton.Destroy;
begin
DeallocateHWnd(FPopupWnd);
Dec(ButtonCount);
// the color popup window will automatically be freed since the button is the owner
// of the popup
TButtonGlyph(FGlyph).Free;
inherited Destroy;
end;
//-----------------------------------------------------------------------------
procedure TColorPickerButton.PopupWndProc(var Msg: TMessage);
var P: TPoint;
begin
case Msg.Msg of
WM_MOUSEFIRST..WM_MOUSELAST:
begin
with TWMMouse(Msg) do
begin
P := SmallPointToPoint(Pos);
MapWindowPoints(FPopupWnd, FColorPopup.Handle, P, 1);
Pos := PointToSmallPoint(P);
end;
FColorPopup.WindowProc(Msg);
end;
CN_KEYDOWN,
CN_SYSKEYDOWN:
FColorPopup.WindowProc(Msg);
else
with Msg do
Result := DefWindowProc(FPopupWnd, Msg, wParam, lParam);
end;
end;
//-----------------------------------------------------------------------------
procedure TColorPickerButton.SetDropDownArrowColor(Value: TColor);
begin
if not (FDropDownArrowColor = Value) then;
begin
FDropDownArrowColor := Value;
Invalidate;
end;
end;
//-----------------------------------------------------------------------------
procedure TColorPickerButton.SetDropDownWidth(Value: integer);
begin
if not (FDropDownWidth = Value) then;
begin
FDropDownWidth := Value;
Invalidate;
end;
end;
//-----------------------------------------------------------------------------
procedure TColorPickerButton.Paint;
const MAX_WIDTH = 5;
DownStyles: array[Boolean] of Integer = (BDR_RAISEDINNER, BDR_SUNKENOUTER);
FillStyles: array[Boolean] of Integer = (BF_MIDDLE, 0);
var PaintRect: TRect;
ExtraRect: TRect;
DrawFlags: Integer;
Offset: TPoint;
LeftPos: Integer;
begin
if not Enabled then
begin
FState := bsDisabled;
FDragging := False;
end
else
if (FState = bsDisabled) then
begin
if FDown and (GroupIndex <> 0) then FState := bsExclusive
else FState := bsUp;
end;
Canvas.Font := Self.Font;
// Creates a rectangle that represent the button and the drop down area,
// determines also the position to draw the arrow...
PaintRect := Rect(0, 0, Width, Height);
ExtraRect := Rect(Width - FDropDownWidth, 0, Width, Height);
LeftPos := (Width - FDropDownWidth) + ((FDropDownWidth + MAX_WIDTH) div 2) - MAX_WIDTH - 1;
// Determines if the button is a flat or normal button... each uses
// different painting methods
if not FFlat then
begin
DrawFlags := DFCS_BUTTONPUSH or DFCS_ADJUSTRECT;
if FState in [bsDown, bsExclusive] then DrawFlags := DrawFlags or DFCS_PUSHED;
// Check if the mouse is in the drop down zone. If it is we then check
// the state of the button to determine the drawing sequence
if FDropDownZone then
begin
if FDroppedDown then
begin
// paint pressed Drop Down Button
DrawFrameControl(Canvas.Handle, PaintRect, DFC_BUTTON, DRAW_BUTTON_UP);
DrawFrameControl(Canvas.Handle, ExtraRect, DFC_BUTTON, DRAW_BUTTON_DOWN);
end
else
begin
// paint depressed Drop Down Button
DrawFrameControl(Canvas.Handle, PaintRect, DFC_BUTTON, DRAW_BUTTON_UP);
DrawFrameControl(Canvas.Handle, ExtraRect, DFC_BUTTON, DRAW_BUTTON_UP);
DrawButtonSeperatorUp(Canvas);
end;
end
else
begin
DrawFrameControl(Canvas.Handle, PaintRect, DFC_BUTTON, DrawFlags);
// Determine the type of drop down seperator...
if (FState in [bsDown, bsExclusive]) then DrawButtonSeperatorDown(Canvas)
else DrawButtonSeperatorUp(Canvas);
end;
end
else
begin
if (FState in [bsDown, bsExclusive]) or
(FMouseInControl and (FState <> bsDisabled)) or
(csDesigning in ComponentState) then
begin
// Check if the mouse is in the drop down zone. If it is we then check
// the state of the button to determine the drawing sequence
if FDropDownZone then
begin
if FDroppedDown then
begin
// Paint pressed Drop Down Button
DrawEdge(Canvas.Handle, PaintRect, DownStyles[False], FillStyles[FTransparent] or BF_RECT);
DrawEdge(Canvas.Handle, ExtraRect, DownStyles[True], FillStyles[FTransparent] or BF_RECT);
end
else
begin
// Paint depressed Drop Down Button
DrawEdge(Canvas.Handle, PaintRect, DownStyles[False], FillStyles[FTransparent] or BF_RECT);
DrawEdge(Canvas.Handle, ExtraRect, DownStyles[False], FillStyles[FTransparent] or BF_RECT);
DrawButtonSeperatorUp(Canvas);
end;
end
else
begin
DrawEdge(Canvas.Handle, PaintRect, DownStyles[FState in [bsDown, bsExclusive]], FillStyles[FTransparent] or BF_RECT);
if (FState in [bsDown, bsExclusive]) then DrawButtonSeperatorDown(Canvas)
else DrawButtonSeperatorUp(Canvas);
end;
end
else
if not FTransparent then
begin
Canvas.Brush.Style := bsSolid;
Canvas.Brush.Color := Color;
Canvas.FillRect(PaintRect);
end;
InflateRect(PaintRect, -1, -1);
end;
if (FState in [bsDown, bsExclusive]) and not (FDropDownZone) then
begin
if (FState = bsExclusive) and (not FFlat or not FMouseInControl) then
begin
Canvas.Brush.Bitmap := AllocPatternBitmap(clBtnFace, clBtnHighlight);
Canvas.FillRect(PaintRect);
end;
Offset.X := 1;
Offset.Y := 1;
end
else
begin
Offset.X := 0;
Offset.Y := 0;
end;
PaintRect := TButtonGlyph(FGlyph).Draw(Canvas, PaintRect, Offset, Caption, FLayout, FMargin,
FSpacing, FState, FTransparent, FDropDownWidth, DrawTextBiDiModeFlags(0));
// draw color indicator
Canvas.Brush.Color := FSelectionColor;
Canvas.Pen.Color := clBtnShadow;
case FIndicatorBorder of
ibNone:
Canvas.FillRect(PaintRect);
ibFlat:
with PaintRect do
Canvas.Rectangle(Left, Top, Right, Bottom);
else
if FIndicatorBorder = ibSunken then DrawEdge(Canvas.Handle, PaintRect, BDR_SUNKENOUTER, BF_RECT)
else DrawEdge(Canvas.Handle, PaintRect, BDR_RAISEDINNER, BF_RECT);
InflateRect(PaintRect, -1, -1);
Canvas.FillRect(PaintRect);
end;
// Draws the arrow for the correct state
if FState = bsDisabled then
begin
Canvas.Pen.Style := psClear;
Canvas.Brush.Color := clBtnShadow;
end
else
begin
Canvas.Pen.Color := FDropDownArrowColor;
Canvas.Brush.Color := FDropDownArrowColor;
end;
if FDropDownZone and FDroppedDown or (FState = bsDown) and not (FDropDownZone) then
DrawTriangle(Canvas, (Height div 2) + 1, LeftPos + 1, MAX_WIDTH)
else
DrawTriangle(Canvas, (Height div 2), LeftPos, MAX_WIDTH);
end;
//-----------------------------------------------------------------------------
procedure TColorPickerButton.UpdateTracking;
var P: TPoint;
begin
if FFlat then
begin
if Enabled then
begin
GetCursorPos(P);
FMouseInControl := not (FindDragTarget(P, True) = Self);
if FMouseInControl then Perform(CM_MOUSELEAVE, 0, 0)
else Perform(CM_MOUSEENTER, 0, 0);
end;
end;
end;
//-----------------------------------------------------------------------------
procedure TColorPickerButton.Loaded;
var State: TButtonState;
begin
inherited Loaded;
if Enabled then State := bsUp
else State := bsDisabled;
TButtonGlyph(FGlyph).CreateButtonGlyph(State);
end;
//-----------------------------------------------------------------------------
procedure TColorPickerButton.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
inherited MouseDown(Button, Shift, X, Y);
if (Button = mbLeft) and Enabled then
begin
// Determine if mouse is currently in the drop down section...
FDropDownZone := (X > Width - FDropDownWidth);
// If so display the button in the proper state and display the menu
if FDropDownZone then
begin
if not FDroppedDown then
begin
Update;
DroppedDown := True;
end;
// Setting this flag to false is very important, we want the dsUp state to
// be used to display the button properly the next time the mouse moves in
FDragging := False;
end
else
begin
if not FDown then
begin
FState := bsDown;
Invalidate;
end;
FDragging := True;
end;
end;
end;
//-----------------------------------------------------------------------------
procedure TColorPickerButton.MouseMove(Shift: TShiftState; X, Y: Integer);
var NewState: TButtonState;
begin
inherited MouseMove(Shift, X, Y);
if FDragging then
begin
if not FDown then NewState := bsUp
else NewState := bsExclusive;
if (X >= 0) and (X < ClientWidth) and (Y >= 0) and (Y <= ClientHeight) then
if FDown then NewState := bsExclusive
else NewState := bsDown;
if NewState <> FState then
begin
FState := NewState;
Invalidate;
end;
end;
end;
//-----------------------------------------------------------------------------
procedure TColorPickerButton.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var DoClick: Boolean;
begin
inherited MouseUp(Button, Shift, X, Y);
if FDragging then
begin
FDragging := False;
DoClick := (X >= 0) and (X < ClientWidth) and (Y >= 0) and (Y <= ClientHeight);
if FGroupIndex = 0 then
begin
// Redraw face in case mouse is captured
FState := bsUp;
FMouseInControl := False;
if DoClick and not (FState in [bsExclusive, bsDown]) then Invalidate;
end
else
if DoClick then
begin
SetDown(not FDown);
if FDown then Repaint;
end
else
begin
if FDown then FState := bsExclusive;
Repaint;
end;
if DoClick then Click;
UpdateTracking;
end;
end;
//-----------------------------------------------------------------------------
procedure TColorPickerButton.Click;
begin
inherited Click;
end;
//-----------------------------------------------------------------------------
procedure TColorPickerButton.DoDefaultEvent;
begin
if Assigned(FOnDefaultSelect) then FOnDefaultSelect(Self);
end;
//-----------------------------------------------------------------------------
function TColorPickerButton.GetPalette: HPALETTE;
begin
Result := Glyph.Palette;
end;
//-----------------------------------------------------------------------------
function TColorPickerButton.GetGlyph: TBitmap;
begin
Result := TButtonGlyph(FGlyph).Glyph;
end;
//-----------------------------------------------------------------------------
procedure TColorPickerButton.SetGlyph(Value: TBitmap);
begin
TButtonGlyph(FGlyph).Glyph := Value;
Invalidate;
end;
//-----------------------------------------------------------------------------
function TColorPickerButton.GetNumGlyphs: TNumGlyphs;
begin
Result := TButtonGlyph(FGlyph).NumGlyphs;
end;
//-----------------------------------------------------------------------------
procedure TColorPickerButton.DrawButtonSeperatorUp(Canvas: TCanvas);
begin
with Canvas do
begin
Pen.Style := psSolid;
Brush.Style := bsClear;
Pen.Color := clBtnHighlight;
Rectangle(Width - DropDownWidth, 1, Width - DropDownWidth + 1, Height - 1);
Pen.Color := clBtnShadow;
Rectangle(Width - DropDownWidth - 1, 1, Width - DropDownWidth, Height - 1);
end;
end;
//-----------------------------------------------------------------------------
procedure TColorPickerButton.DrawButtonSeperatorDown(Canvas: TCanvas);
begin
with Canvas do
begin
Pen.Style := psSolid;
Brush.Style := bsClear;
Pen.Color := clBtnHighlight;
Rectangle(Width - DropDownWidth + 1, 2, Width - DropDownWidth + 2, Height - 2);
Pen.Color := clBtnShadow;
Rectangle(Width - DropDownWidth, 2, Width - DropDownWidth + 1, Height - 2);
end;
end;
//-----------------------------------------------------------------------------
procedure TColorPickerButton.DrawTriangle(Canvas: TCanvas; Top, Left, Width: Integer);
begin
if Odd(Width) then Inc(Width);
Canvas.Polygon([Point(Left, Top),
Point(Left + Width, Top),
Point(Left + Width div 2, Top + Width div 2)]);
end;
//-----------------------------------------------------------------------------
procedure TColorPickerButton.SetNumGlyphs(Value: TNumGlyphs);
begin
if Value < 0 then Value := 1
else
if Value > 4 then Value := 4;
if Value <> TButtonGlyph(FGlyph).NumGlyphs then
begin
TButtonGlyph(FGlyph).NumGlyphs := Value;
Invalidate;
end;
end;
//-----------------------------------------------------------------------------
procedure TColorPickerButton.GlyphChanged(Sender: TObject);
begin
Invalidate;
end;
//-----------------------------------------------------------------------------
procedure TColorPickerButton.UpdateExclusive;
var Msg: TMessage;
begin
if (FGroupIndex <> 0) and (Parent <> nil) then
begin
Msg.Msg := CM_BUTTONPRESSED;
Msg.WParam := FGroupIndex;
Msg.LParam := Longint(Self);
Msg.Result := 0;
Parent.Broadcast(Msg);
end;
end;
//-----------------------------------------------------------------------------
procedure TColorPickerButton.SetDown(Value: Boolean);
begin
if FGroupIndex = 0 then Value := False;
if Value <> FDown then
begin
if FDown and (not FAllowAllUp) then Exit;
FDown := Value;
if Value then
begin
if FState = bsUp then Invalidate;
FState := bsExclusive;
end
else
begin
FState := bsUp;
Repaint;
end;
if Value then UpdateExclusive;
end;
end;
//-----------------------------------------------------------------------------
procedure TColorPickerButton.SetFlat(Value: Boolean);
begin
if Value <> FFlat then
begin
FFlat := Value;
if Value then ControlStyle := ControlStyle - [csOpaque]
else ControlStyle := ControlStyle + [csOpaque];
Invalidate;
end;
end;
//-----------------------------------------------------------------------------
procedure TColorPickerButton.SetGroupIndex(Value: Integer);
begin
if FGroupIndex <> Value then
begin
FGroupIndex := Value;
UpdateExclusive;
end;
end;
//-----------------------------------------------------------------------------
procedure TColorPickerButton.SetLayout(Value: TButtonLayout);
begin
if FLayout <> Value then
begin
FLayout := Value;
Invalidate;
end;
end;
//-----------------------------------------------------------------------------
procedure TColorPickerButton.SetMargin(Value: Integer);
begin
if (Value <> FMargin) and (Value >= -1) then
begin
FMargin := Value;
Invalidate;
end;
end;
//-----------------------------------------------------------------------------
procedure TColorPickerButton.SetSpacing(Value: Integer);
begin
if Value <> FSpacing then
begin
FSpacing := Value;
Invalidate;
end;
end;
//-----------------------------------------------------------------------------
procedure TColorPickerButton.SetAllowAllUp(Value: Boolean);
begin
if FAllowAllUp <> Value then
begin
FAllowAllUp := Value;
UpdateExclusive;
end;
end;
//-----------------------------------------------------------------------------
procedure TColorPopup.WMActivateApp(var Message: TWMActivateApp);
begin
inherited;
if not Message.Active then EndSelection(True);
end;
//-----------------------------------------------------------------------------
procedure TColorPickerButton.WMLButtonDblClk(var Message: TWMLButtonDown);
begin
inherited;
if FDown then DblClick;
end;
//-----------------------------------------------------------------------------
procedure TColorPickerButton.CMEnabledChanged(var Message: TMessage);
const NewState: array[Boolean] of TButtonState = (bsDisabled, bsUp);
begin
TButtonGlyph(FGlyph).CreateButtonGlyph(NewState[Enabled]);
UpdateTracking;
Repaint;
end;
//-----------------------------------------------------------------------------
procedure TColorPickerButton.CMButtonPressed(var Message: TMessage);
var Sender: TColorPickerButton;
begin
if Message.WParam = FGroupIndex then
begin
Sender := TColorPickerButton(Message.LParam);
if Sender <> Self then
begin
if Sender.Down and FDown then
begin
FDown := False;
FState := bsUp;
Invalidate;
end;
FAllowAllUp := Sender.AllowAllUp;
end;
end;
end;
//-----------------------------------------------------------------------------
procedure TColorPickerButton.CMDialogChar(var Message: TCMDialogChar);
begin
with Message do
if IsAccel(CharCode, Caption) and
Enabled and
Visible and
Assigned(Parent) and
Parent.Showing then
begin
Click;
Result := 1;
end
else inherited;
end;
//-----------------------------------------------------------------------------
procedure TColorPickerButton.CMFontChanged(var Message: TMessage);
begin
Invalidate;
end;
//-----------------------------------------------------------------------------
procedure TColorPickerButton.CMTextChanged(var Message: TMessage);
begin
Invalidate;
end;
//-----------------------------------------------------------------------------
procedure TColorPickerButton.CMSysColorChange(var Message: TMessage);
begin
with TButtonGlyph(FGlyph) do
begin
Invalidate;
CreateButtonGlyph(FState);
end;
end;
//-----------------------------------------------------------------------------
procedure TColorPickerButton.CMMouseEnter(var Message: TMessage);
begin
inherited;
if FFlat and not FMouseInControl and Enabled then
begin
FMouseInControl := True;
Repaint;
end;
end;
//-----------------------------------------------------------------------------
procedure TColorPickerButton.CMMouseLeave(var Message: TMessage);
begin
inherited;
if FFlat and FMouseInControl and Enabled and not FDragging then
begin
FMouseInControl := False;
Invalidate;
end;
end;
//-----------------------------------------------------------------------------
procedure TColorPickerButton.SetDroppedDown(const Value: Boolean);
var Allowed: Boolean;
begin
if FDroppedDown <> Value then
begin
Allowed:= True;
if Assigned(FOnDropChanging) then FOnDropChanging(Self, Allowed);
if Allowed then
begin
FDroppedDown := Value;
if FDroppedDown then
begin
FState := bsDown;
TColorPopup(FColorPopup).SelectedColor := FSelectionColor;
TColorPopup(FColorPopup).ShowPopupAligned;
SetCapture(FPopupWnd);
end
else
begin
FState := bsUp;
ReleaseCapture;
ShowWindow(FColorPopup.Handle, SW_HIDE);
end;
if Assigned(FOnDropChanged) then FOnDropChanged(Self);
Invalidate;
end;
end;
end;
//-----------------------------------------------------------------------------
procedure TColorPickerButton.SetSelectionColor(const Value: TColor);
begin
if FSelectionColor <> Value then
begin
FSelectionColor := Value;
Invalidate;
if FDroppedDown then TColorPopup(FColorPopup).SelectColor(Value);
if Assigned(FOnChange) then FOnChange(Self);
end;
end;
//-----------------------------------------------------------------------------
function TColorPickerButton.GetCustomText: String;
begin
Result := TColorPopup(FColorPopup).FCustomText;
end;
//-----------------------------------------------------------------------------
procedure TColorPickerButton.SetCustomText(const Value: String);
begin
with TColorPopup(FColorPopup) do
begin
if FCustomText <> Value then
begin
FCustomText := Value;
if (FCustomText = '''') and (FSelectedIndex = CustomCell) then FSelectedIndex := NoCell;
AdjustWindow;
if FDroppedDown then
begin
Invalidate;
ShowPopupAligned;
end;
end;
end;
end;
//-----------------------------------------------------------------------------
function TColorPickerButton.GetDefaultText: String;
begin
Result := TColorPopup(FColorPopup).FDefaultText;
end;
//-----------------------------------------------------------------------------
procedure TColorPickerButton.SetDefaultText(const Value: String);
begin
if TColorPopup(FColorPopup).FDefaultText <> Value then
begin
with TColorPopup(FColorPopup) do
begin
FDefaultText := Value;
AdjustWindow;
if FDroppedDown then
begin
Invalidate;
ShowPopupAligned;
end;
end;
end;
end;
//-----------------------------------------------------------------------------
procedure TColorPickerButton.SetShowSystemColors(const Value: Boolean);
begin
with TColorPopup(FColorPopup) do
begin
if FShowSysColors <> Value then
begin
FShowSysColors := Value;
AdjustWindow;
if FDroppedDown then
begin
Invalidate;
ShowPopupAligned;
end;
end;
end;
end;
//-----------------------------------------------------------------------------
function TColorPickerButton.GetShowSystemColors: Boolean;
begin
Result := TColorPopup(FColorPopup).FShowSysColors;
end;
//-----------------------------------------------------------------------------
procedure TColorPickerButton.SetTransparent(const Value: Boolean);
begin
if Value <> FTransparent then
begin
FTransparent := Value;
if Value then ControlStyle := ControlStyle - [csOpaque]
else ControlStyle := ControlStyle + [csOpaque];
Invalidate;
end;
end;
//-----------------------------------------------------------------------------
procedure TColorPickerButton.ActionChange(Sender: TObject; CheckDefaults: Boolean);
//--------------- local functions -----------------------
procedure CopyImage(ImageList: TCustomImageList; Index: Integer);
begin
with Glyph do
begin
Width := ImageList.Width;
Height := ImageList.Height;
Canvas.Brush.Color := clFuchsia;//! for lack of a better color
Canvas.FillRect(Rect(0,0, Width, Height));
ImageList.Draw(Canvas, 0, 0, Index);
end;
end;
//--------------- end local functions -------------------
begin
inherited ActionChange(Sender, CheckDefaults);
if Sender is TCustomAction then
with TCustomAction(Sender) do
begin
// Copy image from action''s imagelist
if Glyph.Empty and
Assigned(ActionList) and
Assigned(ActionList.Images) and
(ImageIndex >= 0) and
(ImageIndex < ActionList.Images.Count) then CopyImage(ActionList.Images, ImageIndex);
end;
end;
//-----------------------------------------------------------------------------
procedure Register;
begin
RegisterComponents(''Tools'', [TColorPickerButton]);
end;
//-----------------------------------------------------------------------------
procedure TColorPickerButton.SetIndicatorBorder(const Value: TIndicatorBorder);
begin
if FIndicatorBorder <> Value then
begin
FIndicatorBorder := Value;
Invalidate;
end;
end;
//-----------------------------------------------------------------------------
function TColorPickerButton.GetPopupSpacing: Integer;
begin
Result := TColorPopup(FColorPopup).Spacing;
end;
//-----------------------------------------------------------------------------
procedure TColorPickerButton.SetPopupSpacing(const Value: Integer);
begin
TColorPopup(FColorPopup).Spacing := Value;
end;
//-----------------------------------------------------------------------------
end.
这个是你要的吧。
9楼: 我先回去看看,下次上网时结帖。
10楼: 不是我要的那种,但是已经比较接近了。
许多文字编辑软件都有那种控件,就是类似于Windows写字板中选择字体颜色的那种,但是右面还有一个
向下的小箭头,点击这个小箭头就出现一个下拉列表,上面有各种颜色及描述文字,最下面是自定义颜色
。那种才是我最想要的。
(如果没有答案就把分散给你了)
11楼: 晕,自己用个popupMenu画一下就可以了啊!!!!
12楼: 自己作啊 如财务记账软件哪个好
13楼: 等到实在不行了再自己做。哈哈。懒人啊。
企业管理软件版14楼: 我写过一个,不过是加在我自己做的控件中的,没单独整理出来,需要的话联系我QQ:523241035