unit mbBasicPicker; {$mode objfpc}{$H+} interface uses LMessages, Classes, SysUtils, Graphics, Controls, ExtCtrls, Forms; type THintState = (hsOff, hsWaitingToShow, hsWaitingToHide); TGetHintStrEvent = procedure (Sender: TObject; X, Y: Integer; var AText: String) of object; { TmbBasicPicker } TmbBasicPicker = class(TCustomControl) private FOnChange: TNotifyEvent; FOnGetHintStr: TGetHintStrEvent; FLockChange: Integer; protected FBufferBmp: TBitmap; FGradientWidth: Integer; FGradientHeight: Integer; FHintShown: Boolean; procedure CreateGradient; virtual; procedure DoChange; virtual; function GetColorUnderCursor: TColor; virtual; function GetGradientColor({%H-}AValue: Integer): TColor; virtual; function GetGradientColor2D({%H-}X, {%H-}Y: Integer): TColor; virtual; function GetHintPos(X, Y: Integer): TPoint; virtual; function GetHintStr(X, Y: Integer): String; virtual; function GetSelectedColor: TColor; virtual; abstract; procedure PaintParentBack; virtual; overload; procedure PaintParentBack(ACanvas: TCanvas); overload; procedure PaintParentBack(ACanvas: TCanvas; ARect: TRect); overload; procedure PaintParentBack(ABitmap: TBitmap); overload; procedure SetSelectedColor(c: TColor); virtual; abstract; procedure CMHintShow(var Message: TCMHintShow); message CM_HINTSHOW; procedure CMParentColorChanged(var Message: TLMessage); message CM_PARENTCOLORCHANGED; property ColorUnderCursor: TColor read GetColorUnderCursor; property OnChange: TNotifyEvent read FOnChange write FOnChange; property OnGetHintStr: TGetHintStrEvent read FOnGetHintStr write FOnGetHintStr; public constructor Create(AOwner: TComponent); override; destructor Destroy; override; function GetColorAtPoint(X, Y: Integer): TColor; virtual; function GetHexColorAtPoint(X, Y: integer): string; function GetHexColorUnderCursor: string; virtual; procedure Lock; function IsLocked: Boolean; procedure Unlock; published property ParentColor default true; property SelectedColor: TColor read GetSelectedColor write SetSelectedColor; end; implementation uses LCLIntf, HTMLColors, mbUtils; constructor TmbBasicPicker.Create(AOwner: TComponent); begin inherited Create(AOwner); // ControlStyle := ControlStyle - [csOpaque]; ParentColor := true; {$IFDEF WINDOWS} DoubleBuffered := true; {$ENDIF} end; destructor TmbBasicPicker.Destroy; begin FBufferBmp.Free; inherited; end; procedure TmbBasicPicker.CMHintShow(var Message: TCMHintShow); var cp: TPoint; hp: TPoint; begin if GetColorUnderCursor <> clNone then with TCMHintShow(Message) do if not ShowHint then Message.Result := 1 else if Hint <> '' then Message.Result := 0 else begin cp := HintInfo^.CursorPos; hp := GetHintPos(cp.X, cp.Y); HintInfo^.ReshowTimeout := 0; // must be zero! HintInfo^.HideTimeout := Application.HintHidePause; HintInfo^.HintStr := GetHintStr(cp.X, cp.Y); HintInfo^.HintPos := ClientToScreen(Point(hp.X + 16, hp.Y)); HintInfo^.CursorRect := Rect(cp.X, cp.Y, cp.X+1, cp.Y+1); Result := 0; // 0 means: show hint end; inherited; end; procedure TmbBasicPicker.CMParentColorChanged(var Message: TLMessage); begin { if ParentColor then ControlStyle := ControlStyle - [csOpaque] else ControlStyle := ControlStyle + [csOpaque]; } inherited; end; procedure TmbBasicPicker.CreateGradient; begin // to be implemented by descendants end; procedure TmbBasicPicker.DoChange; begin if (FLockChange = 0) and Assigned(FOnChange) and (ComponentState = []) then FOnChange(self); end; function TmbBasicPicker.GetColorAtPoint(x, y: integer): TColor; begin Result := Canvas.Pixels[x, y]; // valid for most descendents end; function TmbBasicPicker.GetColorUnderCursor: TColor; var P: TPoint; begin P := ScreenToClient(Mouse.CursorPos); Result := GetColorAtPoint(P.X, P.Y); end; function TmbBasicPicker.GetGradientColor(AValue: Integer): TColor; begin Result := clNone; end; function TmbBasicPicker.GetGradientColor2D(X, Y: Integer): TColor; begin Result := clNone; end; function TmbBasicPicker.GetHexColorAtPoint(X, Y: integer): string; begin Result := ColorToHex(GetColorAtPoint(x, y)); end; function TmbBasicPicker.GetHexColorUnderCursor: string; begin Result := ColorToHex(GetColorUnderCursor); end; function TmbBasicPicker.GetHintPos(X, Y: Integer): TPoint; begin Result := Point(X, Y); end; function TmbBasicPicker.GetHintStr(X, Y: Integer): String; begin Result := ''; if Assigned(FOnGetHintStr) then FOnGetHintStr(Self, X, Y, Result); end; function TmbBasicPicker.IsLocked: Boolean; begin Result := FLockChange > 0; end; procedure TmbBasicPicker.Lock; begin inc(FLockChange); end; procedure TmbBasicPicker.PaintParentBack; begin PaintParentBack(Canvas); end; procedure TmbBasicPicker.PaintParentBack(ABitmap: TBitmap); begin ABitmap.Width := Width; ABitmap.Height := Height; if Color = clDefault then begin ABitmap.Transparent := true; ABitmap.TransparentColor := clForm; ABitmap.Canvas.Brush.Color := clForm; end else ABitmap.Canvas.Brush.Color := Color; ABitmap.Canvas.FillRect(ABitmap.Canvas.ClipRect); end; procedure TmbBasicPicker.PaintParentBack(ACanvas: TCanvas); var R: TRect; begin R := Rect(0, 0, Width, Height); PaintParentBack(ACanvas, R); end; procedure TmbBasicPicker.PaintParentBack(ACanvas: TCanvas; ARect: TRect); var OffScreen: TBitmap; begin Offscreen := TBitmap.Create; try if Color = clDefault then begin Offscreen.Transparent := true; Offscreen.TransparentColor := clForm; end; Offscreen.Width := WidthOfRect(ARect); Offscreen.Height := HeightOfRect(ARect); PaintParentBack(Offscreen); ACanvas.Draw(ARect.Left, ARect.Top, Offscreen); finally Offscreen.Free; end; end; procedure TmbBasicPicker.Unlock; begin dec(FLockChange); end; end.