bcbutton.pas 60 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139
  1. // SPDX-License-Identifier: LGPL-3.0-linking-exception
  2. { Customizable component which using BGRABitmap for drawing. Control mostly rendered
  3. using framework.
  4. Functionality:
  5. - Gradients
  6. - Double gradients
  7. - Rounding
  8. - Drop down list
  9. - Glyph
  10. - States (normal, hover, clicked)
  11. - Caption with shadow
  12. - Full alpha and antialias support
  13. originally written in 2012 by Krzysztof Dibowski dibowski at interia.pl
  14. }
  15. {******************************* CONTRIBUTOR(S) ******************************
  16. - Edivando S. Santos Brasil | [email protected]
  17. (Compatibility with delphi VCL 11/2018)
  18. ***************************** END CONTRIBUTOR(S) *****************************}
  19. unit BCButton;
  20. {$I bgracontrols.inc}
  21. interface
  22. uses
  23. Classes, types, {$IFDEF FPC}LCLType, LResources, {$ENDIF} Controls, Dialogs,
  24. ActnList, ImgList, Menus, // MORA
  25. Buttons, Graphics,
  26. {$IFNDEF FPC}BGRAGraphics, GraphType, FPImage, {$ENDIF}
  27. BGRABitmap, BGRABitmapTypes, BCThemeManager, BCTypes, Forms, BCBasectrls,
  28. fpjsonrtti, Typinfo, fpjson;
  29. {off $DEFINE DEBUG}
  30. type
  31. TBCButtonMemoryUsage = (bmuLow, bmuMedium, bmuHigh);
  32. TBCButtonState = class;
  33. TBCButtonStyle = (bbtButton, bbtDropDown);
  34. TOnAfterRenderBCButton = procedure(Sender: TObject; const ABGRA: TBGRABitmap;
  35. AState: TBCButtonState; ARect: TRect) of object;
  36. TBCButtonPropertyData = (pdNone, pdUpdateSize);
  37. // MORA: DropDown styles
  38. TBCButtonDropDownStyle = (
  39. bdsSeparate, // DropDown is a separate button (default)
  40. bdsCommon // DropDown is same as main button
  41. );
  42. TBCButtonDropDownPosition = (
  43. bdpLeft, // default
  44. bdpBottom);
  45. { TBCButtonState }
  46. TBCButtonState = class(TBCProperty)
  47. private
  48. FBackground: TBCBackground;
  49. FBorder: TBCBorder;
  50. FFontEx: TBCFont;
  51. procedure OnChangeFont({%H-}Sender: TObject; {%H-}AData: PtrInt);
  52. procedure OnChangeChildProperty({%H-}Sender: TObject; AData: PtrInt);
  53. procedure SetBackground(AValue: TBCBackground);
  54. procedure SetBorder(AValue: TBCBorder);
  55. procedure SetFontEx(const AValue: TBCFont);
  56. public
  57. constructor Create(AControl: TControl); override;
  58. destructor Destroy; override;
  59. procedure Assign(Source: TPersistent); override;
  60. procedure Scale(AScale: single; APreserveDefaultFontHeight: boolean = true);
  61. published
  62. property Background: TBCBackground read FBackground write SetBackground;
  63. property Border: TBCBorder read FBorder write SetBorder;
  64. property FontEx: TBCFont read FFontEx write SetFontEx;
  65. end;
  66. { TCustomBCButton }
  67. TCustomBCButton = class(TBCStyleGraphicControl)
  68. private
  69. { Private declarations }
  70. {$IFDEF INDEBUG}
  71. FRenderCount: integer;
  72. {$ENDIF}
  73. FDropDownArrowSize: integer;
  74. FDropDownWidth: integer;
  75. FFlipArrow: boolean;
  76. FActiveButt: TBCButtonStyle;
  77. FBGRANormal, FBGRAHover, FBGRAClick: TBGRABitmapEx;
  78. FCanvasScale, FRenderScale: Single;
  79. FCanvasScaleMode: TBCCanvasScaleMode;
  80. FScaled: boolean;
  81. FGlyphAlignment: TBCAlignment;
  82. FGlyphOldPlacement: boolean;
  83. FGlyphScale: single;
  84. FInnerMargin: single;
  85. FMemoryUsage: TBCButtonMemoryUsage;
  86. FPreserveGlyphOnAssign: boolean;
  87. FRounding: TBCRounding;
  88. FRoundingDropDown: TBCRounding;
  89. FStateClicked: TBCButtonState;
  90. FStateHover: TBCButtonState;
  91. FStateNormal: TBCButtonState;
  92. FDown: boolean;
  93. FGlyph: TBitmap;
  94. FGlyphMargin: integer;
  95. FButtonState: TBCMouseState;
  96. FDownButtonState: TBCMouseState;
  97. FOnAfterRenderBCButton: TOnAfterRenderBCButton;
  98. FOnButtonClick: TNotifyEvent;
  99. FStaticButton: boolean;
  100. FStyle: TBCButtonStyle;
  101. FGlobalOpacity: byte;
  102. FTextApplyGlobalOpacity: boolean;
  103. AutoSizeExtraY: integer;
  104. AutoSizeExtraX: integer;
  105. FLastBorderWidth: integer;
  106. // MORA
  107. FClickOffset: boolean;
  108. FDropDownArrow: boolean;
  109. FDropDownMenu: TPopupMenu;
  110. FDropDownMenuVisible: boolean;
  111. FDropDownClosingTime: TDateTime;
  112. FDropDownPosition: TBCButtonDropDownPosition;
  113. FDropDownStyle: TBCButtonDropDownStyle;
  114. FImageChangeLink: TChangeLink;
  115. FImageIndex: integer;
  116. FImages: TCustomImageList;
  117. FSaveDropDownClosed: TNotifyEvent;
  118. FShowCaption: boolean;
  119. procedure AssignDefaultStyle;
  120. procedure CalculateGlyphSize(out NeededWidth, NeededHeight: integer);
  121. procedure DropDownClosed(Sender: TObject);
  122. function GetBGRAClick: TBGRABitmapEx;
  123. function GetBGRAHover: TBGRABitmapEx;
  124. function GetBGRANormal: TBGRABitmapEx;
  125. procedure OnRestoreProperty(Sender: TObject; AObject: TObject;
  126. Info: PPropInfo; AValue: TJSONData; var Handled: Boolean);
  127. procedure OnStreamProperty(Sender: TObject; AObject: TObject;
  128. Info: PPropInfo; var Res: TJSONData);
  129. procedure RenderAll(ANow: boolean = False);
  130. function GetButtonRect: TRect;
  131. function GetDropDownWidth(AFull: boolean = True): integer;
  132. function GetDropDownRect(AFull: boolean = True): TRect;
  133. procedure SetBCButtonStateClicked(const AValue: TBCButtonState);
  134. procedure SetBCButtonStateHover(const AValue: TBCButtonState);
  135. procedure SetBCButtonStateNormal(const AValue: TBCButtonState);
  136. procedure SetCanvasScaleMode(AValue: TBCCanvasScaleMode);
  137. procedure SetClickOffset(AValue: boolean);
  138. procedure SetDown(AValue: boolean);
  139. procedure SetDropDownArrow(AValue: boolean);
  140. procedure SetDropDownArrowSize(AValue: integer);
  141. procedure SetDropDownPosition(AValue: TBCButtonDropDownPosition);
  142. procedure SetDropDownWidth(AValue: integer);
  143. procedure SetFlipArrow(AValue: boolean);
  144. procedure SetGlyph(const AValue: TBitmap);
  145. procedure SetGlyphAlignment(AValue: TBCAlignment);
  146. procedure SetGlyphMargin(const AValue: integer);
  147. procedure SetGlyphOldPlacement(AValue: boolean);
  148. procedure SetGlyphScale(AValue: single);
  149. procedure SetImageIndex(AValue: integer);
  150. procedure SetImages(AValue: TCustomImageList);
  151. procedure SetInnerMargin(AValue: single);
  152. procedure SetMemoryUsage(AValue: TBCButtonMemoryUsage);
  153. procedure SetRounding(AValue: TBCRounding);
  154. procedure SetRoundingDropDown(AValue: TBCRounding);
  155. procedure SetShowCaption(AValue: boolean);
  156. procedure SetStaticButton(const AValue: boolean);
  157. procedure SetStyle(const AValue: TBCButtonStyle);
  158. procedure SetGlobalOpacity(const AValue: byte);
  159. procedure SetTextApplyGlobalOpacity(const AValue: boolean);
  160. procedure UpdateSize;
  161. procedure OnChangeGlyph({%H-}Sender: TObject);
  162. procedure OnChangeState({%H-}Sender: TObject; AData: PtrInt);
  163. procedure ImageListChange(ASender: TObject);
  164. function GetGlyph: TBitmap;
  165. protected
  166. { Protected declarations }
  167. procedure LimitMemoryUsage;
  168. procedure CalculatePreferredSize(var PreferredWidth, PreferredHeight: integer;
  169. {%H-}WithThemeSpace: boolean); override;
  170. class function GetControlClassDefaultSize: TSize; override;
  171. procedure Click; override;
  172. procedure MouseDown(Button: TMouseButton; Shift: TShiftState;
  173. X, Y: integer); override;
  174. procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: integer); override;
  175. procedure MouseEnter; override;
  176. procedure MouseLeave; override;
  177. procedure MouseMove(Shift: TShiftState; X, Y: integer); override;
  178. procedure SetEnabled(Value: boolean); override;
  179. procedure TextChanged; override;
  180. protected
  181. // MORA
  182. procedure ActionChange(Sender: TObject; CheckDefaults: boolean); override;
  183. function GetActionLinkClass: TControlActionLinkClass; override;
  184. procedure Notification(AComponent: TComponent; Operation: TOperation);
  185. override;
  186. procedure Render(ABGRA: TBGRABitmapEx; AState: TBCButtonState); virtual;
  187. procedure RenderState(ABGRA: TBGRABitmapEx; AState: TBCButtonState;
  188. const ARect: TRect; ARounding: TBCRounding); virtual;
  189. property ClickOffset: boolean read FClickOffset write SetClickOffset default False;
  190. property DropDownArrow: boolean
  191. read FDropDownArrow write SetDropDownArrow default False;
  192. property DropDownMenu: TPopupMenu read FDropDownMenu write FDropDownMenu;
  193. property DropDownStyle: TBCButtonDropDownStyle
  194. read FDropDownStyle write FDropDownStyle default bdsSeparate;
  195. property DropDownPosition: TBCButtonDropDownPosition
  196. read FDropDownPosition write SetDropDownPosition default bdpLeft;
  197. property Images: TCustomImageList read FImages write SetImages;
  198. property ImageIndex: integer read FImageIndex write SetImageIndex default -1;
  199. property ShowCaption: boolean read FShowCaption write SetShowCaption default True;
  200. protected
  201. {$IFDEF INDEBUG}
  202. function GetDebugText: string; override;
  203. {$ENDIF}
  204. function GetStyleExtension: string; override;
  205. procedure ComputeScaling;
  206. procedure DrawControl; override;
  207. procedure RenderControl; override;
  208. property BGRANormal: TBGRABitmapEx read GetBGRANormal;
  209. property BGRAHover: TBGRABitmapEx read GetBGRAHover;
  210. property BGRAClick: TBGRABitmapEx read GetBGRAClick;
  211. protected
  212. property AutoSizeExtraVertical: integer read AutoSizeExtraY;
  213. property AutoSizeExtraHorizontal: integer read AutoSizeExtraX;
  214. property CanvasScaleMode: TBCCanvasScaleMode read FCanvasScaleMode write SetCanvasScaleMode default csmAuto;
  215. property Scaled: boolean read FScaled write FScaled default false;
  216. property StateNormal: TBCButtonState read FStateNormal write SetBCButtonStateNormal;
  217. property StateHover: TBCButtonState read FStateHover write SetBCButtonStateHover;
  218. property StateClicked: TBCButtonState read FStateClicked
  219. write SetBCButtonStateClicked;
  220. property Down: boolean read FDown write SetDown default False;
  221. property DropDownWidth: integer read FDropDownWidth write SetDropDownWidth;
  222. property DropDownArrowSize: integer read FDropDownArrowSize
  223. write SetDropDownArrowSize;
  224. property FlipArrow: boolean read FFlipArrow write SetFlipArrow default False;
  225. property Glyph: TBitmap read GetGlyph write SetGlyph;
  226. property GlyphScale: single read FGlyphScale write SetGlyphScale default 1;
  227. property GlyphMargin: integer read FGlyphMargin write SetGlyphMargin default 5;
  228. property GlyphAlignment: TBCAlignment read FGlyphAlignment write SetGlyphAlignment default bcaCenter;
  229. property GlyphOldPlacement: boolean read FGlyphOldPlacement write SetGlyphOldPlacement default true;
  230. property Style: TBCButtonStyle read FStyle write SetStyle default bbtButton;
  231. property StaticButton: boolean
  232. read FStaticButton write SetStaticButton default False;
  233. property GlobalOpacity: byte read FGlobalOpacity write SetGlobalOpacity;
  234. property Rounding: TBCRounding read FRounding write SetRounding;
  235. property RoundingDropDown: TBCRounding read FRoundingDropDown
  236. write SetRoundingDropDown;
  237. property TextApplyGlobalOpacity: boolean
  238. read FTextApplyGlobalOpacity write SetTextApplyGlobalOpacity;
  239. property OnAfterRenderBCButton: TOnAfterRenderBCButton
  240. read FOnAfterRenderBCButton write FOnAfterRenderBCButton;
  241. property OnButtonClick: TNotifyEvent read FOnButtonClick write FOnButtonClick;
  242. property MemoryUsage: TBCButtonMemoryUsage read FMemoryUsage write SetMemoryUsage;
  243. property InnerMargin: single read FInnerMargin write SetInnerMargin;
  244. property PreserveGlyphOnAssign: boolean read FPreserveGlyphOnAssign write FPreserveGlyphOnAssign default True;
  245. public
  246. { Constructor }
  247. constructor Create(AOwner: TComponent); override;
  248. { Destructor }
  249. destructor Destroy; override;
  250. { Assign the properties from Source to this instance }
  251. procedure Assign(Source: TPersistent); override;
  252. { Set dropdown size and autosize extra padding }
  253. procedure SetSizeVariables(newDropDownWidth, newDropDownArrowSize,
  254. newAutoSizeExtraVertical, newAutoSizeExtraHorizontal: integer);
  255. { Called by EndUpdate }
  256. procedure UpdateControl; override;
  257. property CanvasScale: single read FCanvasScale;
  258. public
  259. procedure ScaleStyle(AScale: single; APreserveDefaultFontHeight: boolean = true);
  260. {$IFDEF FPC}
  261. { Save all published settings to file }
  262. procedure SaveToFile(AFileName: string); override;
  263. procedure SaveToJSONFile(AFileName: string);
  264. function SaveToJSON: string;
  265. { Load and assign all published settings from file }
  266. procedure LoadFromFile(AFileName: string); override;
  267. procedure LoadFromJSONFile(AFileName: string);
  268. procedure LoadFromJSON(AJSON: string);
  269. { Assign the properties from AFileName to this instance }
  270. procedure AssignFromFile(AFileName: string); override;
  271. procedure AssignFromResource(AResourceName: string);
  272. {$ENDIF}
  273. { Used by SaveToFile/LoadFromFile }
  274. procedure OnFindClass({%H-}Reader: TReader; const AClassName: string;
  275. var ComponentClass: TComponentClass);
  276. end;
  277. TBCButton = class(TCustomBCButton)
  278. private
  279. FBCThemeManager: TBCThemeManager;
  280. procedure SetFBCThemeManager(AValue: TBCThemeManager);
  281. published
  282. property Action;
  283. property Align;
  284. property Anchors;
  285. { Click to edit the style. Available when editing only. If you want to stream the style from a file at runtime please use LoadFromFile and SaveToFile methods. }
  286. property AssignStyle;
  287. property AutoSize;
  288. { The style of the button when pressed. }
  289. property StateClicked;
  290. { The style of the button when hovered. }
  291. property StateHover;
  292. { The default style of the button. }
  293. property StateNormal;
  294. property BorderSpacing;
  295. property CanvasScaleMode;
  296. { Whether the component is DPI aware }
  297. property Scaled;
  298. property Caption;
  299. property Color;
  300. property Constraints;
  301. { Set to True to change the button to always show a StateClicked style that will not change when button is clicked or hovered. }
  302. property Down;
  303. { The width of the dropdown arrow area. }
  304. property DropDownWidth;
  305. { The size of the dropdown arrow. }
  306. property DropDownArrowSize;
  307. property Enabled;
  308. { Changes the direction of the arrow. Default: False. }
  309. property FlipArrow;
  310. { Set the opacity that will be applied to the whole button. Default: 255. }
  311. property GlobalOpacity;
  312. { The glyph icon. }
  313. property Glyph;
  314. property GlyphScale;
  315. property GlyphAlignment;
  316. property GlyphOldPlacement;
  317. property PreserveGlyphOnAssign;
  318. { The margin of the glyph icon. }
  319. property GlyphMargin;
  320. property Hint;
  321. property InnerMargin;
  322. { Called when the button finish the render. Use it to add your own drawings to the button. }
  323. property OnAfterRenderBCButton;
  324. { Called when the button part is clicked, not the dropdown. }
  325. property OnButtonClick;
  326. property OnClick;
  327. property OnDblClick;
  328. property OnMouseDown;
  329. property OnMouseEnter;
  330. property OnMouseLeave;
  331. property OnMouseMove;
  332. property OnMouseUp;
  333. property OnMouseWheel;
  334. property OnMouseWheelDown;
  335. property OnMouseWheelUp;
  336. property ParentColor;
  337. property PopupMenu;
  338. { Change the style of the rounded corners of the button. }
  339. property Rounding;
  340. { Change the style of the rounded corners of the dropdown part of the button. }
  341. property RoundingDropDown;
  342. { Set to True to change the button to always show a StateNormal style that will not change when button is clicked or hovered. }
  343. property StaticButton;
  344. property ShowHint;
  345. { The style of button that will be used. bbtButton or bbtDropDown. }
  346. property Style;
  347. { Apply the global opacity to rendered text. Default: False. }
  348. property TextApplyGlobalOpacity;
  349. property Visible;
  350. { -ToDo: Unused property? }
  351. property ClickOffset;
  352. { Show the dropdown arrow. }
  353. property DropDownArrow;
  354. { The dropdown menu that will be displayed when the button is pressed. }
  355. property DropDownMenu;
  356. { The kind of dropdown that will be used. bdsSeparate will show the dropdown down the dropdown arrow side. bdsCommon will show the dropdown down the whole button. }
  357. property DropDownStyle;
  358. { The position of the dropdown arrow. }
  359. property DropDownPosition;
  360. { The image list that holds an image to be used with the button ImageIndex property. }
  361. property Images;
  362. { The index of the image that will be used for the button as glyph icon if glyph property is not set. }
  363. property ImageIndex;
  364. { Show caption or hides it. Default: True. }
  365. property ShowCaption;
  366. { Limit memory usage by selecting one of the options. Default: bmuHigh. }
  367. property MemoryUsage;
  368. { The unique name of the control in the form. }
  369. property Name;
  370. property ThemeManager: TBCThemeManager read FBCThemeManager write SetFBCThemeManager;
  371. end;
  372. { TBCButtonActionLink }
  373. TBCButtonActionLink = class(TControlActionLink)
  374. protected
  375. procedure AssignClient(AClient: TObject); override;
  376. procedure SetChecked(Value: boolean); override;
  377. procedure SetImageIndex(Value: integer); override;
  378. public
  379. function IsCheckedLinked: boolean; override;
  380. function IsImageIndexLinked: boolean; override;
  381. end;
  382. {$IFDEF FPC}procedure Register;{$ENDIF}
  383. implementation
  384. uses {$IFDEF FPC}LCLIntf, PropEdits, GraphPropEdits, LCLProc, {$ENDIF}Math, BCTools, SysUtils;
  385. const
  386. DropDownReopenDelay = 0.2/(24*60*60);
  387. {$IFDEF FPC}//#
  388. type
  389. TBCButtonImageIndexPropertyEditor = class(TImageIndexPropertyEditor)
  390. protected
  391. function GetImageList: TCustomImageList; override;
  392. end;
  393. {$ENDIF}
  394. { TBCButton }
  395. procedure TBCButton.SetFBCThemeManager(AValue: TBCThemeManager);
  396. begin
  397. if FBCThemeManager=AValue then Exit;
  398. FBCThemeManager:=AValue;
  399. end;
  400. {$IFDEF FPC}//#
  401. function TBCButtonImageIndexPropertyEditor.GetImageList: TCustomImageList;
  402. var
  403. Component: TPersistent;
  404. begin
  405. Component := GetComponent(0);
  406. if Component is TCustomBCButton then
  407. Result := TCustomBCButton(Component).Images
  408. else
  409. Result := nil;
  410. end;
  411. {$ENDIF}
  412. {$IFDEF FPC}
  413. procedure Register;
  414. begin
  415. {$R images\bgracontrols_images.res}
  416. RegisterComponents('BGRA Button Controls', [TBCButton]);
  417. RegisterPropertyEditor(TypeInfo(integer), TBCButton,
  418. 'ImageIndex', TBCButtonImageIndexPropertyEditor);
  419. end;
  420. {$ENDIF}
  421. { TBCButtonActionLink }
  422. procedure TBCButtonActionLink.AssignClient(AClient: TObject);
  423. begin
  424. inherited AssignClient(AClient);
  425. FClient := AClient as TCustomBCButton;
  426. end;
  427. procedure TBCButtonActionLink.SetChecked(Value: boolean);
  428. begin
  429. if IsCheckedLinked then
  430. TCustomBCButton(FClient).Down := Value;
  431. end;
  432. procedure TBCButtonActionLink.SetImageIndex(Value: integer);
  433. begin
  434. if IsImageIndexLinked then
  435. TCustomBCButton(FClient).ImageIndex := Value;
  436. end;
  437. function TBCButtonActionLink.IsCheckedLinked: boolean;
  438. begin
  439. Result := inherited IsCheckedLinked and (TCustomBCButton(FClient).Down =
  440. (Action as TCustomAction).Checked);
  441. end;
  442. function TBCButtonActionLink.IsImageIndexLinked: boolean;
  443. begin
  444. Result := inherited IsImageIndexLinked and
  445. (TCustomBCButton(FClient).ImageIndex = (Action as TCustomAction).ImageIndex);
  446. end;
  447. { TBCButtonState }
  448. procedure TBCButtonState.SetFontEx(const AValue: TBCFont);
  449. begin
  450. if FFontEx = AValue then
  451. exit;
  452. FFontEx.Assign(AValue);
  453. Change;
  454. end;
  455. procedure TBCButtonState.OnChangeFont(Sender: TObject; AData: PtrInt);
  456. begin
  457. Change(PtrInt(pdUpdateSize));
  458. end;
  459. procedure TBCButtonState.OnChangeChildProperty(Sender: TObject; AData: PtrInt);
  460. begin
  461. Change(AData);
  462. end;
  463. procedure TBCButtonState.SetBackground(AValue: TBCBackground);
  464. begin
  465. if FBackground = AValue then
  466. Exit;
  467. FBackground.Assign(AValue);
  468. Change;
  469. end;
  470. procedure TBCButtonState.SetBorder(AValue: TBCBorder);
  471. begin
  472. if FBorder = AValue then
  473. Exit;
  474. FBorder.Assign(AValue);
  475. Change;
  476. end;
  477. constructor TBCButtonState.Create(AControl: TControl);
  478. begin
  479. FBackground := TBCBackground.Create(AControl);
  480. FBorder := TBCBorder.Create(AControl);
  481. FFontEx := TBCFont.Create(AControl);
  482. FBackground.OnChange := OnChangeChildProperty;
  483. FBorder.OnChange := OnChangeChildProperty;
  484. FFontEx.OnChange := OnChangeFont;
  485. inherited Create(AControl);
  486. end;
  487. destructor TBCButtonState.Destroy;
  488. begin
  489. FBackground.Free;
  490. FBorder.Free;
  491. FFontEx.Free;
  492. inherited Destroy;
  493. end;
  494. procedure TBCButtonState.Assign(Source: TPersistent);
  495. begin
  496. if Source is TBCButtonState then
  497. begin
  498. FBackground.Assign(TBCButtonState(Source).FBackground);
  499. FBorder.Assign(TBCButtonState(Source).FBorder);
  500. FFontEx.Assign(TBCButtonState(Source).FFontEx);
  501. Change(PtrInt(pdUpdateSize));
  502. end
  503. else
  504. inherited Assign(Source);
  505. end;
  506. procedure TBCButtonState.Scale(AScale: single; APreserveDefaultFontHeight: boolean);
  507. begin
  508. FBackground.Scale(AScale);
  509. FBorder.Scale(AScale);
  510. FFontEx.Scale(AScale, APreserveDefaultFontHeight);
  511. end;
  512. { TCustomBCButton }
  513. procedure TCustomBCButton.AssignDefaultStyle;
  514. begin
  515. FRounding.RoundX := 12;
  516. FRounding.RoundY := 12;
  517. // Normal
  518. with StateNormal do
  519. begin
  520. Border.Style := bboNone;
  521. FontEx.Color := RGBToColor(230, 230, 255);
  522. FontEx.Style := [fsBold];
  523. FontEx.Shadow := True;
  524. FontEx.ShadowOffsetX := 1;
  525. FontEx.ShadowOffsetY := 1;
  526. FontEx.ShadowRadius := 2;
  527. Background.Gradient1EndPercent := 60;
  528. Background.Style := bbsGradient;
  529. // Gradient1
  530. with Background.Gradient1 do
  531. begin
  532. EndColor := RGBToColor(64, 64, 128);
  533. StartColor := RGBToColor(0, 0, 64);
  534. end;
  535. // Gradient2
  536. with Background.Gradient2 do
  537. begin
  538. EndColor := RGBToColor(0, 0, 64);
  539. GradientType := gtRadial;
  540. Point1XPercent := 50;
  541. Point1YPercent := 100;
  542. Point2YPercent := 0;
  543. StartColor := RGBToColor(64, 64, 128);
  544. end;
  545. end;
  546. // Hover
  547. with StateHover do
  548. begin
  549. Border.Style := bboNone;
  550. FontEx.Color := RGBToColor(255, 255, 255);
  551. FontEx.Style := [fsBold];
  552. FontEx.Shadow := True;
  553. FontEx.ShadowOffsetX := 1;
  554. FontEx.ShadowOffsetY := 1;
  555. FontEx.ShadowRadius := 2;
  556. Background.Gradient1EndPercent := 100;
  557. Background.Style := bbsGradient;
  558. // Gradient1
  559. with Background.Gradient1 do
  560. begin
  561. EndColor := RGBToColor(0, 64, 128);
  562. GradientType := gtRadial;
  563. Point1XPercent := 50;
  564. Point1YPercent := 100;
  565. Point2YPercent := 0;
  566. StartColor := RGBToColor(0, 128, 255);
  567. end;
  568. end;
  569. // Clicked
  570. with StateClicked do
  571. begin
  572. Border.Style := bboNone;
  573. FontEx.Color := RGBToColor(230, 230, 255);
  574. FontEx.Style := [fsBold];
  575. FontEx.Shadow := True;
  576. FontEx.ShadowOffsetX := 1;
  577. FontEx.ShadowOffsetY := 1;
  578. FontEx.ShadowRadius := 2;
  579. Background.Gradient1EndPercent := 100;
  580. Background.Style := bbsGradient;
  581. // Gradient1
  582. with Background.Gradient1 do
  583. begin
  584. EndColor := RGBToColor(0, 0, 64);
  585. GradientType := gtRadial;
  586. Point1XPercent := 50;
  587. Point1YPercent := 100;
  588. Point2YPercent := 0;
  589. StartColor := RGBToColor(0, 64, 128);
  590. end;
  591. end;
  592. end;
  593. procedure TCustomBCButton.CalculateGlyphSize(out NeededWidth, NeededHeight: integer);
  594. begin
  595. if Assigned(FGlyph) and not FGlyph.Empty then
  596. begin
  597. NeededWidth := ceil(FGlyph.Width * FGlyphScale * FRenderScale);
  598. NeededHeight := ceil(FGlyph.Height * FGlyphScale * FRenderScale);
  599. end
  600. else
  601. if Assigned(FImages) then
  602. begin
  603. NeededWidth := FImages.ResolutionForPPI[FImages.Width, Screen.PixelsPerInch, 1].Width;
  604. NeededHeight := FImages.ResolutionForPPI[FImages.Width, Screen.PixelsPerInch, 1].Height;
  605. end
  606. else
  607. begin
  608. NeededHeight := 0;
  609. NeededWidth := 0;
  610. end;
  611. end;
  612. procedure TCustomBCButton.RenderAll(ANow: boolean);
  613. begin
  614. if (csCreating in ControlState) or IsUpdating then
  615. Exit;
  616. if ANow then
  617. begin
  618. Render(FBGRANormal, FStateNormal);
  619. Render(FBGRAHover, FStateHover);
  620. Render(FBGRAClick, FStateClicked);
  621. end
  622. else
  623. begin
  624. if Assigned(FBGRANormal) then FBGRANormal.NeedRender := True;
  625. if Assigned(FBGRAHover) then FBGRAHover.NeedRender := True;
  626. if Assigned(FBGRAClick) then FBGRAClick.NeedRender := True;
  627. end;
  628. end;
  629. function TCustomBCButton.GetButtonRect: TRect;
  630. begin
  631. Result := GetClientRect;
  632. if FStyle = bbtDropDown then
  633. case FDropDownPosition of
  634. bdpBottom:
  635. Dec(Result.Bottom, GetDropDownWidth(False));
  636. else
  637. // bdpLeft:
  638. Dec(Result.Right, GetDropDownWidth(False));
  639. end;
  640. end;
  641. function TCustomBCButton.GetDropDownWidth(AFull: boolean): integer;
  642. begin
  643. Result := round(
  644. (FDropDownWidth + (ifthen(AFull, 2, 1) * FStateNormal.FBorder.Width))
  645. * FRenderScale / FCanvasScale);
  646. end;
  647. function TCustomBCButton.GetGlyph: TBitmap;
  648. begin
  649. Result := FGlyph as TBitmap;
  650. end;
  651. function TCustomBCButton.GetDropDownRect(AFull: boolean): TRect;
  652. begin
  653. Result := GetClientRect;
  654. case FDropDownPosition of
  655. bdpBottom:
  656. Result.Top := Result.Bottom - GetDropDownWidth(AFull);
  657. else
  658. // bdpLeft:
  659. Result.Left := Result.Right - GetDropDownWidth(AFull);
  660. end;
  661. end;
  662. procedure TCustomBCButton.Render(ABGRA: TBGRABitmapEx; AState: TBCButtonState);
  663. procedure GetActualGlyph(out ABitmap: TBitmap; out AScale: single);
  664. begin
  665. if Assigned(FGlyph) and not FGlyph.Empty then
  666. begin
  667. ABitmap := FGlyph;
  668. AScale := FRenderScale * FGlyphScale;
  669. end else
  670. if Assigned(FImages) and (FImageIndex > -1) and (FImageIndex < FImages.Count) then
  671. begin
  672. ABitmap := TBitmap.Create;
  673. {$IFDEF FPC}
  674. FImages.ResolutionForPPI[FImages.Width, Screen.PixelsPerInch, FCanvasScale].GetBitmap(FImageIndex, ABitmap);
  675. AScale := 1;
  676. {$ELSE}
  677. FImages.GetBitmapRaw(FImageIndex, result);
  678. AScale := Screen.PixelsPerInch / 96 * FCanvasScale;
  679. {$ENDIF}
  680. end else
  681. begin
  682. ABitmap := nil;
  683. AScale := 1;
  684. end;
  685. end;
  686. procedure RenderGlyph(ARect: TRect; AGlyph: TBitmap);
  687. begin
  688. if ARect.IsEmpty or (AGlyph = nil) then exit;
  689. ABGRA.StretchPutImage(ARect, AGlyph, dmLinearBlend);
  690. end;
  691. var
  692. r, r_a, r_g: TRect;
  693. g: TBitmap;
  694. actualCaption: TCaption;
  695. freeScaled: boolean;
  696. scaledState: TBCButtonState;
  697. scaledArrowSize, scaledGlyphMargin, scaledInnerMargin: integer;
  698. scaledRounding, scaledRoundingDropDown: TBCRounding;
  699. gScale: single;
  700. begin
  701. if (csCreating in ControlState) or IsUpdating or (ABGRA = nil) then
  702. Exit;
  703. if FRenderScale <> 1 then
  704. begin
  705. scaledState := TBCButtonState.Create(nil);
  706. scaledState.Assign(AState);
  707. scaledState.Scale(FRenderScale, false);
  708. scaledRounding := TBCRounding.Create(nil);
  709. scaledRounding.Assign(Rounding);
  710. scaledRounding.Scale(FRenderScale);
  711. scaledRoundingDropDown := TBCRounding.Create(nil);
  712. scaledRoundingDropDown.Assign(RoundingDropDown);
  713. scaledRoundingDropDown.Scale(FRenderScale);
  714. freeScaled := true;
  715. end
  716. else
  717. begin
  718. scaledState := AState;
  719. scaledRounding := Rounding;
  720. scaledRoundingDropDown := RoundingDropDown;
  721. freeScaled := false;
  722. end;
  723. scaledArrowSize := round(DropDownArrowSize * FRenderScale);
  724. scaledGlyphMargin := round(GlyphMargin * FRenderScale);
  725. scaledInnerMargin := round(InnerMargin * FRenderScale);
  726. ABGRA.NeedRender := False;
  727. { Refreshing size }
  728. ABGRA.SetSize(round(Width * FCanvasScale), round(Height * FCanvasScale));
  729. { Clearing previous paint }
  730. ABGRA.Fill(BGRAPixelTransparent);
  731. { Basic body }
  732. r := ScaleRect(GetButtonRect, FCanvasScale);
  733. RenderState(ABGRA, scaledState, r, scaledRounding);
  734. if not GlyphOldPlacement then
  735. r.Inflate(-scaledInnerMargin,-scaledInnerMargin);
  736. { Calculating rect }
  737. CalculateBorderRect(scaledState.Border, r);
  738. if FStyle = bbtDropDown then
  739. begin
  740. r_a := ScaleRect(GetDropDownRect, FCanvasScale);
  741. RenderState(ABGRA, scaledState, r_a, scaledRoundingDropDown);
  742. CalculateBorderRect(scaledState.Border, r_a);
  743. // Click offset for arrow
  744. if FClickOffset and (AState = FStateClicked) then
  745. r_a.Offset(1,1);
  746. if FFlipArrow then
  747. RenderArrow(TBGRABitmap(ABGRA), r_a, scaledArrowSize, badUp,
  748. scaledState.FontEx.Color)
  749. else
  750. RenderArrow(TBGRABitmap(ABGRA), r_a, scaledArrowSize, badDown,
  751. scaledState.FontEx.Color);
  752. end;
  753. // Click offset for text and glyph
  754. if FClickOffset and (AState = FStateClicked) then
  755. r.Offset(round(1 * FRenderScale), round(1 * FRenderScale));
  756. // DropDown arrow
  757. if FDropDownArrow and (FStyle <> bbtDropDown) then
  758. begin
  759. r_a := r;
  760. r_a.Left := r_a.Right - round(FDropDownWidth * FRenderScale);
  761. if FFlipArrow then
  762. RenderArrow(TBGRABitmap(ABGRA), r_a, scaledArrowSize, badUp,
  763. scaledState.FontEx.Color)
  764. else
  765. RenderArrow(TBGRABitmap(ABGRA), r_a, scaledArrowSize, badDown,
  766. scaledState.FontEx.Color);
  767. Dec(R.Right, round(FDropDownWidth * FRenderScale));
  768. end;
  769. GetActualGlyph(g, gScale);
  770. if FShowCaption then actualCaption := self.Caption else actualCaption := '';
  771. r_g := ComputeGlyphPosition(r, g, GlyphAlignment, scaledGlyphMargin, actualCaption,
  772. scaledState.FontEx, GlyphOldPlacement, gScale);
  773. if FTextApplyGlobalOpacity then
  774. begin
  775. { Drawing text }
  776. RenderText(r, scaledState.FontEx, actualCaption, ABGRA, Enabled);
  777. RenderGlyph(r_g, g);
  778. { Set global opacity }
  779. ABGRA.ApplyGlobalOpacity(FGlobalOpacity);
  780. end
  781. else
  782. begin
  783. { Set global opacity }
  784. ABGRA.ApplyGlobalOpacity(FGlobalOpacity);
  785. { Drawing text }
  786. RenderText(r, scaledState.FontEx, actualCaption, ABGRA, Enabled);
  787. RenderGlyph(r_g, g);
  788. end;
  789. if g <> FGlyph then g.Free;
  790. { Convert to gray if not enabled }
  791. if not Enabled then ABGRA.InplaceGrayscale;
  792. if Assigned(FOnAfterRenderBCButton) then
  793. FOnAfterRenderBCButton(Self, ABGRA, scaledState, r);
  794. if freeScaled then
  795. begin
  796. FreeAndNil(scaledState);
  797. FreeAndNil(scaledRounding);
  798. FreeAndNil(scaledRoundingDropDown);
  799. end;
  800. {$IFDEF INDEBUG}
  801. FRenderCount := FRenderCount +1;
  802. {$ENDIF}
  803. end;
  804. procedure TCustomBCButton.RenderState(ABGRA: TBGRABitmapEx;
  805. AState: TBCButtonState; const ARect: TRect; ARounding: TBCRounding);
  806. begin
  807. RenderBackgroundAndBorder(ARect, AState.FBackground, TBGRABitmap(ABGRA),
  808. ARounding, AState.FBorder, round(FInnerMargin * FRenderScale));
  809. end;
  810. procedure TCustomBCButton.OnChangeGlyph(Sender: TObject);
  811. begin
  812. RenderControl;
  813. UpdateSize;
  814. Invalidate;
  815. end;
  816. procedure TCustomBCButton.OnChangeState(Sender: TObject; AData: PtrInt);
  817. begin
  818. RenderControl;
  819. if (TBCButtonPropertyData(AData) = pdUpdateSize) or
  820. (FStateNormal.Border.Width <> FLastBorderWidth) then
  821. UpdateSize;
  822. Invalidate;
  823. end;
  824. procedure TCustomBCButton.ImageListChange(ASender: TObject);
  825. begin
  826. if ASender = Images then
  827. begin
  828. RenderControl;
  829. Invalidate;
  830. end;
  831. end;
  832. procedure TCustomBCButton.LimitMemoryUsage;
  833. begin
  834. {$IFNDEF FPC}//# //@ IN DELPHI NEEDRENDER NEED TO BE TRUE. IF FALSE COMPONENT IN BGRANORMAL BE BLACK AFTER INVALIDATE.
  835. if Assigned(FBGRANormal) then FBGRANormal.NeedRender := True;
  836. if Assigned(FBGRAHover) then FBGRAHover.NeedRender := True;
  837. if Assigned(FBGRAClick) then FBGRAClick.NeedRender := True;
  838. {$ENDIF}
  839. if (FMemoryUsage = bmuLow) and Assigned(FBGRANormal) then FBGRANormal.Discard;
  840. if (FMemoryUsage <> bmuHigh) then
  841. begin
  842. if Assigned(FBGRAHover) then FBGRAHover.Discard;
  843. if Assigned(FBGRAClick) then FBGRAClick.Discard;
  844. end;
  845. end;
  846. procedure TCustomBCButton.SetBCButtonStateClicked(const AValue: TBCButtonState);
  847. begin
  848. if FStateClicked = AValue then
  849. exit;
  850. FStateClicked.Assign(AValue);
  851. RenderControl;
  852. Invalidate;
  853. end;
  854. procedure TCustomBCButton.SetBCButtonStateHover(const AValue: TBCButtonState);
  855. begin
  856. if FStateHover = AValue then
  857. exit;
  858. FStateHover.Assign(AValue);
  859. RenderControl;
  860. Invalidate;
  861. end;
  862. procedure TCustomBCButton.SetBCButtonStateNormal(const AValue: TBCButtonState);
  863. begin
  864. if FStateNormal = AValue then
  865. exit;
  866. FStateNormal.Assign(AValue);
  867. RenderControl;
  868. Invalidate;
  869. end;
  870. procedure TCustomBCButton.SetCanvasScaleMode(AValue: TBCCanvasScaleMode);
  871. begin
  872. if FCanvasScaleMode=AValue then Exit;
  873. FCanvasScaleMode:=AValue;
  874. RenderControl;
  875. Invalidate;
  876. end;
  877. procedure TCustomBCButton.SetClickOffset(AValue: boolean);
  878. begin
  879. if FClickOffset = AValue then
  880. Exit;
  881. FClickOffset := AValue;
  882. RenderControl;
  883. end;
  884. procedure TCustomBCButton.SetDown(AValue: boolean);
  885. begin
  886. if FDown = AValue then
  887. exit;
  888. FDown := AValue;
  889. if FDown then
  890. FButtonState := msClicked
  891. else
  892. FButtonState := msNone;
  893. RenderControl;
  894. Invalidate;
  895. end;
  896. procedure TCustomBCButton.SetDropDownArrow(AValue: boolean);
  897. begin
  898. if FDropDownArrow = AValue then
  899. Exit;
  900. FDropDownArrow := AValue;
  901. RenderControl;
  902. Invalidate;
  903. end;
  904. procedure TCustomBCButton.SetDropDownArrowSize(AValue: integer);
  905. begin
  906. if FDropDownArrowSize = AValue then
  907. Exit;
  908. FDropDownArrowSize := AValue;
  909. RenderControl;
  910. Invalidate;
  911. end;
  912. procedure TCustomBCButton.SetDropDownPosition(AValue: TBCButtonDropDownPosition);
  913. begin
  914. if FDropDownPosition = AValue then
  915. Exit;
  916. FDropDownPosition := AValue;
  917. if FStyle <> bbtDropDown then
  918. Exit;
  919. RenderControl;
  920. UpdateSize;
  921. Invalidate;
  922. end;
  923. procedure TCustomBCButton.SetDropDownWidth(AValue: integer);
  924. begin
  925. if FDropDownWidth = AValue then
  926. Exit;
  927. FDropDownWidth := AValue;
  928. RenderControl;
  929. UpdateSize;
  930. Invalidate;
  931. end;
  932. procedure TCustomBCButton.SetFlipArrow(AValue: boolean);
  933. begin
  934. if FFlipArrow = AValue then
  935. Exit;
  936. FFlipArrow := AValue;
  937. RenderControl;
  938. Invalidate;
  939. end;
  940. procedure TCustomBCButton.SetGlyph(const AValue: TBitmap);
  941. begin
  942. if (FGlyph <> nil) and (FGlyph = AValue) then
  943. exit;
  944. FGlyph.Assign(AValue);
  945. RenderControl;
  946. UpdateSize;
  947. Invalidate;
  948. end;
  949. procedure TCustomBCButton.SetGlyphAlignment(AValue: TBCAlignment);
  950. begin
  951. if FGlyphAlignment=AValue then Exit;
  952. FGlyphAlignment:=AValue;
  953. RenderControl;
  954. UpdateSize;
  955. Invalidate;
  956. end;
  957. procedure TCustomBCButton.SetGlyphMargin(const AValue: integer);
  958. begin
  959. if FGlyphMargin = AValue then
  960. exit;
  961. FGlyphMargin := AValue;
  962. RenderControl;
  963. UpdateSize;
  964. Invalidate;
  965. end;
  966. procedure TCustomBCButton.SetGlyphOldPlacement(AValue: boolean);
  967. begin
  968. if FGlyphOldPlacement=AValue then Exit;
  969. FGlyphOldPlacement:=AValue;
  970. RenderControl;
  971. UpdateSize;
  972. Invalidate;
  973. end;
  974. procedure TCustomBCButton.SetGlyphScale(AValue: single);
  975. begin
  976. if FGlyphScale=AValue then Exit;
  977. FGlyphScale:=AValue;
  978. RenderControl;
  979. UpdateSize;
  980. Invalidate;
  981. end;
  982. procedure TCustomBCButton.SetImageIndex(AValue: integer);
  983. begin
  984. if FImageIndex = AValue then
  985. Exit;
  986. FImageIndex := AValue;
  987. RenderControl;
  988. Invalidate;
  989. end;
  990. procedure TCustomBCButton.SetImages(AValue: TCustomImageList);
  991. begin
  992. if FImages = AValue then
  993. Exit;
  994. FImages := AValue;
  995. RenderControl;
  996. UpdateSize;
  997. Invalidate;
  998. end;
  999. procedure TCustomBCButton.SetInnerMargin(AValue: single);
  1000. begin
  1001. if FInnerMargin=AValue then Exit;
  1002. FInnerMargin:=AValue;
  1003. RenderControl;
  1004. UpdateSize;
  1005. Invalidate;
  1006. end;
  1007. procedure TCustomBCButton.SetMemoryUsage(AValue: TBCButtonMemoryUsage);
  1008. begin
  1009. if FMemoryUsage=AValue then Exit;
  1010. FMemoryUsage:=AValue;
  1011. LimitMemoryUsage;
  1012. end;
  1013. procedure TCustomBCButton.SetRounding(AValue: TBCRounding);
  1014. begin
  1015. if FRounding = AValue then
  1016. Exit;
  1017. FRounding.Assign(AValue);
  1018. RenderControl;
  1019. Invalidate;
  1020. end;
  1021. procedure TCustomBCButton.SetRoundingDropDown(AValue: TBCRounding);
  1022. begin
  1023. if FRoundingDropDown = AValue then
  1024. Exit;
  1025. FRoundingDropDown.Assign(AValue);
  1026. RenderControl;
  1027. Invalidate;
  1028. end;
  1029. procedure TCustomBCButton.SetShowCaption(AValue: boolean);
  1030. begin
  1031. if FShowCaption = AValue then
  1032. Exit;
  1033. FShowCaption := AValue;
  1034. RenderControl;
  1035. UpdateSize;
  1036. Invalidate;
  1037. end;
  1038. procedure TCustomBCButton.SetStaticButton(const AValue: boolean);
  1039. begin
  1040. if FStaticButton = AValue then
  1041. exit;
  1042. FStaticButton := AValue;
  1043. RenderControl;
  1044. Invalidate;
  1045. end;
  1046. procedure TCustomBCButton.SetStyle(const AValue: TBCButtonStyle);
  1047. begin
  1048. if FStyle = AValue then
  1049. exit;
  1050. FStyle := AValue;
  1051. RenderControl;
  1052. UpdateSize;
  1053. Invalidate;
  1054. end;
  1055. procedure TCustomBCButton.UpdateSize;
  1056. begin
  1057. InvalidatePreferredSize;
  1058. AdjustSize;
  1059. end;
  1060. procedure TCustomBCButton.CalculatePreferredSize(
  1061. var PreferredWidth, PreferredHeight: integer; WithThemeSpace: boolean);
  1062. var
  1063. // AWidth: integer;
  1064. gh, gw, gm: integer;
  1065. actualCaption: TCaption;
  1066. horizAlign, relHorizAlign: TAlignment;
  1067. vertAlign, relVertAlign: TTextLayout;
  1068. glyphHorzMargin, glyphVertMargin: integer;
  1069. tw, th, availW: integer;
  1070. scaledFont: TBCFont;
  1071. ownScaledFont: Boolean;
  1072. begin
  1073. if (Parent = nil) or (not Parent.HandleAllocated) then
  1074. Exit;
  1075. ComputeScaling;
  1076. FLastBorderWidth := FStateNormal.Border.Width;
  1077. CalculateGlyphSize(gw, gh);
  1078. gm := round(GlyphMargin * FRenderScale);
  1079. // more precise computation of font with Retina scaling
  1080. // and DPI aware computation
  1081. if (FRenderScale <> 1) and FShowCaption then
  1082. begin
  1083. scaledFont := TBCFont.Create(nil);
  1084. scaledFont.Assign(FStateNormal.FontEx);
  1085. scaledFont.Scale(FRenderScale, false);
  1086. ownScaledFont := true;
  1087. end else
  1088. begin
  1089. scaledFont := FStateNormal.FontEx;
  1090. ownScaledFont := false;
  1091. end;
  1092. if GlyphOldPlacement then
  1093. begin
  1094. { if WidthIsAnchored then
  1095. AWidth := Width
  1096. else
  1097. AWidth := 10000;}
  1098. PreferredWidth := 0;
  1099. PreferredHeight := 0;
  1100. if FShowCaption then
  1101. begin
  1102. CalculateTextSize(Caption, scaledFont, PreferredWidth, PreferredHeight);
  1103. end;
  1104. // Extra pixels for DropDown
  1105. if Style = bbtDropDown then
  1106. if FDropDownPosition in [bdpBottom] then
  1107. Inc(PreferredHeight, round(GetDropDownWidth * FCanvasScale))
  1108. else
  1109. Inc(PreferredWidth, round(GetDropDownWidth * FCanvasScale));
  1110. if (Style = bbtButton) and FDropDownArrow then
  1111. Inc(PreferredWidth, round(FDropDownArrowSize * FRenderScale));
  1112. //if (FGlyph <> nil) and (not FGlyph.Empty) then
  1113. if (gw > 0) and (gh > 0) then
  1114. begin
  1115. //if Caption = '' then
  1116. if PreferredWidth = 0 then
  1117. begin
  1118. Inc(PreferredWidth, gw{ - AutoSizeExtraY * 2});
  1119. Inc(PreferredHeight, gh);
  1120. end
  1121. else
  1122. begin
  1123. Inc(PreferredWidth, gw + gm);
  1124. if gh > PreferredHeight then
  1125. PreferredHeight := gh;
  1126. end;
  1127. end;
  1128. // Extra pixels for AutoSize
  1129. Inc(PreferredWidth, round(AutoSizeExtraX * FRenderScale));
  1130. Inc(PreferredHeight, round(AutoSizeExtraY * FRenderScale));
  1131. end else
  1132. begin
  1133. if ShowCaption then actualCaption := Caption else actualCaption := '';
  1134. PreferredWidth := round(InnerMargin * FRenderScale);
  1135. PreferredHeight := round(InnerMargin * FRenderScale);
  1136. case FStyle of
  1137. bbtDropDown:
  1138. case FDropDownPosition of
  1139. bdpBottom: inc(PreferredHeight, round(GetDropDownWidth(False) * FCanvasScale));
  1140. else{bdpLeft} inc(PreferredWidth, round(GetDropDownWidth(False) * FCanvasScale));
  1141. end;
  1142. else{bbtButton} if FDropDownArrow then
  1143. inc(PreferredWidth, round(FDropDownWidth * FRenderScale));
  1144. end;
  1145. inc(PreferredWidth, round(FStateNormal.Border.Width * FRenderScale));
  1146. inc(PreferredHeight, round(FStateNormal.Border.Width * FRenderScale));
  1147. if actualCaption='' then
  1148. begin
  1149. inc(PreferredWidth,gw);
  1150. inc(PreferredHeight,gh);
  1151. if gw>0 then inc(PreferredWidth, gm*2);
  1152. if gh>0 then inc(PreferredHeight, gm*2);
  1153. end else
  1154. begin
  1155. GetGlyphActualLayout(actualCaption, scaledFont, GlyphAlignment, gm,
  1156. horizAlign, vertAlign, relHorizAlign, relVertAlign, glyphHorzMargin, glyphVertMargin);
  1157. availW := 65535;
  1158. if (Align in [alTop,alBottom]) and (Parent <> nil) then
  1159. availW := round((Parent.ClientWidth - BorderSpacing.Left - BorderSpacing.Right) * FCanvasScale - PreferredWidth);
  1160. CalculateTextSizeEx(actualCaption, scaledFont, tw, th, availW);
  1161. if (tw<>0) and scaledFont.WordBreak then inc(tw);
  1162. if vertAlign<>relVertAlign then
  1163. begin
  1164. inc(PreferredWidth, max(gw+2*gm,tw));
  1165. inc(PreferredHeight, gm+gh+th);
  1166. end
  1167. else
  1168. begin
  1169. inc(PreferredWidth, gm+gw+tw);
  1170. inc(PreferredHeight, max(gh+2*gm,th));
  1171. end;
  1172. end;
  1173. end;
  1174. if ownScaledFont then scaledFont.Free;
  1175. PreferredWidth:= ceil(PreferredWidth / FCanvasScale);
  1176. PreferredHeight := ceil(PreferredHeight / FCanvasScale);
  1177. end;
  1178. class function TCustomBCButton.GetControlClassDefaultSize: TSize;
  1179. begin
  1180. Result.CX := 123;
  1181. Result.CY := 33;
  1182. end;
  1183. procedure TCustomBCButton.Click;
  1184. begin
  1185. if (FActiveButt = bbtDropDown) and Assigned(FOnButtonClick) then
  1186. begin
  1187. FOnButtonClick(Self);
  1188. Exit;
  1189. end;
  1190. inherited Click;
  1191. end;
  1192. procedure TCustomBCButton.DropDownClosed(Sender: TObject);
  1193. begin
  1194. if Assigned(FSaveDropDownClosed) then
  1195. FSaveDropDownClosed(Sender);
  1196. {$IFDEF FPC}//#
  1197. if Assigned(FDropDownMenu) then
  1198. FDropDownMenu.OnClose := FSaveDropDownClosed;
  1199. {$ENDIF}
  1200. FDropDownMenuVisible := False;
  1201. FDropDownClosingTime := Now;
  1202. end;
  1203. function TCustomBCButton.GetBGRAClick: TBGRABitmapEx;
  1204. begin
  1205. if FBGRAClick = nil then
  1206. begin
  1207. FBGRAClick := TBGRABitmapEx.Create(round(Width * FCanvasScale),
  1208. round(Height * FCanvasScale), BGRAPixelTransparent);
  1209. FBGRAClick.CustomData := PtrInt(FStateClicked);
  1210. end;
  1211. result := FBGRAClick;
  1212. end;
  1213. function TCustomBCButton.GetBGRAHover: TBGRABitmapEx;
  1214. begin
  1215. if FBGRAHover = nil then
  1216. begin
  1217. FBGRAHover := TBGRABitmapEx.Create(round(Width * FCanvasScale),
  1218. round(Height * FCanvasScale), BGRAPixelTransparent);
  1219. FBGRAHover.CustomData := PtrInt(FStateHover);
  1220. end;
  1221. result := FBGRAHover;
  1222. end;
  1223. function TCustomBCButton.GetBGRANormal: TBGRABitmapEx;
  1224. begin
  1225. if FBGRANormal = nil then
  1226. begin
  1227. FBGRANormal := TBGRABitmapEx.Create(round(Width * FCanvasScale),
  1228. round(Height * FCanvasScale), BGRAPixelTransparent);
  1229. FBGRANormal.CustomData := PtrInt(FStateNormal);
  1230. end;
  1231. result := FBGRANormal;
  1232. end;
  1233. procedure TCustomBCButton.OnRestoreProperty(Sender: TObject; AObject: TObject;
  1234. Info: PPropInfo; AValue: TJSONData; var Handled: Boolean);
  1235. var
  1236. bgracolor: TBGRAPixel;
  1237. begin
  1238. Handled := False;
  1239. if (Info^.PropType^.Name = 'TGraphicsColor') then
  1240. begin
  1241. Handled := True;
  1242. bgracolor := StrToBGRA(AValue.AsString);
  1243. SetPropValue(AObject, Info, BGRAToColor(bgracolor));
  1244. end;
  1245. // fix to don't assign null values
  1246. if AValue.JSONType = jtNULL then
  1247. Handled := True;
  1248. end;
  1249. procedure TCustomBCButton.OnStreamProperty(Sender: TObject; AObject: TObject;
  1250. Info: PPropInfo; var Res: TJSONData);
  1251. var
  1252. bgracolor: TBGRAPixel;
  1253. begin
  1254. if (Info^.PropType^.Name = 'TGraphicsColor') then
  1255. begin
  1256. bgracolor := ColorToBGRA(TColor(GetPropValue(AObject, Info, False)));
  1257. Res.Free;
  1258. Res := TJSONString.Create('rgb('+IntToStr(bgracolor.red)+','+IntToStr(bgracolor.green)+','+IntToStr(bgracolor.blue)+')');
  1259. end;
  1260. end;
  1261. procedure TCustomBCButton.MouseDown(Button: TMouseButton; Shift: TShiftState;
  1262. X, Y: integer);
  1263. var
  1264. ClientToScreenPoint : TPoint;
  1265. begin
  1266. inherited MouseDown(Button, Shift, X, Y);
  1267. if csDesigning in ComponentState then
  1268. exit;
  1269. if (Button = mbLeft) and Enabled {and (not (FButtonState = msClicked)) } then
  1270. begin
  1271. case FActiveButt of
  1272. bbtButton:
  1273. if not (FButtonState = msClicked) then
  1274. begin
  1275. FButtonState := msClicked;
  1276. if FDropDownStyle = bdsCommon then
  1277. FDownButtonState := msClicked
  1278. else
  1279. FDownButtonState := msNone;
  1280. Invalidate;
  1281. end;
  1282. bbtDropDown:
  1283. if not (FDownButtonState = msClicked) then
  1284. begin
  1285. if FDropDownStyle = bdsCommon then
  1286. FButtonState := msClicked
  1287. else
  1288. FButtonState := msNone;
  1289. FDownButtonState := msClicked;
  1290. Invalidate;
  1291. end;
  1292. end;
  1293. // Old
  1294. {FButtonState := msClicked;
  1295. Invalidate;}
  1296. // MORA: Show DropDown menu
  1297. if FDropDownMenuVisible or (Now < FDropDownClosingTime+DropDownReopenDelay) then
  1298. FDropDownMenuVisible := False // Prevent redropping
  1299. else
  1300. if ((FActiveButt = bbtDropDown) or (FStyle = bbtButton)) and
  1301. (FDropDownMenu <> nil) and Enabled then
  1302. begin
  1303. ClientToScreenPoint := ClientToScreen(Point(0, Height));
  1304. with ClientToScreenPoint do
  1305. begin
  1306. // normal button
  1307. if FStyle = bbtButton then
  1308. begin
  1309. x := x + Width * integer(FDropDownMenu.Alignment = paRight);
  1310. if FFlipArrow then
  1311. y := y -Height;
  1312. end
  1313. else
  1314. // dropdown button
  1315. begin
  1316. if FDropDownPosition = bdpBottom then
  1317. begin
  1318. x := x + Width * integer(FDropDownMenu.Alignment = paRight);
  1319. if FFlipArrow then
  1320. y := y -(FDropDownWidth + (FStateNormal.FBorder.Width * 2));
  1321. end
  1322. else
  1323. begin
  1324. if FFlipArrow then
  1325. y := y -Height;
  1326. if FDropDownStyle = bdsSeparate then
  1327. x := x + Width - (FDropDownWidth + (FStateNormal.FBorder.Width * 2)) *
  1328. integer(FDropDownMenu.Alignment <> paRight)
  1329. else
  1330. x := x + Width * integer(FDropDownMenu.Alignment = paRight);
  1331. end;
  1332. end;
  1333. FDropDownMenuVisible := True;
  1334. {$IFDEF FPC}//#
  1335. FSaveDropDownClosed := FDropDownMenu.OnClose;
  1336. FDropDownMenu.OnClose := DropDownClosed;
  1337. {$ENDIF}
  1338. FDropDownMenu.PopUp(x, y);
  1339. end;
  1340. end;
  1341. end;
  1342. end;
  1343. procedure TCustomBCButton.MouseUp(Button: TMouseButton; Shift: TShiftState;
  1344. X, Y: integer);
  1345. {var
  1346. p: TPoint;}
  1347. begin
  1348. inherited MouseUp(Button, Shift, X, Y);
  1349. if csDesigning in ComponentState then
  1350. exit;
  1351. if (Button = mbLeft) and Enabled {and (FButtonState = msClicked)} then
  1352. begin
  1353. case FActiveButt of
  1354. bbtButton:
  1355. if FButtonState = msClicked then
  1356. begin
  1357. FButtonState := msHover;
  1358. if FDropDownStyle = bdsCommon then
  1359. FDownButtonState := msHover
  1360. else
  1361. FDownButtonState := msNone;
  1362. Invalidate;
  1363. end;
  1364. bbtDropDown:
  1365. if FDownButtonState = msClicked then
  1366. begin
  1367. FDownButtonState := msHover;
  1368. if FDropDownStyle = bdsCommon then
  1369. FButtonState := msHover
  1370. else
  1371. FButtonState := msNone;
  1372. Invalidate;
  1373. end;
  1374. end;
  1375. // Old
  1376. {FButtonState := msHover;
  1377. Invalidate;}
  1378. end;
  1379. //if (FActiveButt = bbtDropDown) and (PopupMenu <> nil) and Enabled then
  1380. //begin
  1381. // if FFlipArrow then
  1382. // p := ClientToScreen(Point(Width - FDropDownWidth - (FStateNormal.FBorder.Width * 2),
  1383. // {PopupMenu.Height} -1))
  1384. // else
  1385. // p := ClientToScreen(Point(Width - FDropDownWidth - (FStateNormal.FBorder.Width * 2), Height + 1));
  1386. // PopupMenu.PopUp(p.x, p.y);
  1387. //end;
  1388. end;
  1389. procedure TCustomBCButton.MouseEnter;
  1390. begin
  1391. if csDesigning in ComponentState then
  1392. exit;
  1393. case FActiveButt of
  1394. bbtButton:
  1395. begin
  1396. if FDown then
  1397. FButtonState := msClicked
  1398. else
  1399. FButtonState := msHover;
  1400. if FDropDownStyle = bdsSeparate then
  1401. FDownButtonState := msNone
  1402. else
  1403. FDownButtonState := msHover;
  1404. end;
  1405. bbtDropDown:
  1406. begin
  1407. if FDown then
  1408. FButtonState := msClicked
  1409. else
  1410. if FDropDownStyle = bdsSeparate then
  1411. FButtonState := msNone
  1412. else
  1413. FButtonState := msHover;
  1414. FDownButtonState := msHover;
  1415. end;
  1416. end;
  1417. Invalidate;
  1418. // Old
  1419. {FButtonState := msHover;
  1420. Invalidate;}
  1421. inherited MouseEnter;
  1422. end;
  1423. procedure TCustomBCButton.MouseLeave;
  1424. begin
  1425. if csDesigning in ComponentState then
  1426. exit;
  1427. if FDown then
  1428. begin
  1429. FButtonState := msClicked;
  1430. FActiveButt := bbtButton;
  1431. end
  1432. else
  1433. FButtonState := msNone;
  1434. FDownButtonState := msNone;
  1435. Invalidate;
  1436. inherited MouseLeave;
  1437. end;
  1438. procedure TCustomBCButton.MouseMove(Shift: TShiftState; X, Y: integer);
  1439. function IsOverDropDown: boolean;
  1440. begin
  1441. with GetButtonRect do
  1442. case FDropDownPosition of
  1443. bdpBottom:
  1444. Result := Y > Bottom;
  1445. else
  1446. Result := X > GetButtonRect.Right;
  1447. end;
  1448. end;
  1449. begin
  1450. inherited MouseMove(Shift, X, Y);
  1451. if FStyle = bbtButton then
  1452. FActiveButt := bbtButton
  1453. else
  1454. begin
  1455. // Calling invalidate only when active button changed. Otherwise, we leave
  1456. // this for LCL. This reduce paint call
  1457. if (FActiveButt = bbtButton) and IsOverDropDown then
  1458. begin
  1459. FActiveButt := bbtDropDown;
  1460. if FDropDownStyle <> bdsCommon then // Don't need invalidating
  1461. begin
  1462. FDownButtonState := msHover;
  1463. if FDown then
  1464. FButtonState := msClicked
  1465. else
  1466. FButtonState := msNone;
  1467. Invalidate;
  1468. end;
  1469. end
  1470. else
  1471. if (FActiveButt = bbtDropDown) and not IsOverDropDown then
  1472. begin
  1473. FActiveButt := bbtButton;
  1474. if FDropDownStyle <> bdsCommon then // Don't need invalidating
  1475. begin
  1476. if FDown then
  1477. FButtonState := msClicked
  1478. else
  1479. FButtonState := msHover;
  1480. FDownButtonState := msNone;
  1481. Invalidate;
  1482. end;
  1483. end;
  1484. end;
  1485. end;
  1486. procedure TCustomBCButton.SetEnabled(Value: boolean);
  1487. begin
  1488. inherited SetEnabled(Value);
  1489. RenderControl;
  1490. Invalidate;
  1491. end;
  1492. procedure TCustomBCButton.TextChanged;
  1493. begin
  1494. inherited TextChanged;
  1495. RenderControl;
  1496. UpdateSize;
  1497. Invalidate;
  1498. end;
  1499. procedure TCustomBCButton.ActionChange(Sender: TObject; CheckDefaults: boolean);
  1500. var
  1501. NewAction: TCustomAction;
  1502. begin
  1503. inherited ActionChange(Sender, CheckDefaults);
  1504. if Sender is TCustomAction then
  1505. begin
  1506. NewAction := TCustomAction(Sender);
  1507. if (not CheckDefaults) or (not Down) then
  1508. Down := NewAction.Checked;
  1509. if (not CheckDefaults) or (ImageIndex < 0) then
  1510. ImageIndex := NewAction.ImageIndex;
  1511. end;
  1512. end;
  1513. function TCustomBCButton.GetActionLinkClass: TControlActionLinkClass;
  1514. begin
  1515. Result := TBCButtonActionLink;
  1516. end;
  1517. procedure TCustomBCButton.Notification(AComponent: TComponent; Operation: TOperation);
  1518. begin
  1519. inherited Notification(AComponent, Operation);
  1520. if (AComponent = FImages) and (Operation = opRemove) then
  1521. Images := nil;
  1522. end;
  1523. procedure TCustomBCButton.UpdateControl;
  1524. begin
  1525. RenderControl;
  1526. inherited UpdateControl; // indalidate
  1527. end;
  1528. procedure TCustomBCButton.ScaleStyle(AScale: single; APreserveDefaultFontHeight: boolean);
  1529. begin
  1530. StateNormal.Scale(AScale, APreserveDefaultFontHeight);
  1531. StateHover.Scale(AScale, APreserveDefaultFontHeight);
  1532. StateClicked.Scale(AScale, APreserveDefaultFontHeight);
  1533. Rounding.Scale(AScale);
  1534. RoundingDropDown.Scale(AScale);
  1535. DropDownWidth:= round(DropDownWidth*AScale);
  1536. DropDownArrowSize:= round(DropDownArrowSize*AScale);
  1537. GlyphMargin:= round(GlyphMargin*AScale);
  1538. GlyphScale := GlyphScale*AScale;
  1539. InnerMargin:= round(InnerMargin*AScale);
  1540. end;
  1541. {$IFDEF FPC}//#
  1542. procedure TCustomBCButton.SaveToFile(AFileName: string);
  1543. var
  1544. AStream: TMemoryStream;
  1545. begin
  1546. AStream := TMemoryStream.Create;
  1547. try
  1548. WriteComponentAsTextToStream(AStream, Self);
  1549. AStream.SaveToFile(AFileName);
  1550. finally
  1551. AStream.Free;
  1552. end;
  1553. end;
  1554. procedure TCustomBCButton.SaveToJSONFile(AFileName: string);
  1555. begin
  1556. with TStringList.Create do
  1557. begin
  1558. try
  1559. Text := SaveToJSON;
  1560. SaveToFile(AFileName);
  1561. finally
  1562. Free;
  1563. end;
  1564. end;
  1565. end;
  1566. function TCustomBCButton.SaveToJSON: string;
  1567. var
  1568. Streamer: TJSONStreamer;
  1569. begin
  1570. Streamer := TJSONStreamer.Create(nil);
  1571. try
  1572. Streamer.OnStreamProperty := OnStreamProperty;
  1573. Result := Streamer.ObjectToJSONString(Self);
  1574. finally
  1575. Streamer.Destroy;
  1576. end;
  1577. end;
  1578. procedure TCustomBCButton.LoadFromFile(AFileName: string);
  1579. var
  1580. AStream: TMemoryStream;
  1581. begin
  1582. AStream := TMemoryStream.Create;
  1583. try
  1584. AStream.LoadFromFile(AFileName);
  1585. ReadComponentFromTextStream(AStream, TComponent(Self), OnFindClass);
  1586. finally
  1587. AStream.Free;
  1588. end;
  1589. end;
  1590. procedure TCustomBCButton.LoadFromJSONFile(AFileName: string);
  1591. var
  1592. sFile: TStringList;
  1593. begin
  1594. try
  1595. sFile := TStringList.Create;
  1596. sFile.LoadFromFile(AFileName);
  1597. LoadFromJSON(sFile.Text);
  1598. finally
  1599. sFile.Free;
  1600. end;
  1601. end;
  1602. procedure TCustomBCButton.LoadFromJSON(AJSON: string);
  1603. var
  1604. DeStreamer: TJSONDeStreamer;
  1605. temp: TBCButton;
  1606. begin
  1607. temp := TBCButton.Create(nil);
  1608. DeStreamer := TJSONDeStreamer.Create(nil);
  1609. try
  1610. DeStreamer.OnRestoreProperty := OnRestoreProperty;
  1611. DeStreamer.JSONToObject(AJSON, temp);
  1612. // Cascading
  1613. Self.BeginUpdate;
  1614. Self.Assign(temp);
  1615. Self.StateNormal.Assign(temp.StateNormal);
  1616. Self.StateHover.Assign(temp.StateNormal);
  1617. Self.StateClicked.Assign(temp.StateNormal);
  1618. // All other properties
  1619. DeStreamer.JSONToObject(AJSON, Self);
  1620. Self.EndUpdate;
  1621. finally
  1622. temp.Free;
  1623. DeStreamer.Destroy;
  1624. end;
  1625. end;
  1626. procedure TCustomBCButton.AssignFromFile(AFileName: string);
  1627. var
  1628. AStream: TMemoryStream;
  1629. AButton: TBCButton;
  1630. begin
  1631. AButton := TBCButton.Create(nil);
  1632. AStream := TMemoryStream.Create;
  1633. try
  1634. AStream.LoadFromFile(AFileName);
  1635. ReadComponentFromTextStream(AStream, TComponent(AButton), OnFindClass);
  1636. Assign(AButton);
  1637. finally
  1638. AStream.Free;
  1639. AButton.Free;
  1640. end;
  1641. end;
  1642. procedure TCustomBCButton.AssignFromResource(AResourceName: string);
  1643. var
  1644. AStream : TStream;
  1645. AButton : TBCButton;
  1646. begin
  1647. AButton := TBCButton.Create(nil);
  1648. try
  1649. AStream := BGRAResource.GetResourceStream(AResourceName);
  1650. ReadComponentFromTextStream(AStream, TComponent(AButton), OnFindClass);
  1651. Assign(AButton);
  1652. finally
  1653. AStream.Free;
  1654. AButton.Free;
  1655. end;
  1656. end;
  1657. {$ENDIF}
  1658. procedure TCustomBCButton.OnFindClass(Reader: TReader; const AClassName: string;
  1659. var ComponentClass: TComponentClass);
  1660. begin
  1661. if CompareText(AClassName, 'TBCButton') = 0 then
  1662. ComponentClass := TBCButton;
  1663. end;
  1664. {$IFDEF INDEBUG}
  1665. function TCustomBCButton.GetDebugText: string;
  1666. begin
  1667. Result := 'R: ' + IntToStr(FRenderCount);
  1668. end;
  1669. {$ENDIF}
  1670. procedure TCustomBCButton.ComputeScaling;
  1671. begin
  1672. // Scaling relative to screen coordinates
  1673. if (CanvasScaleMode = csmFullResolution) or
  1674. ((CanvasScaleMode = csmAuto) and not Assigned(OnAfterRenderBCButton)) then
  1675. FCanvasScale := GetCanvasScaleFactor
  1676. else FCanvasScale := 1;
  1677. // Scaling relative to DPI and or screen coordinates
  1678. if Scaled then
  1679. FRenderScale := (Screen.PixelsPerInch / GetDesignTimePPI(self)) * FCanvasScale
  1680. else
  1681. FRenderScale := FCanvasScale;
  1682. end;
  1683. procedure TCustomBCButton.DrawControl;
  1684. var
  1685. bgra: TBGRABitmapEx;
  1686. r: TRect;
  1687. begin
  1688. ComputeScaling;
  1689. // If style is without dropdown button or state of each button
  1690. // is the same (possible only for msNone) or static button then
  1691. // we can draw whole BGRABitmap
  1692. if (FStyle = bbtButton) or (FButtonState = FDownButtonState) or FStaticButton then
  1693. begin
  1694. // Main button
  1695. if FStaticButton then
  1696. bgra := BGRANormal
  1697. else
  1698. if FDown then
  1699. bgra := BGRAClick
  1700. else
  1701. case FButtonState of
  1702. msNone: bgra := BGRANormal;
  1703. msHover: bgra := BGRAHover;
  1704. msClicked: bgra := BGRAClick;
  1705. end;
  1706. if {%H-}bgra.NeedRender then
  1707. Render(bgra, TBCButtonState(bgra.CustomData));
  1708. bgra.Draw(Self.Canvas, rect(0, 0, Width, Height), False);
  1709. end
  1710. // Otherwise we must draw part of state for each button
  1711. else
  1712. begin
  1713. // The active button must be draw as last because right edge of button and
  1714. // left edge of dropdown are overlapping each other, so we must draw edge
  1715. // for current state of active button
  1716. case FActiveButt of
  1717. bbtButton:
  1718. begin
  1719. // Drop down button
  1720. case FDownButtonState of
  1721. msNone: bgra := BGRANormal;
  1722. msHover: bgra := BGRAHover;
  1723. msClicked: bgra := BGRAClick;
  1724. end;
  1725. if bgra.NeedRender then
  1726. Render(bgra, TBCButtonState(bgra.CustomData));
  1727. r := GetDropDownRect;
  1728. bgra.DrawPart(ScaleRect(r, FCanvasScale), Self.Canvas, r, False);
  1729. // Main button
  1730. if FDown then
  1731. bgra := BGRAClick
  1732. else
  1733. case FButtonState of
  1734. msNone: bgra := BGRANormal;
  1735. msHover: bgra := BGRAHover;
  1736. msClicked: bgra := BGRAClick;
  1737. end;
  1738. if bgra.NeedRender then
  1739. Render(bgra, TBCButtonState(bgra.CustomData));
  1740. r := GetButtonRect;
  1741. bgra.DrawPart(ScaleRect(r, FCanvasScale), Self.Canvas, r, False);
  1742. end;
  1743. bbtDropDown:
  1744. begin
  1745. // Main button
  1746. if FDown then
  1747. bgra := BGRAClick
  1748. else
  1749. case FButtonState of
  1750. msNone: bgra := BGRANormal;
  1751. msHover: bgra := BGRAHover;
  1752. msClicked: bgra := BGRAClick;
  1753. end;
  1754. if bgra.NeedRender then
  1755. Render(bgra, TBCButtonState(bgra.CustomData));
  1756. r := GetButtonRect;
  1757. bgra.DrawPart(ScaleRect(r, FCanvasScale), Self.Canvas, r, False);
  1758. // Drop down button
  1759. case FDownButtonState of
  1760. msNone: bgra := BGRANormal;
  1761. msHover: bgra := BGRAHover;
  1762. msClicked: bgra := BGRAClick;
  1763. end;
  1764. if bgra.NeedRender then
  1765. Render(bgra, TBCButtonState(bgra.CustomData));
  1766. r := GetDropDownRect;
  1767. bgra.DrawPart(ScaleRect(r, FCanvasScale), Self.Canvas, r, False);
  1768. end;
  1769. end;
  1770. end;
  1771. LimitMemoryUsage;
  1772. end;
  1773. procedure TCustomBCButton.RenderControl;
  1774. begin
  1775. inherited RenderControl;
  1776. RenderAll;
  1777. end;
  1778. procedure TCustomBCButton.SetGlobalOpacity(const AValue: byte);
  1779. begin
  1780. if FGlobalOpacity = AValue then
  1781. exit;
  1782. FGlobalOpacity := AValue;
  1783. RenderControl;
  1784. Invalidate;
  1785. end;
  1786. procedure TCustomBCButton.SetTextApplyGlobalOpacity(const AValue: boolean);
  1787. begin
  1788. if FTextApplyGlobalOpacity = AValue then
  1789. exit;
  1790. FTextApplyGlobalOpacity := AValue;
  1791. RenderControl;
  1792. Invalidate;
  1793. end;
  1794. constructor TCustomBCButton.Create(AOwner: TComponent);
  1795. begin
  1796. inherited Create(AOwner);
  1797. {$IFDEF INDEBUG}
  1798. FRenderCount := 0;
  1799. {$ENDIF}
  1800. FMemoryUsage := bmuHigh;
  1801. {$IFDEF FPC}
  1802. DisableAutoSizing;
  1803. Include(FControlState, csCreating);
  1804. {$ELSE} //#
  1805. {$ENDIF}
  1806. //{$IFDEF WINDOWS}
  1807. // default sizes under different dpi settings
  1808. //SetSizeVariables(ScaleX(8,96), ScaleX(16,96), ScaleY(8,96), ScaleX(24,96));
  1809. //{$ELSE}
  1810. // default sizes
  1811. SetSizeVariables(16, 8, 8, 24);
  1812. //{$ENDIF}
  1813. BeginUpdate;
  1814. try
  1815. with GetControlClassDefaultSize do
  1816. SetInitialBounds(0, 0, CX, CY);
  1817. ControlStyle := ControlStyle + [csAcceptsControls];
  1818. ParentColor := False;
  1819. Color := clNone;
  1820. FCanvasScale:= 1; //will be updated after control is created
  1821. FStateNormal := TBCButtonState.Create(Self);
  1822. FStateHover := TBCButtonState.Create(Self);
  1823. FStateClicked := TBCButtonState.Create(Self);
  1824. FStateNormal.OnChange := OnChangeState;
  1825. FStateHover.OnChange := OnChangeState;
  1826. FStateClicked.OnChange := OnChangeState;
  1827. FRounding := TBCRounding.Create(Self);
  1828. FRounding.OnChange := OnChangeState;
  1829. FRoundingDropDown := TBCRounding.Create(Self);
  1830. FRoundingDropDown.OnChange := OnChangeState;
  1831. FButtonState := msNone;
  1832. FDownButtonState := msNone;
  1833. FFlipArrow := False;
  1834. FGlyph := TBitmap.Create;
  1835. FGlyph.OnChange := OnChangeGlyph;
  1836. FGlyphMargin := 5;
  1837. FGlyphAlignment:= bcaCenter;
  1838. FGlyphOldPlacement:= true;
  1839. FGlyphScale:= 1;
  1840. FStyle := bbtButton;
  1841. FStaticButton := False;
  1842. FActiveButt := bbtButton;
  1843. FGlobalOpacity := 255;
  1844. FTextApplyGlobalOpacity := False;
  1845. //FStates := [];
  1846. FDown := False;
  1847. { Default style }
  1848. AssignDefaultStyle;
  1849. FImageChangeLink := TChangeLink.Create;
  1850. FImageChangeLink.OnChange := ImageListChange;
  1851. FImageIndex := -1;
  1852. FShowCaption := True;
  1853. FPreserveGlyphOnAssign := True;
  1854. finally
  1855. {$IFDEF FPC}
  1856. Exclude(FControlState, csCreating);
  1857. EnableAutoSizing;
  1858. {$ELSE} //#
  1859. {$ENDIF}
  1860. EndUpdate;
  1861. end;
  1862. end;
  1863. destructor TCustomBCButton.Destroy;
  1864. begin
  1865. FImageChangeLink.Free;
  1866. FStateNormal.Free;
  1867. FStateHover.Free;
  1868. FStateClicked.Free;
  1869. FBGRANormal.Free;
  1870. FBGRAHover.Free;
  1871. FBGRAClick.Free;
  1872. FreeAndNil(FGlyph);
  1873. FRounding.Free;
  1874. FRoundingDropDown.Free;
  1875. inherited Destroy;
  1876. end;
  1877. procedure TCustomBCButton.Assign(Source: TPersistent);
  1878. begin
  1879. if Source is TCustomBCButton then
  1880. begin
  1881. if not PreserveGlyphOnAssign then
  1882. Glyph := TCustomBCButton(Source).Glyph;
  1883. FGlyphMargin := TCustomBCButton(Source).FGlyphMargin;
  1884. FStyle := TCustomBCButton(Source).FStyle;
  1885. FFlipArrow := TCustomBCButton(Source).FFlipArrow;
  1886. FStaticButton := TCustomBCButton(Source).FStaticButton;
  1887. FGlobalOpacity := TCustomBCButton(Source).FGlobalOpacity;
  1888. FTextApplyGlobalOpacity := TCustomBCButton(Source).FTextApplyGlobalOpacity;
  1889. FStateNormal.Assign(TCustomBCButton(Source).FStateNormal);
  1890. FStateHover.Assign(TCustomBCButton(Source).FStateHover);
  1891. FStateClicked.Assign(TCustomBCButton(Source).FStateClicked);
  1892. FDropDownArrowSize := TCustomBCButton(Source).FDropDownArrowSize;
  1893. FDropDownWidth := TCustomBCButton(Source).FDropDownWidth;
  1894. AutoSizeExtraX := TCustomBCButton(Source).AutoSizeExtraX;
  1895. AutoSizeExtraY := TCustomBCButton(Source).AutoSizeExtraY;
  1896. FDown := TCustomBCButton(Source).FDown;
  1897. FRounding.Assign(TCustomBCButton(Source).FRounding);
  1898. FRoundingDropDown.Assign(TCustomBCButton(Source).FRoundingDropDown);
  1899. RenderControl;
  1900. Invalidate;
  1901. UpdateSize;
  1902. end
  1903. else
  1904. inherited Assign(Source);
  1905. end;
  1906. procedure TCustomBCButton.SetSizeVariables(newDropDownWidth,
  1907. newDropDownArrowSize, newAutoSizeExtraVertical, newAutoSizeExtraHorizontal: integer);
  1908. begin
  1909. FDropDownArrowSize := newDropDownArrowSize;
  1910. FDropDownWidth := newDropDownWidth;
  1911. AutoSizeExtraY := newAutoSizeExtraVertical;
  1912. AutoSizeExtraX := newAutoSizeExtraHorizontal;
  1913. if csCreating in ControlState then
  1914. Exit;
  1915. RenderControl;
  1916. UpdateSize;
  1917. Invalidate;
  1918. end;
  1919. function TCustomBCButton.GetStyleExtension: string;
  1920. begin
  1921. Result := 'bcbtn';
  1922. end;
  1923. end.