{%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 copyright. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * * ***************************************************************************** } {$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; 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; end; {------------------------------------------------------------------------------ Method: TCustomSpeedButton.Destroy Params: None Returns: Nothing Destructor for the class. ------------------------------------------------------------------------------} destructor TCustomSpeedButton.Destroy; begin FreeAndNil(FGlyph); 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; {------------------------------------------------------------------------------ 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; {------------------------------------------------------------------------------ Method: TCustomSpeedButton.SetMargin Params: Value: Returns: nothing ------------------------------------------------------------------------------} procedure TCustomSpeedButton.SetMargin(const Value : Integer); begin if FMargin <> Value then begin FMargin := Value; 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 < 0 then Value := 1; 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; 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; 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) and (not FDragging) and (not FDown) then begin // return to normal FState := bsUp; end else if (FState = bsUp) and FMouseInControl then FState := bsHot; end; if FState <> OldState 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); begin inherited ActionChange(Sender,CheckDefaults); if Sender is TCustomAction then begin with TCustomAction(Sender) do begin if CheckDefaults or (Self.GroupIndex = 0) then Self.GroupIndex := GroupIndex; if (Glyph.Empty) and (ActionList <> nil) and (ActionList.Images <> nil) and (ImageIndex >= 0) and (ImageIndex < ActionList.Images.Count) then ActionList.Images.GetBitmap(ImageIndex, Glyph); end; end; 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; 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; 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 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); end; procedure TCustomSpeedButton.MeasureDraw(Draw: boolean; PaintRect: TRect; out PreferredWidth, PreferredHeight: integer); var GlyphWidth, GlyphHeight: Integer; Offset, 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; 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 AAA1 ',DbgSName(Self),' PaintRect=',dbgs(PaintRect)]); // compute size of glyph GlyphSize := GetGlyphSize(Draw,PaintRect); GlyphWidth := GlyphSize.CX; if TButtonGlyph(FGlyph).NumGlyphs > 1 then GlyphWidth:=GlyphWidth div NumGlyphs; GlyphHeight := GlyphSize.CY; HasGlyph:=(GlyphWidth<>0) and (GlyphHeight<>0); //debugln(['TCustomSpeedButton.MeasureDraw AAA2 ',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 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 = -1 then begin // auto compute margin to center content if S = -1 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 and 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 = -1 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 AAA3 ',DbgSName(Self),' PaintRect=',dbgs(PaintRect),' GlyphSize=',GlyphWidth,'x',GlyphHeight,' TextSize=',TextSize.cx,'x',TextSize.cy,' S=',S,' M=',M]); if Draw then begin case CurLayout of blGlyphLeft : begin Offset.X:= M; Offset.Y:= (ClientSize.cy - GlyphHeight) div 2; OffsetCap.X:= Offset.X + GlyphWidth + S; OffsetCap.Y:= (ClientSize.cy - TextSize.cy) div 2; end; blGlyphRight : begin Offset.X:= ClientSize.cx - M - GlyphWidth; Offset.Y:= (ClientSize.cy - GlyphHeight) div 2; OffsetCap.X:= Offset.X - S - TextSize.cx; OffsetCap.Y:= (ClientSize.cy - TextSize.cy) div 2; end; blGlyphTop : begin Offset.X:= (ClientSize.cx - GlyphWidth) div 2; Offset.Y:= M; OffsetCap.X:= (ClientSize.cx - TextSize.cx) div 2; OffsetCap.Y:= Offset.Y + GlyphHeight + S; end; blGlyphBottom : begin Offset.X:= (ClientSize.cx - GlyphWidth) div 2; Offset.Y:= ClientSize.cy - M - GlyphHeight; OffsetCap.X:= (ClientSize.cx - TextSize.cx) div 2; OffsetCap.Y:= Offset.Y - S - TextSize.cy; end; end; DrawGlyph(Canvas, PaintRect, Offset, FState, Transparent, 0); if FShowCaption and (Caption <> '') then begin with PaintRect, OffsetCap do begin Left := Left + X; Top := Top + Y; end; TextFlags := DT_LEFT or DT_TOP; if UseRightToLeftReading then TextFlags := TextFlags or DT_RTLREADING; if Draw then ThemeServices.DrawText(Canvas, DrawDetails, Caption, PaintRect, TextFlags, 0); 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; {------------------------------------------------------------------------------ TCustomSpeedButton DoMouseUp "Event Handler" ------------------------------------------------------------------------------} procedure TCustomSpeedButton.DoMouseUp(var Message: TLMMouse; Button: TMouseButton); begin if not (csNoStdEvents in ControlStyle) then with Message do MouseUp(Button, KeysToShiftState(Keys), XPos, YPos); 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; Invalidate; 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.LoadGlyphFromLazarusResource(const AName: String); begin Buttons.LoadGlyphFromLazarusResource(FGlyph, AName); end; function TCustomSpeedButton.GetGlyphSize(Drawing: boolean; PaintRect: TRect): TSize; begin Result.CX := FGlyph.Glyph.Width; Result.CY := FGlyph.Glyph.Height; end; function TCustomSpeedButton.GetTextSize(Drawing: boolean; PaintRect: TRect): TSize; var TMP: String; TXTStyle: TTextStyle; Flags: Cardinal; begin if FShowCaption and (Caption <> '') then begin TMP := Caption; TXTStyle := Canvas.TextStyle; TXTStyle.Opaque := False; TXTStyle.Clipping := True; TXTStyle.ShowPrefix := ShowAccelChar; TXTStyle.Alignment := taLeftJustify; TXTStyle.Layout := tlTop; TXTStyle.RightToLeft := UseRightToLeftReading; TXTStyle.SystemFont := Canvas.Font.IsDefault;//Match System Default Style DeleteAmpersands(TMP); Flags := DT_CalcRect; if not TXTStyle.SingleLine then Inc(Flags, DT_WordBreak); DrawText(Canvas.Handle, PChar(TMP), Length(TMP), PaintRect, Flags); 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 begin if (AState = bsDown) or (Down = true) then Result := FGlyph.Draw(ACanvas, AClient, point(AOffset.x + 1, AOffset.y + 1), AState, ATransparent, BiDiFlags) else Result := FGlyph.Draw(ACanvas, AClient, AOffset, AState, ATransparent, BiDiFlags); end; 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; 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; {$IFDEF ASSERT_IS_ON} {$UNDEF ASSERT_IS_ON} {$C-} {$ENDIF} // included by buttons.pp