colorspeedbutton.pas 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610
  1. // SPDX-License-Identifier: LGPL-3.0-linking-exception
  2. {******************************* CONTRIBUTOR(S) ******************************
  3. - Edivando S. Santos Brasil | [email protected]
  4. (Compatibility with delphi VCL 11/2018)
  5. ***************************** END CONTRIBUTOR(S) *****************************}
  6. unit ColorSpeedButton;
  7. {$I bgracontrols.inc}
  8. {$ifdef windows}
  9. {$define overridepaint}
  10. {$endif}
  11. interface
  12. uses
  13. Classes, SysUtils, Types, {$IFDEF FPC}LCLType, LCLProc, LResources,{$ENDIF}
  14. {$IFNDEF FPC}BGRAGraphics, GraphType, FPImage, {$ENDIF}
  15. Forms, Controls, Graphics, Dialogs, Buttons, BGRASpeedButton, Themes
  16. {$ifdef overridepaint}, Math{$ENDIF};
  17. type
  18. { TColorState }
  19. TColorState = class(TPersistent)
  20. private
  21. FOwner: TControl;
  22. FBorderColor: TColor;
  23. FBorderWidth: integer;
  24. FColor: TColor;
  25. procedure SetFBorderColor(AValue: TColor);
  26. procedure SetFBorderWidth(AValue: integer);
  27. procedure SetFColor(AValue: TColor);
  28. public
  29. constructor Create(AOwner: TControl);
  30. published
  31. property Color: TColor read FColor write SetFColor;
  32. property BorderColor: TColor read FBorderColor write SetFBorderColor;
  33. property BorderWidth: integer read FBorderWidth write SetFBorderWidth;
  34. end;
  35. { TColorSpeedButton }
  36. TColorSpeedButton = class(TBGRASpeedButton)
  37. private
  38. {$ifdef overridepaint}
  39. FLastDrawDetails: TThemedElementDetails;
  40. {$endif}
  41. FPopupMode: boolean;
  42. FPressed: boolean;
  43. FStateActive: TColorState;
  44. FStateDisabled: TColorState;
  45. FStateHover: TColorState;
  46. FStateNormal: TColorState;
  47. FTextAutoSize: boolean;
  48. FToggle: boolean;
  49. procedure SetFPopupMode(AValue: boolean);
  50. procedure SetFPressed(AValue: boolean);
  51. procedure SetFStateActive(AValue: TColorState);
  52. procedure SetFStateDisabled(AValue: TColorState);
  53. procedure SetFStateHover(AValue: TColorState);
  54. procedure SetFStateNormal(AValue: TColorState);
  55. procedure SetFTextAutoSize(AValue: boolean);
  56. procedure SetFToggle(AValue: boolean);
  57. protected
  58. {$ifdef overridepaint}
  59. procedure DrawText({%H-}ACanvas: TPersistent; {%H-}Details: TThemedElementDetails;
  60. const S: string; R: TRect; Flags, {%H-}Flags2: cardinal);
  61. procedure MeasureDraw(Draw: boolean; PaintRect: TRect;
  62. out PreferredWidth, PreferredHeight: integer);
  63. procedure Paint; override;
  64. procedure CalculatePreferredSize(var PreferredWidth, PreferredHeight: integer;
  65. {%H-}WithThemeSpace: boolean); override;
  66. {$endif}
  67. procedure PaintBackground(var PaintRect: TRect); {$IFDEF FPC}override;{$ENDIF}
  68. public
  69. constructor Create(TheOwner: TComponent); override;
  70. destructor Destroy; override;
  71. procedure Click; override;
  72. published
  73. property TextAutoSize: boolean read FTextAutoSize write SetFTextAutoSize;
  74. property Toggle: boolean read FToggle write SetFToggle;
  75. property Pressed: boolean read FPressed write SetFPressed;
  76. property PopupMode: boolean read FPopupMode write SetFPopupMode;
  77. property StateNormal: TColorState read FStateNormal write SetFStateNormal;
  78. property StateHover: TColorState read FStateHover write SetFStateHover;
  79. property StateActive: TColorState read FStateActive write SetFStateActive;
  80. property StateDisabled: TColorState read FStateDisabled write SetFStateDisabled;
  81. end;
  82. {$IFDEF FPC}procedure Register;{$ENDIF}
  83. implementation
  84. {$IFDEF FPC}
  85. procedure Register;
  86. begin
  87. RegisterComponents('BGRA Button Controls', [TColorSpeedButton]);
  88. end;
  89. {$ENDIF}
  90. { TColorSpeedButton }
  91. procedure TColorSpeedButton.SetFStateActive(AValue: TColorState);
  92. begin
  93. if FStateActive = AValue then
  94. Exit;
  95. FStateActive := AValue;
  96. Invalidate;
  97. end;
  98. procedure TColorSpeedButton.SetFPopupMode(AValue: boolean);
  99. begin
  100. if FPopupMode = AValue then
  101. Exit;
  102. FPopupMode := AValue;
  103. end;
  104. procedure TColorSpeedButton.SetFPressed(AValue: boolean);
  105. begin
  106. if FPressed = AValue then
  107. Exit;
  108. FPressed := AValue;
  109. Invalidate;
  110. end;
  111. procedure TColorSpeedButton.SetFStateDisabled(AValue: TColorState);
  112. begin
  113. if FStateDisabled = AValue then
  114. Exit;
  115. FStateDisabled := AValue;
  116. Invalidate;
  117. end;
  118. procedure TColorSpeedButton.SetFStateHover(AValue: TColorState);
  119. begin
  120. if FStateHover = AValue then
  121. Exit;
  122. FStateHover := AValue;
  123. Invalidate;
  124. end;
  125. procedure TColorSpeedButton.SetFStateNormal(AValue: TColorState);
  126. begin
  127. if FStateNormal = AValue then
  128. Exit;
  129. FStateNormal := AValue;
  130. Invalidate;
  131. end;
  132. procedure TColorSpeedButton.SetFTextAutoSize(AValue: boolean);
  133. begin
  134. if FTextAutoSize = AValue then
  135. Exit;
  136. FTextAutoSize := AValue;
  137. end;
  138. procedure TColorSpeedButton.SetFToggle(AValue: boolean);
  139. begin
  140. if FToggle = AValue then
  141. Exit;
  142. FToggle := AValue;
  143. Invalidate;
  144. end;
  145. {$ifdef overridepaint}
  146. procedure TColorSpeedButton.DrawText(ACanvas: TPersistent;
  147. Details: TThemedElementDetails; const S: string; R: TRect; Flags, Flags2: cardinal);
  148. var
  149. TXTStyle: TTextStyle;
  150. begin
  151. TXTStyle := Canvas.TextStyle;
  152. TXTStyle.Opaque := False;
  153. TXTStyle.Clipping := (Flags and DT_NOCLIP) = 0;
  154. TXTStyle.ShowPrefix := (Flags and DT_NOPREFIX) = 0;
  155. TXTStyle.SingleLine := (Flags and DT_SINGLELINE) <> 0;
  156. if (Flags and DT_CENTER) <> 0 then
  157. TXTStyle.Alignment := taCenter
  158. else
  159. if (Flags and DT_RIGHT) <> 0 then
  160. TXTStyle.Alignment := taRightJustify
  161. else
  162. TXTStyle.Alignment := taLeftJustify;
  163. if (Flags and DT_VCENTER) <> 0 then
  164. TXTStyle.Layout := tlCenter
  165. else
  166. if (Flags and DT_BOTTOM) <> 0 then
  167. TXTStyle.Layout := tlBottom
  168. else
  169. TXTStyle.Layout := tlTop;
  170. TXTStyle.RightToLeft := (Flags and DT_RTLREADING) <> 0;
  171. // set color here, otherwise SystemFont is wrong if the button was disabled before
  172. TXTStyle.SystemFont := Canvas.Font.IsDefault;//Match System Default Style
  173. TXTStyle.Wordbreak := (Flags and DT_WORDBREAK) <> 0;
  174. if not TXTStyle.Wordbreak then
  175. TXTStyle.EndEllipsis := (Flags and DT_END_ELLIPSIS) <> 0
  176. else
  177. TXTStyle.EndEllipsis := False;
  178. Canvas.TextRect(R, R.Left, R.Top, S, TXTStyle);
  179. end;
  180. procedure TColorSpeedButton.MeasureDraw(Draw: boolean; PaintRect: TRect;
  181. out PreferredWidth, PreferredHeight: integer);
  182. var
  183. GlyphWidth, GlyphHeight: integer;
  184. Offset, OffsetCap: TPoint;
  185. ClientSize, TotalSize, TextSize, GlyphSize: TSize;
  186. M, S: integer;
  187. SIndex: longint;
  188. TMP: string;
  189. TextFlags: integer;
  190. DrawDetails: TThemedElementDetails;
  191. FixedWidth: boolean;
  192. FixedHeight: boolean;
  193. TextRect: TRect;
  194. HasGlyph: boolean;
  195. HasText: boolean;
  196. CurLayout: TButtonLayout;
  197. begin
  198. if Glyph = nil then
  199. exit;
  200. DrawDetails := GetDrawDetails;
  201. PreferredWidth := 0;
  202. PreferredHeight := 0;
  203. if Draw then
  204. begin
  205. FLastDrawDetails := DrawDetails;
  206. PaintBackground(PaintRect);
  207. FixedWidth := True;
  208. FixedHeight := True;
  209. end
  210. else
  211. begin
  212. FixedWidth := WidthIsAnchored;
  213. FixedHeight := HeightIsAnchored;
  214. end;
  215. ClientSize.cx := PaintRect.Right - PaintRect.Left;
  216. ClientSize.cy := PaintRect.Bottom - PaintRect.Top;
  217. //debugln(['TCustomSpeedButton.MeasureDraw Step1 ',DbgSName(Self),' PaintRect=',dbgs(PaintRect)]);
  218. // compute size of glyph
  219. GlyphSize := GetGlyphSize(Draw, PaintRect);
  220. GlyphWidth := GlyphSize.CX;
  221. if NumGlyphs > 1 then
  222. GlyphWidth := GlyphWidth div NumGlyphs;
  223. GlyphHeight := GlyphSize.CY;
  224. HasGlyph := (GlyphWidth <> 0) and (GlyphHeight <> 0);
  225. //debugln(['TCustomSpeedButton.MeasureDraw Step2 ',DbgSName(Self),' PaintRect=',dbgs(PaintRect),' GlyphSize=',GlyphWidth,'x',GlyphHeight]);
  226. // compute size of text
  227. CurLayout := BidiAdjustButtonLayout(UseRightToLeftReading, Layout);
  228. if ShowCaption and (Caption <> '') then
  229. begin
  230. TextRect := PaintRect;
  231. // for wordbreak compute the maximum size for the text
  232. if Margin > 0 then
  233. InflateRect(TextRect, -Margin, -Margin);
  234. if HasGlyph then
  235. begin
  236. if (Spacing >= 0) then
  237. if CurLayout in [blGlyphLeft, blGlyphRight] then
  238. Dec(TextRect.Right, Spacing)
  239. else
  240. Dec(TextRect.Bottom, Spacing);
  241. if CurLayout in [blGlyphLeft, blGlyphRight] then
  242. Dec(TextRect.Right, GlyphWidth)
  243. else
  244. Dec(TextRect.Bottom, GlyphHeight);
  245. end;
  246. if not FixedWidth then
  247. begin
  248. TextRect.Left := 0;
  249. TextRect.Right := High(TextRect.Right) div 2;
  250. end;
  251. if not FixedHeight then
  252. begin
  253. TextRect.Top := 0;
  254. TextRect.Bottom := High(TextRect.Bottom) div 2;
  255. end;
  256. TextSize := GetTextSize(Draw, TextRect);
  257. end
  258. else
  259. begin
  260. TextSize.cx := 0;
  261. TextSize.cy := 0;
  262. end;
  263. HasText := (TextSize.cx <> 0) or (TextSize.cy <> 0);
  264. if Caption <> '' then
  265. begin
  266. TMP := Caption;
  267. SIndex := DeleteAmpersands(TMP);
  268. if SIndex > 0 then
  269. if SIndex <= Length(TMP) then
  270. begin
  271. //FShortcut := Ord(TMP[SIndex]);
  272. end;
  273. end;
  274. if HasGlyph and HasText then
  275. S := Spacing
  276. else
  277. S := 0;
  278. M := Margin;
  279. if not Draw then
  280. begin
  281. if M < 0 then
  282. M := 2;
  283. if S < 0 then
  284. S := M;
  285. end;
  286. // Calculate caption and glyph layout
  287. if M = -1 then
  288. begin
  289. // auto compute margin to center content
  290. if S = -1 then
  291. begin
  292. // use the same value for Spacing and Margin
  293. TotalSize.cx := TextSize.cx + GlyphWidth;
  294. TotalSize.cy := TextSize.cy + GlyphHeight;
  295. if Layout in [blGlyphLeft, blGlyphRight] then
  296. M := (ClientSize.cx - TotalSize.cx) div 3
  297. else
  298. M := (ClientSize.cy - TotalSize.cy) div 3;
  299. S := M;
  300. end
  301. else
  302. begin
  303. // fixed Spacing and center content
  304. TotalSize.cx := GlyphWidth + S + TextSize.cx;
  305. TotalSize.cy := GlyphHeight + S + TextSize.cy;
  306. if Layout in [blGlyphLeft, blGlyphRight] then
  307. M := (ClientSize.cx - TotalSize.cx) div 2
  308. else
  309. M := (ClientSize.cy - TotalSize.cy) div 2;
  310. end;
  311. end
  312. else
  313. begin
  314. // fixed Margin
  315. if S = -1 then
  316. begin
  317. // use the rest for Spacing between Glyph and Caption
  318. TotalSize.cx := ClientSize.cx - (Margin + GlyphWidth);
  319. TotalSize.cy := ClientSize.cy - (Margin + GlyphHeight);
  320. if Layout in [blGlyphLeft, blGlyphRight] then
  321. S := (TotalSize.cx - TextSize.cx) div 2
  322. else
  323. S := (TotalSize.cy - TextSize.cy) div 2;
  324. end;
  325. end;
  326. //debugln(['TCustomSpeedButton.MeasureDraw Step3 ',DbgSName(Self),' PaintRect=',dbgs(PaintRect),' GlyphSize=',GlyphWidth,'x',GlyphHeight,' TextSize=',TextSize.cx,'x',TextSize.cy,' S=',S,' M=',M]);
  327. if Draw then
  328. begin
  329. case CurLayout of
  330. blGlyphLeft:
  331. begin
  332. Offset.X := M;
  333. Offset.Y := (ClientSize.cy - GlyphHeight) div 2;
  334. OffsetCap.X := Offset.X + GlyphWidth + S;
  335. OffsetCap.Y := (ClientSize.cy - TextSize.cy) div 2;
  336. end;
  337. blGlyphRight:
  338. begin
  339. Offset.X := ClientSize.cx - M - GlyphWidth;
  340. Offset.Y := (ClientSize.cy - GlyphHeight) div 2;
  341. OffsetCap.X := Offset.X - S - TextSize.cx;
  342. OffsetCap.Y := (ClientSize.cy - TextSize.cy) div 2;
  343. end;
  344. blGlyphTop:
  345. begin
  346. Offset.X := (ClientSize.cx - GlyphWidth) div 2;
  347. Offset.Y := M;
  348. OffsetCap.X := (ClientSize.cx - TextSize.cx) div 2;
  349. OffsetCap.Y := Offset.Y + GlyphHeight + S;
  350. end;
  351. blGlyphBottom:
  352. begin
  353. Offset.X := (ClientSize.cx - GlyphWidth) div 2;
  354. Offset.Y := ClientSize.cy - M - GlyphHeight;
  355. OffsetCap.X := (ClientSize.cx - TextSize.cx) div 2;
  356. OffsetCap.Y := Offset.Y - S - TextSize.cy;
  357. end;
  358. end;
  359. DrawGlyph(Canvas, PaintRect, Offset, FState, Transparent, 0);
  360. if ShowCaption and (Caption <> '') then
  361. begin
  362. with PaintRect, OffsetCap do
  363. begin
  364. Left := Left + X;
  365. Top := Top + Y;
  366. end;
  367. TextFlags := DT_LEFT or DT_TOP;
  368. if UseRightToLeftReading then
  369. TextFlags := TextFlags or DT_RTLREADING;
  370. if Draw then
  371. DrawText(Canvas, DrawDetails, Caption, PaintRect,
  372. TextFlags, 0);
  373. end;
  374. end
  375. else
  376. begin
  377. // measuring, not drawing
  378. case CurLayout of
  379. blGlyphLeft, blGlyphRight:
  380. begin
  381. // use text size for autosize
  382. if FTextAutoSize then
  383. begin
  384. PreferredWidth := 2 * M + S + GlyphWidth + TextSize.cx;
  385. PreferredHeight := 2 * M + Max(GlyphHeight, TextSize.cy);
  386. end
  387. else
  388. begin
  389. // ignore text size width and height
  390. PreferredWidth := 2 * M + S + GlyphWidth;
  391. PreferredHeight := 2 * M + {Max(}GlyphHeight{, TextSize.cy)};
  392. end;
  393. end;
  394. blGlyphTop, blGlyphBottom:
  395. begin
  396. if FTextAutoSize then
  397. begin
  398. PreferredWidth := 2 * M + Max(GlyphWidth, TextSize.cx);
  399. PreferredHeight := 2 * M + S + GlyphHeight + TextSize.cy;
  400. end
  401. else
  402. begin
  403. // ignore text size width and height
  404. PreferredWidth := 2 * M + S + GlyphWidth;
  405. PreferredHeight := 2 * M + S + GlyphHeight{ + TextSize.cy};
  406. end;
  407. end;
  408. end;
  409. end;
  410. end;
  411. procedure TColorSpeedButton.Paint;
  412. var
  413. PaintRect: TRect;
  414. PreferredWidth: integer;
  415. PreferredHeight: integer;
  416. begin
  417. UpdateState(False);
  418. if Glyph = nil then
  419. exit;
  420. PaintRect := ClientRect;
  421. MeasureDraw(True, PaintRect, PreferredWidth, PreferredHeight);
  422. if Assigned(OnPaint) then
  423. OnPaint(Self);
  424. end;
  425. procedure TColorSpeedButton.CalculatePreferredSize(
  426. var PreferredWidth, PreferredHeight: integer; WithThemeSpace: boolean);
  427. var
  428. r: TRect;
  429. begin
  430. r := Rect(0, 0, 0, 0);
  431. MeasureDraw(False, r, PreferredWidth, PreferredHeight);
  432. end;
  433. {$endif}
  434. procedure TColorSpeedButton.PaintBackground(var PaintRect: TRect);
  435. var
  436. TempState: TButtonState;
  437. begin
  438. TempState := FState;
  439. if Toggle and Pressed then
  440. TempState := bsDown;
  441. Canvas.Pen.JoinStyle := pjsMiter; // remove rounded borders
  442. Canvas.Pen.Style := psInsideframe; // draws border width inside equally
  443. case TempState of
  444. bsUp:
  445. begin
  446. Canvas.Pen.Color := FStateNormal.BorderColor;
  447. Canvas.Pen.Width := FStateNormal.BorderWidth;
  448. Canvas.Brush.Color := FStateNormal.Color;
  449. end;
  450. bsDisabled:
  451. begin
  452. Canvas.Pen.Color := FStateDisabled.BorderColor;
  453. Canvas.Pen.Width := FStateDisabled.BorderWidth;
  454. Canvas.Brush.Color := FStateDisabled.Color;
  455. end;
  456. bsDown, bsExclusive:
  457. begin
  458. Canvas.Pen.Color := FStateActive.BorderColor;
  459. Canvas.Pen.Width := FStateActive.BorderWidth;
  460. Canvas.Brush.Color := FStateActive.Color;
  461. end;
  462. {$IFDEF FPC}//#
  463. bsHot:
  464. begin
  465. Canvas.Pen.Color := FStateHover.BorderColor;
  466. Canvas.Pen.Width := FStateHover.BorderWidth;
  467. Canvas.Brush.Color := FStateHover.Color;
  468. end;
  469. {$ENDIF}
  470. end;
  471. if Canvas.Pen.Width = 0 then
  472. Canvas.Pen.Color := Canvas.Brush.Color;
  473. Canvas.Rectangle(PaintRect);
  474. end;
  475. constructor TColorSpeedButton.Create(TheOwner: TComponent);
  476. begin
  477. inherited Create(TheOwner);
  478. FStateNormal := TColorState.Create(Self);
  479. FStateHover := TColorState.Create(Self);
  480. FStateActive := TColorState.Create(Self);
  481. FStateDisabled := TColorState.Create(Self);
  482. { Windows Style }
  483. FStateNormal.Color := RGBToColor(225, 225, 225);
  484. FStateNormal.BorderColor := RGBToColor(173, 173, 173);
  485. FStateHover.Color := RGBToColor(229, 241, 251);
  486. FStateHover.BorderColor := RGBToColor(0, 120, 215);
  487. FStateActive.Color := RGBToColor(204, 228, 247);
  488. FStateActive.BorderColor := RGBToColor(0, 84, 153);
  489. FStateDisabled.Color := RGBToColor(204, 204, 204);
  490. FStateDisabled.BorderColor := RGBToColor(191, 191, 191);
  491. Font.Color := clBlack;
  492. FTextAutoSize := True;
  493. end;
  494. destructor TColorSpeedButton.Destroy;
  495. begin
  496. FStateNormal.Free;
  497. FStateHover.Free;
  498. FStateActive.Free;
  499. FStateDisabled.Free;
  500. inherited Destroy;
  501. end;
  502. procedure TColorSpeedButton.Click;
  503. var
  504. p: TPoint;
  505. begin
  506. if Toggle then
  507. Pressed := not Pressed;
  508. if PopupMode and Assigned(PopupMenu) then
  509. begin
  510. p := Parent.ClientToScreen(Point(Left, Top));
  511. PopupMenu.PopUp(p.x, p.y + Height);
  512. end;
  513. inherited Click;
  514. end;
  515. { TColorState }
  516. procedure TColorState.SetFBorderColor(AValue: TColor);
  517. begin
  518. if FBorderColor = AValue then
  519. Exit;
  520. FBorderColor := AValue;
  521. FOwner.Perform(CM_CHANGED, 0, 0);
  522. FOwner.Invalidate;
  523. end;
  524. procedure TColorState.SetFBorderWidth(AValue: integer);
  525. begin
  526. if FBorderWidth = AValue then
  527. Exit;
  528. FBorderWidth := AValue;
  529. FOwner.Perform(CM_CHANGED, 0, 0);
  530. FOwner.Invalidate;
  531. end;
  532. procedure TColorState.SetFColor(AValue: TColor);
  533. begin
  534. if FColor = AValue then
  535. Exit;
  536. FColor := AValue;
  537. FOwner.Perform(CM_CHANGED, 0, 0);
  538. FOwner.Invalidate;
  539. end;
  540. constructor TColorState.Create(AOwner: TControl);
  541. begin
  542. inherited Create;
  543. FOwner := AOwner;
  544. BorderWidth := 1;
  545. BorderColor := clBlack;
  546. Color := clWhite;
  547. end;
  548. end.