bcbutton.pas 59 KB

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