Files
lazarus-ccr/components/industrialstuff/source/switches.pas
2023-11-15 19:03:00 +00:00

1050 lines
30 KiB
ObjectPascal

{
/***************************************************************************
switches.pp
License: Modified LGPL (with linking exception)
See the file COPYING.modifiedLGPL.txt, included in the Lazarus distribution,
for details about the license.
Autho: Werner Pamler
*****************************************************************************
}
unit Switches;
{$mode objfpc}{$H+}
interface
uses
LCLIntf, LCLType, LMessages, LCLVersion,
Graphics, GraphType, Classes, SysUtils, Types, Controls, ExtCtrls, ImgList;
type
TSwitchBorderStyle = (
bsNone, bsThin, bsThick, bsThin3D, bsThick3D,
bsNoneRounded, bsThinRounded, bsThickRounded
);
TSwitchMode = (smClick, smDblClick, smSlide, smSlideDblClick);
TSwitchOrientation = (soHorizontal, soVertical);
TSwitchAnimationEffect = (saeBlendColors, saeBlendImages, saeRotateImages);
TSwitchAnimationEffects = set of TSwitchAnimationEffect;
TCustomOnOffSwitch = class(TCustomControl)
private
const
DEFAULT_BUTTON_SIZE = 24;
private
FAnimated: Boolean;
FAnimationEffects: TSwitchAnimationEffects;
FAnimationTimer: TTimer;
FBorderStyle: TSwitchBorderStyle;
FButtonPos: Integer;
FButtonSize: Integer;
FCaptions: array[boolean] of string;
FChecked: Boolean;
FCheckedAfterAnimation: Boolean;
FColors: array [0..2] of TColor;
FInverse: Boolean;
FDragging: Boolean;
FMaxDistance: Integer;
FFlippedColors: Boolean;
FTogglePending: Boolean;
FMousePt: TPoint;
FButtonRect: TRect;
FReadOnly: Boolean;
FShowButtonBorder: Boolean;
FShowCaption: Boolean;
FShowFocusRect: Boolean;
FSwitchMode: TSwitchMode;
FOnChange: TNotifyEvent;
FDblClickTimer: TTimer;
FPicture: array[boolean] of TPicture;
FImageIndex: array[boolean] of TImageIndex;
FImageListChangeLink: TChangeLink;
FImages: TCustomImagelist;
FImagesWidth: Integer;
function GetBorderWidth: Integer;
function GetCaptions(AChecked: Boolean): String;
function GetColors(AIndex: Integer): TColor;
function GetImageIndex(AChecked: Boolean): TImageIndex;
function GetOrientation: TSwitchOrientation;
function GetPicture(AChecked: Boolean): TPicture;
procedure SetAnimated(AValue: Boolean);
procedure SetBorderStyle(AValue: TSwitchBorderStyle); reintroduce;
procedure SetButtonSize(AValue: Integer);
procedure SetCaptions(AChecked: Boolean; AValue: string);
procedure SetChecked(AValue: Boolean);
procedure SetColors(AIndex: Integer; AValue: TColor);
procedure SetFlippedColors(AValue: Boolean);
procedure SetImageIndex(AChecked: Boolean; AValue: TImageIndex);
procedure SetImages(const AValue: TCustomImageList);
procedure SetInverse(AValue: Boolean);
procedure SetPicture(AChecked: Boolean; AValue: TPicture);
procedure SetShowButtonBorder(AValue: Boolean);
procedure SetShowCaption(AValue: Boolean);
procedure SetShowFocusRect(AValue: Boolean);
procedure UpdateButtonPos;
procedure UpdateMaxDistance;
procedure UpdateShape;
protected
function CalcButtonRect: TRect;
function CalcMargin: Integer;
function CanChange: Boolean; virtual;
procedure DblClick; override;
procedure DblClickTimerHandler(Sender: TObject);
procedure DoAnimation(Sender: TObject);
procedure DoAutoAdjustLayout(const AMode: TLayoutAdjustmentPolicy;
const AXProportion, AYProportion: Double); override;
procedure DoChange; virtual;
procedure DoEnter; override;
procedure DoExit; override;
procedure DoImageListChange(Sender: TObject);
function DraggedToON: Boolean;
procedure DrawButton(ARect: TRect); virtual;
procedure DrawCaption(ARect: TRect; AChecked: Boolean); virtual;
procedure DrawFocusRect(ARect: TRect);
class function GetControlClassDefaultSize: TSize; override;
function HasPicture: Boolean;
procedure KeyDown(var Key: Word; Shift: TShiftState); override;
procedure MouseDown(Button: TMouseButton; Shift:TShiftState; X,Y:Integer); override;
procedure MouseMove(Shift: TShiftState; X,Y: Integer); override;
function MouseOnButton(X, Y: Integer): Boolean; virtual;
procedure MouseUp(Button: TMouseButton; Shift:TShiftState; X,Y:Integer); override;
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
procedure Paint; override;
property Animated: Boolean read FAnimated write SetAnimated default false;
property AnimationEffects: TSwitchAnimationEffects read FAnimationEffects write FAnimationEffects default [saeBlendColors, saeBlendImages];
property BorderColor: TColor index 2 read GetColors write SetColors default clGray;
property BorderStyle: TSwitchBorderStyle read FBorderStyle write SetBorderStyle default bsThin;
property ButtonSize: Integer read FButtonSize write SetButtonSize default DEFAULT_BUTTON_SIZE;
property CaptionOFF: String index false read GetCaptions write SetCaptions;
property CaptionON: String index true read GetCaptions write SetCaptions;
property Checked: Boolean read FChecked write SetChecked default false;
property Color default clWindow;
property ColorOFF: TColor index 0 read GetColors write SetColors default clMaroon;
property ColorON: TColor index 1 read GetColors write SetColors default clGreen;
property FlippedColors: Boolean read FFlippedColors write SetFlippedColors default false;
property ImageIndexOFF: TImageIndex index false read GetImageIndex write SetImageIndex default -1;
property ImageIndexON: TImageIndex index true read GetImageIndex write SetImageIndex default -1;
property Images: TCustomImageList read FImages write SetImages;
property ImagesWidth: Integer read FImagesWidth write FImagesWidth default 0;
property Inverse: Boolean read FInverse write SetInverse default false;
property PictureOFF: TPicture index false read GetPicture write SetPicture;
property PictureON: TPicture index true read GetPicture write SetPicture;
property ReadOnly: boolean read FReadOnly write FReadOnly default false;
property ShowButtonBorder: Boolean read FShowButtonBorder write SetShowButtonBorder default true;
property ShowFocusRect: Boolean read FShowFocusRect write SetShowFocusRect default true;
property ShowCaption: Boolean read FShowCaption write SetShowCaption default true;
property SwitchMode: TSwitchMode read FSwitchMode write FSwitchMode default smSlideDblClick;
property OnChange: TNotifyEvent read FOnChange write FOnChange;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure SetBounds(ALeft, ATop, AWidth, AHeight: Integer); override;
property Orientation: TSwitchOrientation read GetOrientation;
end;
TOnOffSwitch = class(TCustomOnOffSwitch)
published
property Animated;
property AnimationEffects;
property BorderColor;
property BorderStyle;
property ButtonSize;
property CaptionOFF;
property CaptionON;
property Checked;
property Color;
property ColorOFF;
property ColorON;
property Enabled;
property FlippedColors;
property ImageIndexON;
property ImageIndexOFF;
property Images;
property ImagesWidth;
property Inverse;
property PictureOFF;
property PictureON;
property ReadOnly;
property ShowButtonBorder;
property ShowCaption;
property ShowFocusRect;
property SwitchMode;
// inherited
property Action;
property Align;
property Anchors;
property BorderSpacing;
property Constraints;
property DoubleBuffered;
property Font;
property PopupMenu;
property ShowHint;
property TabOrder;
property TabStop default true;
property Visible;
// new
property OnChange;
// inherited
property OnChangeBounds;
property OnClick;
property OnEnter;
property OnExit;
property OnKeyDown;
property OnKeyPress;
property OnKeyUp;
property OnMouseDown;
property OnMouseEnter;
property OnMouseLeave;
property OnMouseUp;
property OnResize;
property OnShowHint;
end;
implementation
uses
Math, FPImage, GraphMath, IntfGraphics;
function BlendColors(AFactor: Double; AColor1, AColor2: TColor): TColor;
var
f1, f2: Double;
r, g, b: Byte;
begin
f1 := 1.0 - AFactor;
f2 := AFactor;
r := EnsureRange(round(GetRValue(AColor1) * f1 + GetRValue(AColor2) * f2), 0, 255);
g := EnsureRange(round(GetGValue(AColor1) * f1 + GetGValue(AColor2) * f2), 0, 255);
b := EnsureRange(round(GetBValue(AColor1) * f1 + GetBValue(AColor2) * f2), 0, 255);
Result := RGBToColor(r, g, b);
end;
procedure BlendImages(Img, Img1, Img2: TFPCustomImage; AFactor: Double);
var
x, y: Integer;
r,g,b,a: Word;
f1, f2: Double;
begin
f1 := 1.0 - AFactor;
f2 := AFactor;
for y := 0 to Img1.Height-1 do
for x := 0 to Img1.Width-1 do
begin
r := round(f1 * Img1.Colors[x, y].Red + f2 * Img2.Colors[x, y].Red);
g := round(f1 * Img1.Colors[x, y].Green + f2 * Img2.Colors[x, y].Green);
b := round(f1 * Img1.Colors[x, y].Blue + f2 * Img2.Colors[x, y].Blue);
a := round(f1 * Img1.Colors[x, y].Alpha + f2 * Img2.Colors[x, y].Alpha);
Img.Colors[x, y] := FPColor(r, g, b, a);
end;
end;
{$IF LCL_FullVersion < 3000000}
function RotatePoint(const APoint: TPoint; AAngle: Double): TPoint;
var
sa, ca: Double;
begin
SinCos(AAngle, sa, ca);
Result.X := Round( ca * APoint.X + sa * APoint.Y);
Result.Y := Round(-sa * APoint.X + ca * APoint.Y);
end;
{$ENDIF}
procedure RotateImage(Img: TFPCustomImage; Angle: Double);
var
Buffer: TFPCustomImage;
x, y: Integer;
C, P: TPoint;
begin
Buffer := TFPMemoryImage.Create(Img.Width, Img.Height);
try
C := Point(Img.Width div 2, Img.Height div 2);
for y := 0 to Buffer.Height-1 do
for x := 0 to Buffer.Width-1 do
begin
P := RotatePoint(Point(x, y) - C, Angle) + C;
if (P.X >= 0) and (P.Y >= 0) and (P.X < Img.Width) and (P.Y < Img.Height) then
Buffer.Colors[x, y] := Img.Colors[P.X, P.Y]
else
Buffer.Colors[x, y] := colTransparent;
end;
for y := 0 to Img.Height-1 do
for x := 0 to Img.Width-1 do
Img.Colors[x, y] := Buffer[x, y];
finally
Buffer.Free;
end;
end;
function TintedColor(AColor: TColor; ADelta: Integer): TColor;
var
r, g, b: Byte;
begin
AColor := ColorToRGB(AColor);
r := GetRValue(AColor);
g := GetGValue(AColor);
b := GetBValue(AColor);
if r + g + b < 3*128 then
// Dark color --> make it brighter
ADelta := abs(ADelta)
else
// Bright color --> make it darker
ADelta := -abs(ADelta);
r := EnsureRange(r + ADelta, 0, 255);
g := EnsureRange(g + ADelta, 0, 255);
b := EnsureRange(b + ADelta, 0, 255);
Result := RGBToColor(r, g, b);
end;
{ TOnOffSwitch }
constructor TCustomOnOffSwitch.Create(AOwner: TComponent);
begin
inherited;
TabStop := true;
Color := clWindow;
FAnimationEffects := [saeBlendColors, saeBlendImages];
FBorderStyle := bsThin;
FButtonSize := DEFAULT_BUTTON_SIZE;
FColors[0] := clMaroon; // unchecked color
FColors[1] := clGreen; // checked color
FColors[2] := clGray; // Border color
FCaptions[false] := 'OFF';
FCaptions[true] := 'ON';
FImageListChangeLink := TChangeLink.Create;
FImageListChangeLink.OnChange := @DoImageListChange;
FImageIndex[false] := -1;
FImageIndex[true] := -1;
FPicture[false] := TPicture.Create;
FPicture[true] := TPicture.Create;
FShowCaption := true;
FShowButtonBorder := true;
FShowFocusRect := true;
FSwitchMode := smSlideDblClick;
FDblClickTimer := TTimer.Create(self);
FDblClickTimer.Interval := 500;
FDblClickTimer.Enabled := false;
FDblClickTimer.OnTimer := @DblClickTimerHandler;
with GetControlClassDefaultSize do
SetInitialBounds(0, 0, CX, CY);
end;
destructor TCustomOnOffSwitch.Destroy;
begin
FPicture[false].Free;
FPicture[true].Free;
FImageListChangeLink.Free;
inherited;
end;
function TCustomOnOffSwitch.CalcButtonRect: TRect;
begin
Result := FButtonRect;
case Orientation of
soHorizontal : OffsetRect(Result, FButtonPos + CalcMargin, 0);
soVertical : OffsetRect(Result, 0, FButtonPos + CalcMargin);
end;
end;
function TCustomOnOffSwitch.CalcMargin: Integer;
begin
Result := 3 + GetBorderWidth;
end;
function TCustomOnOffSwitch.CanChange: Boolean;
begin
Result := Enabled and (not FReadOnly);
end;
procedure TCustomOnOffSwitch.DblClick;
begin
inherited;
FDblClickTimer.Enabled := false;
if CanChange and FTogglePending then begin
FTogglePending := false;
SetChecked(not FChecked);
end;
end;
procedure TCustomOnOffSwitch.DblClickTimerHandler(Sender: TObject);
begin
FTogglePending := true;
end;
procedure TCustomOnOffSwitch.DoAutoAdjustLayout(
const AMode: TLayoutAdjustmentPolicy;
const AXProportion, AYProportion: Double);
begin
inherited DoAutoAdjustLayout(AMode, AXProportion, AYProportion);
if AMode in [lapAutoAdjustWithoutHorizontalScrolling, lapAutoAdjustForDPI] then
begin
case Orientation of
soHorizontal : FButtonSize := Round(FButtonSize * AXProportion);
soVertical : FButtonSize := Round(FButtonSize * AYProportion);
end;
end;
end;
{ Handler for the animation timer }
procedure TCustomOnOffSwitch.DoAnimation(Sender: TObject);
const
SPEED = 3;
begin
if FCheckedAfterAnimation xor FInverse then
begin
inc(FButtonPos, SPEED);
if FButtonPos >= FMaxDistance then
begin
FAnimationTimer.Enabled := false;
FChecked := FCheckedAfterAnimation;
UpdateButtonPos;
DoChange;
end;
end else
begin
dec(FButtonPos, SPEED);
if FButtonPos <= 0 then
begin
FAnimationTimer.Enabled := false;
FChecked := FCheckedAfterAnimation;
UpdateButtonPos;
DoChange;
end;
end;
Invalidate;
end;
procedure TCustomOnOffSwitch.DoChange;
begin
if Assigned(FOnChange) then FOnChange(self);
end;
procedure TCustomOnOffSwitch.DoEnter;
begin
inherited;
Invalidate;
end;
procedure TCustomOnOffSwitch.DoExit;
begin
inherited;
Invalidate;
end;
procedure TCustomOnOffSwitch.DoImageListChange(Sender: TObject);
begin
Invalidate;
end;
{ Determines whether the dragged distance lands in the part of the
ON (Result = true) or OFF state (Result = false) }
function TCustomOnOffSwitch.DraggedToON: Boolean;
begin
Result := FButtonPos > FMaxDistance div 2;
if FInverse then Result := not Result;
end;
procedure TCustomOnOffSwitch.DrawButton(ARect: TRect);
var
rawON, rawOFF: TRawImage;
Img, ImgOn, ImgOff: TLazIntfImage;
bmp: TBitmap;
ImgSize: TSize;
x, y: Integer;
idx: Integer;
ppi: Integer;
begin
if not Enabled then begin
Canvas.Brush.Color := clGrayText;
Canvas.Pen.Color := clGrayText;
end else begin
if FFlippedColors then
Canvas.Brush.Color := Color
else
if (saeBlendColors in FAnimationEffects) then
Canvas.Brush.Color := BlendColors(FButtonPos/FMaxDistance, ColorOFF, ColorON)
else
if FChecked then
Canvas.Brush.Color := ColorON
else
Canvas.Brush.Color := ColorOFF;
Canvas.Pen.Color := clBlack;
end;
if not FShowButtonBorder then
Canvas.Pen.Color := Canvas.Brush.Color;
Canvas.Pen.Width := 1;
Canvas.Pen.Style := psSolid;
if FBorderStyle in [bsNoneRounded, bsThinRounded, bsThickRounded] then
begin
dec(ARect.Bottom);
dec(ARect.Right);
Canvas.Ellipse(ARect);
end else
Canvas.Rectangle(ARect);
if FImages <> nil then
begin
ppi := Font.PixelsPerInch;
imgSize := FImages.SizeForPPI[FImagesWidth, ppi];
x := (ARect.Left + ARect.Right - imgSize.CX) div 2;
y := (ARect.Top + ARect.Bottom - imgSize.CY) div 2;
idx := FImageIndex[FChecked];
if idx <> -1 then
begin
if ([saeBlendImages, saeRotateImages] * FAnimationEffects <> []) then
begin
bmp := TBitmap.Create;
try
bmp.PixelFormat := pf32Bit;
rawOFF.Init;
rawON.Init;
FImages.GetRawImage(FImageIndex[false], rawOFF);
FImages.GetRawImage(FImageIndex[true], rawON);
imgON := TLazIntfImage.Create(rawON, false);
imgOFF := TLazIntfImage.Create(rawOFF, false);
img := TLazIntfImage.CreateCompatible(imgOFF, imgOFF.Width, imgOFF.Height);
try
if saeBlendImages in FAnimationEffects then
BlendImages(img, imgOFF, imgON, FButtonPos/FMaxDistance)
else
if FChecked then
img.Assign(imgON)
else
img.Assign(imgOFF);
if saeRotateImages in FAnimationEffects then
RotateImage(img, 2*pi * FButtonPos/FMaxDistance);
bmp.Assign(img);
Canvas.Draw(x, y, bmp);
finally
img.Free;
imgON.Free;
imgOFF.Free;
end;
finally
bmp.Free;
end;
end else
FImages.DrawForPPI(Canvas, x, y, idx, FImagesWidth, ppi, GetCanvasScaleFactor, Enabled);
end;
end;
end;
procedure TCustomOnOffSwitch.DrawCaption(ARect: TRect; AChecked: Boolean);
var
ts: TTextStyle;
begin
Canvas.Font.Assign(Font);
if not Enabled then
Canvas.Font.Color := clGrayText;
ts := Canvas.TextStyle;
ts.Alignment := taCenter;
ts.Layout := tlCenter;
Canvas.TextStyle := ts;
if AChecked then
Canvas.TextRect(ARect, ARect.Left, ARect.Top, CaptionON)
else
Canvas.TextRect(ARect, ARect.Left, ARect.Top, CaptionOFF);
end;
procedure TCustomOnOffSwitch.DrawFocusRect(ARect: TRect);
var
m: TPenMode;
c: Boolean;
begin
m := Canvas.Pen.Mode;
c := Canvas.Pen.Cosmetic;
try
Canvas.Pen.Color := clBlack;
Canvas.Pen.Mode := pmXOR;
Canvas.Pen.Color := clWhite;
Canvas.Pen.Style := psDot;
Canvas.Brush.Style := bsClear;
if FBorderStyle in [bsNoneRounded, bsThinRounded, bsThickRounded] then
begin
Canvas.Pen.Cosmetic := true;
Canvas.Ellipse(ARect);
end else
begin
Canvas.Pen.Cosmetic := false;
Canvas.Rectangle(ARect);
end;
finally
Canvas.Pen.Mode := m;
Canvas.Pen.Cosmetic := c;
end;
end;
function TCustomOnOffSwitch.GetBorderWidth: Integer;
begin
case FBorderStyle of
bsNone, bsThin, bsThin3D, bsNoneRounded, bsThinRounded:
Result := 1;
bsThick, bsThick3D, bsThickRounded:
Result := 2;
end;
end;
function TCustomOnOffSwitch.GetCaptions(AChecked: Boolean): string;
begin
Result := FCaptions[AChecked];
end;
function TCustomOnOffSwitch.GetColors(AIndex: Integer): TColor;
begin
Result := FColors[AIndex];
end;
class function TCustomOnOffSwitch.GetControlClassDefaultSize: TSize;
begin
Result.CX := 60;
Result.CY := 30;
end;
function TCustomOnOffSwitch.GetImageIndex(AChecked: Boolean): TImageIndex;
begin
Result := FImageIndex[AChecked];
end;
function TCustomOnOffSwitch.GetOrientation: TSwitchOrientation;
begin
if Width > Height then Result := soHorizontal else Result := soVertical;
end;
function TCustomOnOffSwitch.GetPicture(AChecked: Boolean): TPicture;
begin
Result := FPicture[AChecked];
end;
function TCustomOnOffSwitch.HasPicture: Boolean;
begin
Result := (FPicture[false].Width <> 0) or (FPicture[true].Width <> 0);
end;
procedure TCustomOnOffSwitch.KeyDown(var Key: Word; Shift: TShiftState);
begin
inherited;
if CanChange and ((Key = VK_SPACE) or (Key = VK_RETURN)) then
Checked := not Checked;
end;
procedure TCustomOnOffSwitch.MouseDown(Button: TMouseButton; Shift: TShiftState;
X, Y: Integer);
begin
inherited;
SetFocus;
if CanChange and (Button = mbLeft) and MouseOnButton(X, Y) then
begin
if (FSwitchMode in [smSlide, smSlideDblClick]) then FDragging := true;
FMousePt := Point(X, Y);
FDblClickTimer.Enabled := FSwitchMode in [smDblClick, smSlideDblClick];
end;
end;
procedure TCustomOnOffSwitch.MouseMove(Shift: TShiftState; X,Y: Integer);
var
delta: Integer;
begin
inherited;
if FDragging then begin
case Orientation of
soHorizontal : delta := X - FMousePt.X;
soVertical : delta := Y - FMousePt.Y;
end;
FMousePt := Point(X, Y);
FButtonPos := EnsureRange(FButtonPos + delta, 0, FMaxDistance);
Invalidate;
end;
end;
function TCustomOnOffSwitch.MouseOnButton(X, Y: Integer): Boolean;
var
R: TRect;
begin
R := CalcButtonRect;
Result := PtInRect(R, Point(X, Y));
end;
procedure TCustomOnOffSwitch.MouseUp(Button: TMouseButton; Shift:TShiftState; X,Y:Integer);
var
oldChecked: Boolean;
begin
inherited;
if (Button = mbLeft) and CanChange then begin
oldChecked := FChecked;
if FDragging then begin
FDragging := false;
FChecked := DraggedToON;
UpdateButtonPos;
end else
begin
if (FSwitchMode = smClick) or (HasPicture and (FSwitchMode = smSlide)) then
SetChecked(not FChecked);
if FChecked <> oldChecked then
DoChange
else
if (FSwitchMode in [smDblClick, smSlideDblClick]) then
FTogglePending := true;
end;
if FChecked <> oldChecked then
Invalidate;
end;
end;
procedure TCustomOnOffSwitch.Notification(AComponent: TComponent;
Operation: TOperation);
begin
inherited Notification(AComponent, Operation);
if (Operation = opRemove) and (AComponent = Images) then
SetImages(nil);
end;
procedure TCustomOnOffSwitch.Paint;
var
R: TRect;
margin: Integer;
diam: Integer;
newChecked: Boolean;
begin
R := Rect(0, 0, Width, Height);
if HasPicture then
begin
Canvas.StretchDraw(R, FPicture[FChecked].Graphic);
exit;
end;
if Enabled then begin
if FFlippedColors then begin
if (saeBlendColors in FAnimationEffects) then
Canvas.Brush.Color := BlendColors(FButtonPos/FMaxDistance, ColorOFF, ColorON)
else
if FChecked then
Canvas.Brush.Color := ColorON
else
Canvas.Brush.Color := ColorOFF;
end else
Canvas.Brush.Color := Color;
Canvas.Pen.Color := BorderColor;
end else begin
Canvas.Brush.Color := clInactiveBorder;
Canvas.Pen.Color := clGrayText;
end;
Canvas.Brush.Style := bsSolid;
Canvas.Pen.Style := psSolid;
Canvas.Pen.Width := GetBorderWidth;
case FBorderStyle of
bsNone:
begin
Canvas.Pen.Style := psClear;
Canvas.Rectangle(R);
end;
bsThin:
Canvas.Rectangle(R);
bsThick:
Canvas.Rectangle(1, 1, Width, Height);
bsThin3D, bsThick3D:
begin
Canvas.Pen.Width := 1;
Canvas.Pen.Color := clBtnShadow;
Canvas.Line(R.Left, R.Top, R.Right, R.Top);
Canvas.Line(R.Left, R.Top, R.Left, R.Bottom);
if FBorderStyle = bsThick3D then begin
Canvas.Line(R.Left+1, R.Top+1, R.Right-1, R.Top+1);
Canvas.Line(R.Left+1, R.Top+1, R.Left+1, R.Bottom-1);
end;
Canvas.Pen.Color := clBtnHighlight;
Canvas.Line(R.Left, R.Bottom-1, R.Right, R.Bottom-1);
Canvas.Line(R.Right-1, R.Top, R.Right-1, R.Bottom);
if FBorderStyle = bsThin3D then
begin
InflateRect(R, -1, -1);
Canvas.FillRect(R)
end else
begin
InflateRect(R, -2, -2);
Canvas.Line(R.Left-1, R.Bottom, R.Right+1, R.Bottom);
Canvas.Line(R.Right, R.Top-1, R.Right, R.Bottom);
Canvas.FillRect(R);
end;
end;
bsNoneRounded, bsThinRounded, bsThickRounded:
begin
case FBorderStyle of
bsNoneRounded: Canvas.Pen.Style := psClear;
bsThickRounded: InflateRect(R, -1, -1);
end;
if Orientation = soHorizontal then
diam := R.Height
else
diam := R.Width;
Canvas.RoundRect(R, diam, diam);
end;
end;
margin := CalcMargin;
case Orientation of
soHorizontal:
FButtonRect := Rect(0, margin, FButtonSize, Height - margin);
soVertical:
FButtonRect := Rect(margin, 0, Width - margin, FButtonSize);
end;
if FShowCaption then begin
if FDragging then
newChecked := DraggedToOn //DraggingToON(FMovedDistance)
else
newchecked := FChecked;
case Orientation of
soHorizontal:
if FChecked xor FInverse then begin
// Drag begins from button at right
if FDragging and not (FInverse xor newChecked) then
DrawCaption(Rect(margin + FButtonSize, margin, Width, Height - margin), FInverse)
else
DrawCaption(Rect(0, margin, Width - margin - FButtonSize, Height - margin), not FInverse);
end else begin
// Drag begins from button at left
if FDragging and (FInverse xor newChecked) then
DrawCaption(Rect(0, margin, Width - margin - FButtonSize, Height - margin), not FInverse)
else
DrawCaption(Rect(margin + FButtonSize, margin, Width, Height - margin), FInverse);
end;
soVertical:
if FChecked xor FInverse then begin
// Drag begins from button at bottom
if FDragging and not (FInverse xor newChecked) then
DrawCaption(Rect(margin, margin + FButtonSize, Width-margin, Height), FInverse)
else
DrawCaption(Rect(margin, 0, Width - margin, Height - margin - FButtonSize), not FInverse);
end else begin
// Drag begins from button at top
if FDragging and (FInverse xor newChecked) then
DrawCaption(Rect(margin, 0, Width - margin, Height - margin - FButtonSize), not FInverse)
else
DrawCaption(Rect(margin, margin + FButtonsize, Width - margin, Height), FInverse);
end;
end;
end;
R := CalcButtonRect;
DrawButton(R);
if Focused and FShowFocusRect then begin
InflateRect(R, 2, 2);
DrawFocusRect(R);
end;
end;
procedure TCustomOnOffSwitch.SetAnimated(AValue: Boolean);
begin
if AValue = FAnimated then exit;
FAnimated := AValue;
if FAnimated then
begin
if (FAnimationTimer = nil) then
begin
FAnimationTimer := TTimer.Create(self);
FAnimationTimer.Interval := 10;
FAnimationTimer.Enabled := false;
FAnimationTimer.OnTimer := @DoAnimation;
end
end else
FreeAndNil(FAnimationTimer);
end;
procedure TCustomOnOffSwitch.SetBorderStyle(AValue: TSwitchBorderStyle);
begin
if AValue = FBorderStyle then exit;
FBorderStyle := AValue;
UpdateShape;
Invalidate;
end;
procedure TCustomOnOffSwitch.SetBounds(ALeft, ATop, AWidth, AHeight: Integer);
begin
inherited;
UpdateShape;
UpdateMaxDistance;
end;
procedure TCustomOnOffSwitch.SetButtonSize(AValue: Integer);
begin
if (AValue = FButtonSize) and (AValue > 0) then
exit;
FButtonSize := AValue;
UpdateMaxDistance;
Invalidate;
end;
procedure TCustomOnOffSwitch.SetCaptions(AChecked: Boolean; AValue: String);
begin
if AValue = FCaptions[AChecked] then exit;
FCaptions[AChecked] := AValue;
Invalidate;
end;
procedure TCustomOnOffSwitch.SetChecked(AValue: Boolean);
begin
if AValue = FChecked then exit;
if FDblClickTimer.Enabled then exit;
if FAnimated then
begin
case Orientation of
soHorizontal: FMaxDistance := Width - FButtonSize - 2*CalcMargin;
soVertical: FMaxDistance := Height - FButtonSize - 2*CalcMargin;
end;
FAnimationTimer.Enabled := true;
FCheckedAfterAnimation := AValue;
end else
begin
FChecked := AValue;
UpdateButtonPos;
DoChange;
Invalidate;
end;
end;
procedure TCustomOnOffSwitch.SetColors(AIndex: Integer; AValue: TColor);
begin
if AValue = FColors[AIndex] then exit;
FColors[AIndex] := AValue;
Invalidate;
end;
procedure TCustomOnOffSwitch.SetFlippedColors(AValue: Boolean);
begin
if AValue = FFlippedColors then exit;
FFlippedColors := AValue;
Invalidate;
end;
procedure TCustomOnOffSwitch.SetImageIndex(AChecked: Boolean; AValue: TImageIndex);
begin
if FImageIndex[AChecked] = AValue then Exit;
FImageIndex[AChecked] := AValue;
Invalidate;
end;
procedure TCustomOnOffSwitch.SetImages(const AValue: TCustomImageList);
begin
if FImages = AValue then Exit;
if FImages <> nil then
begin
FImages.UnRegisterChanges(FImageListChangeLink);
FImages.RemoveFreeNotification(Self);
end;
FImages := AValue;
if FImages <> nil then
begin
FImages.FreeNotification(Self);
FImages.RegisterChanges(FImageListChangeLink);
end;
DoImageListChange(Self);
end;
procedure TCustomOnOffSwitch.SetInverse(AValue: boolean);
begin
if AValue = FInverse then exit;
FInverse := AValue;
UpdateButtonPos;
Invalidate;
end;
procedure TCustomOnOffSwitch.SetPicture(AChecked: Boolean; AValue: TPicture);
begin
if AValue = FPicture[AChecked] then
exit;
if AValue = nil then
FPicture[AChecked].Clear
else
FPicture[AChecked].Assign(AValue);
Invalidate;
end;
procedure TCustomOnOffSwitch.SetShowButtonBorder(AValue: Boolean);
begin
if AValue = FShowButtonBorder then exit;
FShowButtonBorder := AValue;
DoChange;
Invalidate;
end;
procedure TCustomOnOffSwitch.SetShowCaption(AValue: Boolean);
begin
if AValue = FShowCaption then exit;
FShowCaption := AValue;
DoChange;
Invalidate;
end;
procedure TCustomOnOffSwitch.SetShowFocusRect(AValue: Boolean);
begin
if AValue = FShowFocusRect then exit;
FShowFocusRect := AValue;
DoChange;
Invalidate;
end;
procedure TCustomOnOffSwitch.UpdateButtonPos;
begin
if FChecked xor FInverse then
FButtonPos := FMaxDistance
else
FButtonPos := 0;
end;
procedure TCustomOnOffSwitch.UpdateMaxDistance;
begin
case Orientation of
soHorizontal: FMaxDistance := Width - FButtonSize - 2*CalcMargin;
soVertical: FMaxDistance := Height - FButtonSize - 2*CalcMargin;
end;
UpdateButtonPos;
end;
procedure TCustomOnOffSwitch.UpdateShape;
var
R: TRect;
bmp: TBitmap;
diam: Integer;
begin
R := Rect(0, 0, Width, Height);
bmp := TBitmap.Create;
try
bmp.Monochrome := true;
bmp.SetSize(R.Width, R.Height);
if FBorderStyle in [bsNoneRounded, bsThinRounded, bsThickRounded] then
begin
if Orientation = soHorizontal then
diam := R.Height
else
diam := R.Width;
bmp.Canvas.Brush.Color := clBlack;
bmp.Canvas.FillRect(R);
bmp.Canvas.Brush.Color := clWhite;
// bmp.Canvas.Pen.Style := psClear;
bmp.Canvas.Pen.Color := clWhite;
bmp.Canvas.Pen.Width := GetBorderWidth;
if FBorderStyle = bsThickRounded then
InflateRect(R, -1, -1);
bmp.Canvas.RoundRect(R.Left, R.Top, R.Right, R.Bottom, diam, diam);
end else
begin
bmp.Canvas.Brush.Color := clWhite;
bmp.Canvas.FillRect(R);
end;
SetShape(bmp);
finally
bmp.Free;
end;
end;
end.