2
0

kasbutton.pas 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. {
  2. Double Commander
  3. -------------------------------------------------------------------------
  4. Control like TButton which does not steal focus on click
  5. Copyright (C) 2021-2023 Alexander Koblov ([email protected])
  6. This library is free software; you can redistribute it and/or
  7. modify it under the terms of the GNU Lesser General Public
  8. License as published by the Free Software Foundation; either
  9. version 2.1 of the License, or (at your option) any later version.
  10. This library is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  13. Lesser General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. }
  17. unit KASButton;
  18. {$mode delphi}
  19. interface
  20. uses
  21. Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, ExtCtrls,
  22. Buttons, Themes, Types;
  23. type
  24. { TKASButton }
  25. TKASButton = class(TPanel)
  26. private
  27. FState: TButtonState;
  28. FShowCaption: Boolean;
  29. FButtonGlyph: TButtonGlyph;
  30. function GetGlyph: TBitmap;
  31. function IsGlyphStored: Boolean;
  32. procedure SetGlyph(AValue: TBitmap);
  33. procedure SetShowCaption(AValue: Boolean);
  34. function GetDrawDetails: TThemedElementDetails;
  35. protected
  36. procedure Paint; override;
  37. procedure DoExit; override;
  38. procedure DoEnter; override;
  39. procedure MouseEnter; override;
  40. procedure MouseLeave; override;
  41. procedure KeyUp(var Key: Word; Shift: TShiftState); override;
  42. procedure KeyDown(var Key: Word; Shift: TShiftState); override;
  43. procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
  44. procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
  45. protected
  46. procedure GlyphChanged(Sender: TObject);
  47. class function GetControlClassDefaultSize: TSize; override;
  48. procedure ActionChange(Sender: TObject; CheckDefaults: Boolean); override;
  49. procedure CalculatePreferredSize(var PreferredWidth, PreferredHeight: Integer; WithThemeSpace: Boolean); override;
  50. public
  51. constructor Create(TheOwner: TComponent); override;
  52. destructor Destroy; override;
  53. published
  54. property Action;
  55. property Glyph: TBitmap read GetGlyph write SetGlyph stored IsGlyphStored;
  56. property ShowCaption: Boolean read FShowCaption write SetShowCaption default True;
  57. end;
  58. procedure Register;
  59. implementation
  60. uses
  61. LCLType, LCLProc, LCLIntf, ActnList;
  62. procedure Register;
  63. begin
  64. RegisterComponents('KASComponents',[TKASButton]);
  65. end;
  66. { TKASButton }
  67. procedure TKASButton.DoEnter;
  68. begin
  69. inherited DoEnter;
  70. FState:= bsExclusive;
  71. Invalidate;
  72. end;
  73. procedure TKASButton.DoExit;
  74. begin
  75. inherited DoExit;
  76. FState:= bsUp;
  77. Invalidate;
  78. end;
  79. function TKASButton.GetDrawDetails: TThemedElementDetails;
  80. var
  81. Detail: TThemedButton;
  82. begin
  83. if not IsEnabled then
  84. Detail := tbPushButtonDisabled
  85. else if FState = bsDown then
  86. Detail := tbPushButtonPressed
  87. else if FState = bsHot then
  88. Detail := tbPushButtonHot
  89. else if FState = bsExclusive then
  90. Detail := tbPushButtonDefaulted
  91. else begin
  92. Detail := tbPushButtonNormal;
  93. end;
  94. Result := ThemeServices.GetElementDetails(Detail)
  95. end;
  96. procedure TKASButton.SetShowCaption(AValue: Boolean);
  97. begin
  98. if FShowCaption = AValue then Exit;
  99. FShowCaption:= AValue;
  100. Invalidate;
  101. end;
  102. function TKASButton.GetGlyph: TBitmap;
  103. begin
  104. Result:= FButtonGlyph.Glyph;
  105. end;
  106. function TKASButton.IsGlyphStored: Boolean;
  107. var
  108. Act: TCustomAction;
  109. begin
  110. if Action <> nil then
  111. begin
  112. Result:= True;
  113. Act:= TCustomAction(Action);
  114. if (Act.ActionList <> nil) and (Act.ActionList.Images <> nil) and
  115. (Act.ImageIndex >= 0) and (Act.ImageIndex < Act.ActionList.Images.Count) then
  116. Result := False;
  117. end
  118. else Result:= (FButtonGlyph.Glyph <> nil) and (not FButtonGlyph.Glyph.Empty) and
  119. (FButtonGlyph.Glyph.Width > 0) and (FButtonGlyph.Glyph.Height > 0);
  120. end;
  121. procedure TKASButton.SetGlyph(AValue: TBitmap);
  122. begin
  123. FButtonGlyph.Glyph := AValue;
  124. InvalidatePreferredSize;
  125. AdjustSize;
  126. end;
  127. procedure TKASButton.Paint;
  128. var
  129. APoint: TPoint;
  130. SysFont: TFont;
  131. PaintRect: TRect;
  132. TextFlags: Integer;
  133. Details: TThemedElementDetails;
  134. begin
  135. PaintRect:= ClientRect;
  136. Details:= GetDrawDetails;
  137. ThemeServices.DrawElement(Canvas.Handle, Details, PaintRect);
  138. PaintRect := ThemeServices.ContentRect(Canvas.Handle, Details, PaintRect);
  139. if FShowCaption and (Caption <> EmptyStr) then
  140. begin
  141. TextFlags := DT_CENTER or DT_VCENTER;
  142. if UseRightToLeftReading then begin
  143. TextFlags := TextFlags or DT_RTLREADING;
  144. end;
  145. SysFont := Screen.SystemFont;
  146. if (SysFont.Color = Font.Color) and
  147. ((SysFont.Name = Font.Name) or IsFontNameDefault(Font.Name)) and
  148. (SysFont.Pitch = Font.Pitch) and (SysFont.Style = Font.Style) then
  149. begin
  150. ThemeServices.DrawText(Canvas, Details, Caption, PaintRect, TextFlags, 0);
  151. end
  152. else begin
  153. Canvas.Brush.Style := bsClear;
  154. DrawText(Canvas.Handle, PChar(Caption), Length(Caption), PaintRect, TextFlags);
  155. end;
  156. end
  157. else if not FButtonGlyph.Glyph.Empty then
  158. begin
  159. APoint.X:= (PaintRect.Width - FButtonGlyph.Width) div 2;
  160. APoint.Y:= (PaintRect.Height - FButtonGlyph.Height) div 2;
  161. FButtonGlyph.Draw(Canvas, PaintRect, APoint, FState, True, 0);
  162. end;
  163. end;
  164. procedure TKASButton.MouseEnter;
  165. begin
  166. inherited MouseEnter;
  167. FState:= bsHot;
  168. Invalidate;
  169. end;
  170. procedure TKASButton.MouseLeave;
  171. begin
  172. inherited MouseLeave;
  173. FState:= bsUp;
  174. Invalidate;
  175. end;
  176. procedure TKASButton.KeyUp(var Key: Word; Shift: TShiftState);
  177. begin
  178. inherited KeyUp(Key, Shift);
  179. if (Key in [VK_SPACE, VK_RETURN]) and (Shift = []) then
  180. begin
  181. FState:= bsUp;
  182. Invalidate;
  183. Click;
  184. end;
  185. end;
  186. procedure TKASButton.KeyDown(var Key: Word; Shift: TShiftState);
  187. begin
  188. inherited KeyDown(Key, Shift);
  189. if (Key in [VK_SPACE, VK_RETURN]) and (Shift = []) then
  190. begin
  191. FState:= bsDown;
  192. Invalidate;
  193. end;
  194. end;
  195. procedure TKASButton.MouseUp(Button: TMouseButton; Shift: TShiftState; X,
  196. Y: Integer);
  197. begin
  198. inherited MouseUp(Button, Shift, X, Y);
  199. FState:= bsUp;
  200. Invalidate;
  201. end;
  202. procedure TKASButton.MouseDown(Button: TMouseButton; Shift: TShiftState;
  203. X, Y: Integer);
  204. begin
  205. inherited MouseDown(Button, Shift, X, Y);
  206. FState:= bsDown;
  207. Invalidate;
  208. end;
  209. procedure TKASButton.GlyphChanged(Sender: TObject);
  210. begin
  211. InvalidatePreferredSize;
  212. AdjustSize;
  213. end;
  214. class function TKASButton.GetControlClassDefaultSize: TSize;
  215. begin
  216. Result.CX := 23;
  217. Result.CY := 22;
  218. end;
  219. procedure TKASButton.ActionChange(Sender: TObject; CheckDefaults: Boolean);
  220. begin
  221. inherited ActionChange(Sender, CheckDefaults);
  222. if Sender is TCustomAction then
  223. begin
  224. with TCustomAction(Sender) do
  225. begin
  226. if (Glyph.Empty) and (ActionList <> nil) and (ActionList.Images <> nil) and
  227. (ImageIndex >= 0) and (ImageIndex < ActionList.Images.Count) then
  228. ActionList.Images.GetBitmap(ImageIndex, Glyph);
  229. end;
  230. end;
  231. end;
  232. procedure TKASButton.CalculatePreferredSize(var PreferredWidth,
  233. PreferredHeight: Integer; WithThemeSpace: Boolean);
  234. var
  235. PaintRect: TRect;
  236. ClientRect: TRect;
  237. Details: TThemedElementDetails;
  238. begin
  239. inherited CalculatePreferredSize(PreferredWidth, PreferredHeight, WithThemeSpace);
  240. if (not FButtonGlyph.Glyph.Empty) then
  241. begin
  242. Details:= GetDrawDetails;
  243. PaintRect:= TRect.Create(0, 0, 32, 32);
  244. ClientRect:= ThemeServices.ContentRect(Canvas.Handle, Details, PaintRect);
  245. PreferredWidth:= Abs(PaintRect.Width - ClientRect.Width) + FButtonGlyph.Width;
  246. PreferredHeight:= Abs(PaintRect.Height - ClientRect.Height) + FButtonGlyph.Height;
  247. end;
  248. end;
  249. constructor TKASButton.Create(TheOwner: TComponent);
  250. begin
  251. inherited Create(TheOwner);
  252. FButtonGlyph := TButtonGlyph.Create;
  253. FButtonGlyph.NumGlyphs := 1;
  254. FButtonGlyph.OnChange := GlyphChanged;
  255. FButtonGlyph.IsDesigning := csDesigning in ComponentState;
  256. FShowCaption:= True;
  257. TabStop:= True;
  258. end;
  259. destructor TKASButton.Destroy;
  260. begin
  261. FreeAndNil(FButtonGlyph);
  262. inherited Destroy;
  263. end;
  264. end.