1{%MainUnit ../buttons.pp} 2 3{****************************************************************************** 4 TCustomSpeedButton 5 ****************************************************************************** 6 7 ***************************************************************************** 8 This file is part of the Lazarus Component Library (LCL) 9 10 See the file COPYING.modifiedLGPL.txt, included in this distribution, 11 for details about the license. 12 ***************************************************************************** 13} 14 15{$IFOPT C-} 16// Uncomment for local trace 17// {$C+} 18// {$DEFINE ASSERT_IS_ON} 19{$ENDIF} 20 21const 22 UpState: array[Boolean] of TButtonState = 23 ( 24{False} bsUp, // mouse in control = false 25{True } bsHot // mouse in contorl = true 26 ); 27 28{------------------------------------------------------------------------------ 29 Method: TCustomSpeedButton.Create 30 Params: none 31 Returns: Nothing 32 33 Constructor for the class. 34 ------------------------------------------------------------------------------} 35constructor TCustomSpeedButton.Create(AOwner: TComponent); 36begin 37 inherited Create(AOwner); 38 FGlyph := TButtonGlyph.Create; 39 FGlyph.IsDesigning := csDesigning in ComponentState; 40 FGlyph.ShowMode := gsmAlways; 41 FGlyph.SetTransparentMode(gtmTransparent); 42 FGlyph.OnChange := @GlyphChanged; 43 FImageChangeLink := TChangeLink.Create; 44 FImageChangeLink.OnChange := @ImageListChange; 45 46 with GetControlClassDefaultSize do 47 SetInitialBounds(0, 0, CX, CY); 48 ControlStyle := ControlStyle + [csCaptureMouse]-[csSetCaption, csClickEvents, csOpaque]; 49 50 FLayout := blGlyphLeft; 51 FAllowAllUp := False; 52 FMouseInControl := False; 53 FDragging := False; 54 FShowAccelChar := True; 55 FSpacing := 4; 56 FMargin := -1; 57 Color := clBtnFace; 58 FShowCaption := true; 59end; 60 61{------------------------------------------------------------------------------ 62 Method: TCustomSpeedButton.Destroy 63 Params: None 64 Returns: Nothing 65 66 Destructor for the class. 67 ------------------------------------------------------------------------------} 68destructor TCustomSpeedButton.Destroy; 69begin 70 FreeAndNil(FGlyph); 71 FreeAndNil(FImageChangeLink); 72 inherited Destroy; 73end; 74 75{------------------------------------------------------------------------------ 76 Method: TCustomSpeedButton.FindDownButton: TCustomSpeedButton; 77 78 Searches the speed button with Down=true and the same GroupIndex. 79 ------------------------------------------------------------------------------} 80function TCustomSpeedButton.FindDownButton: TCustomSpeedButton; 81 82 function FindDown(AWinControl: TWinControl): TCustomSpeedButton; 83 var 84 i: Integer; 85 Child: TControl; 86 Button: TCustomSpeedButton; 87 begin 88 if AWinControl = nil then Exit(nil); 89 for i := 0 to AWinControl.ControlCount-1 do 90 begin 91 Child := AWinControl.Controls[i]; 92 if Child is TCustomSpeedButton then 93 begin 94 Button := TCustomSpeedButton(Child); 95 if (Button.GroupIndex=GroupIndex) and (Button.Down) then 96 Exit(Button); 97 end; 98 if Child is TWinControl then 99 begin 100 Result := FindDown(TWinControl(Child)); 101 if Result <> nil then Exit; 102 end; 103 end; 104 Result := nil; 105 end; 106 107begin 108 if Down or (GroupIndex=0) then exit(Self); 109 Result := FindDown(GetFirstParentForm(Self)); 110end; 111 112procedure TCustomSpeedButton.Click; 113begin 114 inherited Click; 115end; 116 117{------------------------------------------------------------------------------ 118 Method: TCustomSpeedButton.SetAllowAllUp 119 Params: Value: 120 Returns: nothing 121 122 ------------------------------------------------------------------------------} 123procedure TCustomSpeedButton.SetAllowAllUp(Value : Boolean); 124begin 125 if FAllowAllUp <> Value then 126 begin 127 FAllowAllUp := Value; 128 UpdateExclusive; 129 end; 130end; 131 132{------------------------------------------------------------------------------ 133 Method: TCustomSpeedButton.SetDown 134 Params: Value: 135 Returns: nothing 136 137 ------------------------------------------------------------------------------} 138procedure TCustomSpeedButton.SetDown(Value : Boolean); 139var 140 OldState: TButtonState; 141 OldDown: Boolean; 142begin 143 //since Down needs GroupIndex, then we need to wait that all properties 144 //loaded before we continue 145 if (csLoading in ComponentState) then 146 begin 147 FDownLoaded := Value; 148 exit; 149 end else 150 begin 151 if FGroupIndex = 0 then 152 Value:= false; 153 if FDown <> Value then 154 begin 155 if FDown and not FAllowAllUp then 156 Exit; 157 OldDown := FDown; 158 FDown := Value; 159 OldState := FState; 160 if FDown then 161 FState := bsExclusive 162 else 163 FState := UpState[FMouseInControl]; 164 if (OldDown <> FDown) or (OldState <> FState) then 165 Invalidate; 166 if Value then 167 UpdateExclusive; 168 end; 169 end; 170end; 171 172{------------------------------------------------------------------------------ 173 Method: TCustomSpeedButton.SetFlat 174 Params: Value: 175 Returns: nothing 176 177 ------------------------------------------------------------------------------} 178procedure TCustomSpeedButton.SetFlat(const Value: Boolean); 179begin 180 if FFlat <> Value then 181 begin 182 FFlat := Value; 183 Invalidate; 184 end; 185end; 186 187{------------------------------------------------------------------------------ 188 Method: TCustomSpeedButton.SetGlyph 189 Params: Value: 190 Returns: nothing 191 192 ------------------------------------------------------------------------------} 193procedure TCustomSpeedButton.SetGlyph(Value : TBitmap); 194begin 195 FGlyph.Glyph := Value; 196 Invalidate; 197end; 198 199{------------------------------------------------------------------------------ 200 Method: TCustomSpeedButton.SetGroupIndex 201 Params: Value: 202 Returns: nothing 203 204 ------------------------------------------------------------------------------} 205procedure TCustomSpeedButton.SetGroupIndex(const Value : Integer); 206begin 207 if FGroupIndex <> Value then 208 begin 209 FGroupIndex := Value; 210 UpdateExclusive; 211 end; 212end; 213 214procedure TCustomSpeedButton.SetImageIndex(const aImageIndex: TImageIndex); 215begin 216 FGlyph.ExternalImageIndex := aImageIndex; 217end; 218 219procedure TCustomSpeedButton.SetImages(const aImages: TCustomImageList); 220begin 221 if FGlyph.ExternalImages <> nil then 222 begin 223 FGlyph.ExternalImages.UnRegisterChanges(FImageChangeLink); 224 FGlyph.ExternalImages.RemoveFreeNotification(Self); 225 end; 226 FGlyph.ExternalImages := aImages; 227 if FGlyph.ExternalImages <> nil then 228 begin 229 FGlyph.ExternalImages.FreeNotification(Self); 230 FGlyph.ExternalImages.RegisterChanges(FImageChangeLink); 231 end; 232 InvalidatePreferredSize; 233 AdjustSize; 234end; 235 236procedure TCustomSpeedButton.SetImageWidth(const aImageWidth: Integer); 237begin 238 FGlyph.ExternalImageWidth := aImageWidth; 239 InvalidatePreferredSize; 240 AdjustSize; 241end; 242 243{------------------------------------------------------------------------------ 244 Method: TCustomSpeedButton.SetMargin 245 Params: Value: 246 Returns: nothing 247 248 ------------------------------------------------------------------------------} 249procedure TCustomSpeedButton.SetMargin(const Value: integer); 250begin 251 if FMargin <> Value then 252 begin 253 FMargin := Value; 254 Invalidate; 255 end; 256end; 257 258{------------------------------------------------------------------------------ 259 Method: TCustomSpeedButton.SetNumGlyphs 260 Params: Value : Integer = Number of glyphs in the file/resource 261 Returns: nothing 262 263 ------------------------------------------------------------------------------} 264procedure TCustomSpeedButton.SetNumGlyphs(Value : integer); 265begin 266 if Value < Low(TNumGlyphs) then Value := Low(TNumGlyphs); 267 if Value > High(TNumGlyphs) then Value := High(TNumGlyphs); 268 269 if Value <> TButtonGlyph(fGlyph).NumGlyphs then 270 begin 271 TButtonGlyph(fGlyph).NumGlyphs := TNumGlyphs(Value); 272 Invalidate; 273 end; 274end; 275 276{------------------------------------------------------------------------------ 277 Method: TCustomSpeedButton.SetSpacing 278 Params: Value: 279 Returns: nothing 280 281 ------------------------------------------------------------------------------} 282procedure TCustomSpeedButton.SetSpacing(const Value: integer); 283begin 284 if FSpacing <> Value then 285 begin 286 FSpacing := Value; 287 Invalidate; 288 end; 289end; 290 291procedure TCustomSpeedButton.SetShowAccelChar(Value: boolean); 292begin 293 If FShowAccelChar <> Value then 294 begin 295 FShowAccelChar := Value; 296 Invalidate; 297 end; 298end; 299 300 301{------------------------------------------------------------------------------ 302 procedure TCustomSpeedButton.RealSetText(const Value: TCaption); 303 ------------------------------------------------------------------------------} 304procedure TCustomSpeedButton.RealSetText(const Value: TCaption); 305begin 306 if Caption = Value then Exit; 307 if (Parent<>nil) and (Parent.HandleAllocated) and (not (csLoading in ComponentState)) then 308 begin 309 InvalidatePreferredSize; 310 inherited RealSetText(Value); 311 AdjustSize; 312 end else 313 inherited RealSetText(Value); 314 315 Invalidate; 316end; 317 318{------------------------------------------------------------------------------ 319 procedure TCustomSpeedButton.UpdateState(InvalidateOnChange: boolean); 320 ------------------------------------------------------------------------------} 321procedure TCustomSpeedButton.UpdateState(InvalidateOnChange: boolean); 322var 323 OldState: TButtonState; 324begin 325 OldState := FState; 326 if not IsEnabled then 327 begin 328 FState := bsDisabled; 329 FDragging := False; 330 end else 331 begin 332 if FState = bsDisabled then 333 begin 334 if FDown and (GroupIndex <> 0) then 335 FState := bsExclusive 336 else 337 FState := UpState[FMouseInControl]; 338 end 339 else 340 if (FState in [bsHot, bsDown]) and (not FMouseInControl) and (not FDragging) and (not FDown) then 341 begin 342 // return to normal 343 FState := bsUp; 344 end 345 else 346 if (FState = bsUp) and FMouseInControl then 347 FState := bsHot; 348 end; 349 if FState <> OldState then 350 if (Action is TCustomAction) then 351 TCustomAction(Action).Checked := FState = bsDown; 352 //if InvalidateOnChange then DebugLn(['TCustomSpeedButton.UpdateState ',DbgSName(Self),' InvalidateOnChange=',InvalidateOnChange,' StateChange=',FState<>OldState]); 353 if InvalidateOnChange and 354 ( 355 (FState <> OldState) or 356 not ThemedElementDetailsEqual(FLastDrawDetails, GetDrawDetails) 357 ) 358 then 359 Invalidate; 360end; 361 362{------------------------------------------------------------------------------ 363 function TCustomSpeedButton.GetDrawDetails: TThemedElementDetails; 364 ------------------------------------------------------------------------------} 365function TCustomSpeedButton.GetDrawDetails: TThemedElementDetails; 366 367 function ButtonPart: TThemedButton; 368 begin 369 // tbPushButtonNormal, tbPushButtonHot, tbPushButtonPressed, 370 // tbPushButtonDisabled, tbPushButtonDefaulted 371 372 // no check states available 373 Result := tbPushButtonNormal; 374 if not IsEnabled then 375 Result := tbPushButtonDisabled 376 else 377 if FState in [bsDown, bsExclusive] then 378 Result := tbPushButtonPressed 379 else 380 if FState = bsHot then 381 Result := tbPushButtonHot 382 else 383 Result := tbPushButtonNormal; 384 end; 385 386 function ToolButtonPart: TThemedToolBar; 387 begin 388 // ttbButtonNormal, ttbButtonHot, ttbButtonPressed, ttbButtonDisabled 389 // ttbButtonChecked, ttbButtonCheckedHot 390 if not IsEnabled then 391 Result := ttbButtonDisabled 392 else 393 begin 394 if Down then 395 begin // checked states 396 if FMouseInControl then 397 Result := ttbButtonCheckedHot 398 else 399 Result := ttbButtonChecked; 400 end 401 else 402 begin 403 if FState in [bsDown, bsExclusive] then 404 Result := ttbButtonPressed else 405 if FState = bsHot then 406 Result := ttbButtonHot 407 else 408 Result := ttbButtonNormal; 409 end; 410 end; 411 end; 412 413begin 414 if Flat then 415 Result := ThemeServices.GetElementDetails(ToolButtonPart) 416 else 417 Result := ThemeServices.GetElementDetails(ButtonPart) 418end; 419 420procedure TCustomSpeedButton.ActionChange(Sender: TObject; CheckDefaults: Boolean); 421var 422 NewAct: TCustomAction; 423 Imgs: TCustomImageList; 424 ImgRes: TScaledImageListResolution; 425begin 426 inherited ActionChange(Sender,CheckDefaults); 427 if Sender is TCustomAction then 428 begin 429 NewAct := TCustomAction(Sender); 430 if (not CheckDefaults) or (GroupIndex = 0) then 431 GroupIndex := NewAct.GroupIndex; 432 if (NewAct.ActionList = nil) or (NewAct.ImageIndex < 0) then Exit; 433 Imgs := NewAct.ActionList.Images; 434 if (Imgs = nil) or (NewAct.ImageIndex >= Imgs.Count) then Exit; 435 ImgRes := Imgs.ResolutionForPPI[ImageWidth,Font.PixelsPerInch,GetCanvasScaleFactor]; 436 ImgRes.GetBitmap(NewAct.ImageIndex, Glyph); 437 end; 438end; 439 440function TCustomSpeedButton.ButtonGlyph: TButtonGlyph; 441begin 442 Result := FGlyph; 443end; 444 445function TCustomSpeedButton.GetActionLinkClass: TControlActionLinkClass; 446begin 447 Result := TSpeedButtonActionLink; 448end; 449 450class function TCustomSpeedButton.GetControlClassDefaultSize: TSize; 451begin 452 Result.CX := 23; 453 Result.CY := 22; 454end; 455 456{------------------------------------------------------------------------------ 457 Method: TCustomSpeedButton.UpdateExclusive 458 Params: none 459 Returns: nothing 460 461 ------------------------------------------------------------------------------} 462procedure TCustomSpeedButton.UpdateExclusive; 463var 464 msg : TLMessage; 465begin 466 if (FGroupIndex <> 0) and (Parent <> nil) and (not (csLoading in ComponentState)) then 467 begin 468 Msg.Msg := CM_ButtonPressed; 469 Msg.WParam := FGroupIndex; 470 Msg.LParam := PtrInt(Self); 471 Msg.Result := 0; 472 Parent.Broadcast(Msg); 473 end; 474end; 475 476{------------------------------------------------------------------------------ 477 Function: TCustomSpeedButton.GetGlyph 478 Params: none 479 Returns: The bitmap 480 481 ------------------------------------------------------------------------------} 482function TCustomSpeedButton.GetGlyph : TBitmap; 483begin 484 Result := FGlyph.Glyph; 485end; 486 487function TCustomSpeedButton.IsGlyphStored: Boolean; 488var 489 act: TCustomAction; 490begin 491 result := true; 492 if Action <> nil then 493 begin 494 act := TCustomAction(Action); 495 if (act.ActionList <> nil) and (act.ActionList.Images <> nil) and 496 (act.ImageIndex >= 0) and (act.ImageIndex < act.ActionList.Images.Count) then 497 result := false; 498 end; 499end; 500 501procedure TCustomSpeedButton.SetShowCaption(const AValue: boolean); 502begin 503 if FShowCaption=AValue then exit; 504 FShowCaption:=AValue; 505 invalidate; 506end; 507 508{------------------------------------------------------------------------------ 509 Method: TCustomSpeedButton.GetNumGlyphs 510 Params: none 511 Returns: The number stored in TButtonGlyph(FGlyph).NumGlyphs 512 513 ------------------------------------------------------------------------------} 514function TCustomSpeedButton.GetNumGlyphs : Integer; 515Begin 516 Result := TButtonGlyph(fGlyph).NumGlyphs; 517end; 518 519{------------------------------------------------------------------------------ 520 Method: TCustomSpeedButton.GlyphChanged 521 Params: Sender - The glyph that changed 522 Returns: zippo 523 524 ------------------------------------------------------------------------------} 525procedure TCustomSpeedButton.GlyphChanged(Sender : TObject); 526Begin 527 //redraw the button; 528 Invalidate; 529end; 530 531procedure TCustomSpeedButton.ImageListChange(Sender: TObject); 532begin 533 if Sender = Images then Invalidate; 534end; 535 536function TCustomSpeedButton.DialogChar(var Message: TLMKey): boolean; 537begin 538 Result := False; 539 // Sometimes LM_CHAR is received instead of LM_SYSCHAR, maybe intentionally 540 // (LCL handles it) or maybe sent by mistake. In either case exit. 541 if (Message.Msg <> LM_SYSCHAR) or not FShowAccelChar then Exit; 542 if Enabled and IsAccel(Message.CharCode, Caption) then 543 begin 544 Result := True; 545 if GroupIndex <> 0 then 546 SetDown(not FDown); 547 Click; 548 end else 549 Result := inherited DialogChar(Message); 550end; 551 552procedure TCustomSpeedButton.CalculatePreferredSize(var PreferredWidth, 553 PreferredHeight: integer; WithThemeSpace: Boolean); 554var 555 r: TRect; 556begin 557 r:=Rect(0,0,0,0); 558 MeasureDraw(false,r,PreferredWidth,PreferredHeight); 559end; 560 561procedure TCustomSpeedButton.MeasureDraw(Draw: boolean; 562 PaintRect: TRect; out PreferredWidth, PreferredHeight: integer); 563var 564 GlyphWidth, GlyphHeight: Integer; 565 Offset, OffsetCap: TPoint; 566 ClientSize, TotalSize, TextSize, GlyphSize: TSize; 567 M, S : integer; 568 SIndex : Longint; 569 TMP : String; 570 TextFlags: Integer; 571 DrawDetails: TThemedElementDetails; 572 FixedWidth: Boolean; 573 FixedHeight: Boolean; 574 TextRect: TRect; 575 HasGlyph: Boolean; 576 HasText: Boolean; 577 CurLayout: TButtonLayout; 578 SysFont: TFont; 579begin 580 if FGlyph = nil then exit; 581 582 DrawDetails := GetDrawDetails; 583 584 PreferredWidth:=0; 585 PreferredHeight:=0; 586 587 if Draw then begin 588 FLastDrawDetails:=DrawDetails; 589 PaintBackground(PaintRect); 590 FixedWidth:=true; 591 FixedHeight:=true; 592 end else begin 593 FixedWidth:=WidthIsAnchored; 594 FixedHeight:=HeightIsAnchored; 595 end; 596 ClientSize.cx:= PaintRect.Right - PaintRect.Left; 597 ClientSize.cy:= PaintRect.Bottom - PaintRect.Top; 598 //debugln(['TCustomSpeedButton.MeasureDraw Step1 ',DbgSName(Self),' PaintRect=',dbgs(PaintRect)]); 599 // compute size of glyph 600 GlyphSize := GetGlyphSize(Draw,PaintRect); 601 GlyphWidth := GlyphSize.CX; 602 GlyphHeight := GlyphSize.CY; 603 HasGlyph:=(GlyphWidth<>0) and (GlyphHeight<>0); 604 //debugln(['TCustomSpeedButton.MeasureDraw Step2 ',DbgSName(Self),' PaintRect=',dbgs(PaintRect),' GlyphSize=',GlyphWidth,'x',GlyphHeight]); 605 606 // compute size of text 607 CurLayout:=BidiAdjustButtonLayout(UseRightToLeftReading, Layout); 608 if ShowCaption and (Caption<>'') then begin 609 TextRect:=PaintRect; 610 // for wordbreak compute the maximum size for the text 611 if Margin>0 then 612 InflateRect(TextRect,-Margin,-Margin); 613 if HasGlyph then 614 begin 615 if (Spacing>=0) then 616 if CurLayout in [blGlyphLeft,blGlyphRight] then 617 dec(TextRect.Right,Spacing) 618 else 619 dec(TextRect.Bottom,Spacing); 620 if CurLayout in [blGlyphLeft,blGlyphRight] then 621 dec(TextRect.Right,GlyphWidth) 622 else 623 dec(TextRect.Bottom,GlyphHeight); 624 end; 625 if not FixedWidth then 626 begin 627 TextRect.Left:=0; 628 TextRect.Right:=High(TextRect.Right) div 2; 629 end; 630 if not FixedHeight then 631 begin 632 TextRect.Top:=0; 633 TextRect.Bottom:=High(TextRect.Bottom) div 2; 634 end; 635 TextSize := GetTextSize(Draw,TextRect); 636 end else begin 637 TextSize.cx:=0; 638 TextSize.cy:=0; 639 end; 640 HasText:=(TextSize.cx <> 0) or (TextSize.cy <> 0); 641 642 if Caption <> '' then 643 begin 644 TMP := Caption; 645 SIndex := DeleteAmpersands(TMP); 646 If SIndex > 0 then 647 If SIndex <= Length(TMP) then begin 648 FShortcut := Ord(TMP[SIndex]); 649 end; 650 end; 651 652 if HasGlyph and HasText then 653 S:= Spacing 654 else 655 S:= 0; 656 M:=Margin; 657 if not Draw then 658 begin 659 if M<0 then M:=2; 660 if S<0 then S:=M; 661 end; 662 663 // Calculate caption and glyph layout 664 if M = -1 then begin 665 // auto compute margin to center content 666 if S = -1 then begin 667 // use the same value for Spacing and Margin 668 TotalSize.cx:= TextSize.cx + GlyphWidth; 669 TotalSize.cy:= TextSize.cy + GlyphHeight; 670 if Layout in [blGlyphLeft, blGlyphRight] then 671 M:= (ClientSize.cx - TotalSize.cx) div 3 672 else 673 M:= (ClientSize.cy - TotalSize.cy) div 3; 674 S:= M; 675 end else begin 676 // fixed Spacing and center content 677 TotalSize.cx:= GlyphWidth + S + TextSize.cx; 678 TotalSize.cy:= GlyphHeight + S + TextSize.cy; 679 if Layout in [blGlyphLeft, blGlyphRight] then 680 M:= (ClientSize.cx - TotalSize.cx) div 2 681 else 682 M:= (ClientSize.cy - TotalSize.cy) div 2; 683 end; 684 end else begin 685 // fixed Margin 686 if S = -1 then begin 687 // use the rest for Spacing between Glyph and Caption 688 TotalSize.cx:= ClientSize.cx - (Margin + GlyphWidth); 689 TotalSize.cy:= ClientSize.cy - (Margin + GlyphHeight); 690 if Layout in [blGlyphLeft, blGlyphRight] then 691 S:= (TotalSize.cx - TextSize.cx) div 2 692 else 693 S:= (TotalSize.cy - TextSize.cy) div 2; 694 end; 695 end; 696 697 //debugln(['TCustomSpeedButton.MeasureDraw Step3 ',DbgSName(Self),' PaintRect=',dbgs(PaintRect),' GlyphSize=',GlyphWidth,'x',GlyphHeight,' TextSize=',TextSize.cx,'x',TextSize.cy,' S=',S,' M=',M]); 698 699 if Draw then 700 begin 701 case CurLayout of 702 blGlyphLeft : begin 703 Offset.X:= M; 704 Offset.Y:= (ClientSize.cy - GlyphHeight) div 2; 705 OffsetCap.X:= Offset.X + GlyphWidth + S; 706 OffsetCap.Y:= (ClientSize.cy - TextSize.cy) div 2; 707 end; 708 blGlyphRight : begin 709 Offset.X:= ClientSize.cx - M - GlyphWidth; 710 Offset.Y:= (ClientSize.cy - GlyphHeight) div 2; 711 OffsetCap.X:= Offset.X - S - TextSize.cx; 712 OffsetCap.Y:= (ClientSize.cy - TextSize.cy) div 2; 713 end; 714 blGlyphTop : begin 715 Offset.X:= (ClientSize.cx - GlyphWidth) div 2; 716 Offset.Y:= M; 717 OffsetCap.X:= (ClientSize.cx - TextSize.cx) div 2; 718 OffsetCap.Y:= Offset.Y + GlyphHeight + S; 719 end; 720 blGlyphBottom : begin 721 Offset.X:= (ClientSize.cx - GlyphWidth) div 2; 722 Offset.Y:= ClientSize.cy - M - GlyphHeight; 723 OffsetCap.X:= (ClientSize.cx - TextSize.cx) div 2; 724 OffsetCap.Y:= Offset.Y - S - TextSize.cy; 725 end; 726 end; 727 728 DrawGlyph(Canvas, PaintRect, Offset, FState, Transparent, 0); 729 730 if FShowCaption and (Caption <> '') then 731 begin 732 with PaintRect, OffsetCap do 733 begin 734 Left := Left + X; 735 Top := Top + Y; 736 end; 737 738 TextFlags := DT_LEFT or DT_TOP; 739 if UseRightToLeftReading then 740 TextFlags := TextFlags or DT_RTLREADING; 741 742 if Draw then 743 begin 744 SysFont := Screen.SystemFont; 745 if (SysFont.Color=Font.Color) 746 and ((SysFont.Name=Font.Name) or IsFontNameDefault(Font.Name)) 747 and (SysFont.Pitch=Font.Pitch) 748 and (SysFont.Style=Font.Style) then 749 ThemeServices.DrawText(Canvas, DrawDetails, Caption, PaintRect, TextFlags, 0) 750 else 751 begin 752 Canvas.Brush.Style := bsClear; 753 DrawText(Canvas.Handle, PChar(Caption), Length(Caption), PaintRect, TextFlags); 754 end; 755 end; 756 end; 757 end else begin 758 // measuring, not drawing 759 case CurLayout of 760 blGlyphLeft, blGlyphRight : 761 begin 762 PreferredWidth:=2*M+S+GlyphWidth+TextSize.cx; 763 PreferredHeight:=2*M+Max(GlyphHeight,TextSize.cy); 764 end; 765 blGlyphTop, blGlyphBottom : 766 begin 767 PreferredWidth:=2*M+Max(GlyphWidth,TextSize.cx); 768 PreferredHeight:=2*M+S+GlyphHeight+TextSize.cy; 769 end; 770 end; 771 end; 772end; 773 774{------------------------------------------------------------------------------ 775 Method: TCustomSpeedButton.Paint 776 Params: none 777 Returns: nothing 778 779 ------------------------------------------------------------------------------} 780procedure TCustomSpeedButton.Paint; 781var 782 PaintRect: TRect; 783 PreferredWidth: integer; 784 PreferredHeight: integer; 785begin 786 UpdateState(false); 787 if FGlyph = nil then exit; 788 789 PaintRect:=ClientRect; 790 MeasureDraw(true,PaintRect,PreferredWidth,PreferredHeight); 791 792 inherited Paint; 793end; 794 795procedure TCustomSpeedButton.PaintBackground(var PaintRect: TRect); 796begin 797 if not Transparent and ThemeServices.HasTransparentParts(FLastDrawDetails) then 798 begin 799 Canvas.Brush.Color := Color; 800 Canvas.FillRect(PaintRect); 801 end; 802 ThemeServices.DrawElement(Canvas.Handle, FLastDrawDetails, PaintRect); 803 PaintRect := ThemeServices.ContentRect(Canvas.Handle, FLastDrawDetails, PaintRect); 804end; 805 806{------------------------------------------------------------------------------ 807 Method: TCustomSpeedButton.MouseDown 808 Params: Button: 809 Shift: 810 X, Y: 811 Returns: nothing 812 ------------------------------------------------------------------------------} 813procedure TCustomSpeedButton.MouseDown(Button: TMouseButton; Shift: TShiftState; 814 X, Y: Integer); 815begin 816 inherited MouseDown(Button, Shift, X, Y); 817 if csDesigning in ComponentState then exit; 818 819 if (Button = mbLeft) and IsEnabled then 820 begin 821 if not FDown then 822 begin 823 FState := bsDown; 824 if (Action is TCustomAction) then 825 TCustomAction(Action).Checked := False; 826 Invalidate; 827 end; 828 FDragging := True; 829 end; 830end; 831 832{------------------------------------------------------------------------------ 833 Method: TCustomSpeedButton.MouseMove 834 Params: Shift: 835 X, Y: 836 Returns: nothing 837 838 ------------------------------------------------------------------------------} 839procedure TCustomSpeedButton.MouseMove(Shift: TShiftState; X, Y: Integer); 840var 841 NewState: TButtonState; 842begin 843 inherited MouseMove(Shift, X, Y); 844 if csDesigning in ComponentState then exit; 845 846 if FDragging then 847 begin 848 //DebugLn('Trace:FDragging is true'); 849 if FDown then 850 NewState := bsExclusive 851 else 852 begin 853 if (X >= 0) and (X < Width) and (Y >= 0) and (Y < Height) then 854 NewState := bsDown 855 else 856 NewState := UpState[FMouseInControl]; 857 end; 858 859 if NewState <> FState then 860 begin 861 //debugln(['TCustomSpeedButton.MouseMove ',DbgSName(Self),' fState=',ord(fstate),' NewState=',ord(NewState)]); 862 FState := NewState; 863 Invalidate; 864 end; 865 end; 866end; 867 868{------------------------------------------------------------------------------ 869 Method: TCustomSpeedButton.MouseUp 870 Params: Button: 871 Shift: 872 X, Y: 873 Returns: nothing 874 ------------------------------------------------------------------------------} 875procedure TCustomSpeedButton.MouseUp(Button: TMouseButton; Shift: TShiftState; 876 X, Y: Integer); 877begin 878 inherited MouseUp(Button, Shift, X, Y); 879end; 880 881procedure TCustomSpeedButton.Notification(AComponent: TComponent; 882 Operation: TOperation); 883begin 884 inherited Notification(AComponent, Operation); 885 if (Operation = opRemove) and (FGlyph<>nil) and (AComponent = FGlyph.ExternalImages) then 886 Images := nil; 887end; 888 889{------------------------------------------------------------------------------ 890 TCustomSpeedButton DoMouseUp "Event Handler" 891------------------------------------------------------------------------------} 892procedure TCustomSpeedButton.DoMouseUp(var Message: TLMMouse; Button: TMouseButton); 893begin 894 if not (csNoStdEvents in ControlStyle) then 895 with Message do 896 MouseUp(Button, KeysToShiftState(Keys), XPos, YPos); 897end; 898 899procedure TCustomSpeedButton.WMLButtonDown(var Message: TLMLButtonDown); 900begin 901 inherited; 902 903 // because csClickEvents is not set no csClicked is set in the inherited method 904 Include(FControlState, csClicked); 905end; 906 907procedure TCustomSpeedButton.WMLButtonDBLCLK(var Message: TLMLButtonDblClk); 908begin 909 inherited; 910 // if in a group, raise dblclick event, otherwise translate to click event 911 if Down then 912 DblClick 913 else 914 Click; 915end; 916 917class procedure TCustomSpeedButton.WSRegisterClass; 918begin 919 inherited WSRegisterClass; 920 RegisterCustomSpeedButton; 921end; 922 923{------------------------------------------------------------------------------ 924 Method: TCustomSpeedButton.WMLButtonUp 925 Params: Message 926 Returns: Nothing 927 928 Mouse event handler 929 ------------------------------------------------------------------------------} 930procedure TCustomSpeedButton.WMLButtonUp(var Message: TLMLButtonUp); 931var 932 OldState: TButtonState; 933 NeedClick: Boolean; 934begin 935 //DebugLn('TCustomSpeedButton.WMLButtonUp A ',DbgSName(Self),' csCaptureMouse=',DbgS(csCaptureMouse in ControlStyle),' csClicked=',DbgS(csClicked in ControlState)); 936 if (csCaptureMouse in ControlStyle) and (mbLeft in CaptureMouseButtons) then 937 begin 938 {$IFDEF VerboseMouseCapture} 939 DebugLn('TCustomSpeedButton.WMLButtonUp ',Name,':',ClassName); 940 {$ENDIF} 941 MouseCapture := False; 942 end; 943 944 NeedClick := False; 945 946 if not (csDesigning in ComponentState) and FDragging then 947 begin 948 OldState := FState; 949 FDragging := False; 950 951 if FGroupIndex = 0 then 952 begin 953 FState := UpState[FMouseInControl]; 954 if OldState <> FState then 955 Invalidate; 956 end 957 else 958 if (Message.XPos >= 0) and (Message.XPos < Width) and (Message.YPos >= 0) and (Message.YPos < Height) then 959 begin 960 SetDown(not FDown); 961 NeedClick := True; 962 end; 963 end; 964 965 DoMouseUp(Message, mbLeft); 966 967 if csClicked in ControlState then 968 begin 969 Exclude(FControlState, csClicked); 970 //DebugLn('TCustomSpeedButton.WMLButtonUp B ',dbgs(ClientRect.Left),',',dbgs(ClientRect.Top),',',dbgs(ClientRect.Right),',',dbgs(ClientRect.Bottom),' ',dbgs(Message.Pos.X),',',dbgs(Message.Pos.Y)); 971 if PtInRect(ClientRect, SmallPointToPoint(Message.Pos)) then 972 begin 973 //DebugLn('TCustomSpeedButton.WMLButtonUp C'); 974 // Important: Calling Click can invoke modal dialogs, so call this as last 975 NeedClick := False; 976 Click; 977 end; 978 end; 979 980 if NeedClick then 981 Click; 982 //DebugLn('TCustomSpeedButton.WMLButtonUp END'); 983end; 984 985{------------------------------------------------------------------------------ 986 Method: TCustomSpeedButton.SetLayout 987 Params: Value: new layout value 988 Returns: nothing 989 990 ------------------------------------------------------------------------------} 991procedure TCustomSpeedButton.SetLayout(const Value : TButtonLayout); 992begin 993 if Value <> FLayout then 994 begin 995 FLayout:= Value; 996 Invalidate; 997 end; 998end; 999 1000{------------------------------------------------------------------------------ 1001 Method: TCustomSpeedButton.SetTransparent 1002 Params: Value: new transparency value 1003 Returns: nothing 1004 1005 ------------------------------------------------------------------------------} 1006procedure TCustomSpeedButton.SetTransparent(const AValue: boolean); 1007const 1008 MODE: array[Boolean] of TGlyphTransparencyMode = (gtmOpaque, gtmTransparent); 1009begin 1010 if AValue = Transparent then Exit; 1011 1012 if AValue then 1013 ControlStyle := ControlStyle - [csOpaque] 1014 else 1015 ControlStyle := ControlStyle + [csOpaque]; 1016 1017 FGlyph.SetTransparentMode(MODE[AValue]); 1018 Invalidate; 1019end; 1020 1021{------------------------------------------------------------------------------ 1022 Method: TCustomSpeedButton.CMButtonPressed 1023 Params: Message: 1024 Returns: nothing 1025 1026 ------------------------------------------------------------------------------} 1027procedure TCustomSpeedButton.CMButtonPressed(var Message : TLMessage); 1028var 1029 Sender : TCustomSpeedButton; 1030begin 1031 if csDestroying in ComponentState then exit; 1032 if Message.WParam = WParam(FGroupIndex) then 1033 begin 1034 Sender := TCustomSpeedButton(Message.LParam); 1035 if Sender <> Self then 1036 begin 1037 if Sender.Down and FDown then 1038 begin 1039 FDown := False; 1040 FState := UpState[FMouseInControl]; 1041 Invalidate; 1042 end; 1043 FAllowAllUp := Sender.AllowAllUp; 1044 end; 1045 end; 1046end; 1047 1048procedure TCustomSpeedButton.Loaded; 1049begin 1050 inherited Loaded; 1051 UpdateExclusive; 1052 if FDownLoaded then 1053 SetDown(FDownLoaded); 1054end; 1055 1056procedure TCustomSpeedButton.LoadGlyphFromResourceName(Instance: THandle; const AName: String); 1057begin 1058 Buttons.LoadGlyphFromResourceName(FGlyph, Instance, AName); 1059end; 1060 1061procedure TCustomSpeedButton.LoadGlyphFromLazarusResource(const AName: String); 1062begin 1063 Buttons.LoadGlyphFromLazarusResource(FGlyph, AName); 1064end; 1065 1066function TCustomSpeedButton.GetGlyphSize(Drawing: boolean; PaintRect: TRect): TSize; 1067var 1068 AImageRes: TScaledImageListResolution; 1069 AIndex: Integer; 1070 AEffect: TGraphicsDrawEffect; 1071begin 1072 FGlyph.GetImageIndexAndEffect(Low(TButtonState), Font.PixelsPerInch, 1073 GetCanvasScaleFactor, AImageRes, AIndex, AEffect); 1074 Result.CX := AImageRes.Width; 1075 Result.CY := AImageRes.Height; 1076end; 1077 1078function TCustomSpeedButton.GetImageIndex: TImageIndex; 1079begin 1080 Result := FGlyph.ExternalImageIndex; 1081end; 1082 1083function TCustomSpeedButton.GetImages: TCustomImageList; 1084begin 1085 Result := FGlyph.ExternalImages; 1086end; 1087 1088function TCustomSpeedButton.GetImageWidth: Integer; 1089begin 1090 Result := FGlyph.ExternalImageWidth; 1091end; 1092 1093function TCustomSpeedButton.GetTextSize(Drawing: boolean; PaintRect: TRect): TSize; 1094var 1095 TMP: String; 1096 Flags: Cardinal; 1097 DC: HDC; // ~bk see : TCustomLabel.CalculateSize 1098 OldFont: HGDIOBJ; // " 1099begin 1100 if FShowCaption and (Caption <> '') then 1101 begin 1102 TMP := Caption; 1103 DeleteAmpersands(TMP); 1104 Flags := DT_CalcRect; 1105 if not Canvas.TextStyle.SingleLine then 1106 Inc(Flags, DT_WordBreak); 1107 DC := GetDC(Parent.Handle); 1108 try 1109 OldFont := SelectObject(DC, HGDIOBJ(Font.Reference.Handle)); 1110 DrawText(DC, PChar(TMP), Length(TMP), PaintRect, Flags); 1111 SelectObject(DC, OldFont); 1112 finally 1113 ReleaseDC(Parent.Handle, DC); 1114 end; 1115 Result.CY := PaintRect.Bottom - PaintRect.Top; 1116 Result.CX := PaintRect.Right - PaintRect.Left; 1117 end 1118 else 1119 begin 1120 Result.CY:= 0; 1121 Result.CX:= 0; 1122 end; 1123end; 1124 1125function TCustomSpeedButton.GetTransparent: Boolean; 1126begin 1127 if FGlyph.TransparentMode = gtmGlyph then 1128 Result := FGlyph.FOriginal.Transparent 1129 else 1130 Result := FGlyph.TransparentMode = gtmTransparent; 1131end; 1132 1133function TCustomSpeedButton.DrawGlyph(ACanvas: TCanvas; const AClient: TRect; 1134 const AOffset: TPoint; AState: TButtonState; ATransparent: Boolean; 1135 BiDiFlags: Longint): TRect; 1136begin 1137 if Assigned(FGlyph) then 1138 Result := FGlyph.Draw(ACanvas, AClient, AOffset, AState, ATransparent, BiDiFlags, 1139 Font.PixelsPerInch, GetCanvasScaleFactor) 1140 else 1141 Result := Rect(0,0,0,0); 1142end; 1143 1144{------------------------------------------------------------------------------ 1145 Method: TCustomSpeedButton.CMEnabledChanged 1146 Params: Message: 1147 Returns: nothing 1148 1149 ------------------------------------------------------------------------------} 1150procedure TCustomSpeedButton.CMEnabledChanged(var Message: TLMessage); 1151Begin 1152 //Should create a new glyph based on the new state 1153 UpdateState(true); 1154end; 1155 1156{------------------------------------------------------------------------------ 1157 Method: TCustomSpeedButton.MouseEnter 1158 Params: 1159 Returns: nothing 1160 1161 ------------------------------------------------------------------------------} 1162procedure TCustomSpeedButton.MouseEnter; 1163begin 1164 if csDesigning in ComponentState then exit; 1165 if not FMouseInControl and IsEnabled and (GetCapture = 0) then 1166 begin 1167 FMouseInControl := True; 1168 UpdateState(true); 1169 inherited MouseEnter; 1170 end; 1171end; 1172 1173{------------------------------------------------------------------------------ 1174 Method: TCustomSpeedButton.MouseLeave 1175 Params: 1176 Returns: nothing 1177 1178 ------------------------------------------------------------------------------} 1179procedure TCustomSpeedButton.MouseLeave; 1180begin 1181 if csDesigning in ComponentState then exit; 1182 ///DebugLn(['TCustomSpeedButton.MouseLeave ',DbgSName(Self),' FMouseInControl=',FMouseInControl,' FDragging=',FDragging]); 1183 if FMouseInControl then 1184 begin 1185 FMouseInControl := False; 1186 if IsEnabled then 1187 begin 1188 if FDragging and (not MouseCapture) then 1189 begin 1190 // something fetched our mouse capture 1191 FDragging:=false; 1192 end; 1193 UpdateState(true); 1194 inherited MouseLeave; 1195 end; 1196 end; 1197end; 1198 1199{ TSpeedButtonActionLink } 1200 1201procedure TSpeedButtonActionLink.AssignClient(AClient: TObject); 1202begin 1203 inherited AssignClient(AClient); 1204 FClient := AClient as TCustomSpeedButton; 1205end; 1206 1207function TSpeedButtonActionLink.IsCheckedLinked: Boolean; 1208var 1209 SpeedButton: TCustomSpeedButton; 1210begin 1211 SpeedButton:=TCustomSpeedButton(FClient); 1212 Result := inherited IsCheckedLinked 1213 and (SpeedButton.GroupIndex <> 0) 1214 and SpeedButton.AllowAllUp 1215 and (SpeedButton.Down = (Action as TCustomAction).Checked); 1216end; 1217 1218function TSpeedButtonActionLink.IsGroupIndexLinked: Boolean; 1219var 1220 SpeedButton: TCustomSpeedButton; 1221begin 1222 SpeedButton:=TCustomSpeedButton(FClient); 1223 Result := (SpeedButton is TCustomSpeedButton) and 1224 (SpeedButton.GroupIndex = (Action as TCustomAction).GroupIndex); 1225end; 1226 1227function TSpeedButtonActionLink.IsImageIndexLinked: Boolean; 1228begin 1229 Result := inherited IsImageIndexLinked and 1230 (TSpeedButton(FClient).ImageIndex = (Action as TCustomAction).ImageIndex); 1231end; 1232 1233procedure TSpeedButtonActionLink.SetGroupIndex(Value: Integer); 1234begin 1235 if IsGroupIndexLinked then TCustomSpeedButton(FClient).GroupIndex := Value; 1236end; 1237 1238procedure TSpeedButtonActionLink.SetChecked(Value: Boolean); 1239begin 1240 if IsCheckedLinked then TCustomSpeedButton(FClient).Down := Value; 1241end; 1242 1243procedure TSpeedButtonActionLink.SetImageIndex(Value: Integer); 1244begin 1245 if IsImageIndexLinked then 1246 TSpeedButton(FClient).ImageIndex := Value; 1247end; 1248 1249 1250{$IFDEF ASSERT_IS_ON} 1251 {$UNDEF ASSERT_IS_ON} 1252 {$C-} 1253{$ENDIF} 1254 1255 1256// included by buttons.pp 1257