lazarus/lcl/include/speedbutton.inc

1338 lines
38 KiB
PHP

{%MainUnit ../buttons.pp}
{******************************************************************************
TCustomSpeedButton
******************************************************************************
*****************************************************************************
This file is part of the Lazarus Component Library (LCL)
See the file COPYING.modifiedLGPL.txt, included in this distribution,
for details about the license.
*****************************************************************************
}
{$IFOPT C-}
// Uncomment for local trace
// {$C+}
// {$DEFINE ASSERT_IS_ON}
{$ENDIF}
const
UpState: array[Boolean] of TButtonState =
(
{False} bsUp, // mouse in control = false
{True } bsHot // mouse in contorl = true
);
{------------------------------------------------------------------------------
Method: TCustomSpeedButton.Create
Params: none
Returns: Nothing
Constructor for the class.
------------------------------------------------------------------------------}
constructor TCustomSpeedButton.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FGlyph := TButtonGlyph.Create;
FGlyph.IsDesigning := csDesigning in ComponentState;
FGlyph.ShowMode := gsmAlways;
FGlyph.SetTransparentMode(gtmTransparent);
FGlyph.OnChange := @GlyphChanged;
FImageChangeLink := TChangeLink.Create;
FImageChangeLink.OnChange := @ImageListChange;
with GetControlClassDefaultSize do
SetInitialBounds(0, 0, CX, CY);
ControlStyle := ControlStyle + [csCaptureMouse]-[csSetCaption, csClickEvents, csOpaque];
FLayout := blGlyphLeft;
FAllowAllUp := False;
FMouseInControl := False;
FDragging := False;
FShowAccelChar := True;
FSpacing := 4;
FMargin := -1;
Color := clBtnFace;
FShowCaption := true;
FAlignment := taCenter;
end;
{------------------------------------------------------------------------------
Method: TCustomSpeedButton.Destroy
Params: None
Returns: Nothing
Destructor for the class.
------------------------------------------------------------------------------}
destructor TCustomSpeedButton.Destroy;
begin
FreeAndNil(FGlyph);
FreeAndNil(FImageChangeLink);
inherited Destroy;
end;
{------------------------------------------------------------------------------
Method: TCustomSpeedButton.FindDownButton: TCustomSpeedButton;
Searches the speed button with Down=true and the same GroupIndex.
------------------------------------------------------------------------------}
function TCustomSpeedButton.FindDownButton: TCustomSpeedButton;
function FindDown(AWinControl: TWinControl): TCustomSpeedButton;
var
i: Integer;
Child: TControl;
Button: TCustomSpeedButton;
begin
if AWinControl = nil then Exit(nil);
for i := 0 to AWinControl.ControlCount-1 do
begin
Child := AWinControl.Controls[i];
if Child is TCustomSpeedButton then
begin
Button := TCustomSpeedButton(Child);
if (Button.GroupIndex=GroupIndex) and (Button.Down) then
Exit(Button);
end;
if Child is TWinControl then
begin
Result := FindDown(TWinControl(Child));
if Result <> nil then Exit;
end;
end;
Result := nil;
end;
begin
if Down or (GroupIndex=0) then exit(Self);
Result := FindDown(GetFirstParentForm(Self));
end;
procedure TCustomSpeedButton.Click;
begin
inherited Click;
end;
procedure TCustomSpeedButton.SetAlignment(Value: TAlignment);
begin
if FAlignment <> Value then
begin
FAlignment := Value;
Invalidate;
end;
end;
{------------------------------------------------------------------------------
Method: TCustomSpeedButton.SetAllowAllUp
Params: Value:
Returns: nothing
------------------------------------------------------------------------------}
procedure TCustomSpeedButton.SetAllowAllUp(Value : Boolean);
begin
if FAllowAllUp <> Value then
begin
FAllowAllUp := Value;
UpdateExclusive;
end;
end;
{------------------------------------------------------------------------------
Method: TCustomSpeedButton.SetDown
Params: Value:
Returns: nothing
------------------------------------------------------------------------------}
procedure TCustomSpeedButton.SetDown(Value : Boolean);
var
OldState: TButtonState;
OldDown: Boolean;
begin
//since Down needs GroupIndex, then we need to wait that all properties
//loaded before we continue
if (csLoading in ComponentState) then
begin
FDownLoaded := Value;
exit;
end else
begin
if FGroupIndex = 0 then
Value:= false;
if FDown <> Value then
begin
if FDown and not FAllowAllUp then
Exit;
OldDown := FDown;
FDown := Value;
OldState := FState;
if FDown then
FState := bsExclusive
else
FState := UpState[FMouseInControl];
if (OldDown <> FDown) or (OldState <> FState) then
Invalidate;
if Value then
UpdateExclusive;
end;
end;
end;
{------------------------------------------------------------------------------
Method: TCustomSpeedButton.SetFlat
Params: Value:
Returns: nothing
------------------------------------------------------------------------------}
procedure TCustomSpeedButton.SetFlat(const Value: Boolean);
begin
if FFlat <> Value then
begin
FFlat := Value;
Invalidate;
end;
end;
{------------------------------------------------------------------------------
Method: TCustomSpeedButton.SetGlyph
Params: Value:
Returns: nothing
------------------------------------------------------------------------------}
procedure TCustomSpeedButton.SetGlyph(Value : TBitmap);
begin
FGlyph.Glyph := Value;
Invalidate;
end;
{------------------------------------------------------------------------------
Method: TCustomSpeedButton.SetGroupIndex
Params: Value:
Returns: nothing
------------------------------------------------------------------------------}
procedure TCustomSpeedButton.SetGroupIndex(const Value : Integer);
begin
if FGroupIndex <> Value then
begin
FGroupIndex := Value;
UpdateExclusive;
end;
end;
procedure TCustomSpeedButton.SetImageIndex(AState: TButtonState;
const AImageIndex: TImageIndex);
begin
FGlyph.SetExternalImageIndex(AState, AImageIndex);
end;
procedure TCustomSpeedButton.SetImages(const aImages: TCustomImageList);
begin
if FGlyph.ExternalImages <> nil then
begin
FGlyph.ExternalImages.UnRegisterChanges(FImageChangeLink);
FGlyph.ExternalImages.RemoveFreeNotification(Self);
end;
FGlyph.ExternalImages := aImages;
if FGlyph.ExternalImages <> nil then
begin
FGlyph.ExternalImages.FreeNotification(Self);
FGlyph.ExternalImages.RegisterChanges(FImageChangeLink);
end;
InvalidatePreferredSize;
AdjustSize;
end;
procedure TCustomSpeedButton.SetImageWidth(const aImageWidth: Integer);
begin
FGlyph.ExternalImageWidth := aImageWidth;
InvalidatePreferredSize;
AdjustSize;
end;
{------------------------------------------------------------------------------
Method: TCustomSpeedButton.SetMargin
Params: Value:
Returns: nothing
------------------------------------------------------------------------------}
procedure TCustomSpeedButton.SetMargin(const Value: integer);
begin
if FMargin <> Value then
begin
FMargin := Value;
InvalidatePreferredSize;
AdjustSize;
Invalidate;
end;
end;
{------------------------------------------------------------------------------
Method: TCustomSpeedButton.SetNumGlyphs
Params: Value : Integer = Number of glyphs in the file/resource
Returns: nothing
------------------------------------------------------------------------------}
procedure TCustomSpeedButton.SetNumGlyphs(Value : integer);
begin
if Value < Low(TNumGlyphs) then Value := Low(TNumGlyphs);
if Value > High(TNumGlyphs) then Value := High(TNumGlyphs);
if Value <> TButtonGlyph(fGlyph).NumGlyphs then
begin
TButtonGlyph(fGlyph).NumGlyphs := TNumGlyphs(Value);
Invalidate;
end;
end;
{------------------------------------------------------------------------------
Method: TCustomSpeedButton.SetSpacing
Params: Value:
Returns: nothing
------------------------------------------------------------------------------}
procedure TCustomSpeedButton.SetSpacing(const Value: integer);
begin
if FSpacing <> Value then
begin
FSpacing := Value;
InvalidatePreferredSize;
AdjustSize;
Invalidate;
end;
end;
procedure TCustomSpeedButton.SetShowAccelChar(Value: boolean);
begin
If FShowAccelChar <> Value then
begin
FShowAccelChar := Value;
Invalidate;
end;
end;
{------------------------------------------------------------------------------
procedure TCustomSpeedButton.RealSetText(const Value: TCaption);
------------------------------------------------------------------------------}
procedure TCustomSpeedButton.RealSetText(const Value: TCaption);
begin
if Caption = Value then Exit;
if (Parent<>nil) and (Parent.HandleAllocated) and (not (csLoading in ComponentState)) then
begin
InvalidatePreferredSize;
inherited RealSetText(Value);
AdjustSize;
end else
inherited RealSetText(Value);
Invalidate;
end;
{------------------------------------------------------------------------------
procedure TCustomSpeedButton.UpdateState(InvalidateOnChange: boolean);
------------------------------------------------------------------------------}
procedure TCustomSpeedButton.UpdateState(InvalidateOnChange: boolean);
var
OldState: TButtonState;
begin
OldState := FState;
if not IsEnabled then
begin
FState := bsDisabled;
FDragging := False;
end else
begin
if FState = bsDisabled then
begin
if FDown and (GroupIndex <> 0) then
FState := bsExclusive
else
FState := UpState[FMouseInControl];
end
else
if (FState in [bsHot,bsDown]) and not (FMouseInControl or FDragging or FDown) then
begin
// return to normal
FState := bsUp;
end
else
if (FState = bsUp) and FMouseInControl then
FState := bsHot;
end;
if (FState <> OldState) and (FState in [bsUp,bsDown]) and (OldState in [bsUp,bsDown]) then
if (Action is TCustomAction) then
TCustomAction(Action).Checked := FState = bsDown;
//if InvalidateOnChange then DebugLn(['TCustomSpeedButton.UpdateState ',DbgSName(Self),' InvalidateOnChange=',InvalidateOnChange,' StateChange=',FState<>OldState]);
if InvalidateOnChange and
(
(FState <> OldState) or
not ThemedElementDetailsEqual(FLastDrawDetails, GetDrawDetails)
)
then
Invalidate;
end;
{------------------------------------------------------------------------------
function TCustomSpeedButton.GetDrawDetails: TThemedElementDetails;
------------------------------------------------------------------------------}
function TCustomSpeedButton.GetDrawDetails: TThemedElementDetails;
function ButtonPart: TThemedButton;
begin
// tbPushButtonNormal, tbPushButtonHot, tbPushButtonPressed,
// tbPushButtonDisabled, tbPushButtonDefaulted
// no check states available
Result := tbPushButtonNormal;
if not IsEnabled then
Result := tbPushButtonDisabled
else
if FState in [bsDown, bsExclusive] then
Result := tbPushButtonPressed
else
if FState = bsHot then
Result := tbPushButtonHot
else
Result := tbPushButtonNormal;
end;
function ToolButtonPart: TThemedToolBar;
begin
// ttbButtonNormal, ttbButtonHot, ttbButtonPressed, ttbButtonDisabled
// ttbButtonChecked, ttbButtonCheckedHot
if not IsEnabled then
Result := ttbButtonDisabled
else
begin
if Down then
begin // checked states
if FMouseInControl then
Result := ttbButtonCheckedHot
else
Result := ttbButtonChecked;
end
else
begin
if FState in [bsDown, bsExclusive] then
Result := ttbButtonPressed else
if FState = bsHot then
Result := ttbButtonHot
else
Result := ttbButtonNormal;
end;
end;
end;
begin
if Flat then
Result := ThemeServices.GetElementDetails(ToolButtonPart)
else
Result := ThemeServices.GetElementDetails(ButtonPart)
end;
procedure TCustomSpeedButton.ActionChange(Sender: TObject; CheckDefaults: Boolean);
var
NewAct: TCustomAction;
Imgs: TCustomImageList;
ImgRes: TScaledImageListResolution;
begin
inherited ActionChange(Sender,CheckDefaults);
if Sender is TCustomAction then
begin
NewAct := TCustomAction(Sender);
if (not CheckDefaults) or (GroupIndex = 0) then
GroupIndex := NewAct.GroupIndex;
if (NewAct.ActionList = nil) or (NewAct.ImageIndex < 0) then Exit;
Imgs := NewAct.ActionList.Images;
if (Imgs = nil) or (NewAct.ImageIndex >= Imgs.Count) then Exit;
Images := Imgs;
ImageIndex := NewAct.ImageIndex;
//ImgRes := Imgs.ResolutionForPPI[ImageWidth,Font.PixelsPerInch,GetCanvasScaleFactor];
//ImgRes.GetBitmap(NewAct.ImageIndex, Glyph);
end;
end;
function TCustomSpeedButton.ButtonGlyph: TButtonGlyph;
begin
Result := FGlyph;
end;
function TCustomSpeedButton.GetActionLinkClass: TControlActionLinkClass;
begin
Result := TSpeedButtonActionLink;
end;
class function TCustomSpeedButton.GetControlClassDefaultSize: TSize;
begin
Result.CX := 23;
Result.CY := 22;
end;
{------------------------------------------------------------------------------
Method: TCustomSpeedButton.UpdateExclusive
Params: none
Returns: nothing
------------------------------------------------------------------------------}
procedure TCustomSpeedButton.UpdateExclusive;
var
msg : TLMessage;
begin
if (FGroupIndex <> 0) and (Parent <> nil) and (not (csLoading in ComponentState)) then
begin
Msg.Msg := CM_ButtonPressed;
Msg.WParam := FGroupIndex;
Msg.LParam := PtrInt(Self);
Msg.Result := 0;
Parent.Broadcast(Msg);
end;
end;
{------------------------------------------------------------------------------
Function: TCustomSpeedButton.GetGlyph
Params: none
Returns: The bitmap
------------------------------------------------------------------------------}
function TCustomSpeedButton.GetGlyph : TBitmap;
begin
Result := FGlyph.Glyph;
end;
function TCustomSpeedButton.IsGlyphStored: Boolean;
var
act: TCustomAction;
begin
Result := true;
if Action <> nil then
begin
act := TCustomAction(Action);
if (act.ActionList <> nil) and (act.ActionList.Images <> nil) and
(act.ImageIndex >= 0) and (act.ImageIndex < act.ActionList.Images.Count) then
Result := false;
end;
end;
procedure TCustomSpeedButton.SetShowCaption(const AValue: boolean);
begin
if FShowCaption=AValue then exit;
FShowCaption:=AValue;
Invalidate;
end;
{------------------------------------------------------------------------------
Method: TCustomSpeedButton.GetNumGlyphs
Params: none
Returns: The number stored in TButtonGlyph(FGlyph).NumGlyphs
------------------------------------------------------------------------------}
function TCustomSpeedButton.GetNumGlyphs : Integer;
Begin
Result := TButtonGlyph(fGlyph).NumGlyphs;
end;
{------------------------------------------------------------------------------
Method: TCustomSpeedButton.GlyphChanged
Params: Sender - The glyph that changed
Returns: zippo
------------------------------------------------------------------------------}
procedure TCustomSpeedButton.GlyphChanged(Sender : TObject);
Begin
//redraw the button;
Invalidate;
end;
procedure TCustomSpeedButton.ImageListChange(Sender: TObject);
begin
if Sender = Images then Invalidate;
end;
function TCustomSpeedButton.DialogChar(var Message: TLMKey): boolean;
begin
Result := False;
// Sometimes LM_CHAR is received instead of LM_SYSCHAR, maybe intentionally
// (LCL handles it) or maybe sent by mistake. In either case exit.
if (Message.Msg <> LM_SYSCHAR) or not FShowAccelChar then Exit;
if Enabled and IsAccel(Message.CharCode, Caption) then
begin
Result := True;
if GroupIndex <> 0 then
SetDown(not FDown);
Click;
end else
Result := inherited DialogChar(Message);
end;
procedure TCustomSpeedButton.CalculatePreferredSize(var PreferredWidth,
PreferredHeight: integer; WithThemeSpace: Boolean);
var
r: TRect;
begin
r:=Rect(0,0,0,0);
MeasureDraw(false,r,PreferredWidth,PreferredHeight);
if WithThemeSpace then
begin
inc(PreferredWidth, 6); // like TButton
inc(PreferredHeight, 6);
end;
end;
procedure TCustomSpeedButton.MeasureDraw(Draw: boolean;
PaintRect: TRect; out PreferredWidth, PreferredHeight: integer);
const
cAlignment: array[TAlignment] of Longint = (DT_LEFT, DT_RIGHT, DT_CENTER);
var
GlyphWidth, GlyphHeight: Integer;
OffsetGlyph, OffsetCap: TPoint;
ClientSize, TotalSize, TextSize, GlyphSize: TSize;
M, S : integer;
SIndex : Longint;
TMP : String;
TextFlags: Integer;
DrawDetails: TThemedElementDetails;
FixedWidth: Boolean;
FixedHeight: Boolean;
TextRect: TRect;
HasGlyph: Boolean;
HasText: Boolean;
CurLayout: TButtonLayout;
SysFont: TFont;
begin
if FGlyph = nil then exit;
DrawDetails := GetDrawDetails;
PreferredWidth:=0;
PreferredHeight:=0;
if Draw then begin
FLastDrawDetails:=DrawDetails;
PaintBackground(PaintRect);
FixedWidth:=true;
FixedHeight:=true;
end else begin
FixedWidth:=WidthIsAnchored;
FixedHeight:=HeightIsAnchored;
end;
ClientSize.cx:= PaintRect.Right - PaintRect.Left;
ClientSize.cy:= PaintRect.Bottom - PaintRect.Top;
//debugln(['TCustomSpeedButton.MeasureDraw Step1 ',DbgSName(Self),' PaintRect=',dbgs(PaintRect)]);
// compute size of glyph
GlyphSize := GetGlyphSize(Draw,PaintRect);
GlyphWidth := GlyphSize.CX;
GlyphHeight := GlyphSize.CY;
HasGlyph:=(GlyphWidth<>0) and (GlyphHeight<>0);
//debugln(['TCustomSpeedButton.MeasureDraw Step2 ',DbgSName(Self),' PaintRect=',dbgs(PaintRect),' GlyphSize=',GlyphWidth,'x',GlyphHeight]);
// compute size of text
CurLayout:=BidiAdjustButtonLayout(UseRightToLeftReading, Layout);
if ShowCaption and (Caption<>'') then begin
TextRect:=PaintRect;
// for wordbreak compute the maximum size for the text
if Margin>0 then
InflateRect(TextRect,-Margin,-Margin);
if HasGlyph then
begin
{
if (Spacing >= 0) then S := Spacing else S := 0;
case CurLayout of
blGlyphLeft: inc(TextRect.Left, GlyphWidth + S);
blGlyphRight: dec(TextRect.Right, GlyphWidth + S);
blGlyphTop: inc(TextRect.Top, GlyphHeight + S);
blGlyphBottom: dec(TextRect.Bottom, GlyphHeight + S);
end;
}
if (Spacing>=0) then
if CurLayout in [blGlyphLeft,blGlyphRight] then
dec(TextRect.Right,Spacing)
else
dec(TextRect.Bottom,Spacing);
if CurLayout in [blGlyphLeft,blGlyphRight] then
dec(TextRect.Right,GlyphWidth)
else
dec(TextRect.Bottom,GlyphHeight);
end;
if not FixedWidth then
begin
TextRect.Left:=0;
TextRect.Right:=High(TextRect.Right) div 2;
end;
if not FixedHeight then
begin
TextRect.Top:=0;
TextRect.Bottom:=High(TextRect.Bottom) div 2;
end;
TextSize := GetTextSize(Draw,TextRect);
end else begin
TextSize.cx:=0;
TextSize.cy:=0;
end;
HasText:=(TextSize.cx <> 0) or (TextSize.cy <> 0);
if Caption <> '' then
begin
TMP := Caption;
SIndex := DeleteAmpersands(TMP);
If SIndex > 0 then
If SIndex <= Length(TMP) then begin
FShortcut := Ord(TMP[SIndex]);
end;
end;
if HasGlyph and HasText then
S:= Spacing
else
S:= 0;
M:=Margin;
if not Draw then
begin
if M<0 then M:=2;
if S<0 then S:=M;
end;
// Calculate caption and glyph layout
if M < 0 then begin
// auto compute margin to center content
if S < 0 then begin
// use the same value for Spacing and Margin
TotalSize.cx:= TextSize.cx + GlyphWidth;
TotalSize.cy:= TextSize.cy + GlyphHeight;
if Layout in [blGlyphLeft, blGlyphRight] then
M:= (ClientSize.cx - TotalSize.cx) div 3
else
M:= (ClientSize.cy - TotalSize.cy) div 3;
S:= M;
end else begin
// fixed Spacing, center content
TotalSize.cx:= GlyphWidth + S + TextSize.cx;
TotalSize.cy:= GlyphHeight + S + TextSize.cy;
if Layout in [blGlyphLeft, blGlyphRight] then
M:= (ClientSize.cx - TotalSize.cx) div 2
else
M:= (ClientSize.cy - TotalSize.cy) div 2;
end;
end else begin
// fixed Margin
if S < 0 then begin
// use the rest for Spacing between Glyph and Caption
TotalSize.cx:= ClientSize.cx - (Margin + GlyphWidth);
TotalSize.cy:= ClientSize.cy - (Margin + GlyphHeight);
if Layout in [blGlyphLeft, blGlyphRight] then
S:= (TotalSize.cx - TextSize.cx) div 2
else
S:= (TotalSize.cy - TextSize.cy) div 2;
end;
end;
//debugln(['TCustomSpeedButton.MeasureDraw Step3 ',DbgSName(Self),' PaintRect=',dbgs(PaintRect),' GlyphSize=',GlyphWidth,'x',GlyphHeight,' TextSize=',TextSize.cx,'x',TextSize.cy,' S=',S,' M=',M]);
if Draw then
begin
if not HasGlyph then
S := 0;
case CurLayout of
blGlyphLeft : begin
OffsetGlyph.X:= M;
OffsetGlyph.Y:= (ClientSize.cy - GlyphHeight) div 2;
if (Margin >= 0) and (Spacing >= 0) then
OffsetCap.X := OffsetGlyph.X + GlyphWidth + S
else
begin
if Spacing >= 0 then
OffsetCap.X := OffsetGlyph.X + GlyphWidth + S
else
OffsetCap.X := (OffsetGlyph.X + GlyphWidth + ClientSize.CX - TextSize.CX) div 2;
//OffsetCap.X := (OffsetGlyph.X + ClientSize.CX - TextSize.CX) div 2;
end;
OffsetCap.Y:= (ClientSize.cy - TextSize.cy) div 2;
end;
blGlyphRight : begin
OffsetGlyph.X:= ClientSize.cx - M - GlyphWidth;
OffsetGlyph.Y:= (ClientSize.cy - GlyphHeight) div 2;
if (Margin >= 0) and (Spacing >= 0) then
OffsetCap.X := Margin
else
begin
if Spacing >= 0 then
OffsetCap.X := OffsetGlyph.X - S - TextSize.CX
else
OffsetCap.X := (OffsetGlyph.X - TextSize.CX) div 2;
end;
OffsetCap.Y:= (ClientSize.cy - TextSize.cy) div 2;
end;
blGlyphTop : begin
OffsetGlyph.X:= (ClientSize.cx - GlyphWidth) div 2;
OffsetGlyph.Y:= M;
if Margin >= 0 then
OffsetCap.X := 0
else
OffsetCap.X := (ClientSize.CX - TextSize.CX) div 2;
OffsetCap.Y:= OffsetGlyph.Y + GlyphHeight + S;
end;
blGlyphBottom : begin
OffsetGlyph.X:= (ClientSize.cx - GlyphWidth) div 2;
OffsetGlyph.Y:= ClientSize.cy - M - GlyphHeight;
if Margin >= 0 then
OffsetCap.X := 0
else
OffsetCap.X := (ClientSize.CX - TextSize.CX) div 2;
OffsetCap.Y:= OffsetGlyph.Y - S - TextSize.cy;
end;
end;
DrawGlyph(Canvas, PaintRect, OffsetGlyph, FState, Transparent, 0);
if FShowCaption and (Caption <> '') then
begin
TextRect.Left := PaintRect.Left + OffsetCap.X;
TextRect.Top := PaintRect.Top + OffsetCap.Y;
TextRect.Right := TextRect.Left + TextSize.CX;
TextRect.Bottom := TextRect.Top + TextSize.CY;
case CurLayout of
blGlyphLeft:
if (Margin >= 0) and (Spacing >= 0) then
begin
TextRect.Right := PaintRect.Right;
if Alignment = taRightJustify then dec(TextRect.Right, Margin);
end;
blGlyphRight:
if (Margin >= 0) and (Spacing >= 0) then
TextRect.Right := PaintRect.Left + OffsetGlyph.X - Spacing;
blGlyphTop,
blGlyphBottom:
if Margin >= 0 then
begin
TextRect.Right := PaintRect.Right;
case Alignment of
taLeftJustify: inc(TextRect.Left, Margin);
taRightJustify: dec(TextRect.Right, Margin);
end;
end;
end;
TextFlags := DT_TOP or cAlignment[BidiFlipAlignment(FAlignment, UseRightToLeftAlignment)]; // or DT_NOCLIP;
if UseRightToLeftReading then
TextFlags := TextFlags or DT_RTLREADING;
if Draw then
begin
SysFont := Screen.SystemFont;
if (SysFont.Color=Font.Color)
and ((SysFont.Name=Font.Name) or IsFontNameDefault(Font.Name))
and (SysFont.Pitch=Font.Pitch)
and (SysFont.Style=Font.Style) then
ThemeServices.DrawText(Canvas, DrawDetails, Caption, TextRect, TextFlags, 0)
else
begin
Canvas.Brush.Style := bsClear;
DrawText(Canvas.Handle, PChar(Caption), Length(Caption), TextRect, TextFlags);
end;
end;
end;
end else begin
// measuring, not drawing
case CurLayout of
blGlyphLeft, blGlyphRight :
begin
PreferredWidth:=2*M+S+GlyphWidth+TextSize.cx;
PreferredHeight:=2*M+Max(GlyphHeight,TextSize.cy);
end;
blGlyphTop, blGlyphBottom :
begin
PreferredWidth:=2*M+Max(GlyphWidth,TextSize.cx);
PreferredHeight:=2*M+S+GlyphHeight+TextSize.cy;
end;
end;
end;
end;
{------------------------------------------------------------------------------
Method: TCustomSpeedButton.Paint
Params: none
Returns: nothing
------------------------------------------------------------------------------}
procedure TCustomSpeedButton.Paint;
var
PaintRect: TRect;
PreferredWidth: integer;
PreferredHeight: integer;
begin
UpdateState(false);
if FGlyph = nil then exit;
PaintRect:=ClientRect;
MeasureDraw(true,PaintRect,PreferredWidth,PreferredHeight);
inherited Paint;
end;
procedure TCustomSpeedButton.PaintBackground(var PaintRect: TRect);
begin
if not Transparent and ThemeServices.HasTransparentParts(FLastDrawDetails) then
begin
Canvas.Brush.Color := Color;
Canvas.FillRect(PaintRect);
end;
ThemeServices.DrawElement(Canvas.Handle, FLastDrawDetails, PaintRect);
PaintRect := ThemeServices.ContentRect(Canvas.Handle, FLastDrawDetails, PaintRect);
end;
{------------------------------------------------------------------------------
Method: TCustomSpeedButton.MouseDown
Params: Button:
Shift:
X, Y:
Returns: nothing
------------------------------------------------------------------------------}
procedure TCustomSpeedButton.MouseDown(Button: TMouseButton; Shift: TShiftState;
X, Y: Integer);
begin
inherited MouseDown(Button, Shift, X, Y);
if csDesigning in ComponentState then exit;
if (Button = mbLeft) and IsEnabled then
begin
if not FDown then
begin
FState := bsDown;
if (Action is TCustomAction) then
TCustomAction(Action).Checked := False;
Invalidate;
end;
FDragging := True;
end;
end;
{------------------------------------------------------------------------------
Method: TCustomSpeedButton.MouseMove
Params: Shift:
X, Y:
Returns: nothing
------------------------------------------------------------------------------}
procedure TCustomSpeedButton.MouseMove(Shift: TShiftState; X, Y: Integer);
var
NewState: TButtonState;
begin
inherited MouseMove(Shift, X, Y);
if csDesigning in ComponentState then exit;
if FDragging then
begin
//DebugLn('Trace:FDragging is true');
if FDown then
NewState := bsExclusive
else
begin
if (X >= 0) and (X < Width) and (Y >= 0) and (Y < Height) then
NewState := bsDown
else
NewState := UpState[FMouseInControl];
end;
if NewState <> FState then
begin
//debugln(['TCustomSpeedButton.MouseMove ',DbgSName(Self),' fState=',ord(fstate),' NewState=',ord(NewState)]);
FState := NewState;
Invalidate;
end;
end;
end;
{------------------------------------------------------------------------------
Method: TCustomSpeedButton.MouseUp
Params: Button:
Shift:
X, Y:
Returns: nothing
------------------------------------------------------------------------------}
procedure TCustomSpeedButton.MouseUp(Button: TMouseButton; Shift: TShiftState;
X, Y: Integer);
begin
inherited MouseUp(Button, Shift, X, Y);
end;
procedure TCustomSpeedButton.Notification(AComponent: TComponent;
Operation: TOperation);
begin
inherited Notification(AComponent, Operation);
if (Operation = opRemove) and (FGlyph<>nil) and (AComponent = FGlyph.ExternalImages) then
Images := nil;
end;
procedure TCustomSpeedButton.WMLButtonDown(var Message: TLMLButtonDown);
begin
inherited;
// because csClickEvents is not set no csClicked is set in the inherited method
Include(FControlState, csClicked);
end;
procedure TCustomSpeedButton.WMLButtonDBLCLK(var Message: TLMLButtonDblClk);
begin
inherited;
// if in a group, raise dblclick event, otherwise translate to click event
if Down then
DblClick
else
Click;
end;
class procedure TCustomSpeedButton.WSRegisterClass;
begin
inherited WSRegisterClass;
RegisterCustomSpeedButton;
end;
{------------------------------------------------------------------------------
Method: TCustomSpeedButton.WMLButtonUp
Params: Message
Returns: Nothing
Mouse event handler
------------------------------------------------------------------------------}
procedure TCustomSpeedButton.WMLButtonUp(var Message: TLMLButtonUp);
var
OldState: TButtonState;
NeedClick: Boolean;
begin
//DebugLn('TCustomSpeedButton.WMLButtonUp A ',DbgSName(Self),' csCaptureMouse=',DbgS(csCaptureMouse in ControlStyle),' csClicked=',DbgS(csClicked in ControlState));
if (csCaptureMouse in ControlStyle) and (mbLeft in CaptureMouseButtons) then
begin
{$IFDEF VerboseMouseCapture}
DebugLn('TCustomSpeedButton.WMLButtonUp ',Name,':',ClassName);
{$ENDIF}
MouseCapture := False;
end;
NeedClick := False;
if not (csDesigning in ComponentState) and FDragging then
begin
OldState := FState;
FDragging := False;
if FGroupIndex = 0 then
begin
FState := UpState[FMouseInControl];
if OldState <> FState then
Invalidate;
end
else
if (Message.XPos >= 0) and (Message.XPos < Width) and (Message.YPos >= 0) and (Message.YPos < Height) then
begin
SetDown(not FDown);
NeedClick := True;
end;
end;
DoMouseUp(Message, mbLeft);
if csClicked in ControlState then
begin
Exclude(FControlState, csClicked);
//DebugLn('TCustomSpeedButton.WMLButtonUp B ',dbgs(ClientRect.Left),',',dbgs(ClientRect.Top),',',dbgs(ClientRect.Right),',',dbgs(ClientRect.Bottom),' ',dbgs(Message.Pos.X),',',dbgs(Message.Pos.Y));
if PtInRect(ClientRect, SmallPointToPoint(Message.Pos)) then
begin
//DebugLn('TCustomSpeedButton.WMLButtonUp C');
// Important: Calling Click can invoke modal dialogs, so call this as last
NeedClick := False;
Click;
end;
end;
if NeedClick then
Click;
//DebugLn('TCustomSpeedButton.WMLButtonUp END');
end;
{------------------------------------------------------------------------------
Method: TCustomSpeedButton.SetLayout
Params: Value: new layout value
Returns: nothing
------------------------------------------------------------------------------}
procedure TCustomSpeedButton.SetLayout(const Value : TButtonLayout);
begin
if Value <> FLayout then
begin
FLayout:= Value;
InvalidatePreferredSize;
AdjustSize;
end;
end;
{------------------------------------------------------------------------------
Method: TCustomSpeedButton.SetTransparent
Params: Value: new transparency value
Returns: nothing
------------------------------------------------------------------------------}
procedure TCustomSpeedButton.SetTransparent(const AValue: boolean);
const
MODE: array[Boolean] of TGlyphTransparencyMode = (gtmOpaque, gtmTransparent);
begin
if AValue = Transparent then Exit;
if AValue then
ControlStyle := ControlStyle - [csOpaque]
else
ControlStyle := ControlStyle + [csOpaque];
FGlyph.SetTransparentMode(MODE[AValue]);
Invalidate;
end;
{------------------------------------------------------------------------------
Method: TCustomSpeedButton.CMButtonPressed
Params: Message:
Returns: nothing
------------------------------------------------------------------------------}
procedure TCustomSpeedButton.CMButtonPressed(var Message : TLMessage);
var
Sender : TCustomSpeedButton;
begin
if csDestroying in ComponentState then exit;
if Message.WParam = WParam(FGroupIndex) then
begin
Sender := TCustomSpeedButton(Message.LParam);
if Sender <> Self then
begin
if Sender.Down and FDown then
begin
FDown := False;
FState := UpState[FMouseInControl];
Invalidate;
end;
FAllowAllUp := Sender.AllowAllUp;
end;
end;
end;
procedure TCustomSpeedButton.Loaded;
begin
inherited Loaded;
UpdateExclusive;
if FDownLoaded then
SetDown(FDownLoaded);
end;
procedure TCustomSpeedButton.LoadGlyphFromResourceName(Instance: TLCLHandle; const AName: String);
begin
Buttons.LoadGlyphFromResourceName(FGlyph, Instance, AName);
end;
procedure TCustomSpeedButton.LoadGlyphFromLazarusResource(const AName: String);
begin
Buttons.LoadGlyphFromLazarusResource(FGlyph, AName);
end;
function TCustomSpeedButton.GetGlyphSize(Drawing: boolean; PaintRect: TRect): TSize;
var
AImageRes: TScaledImageListResolution;
AIndex: Integer;
AEffect: TGraphicsDrawEffect;
begin
if (FGlyph.Glyph.Empty) and ((Images = nil) or (ImageIndex = -1)) and (FGlyph.LCLGlyphName <> '') then
begin
Result.CX := 0;
Result.CY := 0;
exit;
end;
FGlyph.GetImageIndexAndEffect(Low(TButtonState), Font.PixelsPerInch,
GetCanvasScaleFactor, AImageRes, AIndex, AEffect);
Result.CX := AImageRes.Width;
Result.CY := AImageRes.Height;
end;
function TCustomSpeedButton.GetImageIndex(AState: TButtonState): TImageIndex;
begin
Result := FGlyph.FExternalImageIndexes[AState];
end;
function TCustomSpeedButton.GetImages: TCustomImageList;
begin
Result := FGlyph.ExternalImages;
end;
function TCustomSpeedButton.GetImageWidth: Integer;
begin
Result := FGlyph.ExternalImageWidth;
end;
function TCustomSpeedButton.GetTextSize(Drawing: boolean; PaintRect: TRect): TSize;
var
TMP: String;
Flags: Cardinal;
DC: HDC; // ~bk see : TCustomLabel.CalculateSize
OldFont: HGDIOBJ; // "
begin
if FShowCaption and (Caption <> '') then
begin
TMP := Caption;
Flags := DT_CalcRect;
if not Canvas.TextStyle.SingleLine then
Inc(Flags, DT_WordBreak);
DC := GetDC(Parent.Handle);
try
OldFont := SelectObject(DC, HGDIOBJ(Font.Reference.Handle));
DrawText(DC, PChar(TMP), Length(TMP), PaintRect, Flags);
SelectObject(DC, OldFont);
finally
ReleaseDC(Parent.Handle, DC);
end;
Result.CY := PaintRect.Bottom - PaintRect.Top;
Result.CX := PaintRect.Right - PaintRect.Left;
end
else
begin
Result.CY:= 0;
Result.CX:= 0;
end;
end;
function TCustomSpeedButton.GetTransparent: Boolean;
begin
if FGlyph.TransparentMode = gtmGlyph then
Result := FGlyph.FOriginal.Transparent
else
Result := FGlyph.TransparentMode = gtmTransparent;
end;
function TCustomSpeedButton.DrawGlyph(ACanvas: TCanvas; const AClient: TRect;
const AOffset: TPoint; AState: TButtonState; ATransparent: Boolean;
BiDiFlags: Longint): TRect;
begin
if Assigned(FGlyph) then
Result := FGlyph.Draw(ACanvas, AClient, AOffset, AState, ATransparent, BiDiFlags,
Font.PixelsPerInch, GetCanvasScaleFactor)
else
Result := Rect(0,0,0,0);
end;
{------------------------------------------------------------------------------
Method: TCustomSpeedButton.CMEnabledChanged
Params: Message:
Returns: nothing
------------------------------------------------------------------------------}
procedure TCustomSpeedButton.CMEnabledChanged(var Message: TLMessage);
Begin
//Should create a new glyph based on the new state
UpdateState(true);
end;
{------------------------------------------------------------------------------
Method: TCustomSpeedButton.MouseEnter
Params:
Returns: nothing
------------------------------------------------------------------------------}
procedure TCustomSpeedButton.MouseEnter;
begin
if csDesigning in ComponentState then exit;
if not FMouseInControl and IsEnabled and (GetCapture = 0) then
begin
FMouseInControl := True;
UpdateState(true);
inherited MouseEnter;
end;
end;
{------------------------------------------------------------------------------
Method: TCustomSpeedButton.MouseLeave
Params:
Returns: nothing
------------------------------------------------------------------------------}
procedure TCustomSpeedButton.MouseLeave;
begin
if csDesigning in ComponentState then exit;
///DebugLn(['TCustomSpeedButton.MouseLeave ',DbgSName(Self),' FMouseInControl=',FMouseInControl,' FDragging=',FDragging]);
if FMouseInControl then
begin
FMouseInControl := False;
if IsEnabled then
begin
if FDragging and (not MouseCapture) then
begin
// something fetched our mouse capture
FDragging:=false;
end;
UpdateState(true);
inherited MouseLeave;
end;
end;
end;
{ TSpeedButtonActionLink }
procedure TSpeedButtonActionLink.AssignClient(AClient: TObject);
begin
inherited AssignClient(AClient);
FClient := AClient as TCustomSpeedButton;
end;
function TSpeedButtonActionLink.IsCheckedLinked: Boolean;
var
SpeedButton: TCustomSpeedButton;
begin
SpeedButton:=TCustomSpeedButton(FClient);
Result := inherited IsCheckedLinked
and (SpeedButton.GroupIndex <> 0)
and SpeedButton.AllowAllUp
and (SpeedButton.Down = (Action as TCustomAction).Checked);
end;
function TSpeedButtonActionLink.IsGroupIndexLinked: Boolean;
var
SpeedButton: TCustomSpeedButton;
begin
SpeedButton:=TCustomSpeedButton(FClient);
Result := (SpeedButton is TCustomSpeedButton) and
(SpeedButton.GroupIndex = (Action as TCustomAction).GroupIndex);
end;
function TSpeedButtonActionLink.IsImageIndexLinked: Boolean;
begin
Result := inherited IsImageIndexLinked and
(TSpeedButton(FClient).ImageIndex = (Action as TCustomAction).ImageIndex);
end;
procedure TSpeedButtonActionLink.SetGroupIndex(Value: Integer);
begin
if IsGroupIndexLinked then TCustomSpeedButton(FClient).GroupIndex := Value;
end;
procedure TSpeedButtonActionLink.SetChecked(Value: Boolean);
begin
if IsCheckedLinked then TCustomSpeedButton(FClient).Down := Value;
end;
procedure TSpeedButtonActionLink.SetImageIndex(Value: Integer);
begin
if IsImageIndexLinked then
TSpeedButton(FClient).ImageIndex := Value;
end;
{$IFDEF ASSERT_IS_ON}
{$UNDEF ASSERT_IS_ON}
{$C-}
{$ENDIF}
// included by buttons.pp