bgrasvgtheme.pas 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942
  1. unit BGRASVGTheme;
  2. {$mode delphi}
  3. interface
  4. uses
  5. Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs,
  6. BGRATheme, BGRABitmap, BGRABitmapTypes, BGRASVG, BGRASVGType, XMLConf,
  7. ComponentEditors, PropEdits, Menus, BGRASVGImageList, Math;
  8. const
  9. DEFAULT_CHECKBOX_TEXT_SPACING = 2;
  10. DEFAULT_GLYPH_TEXT_SPACING = 6;
  11. DEFAULT_BUTTON_TEXT_SPACING = 6;
  12. type
  13. { TBGRASVGTheme }
  14. TBGRASVGTheme = class(TBGRATheme)
  15. private
  16. FButtonTextSpacing: integer;
  17. FCheckboxTextSpacing: integer;
  18. FColorizeActiveOp: TBlendOperation;
  19. FColorizeDisabledOp: TBlendOperation;
  20. FColorizeHoverOp: TBlendOperation;
  21. FColorizeNormalOp: TBlendOperation;
  22. FGlyphTextSpacing: integer;
  23. FOwner: TComponent;
  24. FButtonActive: TStringList;
  25. FButtonHover: TStringList;
  26. FButtonNormal: TStringList;
  27. FButtonSliceScalingBottom: integer;
  28. FButtonSliceScalingLeft: integer;
  29. FButtonSliceScalingRight: integer;
  30. FButtonSliceScalingTop: integer;
  31. FCheckBoxChecked: TStringList;
  32. FCheckBoxUnchecked: TStringList;
  33. FColorizeActive: string;
  34. FColorizeDisabled: string;
  35. FColorizeHover: string;
  36. FColorizeNormal: string;
  37. FRadioButtonChecked: TStringList;
  38. FRadioButtonUnchecked: TStringList;
  39. procedure SetButtonActive(AValue: TStringList);
  40. procedure SetButtonHover(AValue: TStringList);
  41. procedure SetButtonNormal(AValue: TStringList);
  42. procedure SetButtonSliceScalingBottom(AValue: integer);
  43. procedure SetButtonSliceScalingLeft(AValue: integer);
  44. procedure SetButtonSliceScalingRight(AValue: integer);
  45. procedure SetButtonSliceScalingTop(AValue: integer);
  46. procedure SetButtonTextSpacing(AValue: integer);
  47. procedure SetCheckBoxChecked(AValue: TStringList);
  48. procedure SetCheckboxTextSpacing(AValue: integer);
  49. procedure SetCheckBoxUnchecked(AValue: TStringList);
  50. procedure SetColorizeActive(AValue: string);
  51. procedure SetColorizeActiveOp(AValue: TBlendOperation);
  52. procedure SetColorizeDisabled(AValue: string);
  53. procedure SetColorizeDisabledOp(AValue: TBlendOperation);
  54. procedure SetColorizeHover(AValue: string);
  55. procedure SetColorizeHoverOp(AValue: TBlendOperation);
  56. procedure SetColorizeNormal(AValue: string);
  57. procedure SetColorizeNormalOp(AValue: TBlendOperation);
  58. procedure SetGlyphTextSpacing(AValue: integer);
  59. procedure SetRadioButtonChecked(AValue: TStringList);
  60. procedure SetRadioButtonUnchecked(AValue: TStringList);
  61. protected
  62. procedure LoadTheme(const XMLConf: TXMLConfig);
  63. procedure SaveTheme(const XMLConf: TXMLConfig);
  64. procedure CheckEmptyResourceException(const aResource: string);
  65. procedure SliceScalingDraw(const Source: TBGRASVG;
  66. const marginLeft, marginTop, marginRight, marginBottom: integer;
  67. const Dest: TBGRABitmap; DestDPI: integer);
  68. procedure ColorizeSurface(ASurface: TBGRAThemeSurface; AState: TBGRAThemeButtonState);
  69. public
  70. constructor Create(AOwner: TComponent); override;
  71. destructor Destroy; override;
  72. public
  73. function PreferredButtonHeight(const hasGlyph: boolean): Integer; override;
  74. function PreferredButtonWidth(const hasGlyph: boolean): Integer; override;
  75. procedure DrawButton(Caption: string; State: TBGRAThemeButtonState;
  76. Focused: boolean; ARect: TRect; ASurface: TBGRAThemeSurface; AImageIndex: Integer = -1; AImageList: TBGRASVGImageList = nil); override;
  77. procedure DrawRadioButton(Caption: string; State: TBGRAThemeButtonState;
  78. {%H-}Focused: boolean; Checked: boolean; ARect: TRect;
  79. ASurface: TBGRAThemeSurface); override;
  80. procedure DrawCheckBox(Caption: string; State: TBGRAThemeButtonState;
  81. {%H-}Focused: boolean; Checked: boolean; ARect: TRect;
  82. ASurface: TBGRAThemeSurface); override;
  83. public
  84. // XML File
  85. procedure SaveToFile(AFileName: string);
  86. // XML File
  87. procedure LoadFromFile(AFileName: string);
  88. // String Stream
  89. procedure SaveToStream(AStream: TStream);
  90. // String Stream
  91. procedure LoadFromStream(AStream: TStream);
  92. // Resource
  93. procedure LoadFromResource(AResource: string);
  94. // Default Theme
  95. procedure LoadDefaultTheme;
  96. published
  97. // Check box unchecked state
  98. property CheckBoxUnchecked: TStringList read FCheckBoxUnchecked
  99. write SetCheckBoxUnchecked;
  100. // Check box checked state
  101. property CheckBoxChecked: TStringList read FCheckBoxChecked write SetCheckBoxChecked;
  102. // Radio button unchecked state
  103. property RadioButtonUnchecked: TStringList
  104. read FRadioButtonUnchecked write SetRadioButtonUnchecked;
  105. // Radio button checked state
  106. property RadioButtonChecked: TStringList
  107. read FRadioButtonChecked write SetRadioButtonChecked;
  108. // Spacing between checkbox/radiobutton and its text (in 96 DPI)
  109. property CheckBoxTextSpacing: integer read FCheckboxTextSpacing write SetCheckboxTextSpacing default DEFAULT_CHECKBOX_TEXT_SPACING;
  110. // Button normal state
  111. property ButtonNormal: TStringList read FButtonNormal write SetButtonNormal;
  112. // Button mouse over state
  113. property ButtonHover: TStringList read FButtonHover write SetButtonHover;
  114. // Button pressed state
  115. property ButtonActive: TStringList read FButtonActive write SetButtonActive;
  116. // 9-Slice-Scaling margin left
  117. property ButtonSliceScalingLeft: integer
  118. read FButtonSliceScalingLeft write SetButtonSliceScalingLeft;
  119. // 9-Slice-Scaling margin top
  120. property ButtonSliceScalingTop: integer
  121. read FButtonSliceScalingTop write SetButtonSliceScalingTop;
  122. // 9-Slice-Scaling margin right
  123. property ButtonSliceScalingRight: integer
  124. read FButtonSliceScalingRight write SetButtonSliceScalingRight;
  125. // 9-Slice-Scaling margin bottom
  126. property ButtonSliceScalingBottom: integer
  127. read FButtonSliceScalingBottom write SetButtonSliceScalingBottom;
  128. // Spacing between glyph and its text (in 96 DPI)
  129. property GlyphTextSpacing: integer read FGlyphTextSpacing write SetGlyphTextSpacing default DEFAULT_GLYPH_TEXT_SPACING;
  130. // Spacing between text and button border (in 96 DPI)
  131. property ButtonTextSpacing: integer read FButtonTextSpacing write SetButtonTextSpacing default DEFAULT_BUTTON_TEXT_SPACING;
  132. // CSS Color to tint the normal states, use rgba(0,0,0,0) to disable
  133. property ColorizeNormal: string read FColorizeNormal write SetColorizeNormal;
  134. property ColorizeNormalOp: TBlendOperation read FColorizeNormalOp write SetColorizeNormalOp default boTransparent;
  135. // CSS Color to tint the hover states, use rgba(0,0,0,0) to disable
  136. property ColorizeHover: string read FColorizeHover write SetColorizeHover;
  137. property ColorizeHoverOp: TBlendOperation read FColorizeHoverOp write SetColorizeHoverOp default boTransparent;
  138. // CSS Color to tint the active states, use rgba(0,0,0,0) to disable
  139. property ColorizeActive: string read FColorizeActive write SetColorizeActive;
  140. property ColorizeActiveOp: TBlendOperation read FColorizeActiveOp write SetColorizeActiveOp default boTransparent;
  141. // CSS Color to tint the disabled states, use rgba(0,0,0,0) to disable
  142. property ColorizeDisabled: string read FColorizeDisabled write SetColorizeDisabled;
  143. property ColorizeDisabledOp: TBlendOperation read FColorizeDisabledOp write SetColorizeDisabledOp default boTransparent;
  144. end;
  145. { TBGRASVGThemeComponentEditor }
  146. TBGRASVGThemeComponentEditor = class(TBaseComponentEditor)
  147. private
  148. FComponent: TBGRASVGTheme;
  149. public
  150. constructor Create({%H-}AComponent: TComponent;
  151. {%H-}ADesigner: TComponentEditorDesigner); override;
  152. procedure Copy; override;
  153. procedure Edit; override;
  154. procedure ExecuteVerb(Index: Integer); override;
  155. function GetComponent: TComponent; override;
  156. function GetCustomHint: String; override;
  157. function GetDesigner: TComponentEditorDesigner; override;
  158. function GetHook(out Hook: TPropertyEditorHook): boolean; override;
  159. function GetVerb(Index: Integer): string; override;
  160. function GetVerbCount: Integer; override;
  161. function IsInInlined: Boolean; override;
  162. procedure PrepareItem({%H-}Index: Integer; const {%H-}AnItem: TMenuItem); override;
  163. procedure Modified; override;
  164. end;
  165. procedure Register;
  166. implementation
  167. uses BCTypes, BCTools;
  168. const
  169. RES_CHECKBOXUNCHECKED =
  170. '<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M19 5v14H5V5h14m0-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2z"/></svg>';
  171. RES_CHECKBOXCHECKED =
  172. '<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M19 3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.11 0 2-.9 2-2V5c0-1.1-.89-2-2-2zm-9 14l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/></svg>';
  173. RES_RADIOBUTTONUNCHECKED =
  174. '<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z"/></svg>';
  175. RES_RADIOBUTTONCHECKED =
  176. '<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M12 7c-2.76 0-5 2.24-5 5s2.24 5 5 5 5-2.24 5-5-2.24-5-5-5zm0-5C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z"/></svg>';
  177. RES_BUTTON =
  178. '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">'+
  179. '<defs>'+
  180. ' <linearGradient id="Grad1" x1="0%" y1="0%" x2="0%" y2="100%">'+
  181. ' <stop style="stop-color:#ffffff;stop-opacity:1" offset="30%"/>'+
  182. ' <stop style="stop-color:#87cdde;stop-opacity:1" offset="70%"/>'+
  183. ' </linearGradient>'+
  184. '</defs>'+
  185. ' <path style="fill:url(#Grad1);fill-opacity:1;stroke:#002255;stroke-width:1;stroke-opacity:1"'+
  186. ' d="m9.8,0.5h12.4c5.15,0 9.3,4.15 9.3,9.3v12.4c0,5.15-4.15,9.3-9.3,9.3h-12.4c-5.15, 0-9.3-4.15-9.3-9.3v-12.4c0-5.15 4.15-9.3 9.3-9.3z" />'+
  187. '</svg>';
  188. RES_COLORIZENORMAL = 'rgba(0,0,0,0)';
  189. RES_COLORIZEHOVER = 'rgba(255,255,255,0.5)';
  190. RES_COLORIZEACTIVE = 'rgba(0,0,0,0.5)';
  191. RES_COLORIZEDISABLED = 'rgba(127,127,127,0.7)';
  192. procedure Register;
  193. begin
  194. RegisterComponents('BGRA Themes', [TBGRASVGTheme]);
  195. RegisterComponentEditor(TBGRASVGTheme, TBGRASVGThemeComponentEditor);
  196. end;
  197. { TBGRASVGThemeComponentEditor }
  198. constructor TBGRASVGThemeComponentEditor.Create(AComponent: TComponent;
  199. ADesigner: TComponentEditorDesigner);
  200. begin
  201. FComponent := TBGRASVGTheme(AComponent);
  202. end;
  203. procedure TBGRASVGThemeComponentEditor.Copy;
  204. begin
  205. end;
  206. procedure TBGRASVGThemeComponentEditor.Edit;
  207. begin
  208. end;
  209. procedure TBGRASVGThemeComponentEditor.ExecuteVerb(Index: Integer);
  210. var
  211. openDlg: TOpenDialog;
  212. saveDlg: TSaveDialog;
  213. begin
  214. case Index of
  215. // Load from file
  216. 0: begin
  217. openDlg := TOpenDialog.Create(nil);
  218. openDlg.Filter := 'XML|*.xml';
  219. try
  220. if openDlg.Execute then
  221. begin
  222. TBGRASVGTheme(GetComponent).LoadFromFile(openDlg.FileName);
  223. end;
  224. finally
  225. openDlg.Free;
  226. end;
  227. end;
  228. // Save to file
  229. 1: begin
  230. saveDlg := TSaveDialog.Create(nil);
  231. saveDlg.Filter := 'XML|*.xml';
  232. try
  233. if saveDlg.Execute then
  234. begin
  235. TBGRASVGTheme(GetComponent).SaveToFile(saveDlg.FileName);
  236. end;
  237. finally
  238. saveDlg.Free;
  239. end;
  240. end;
  241. end;
  242. end;
  243. function TBGRASVGThemeComponentEditor.GetVerb(Index: Integer): string;
  244. begin
  245. case Index of
  246. 0: Result := 'Load From File...';
  247. 1: Result := 'Save To File...';
  248. else
  249. result := '';
  250. end;
  251. end;
  252. function TBGRASVGThemeComponentEditor.GetVerbCount: Integer;
  253. begin
  254. Result := 2;
  255. end;
  256. function TBGRASVGThemeComponentEditor.IsInInlined: Boolean;
  257. begin
  258. result := False;
  259. end;
  260. procedure TBGRASVGThemeComponentEditor.PrepareItem(Index: Integer;
  261. const AnItem: TMenuItem);
  262. begin
  263. end;
  264. procedure TBGRASVGThemeComponentEditor.Modified;
  265. begin
  266. end;
  267. function TBGRASVGThemeComponentEditor.GetComponent: TComponent;
  268. begin
  269. Result := FComponent;
  270. end;
  271. function TBGRASVGThemeComponentEditor.GetCustomHint: String;
  272. begin
  273. result := 'SVG Theme';
  274. end;
  275. function TBGRASVGThemeComponentEditor.GetDesigner: TComponentEditorDesigner;
  276. begin
  277. result := nil;
  278. end;
  279. function TBGRASVGThemeComponentEditor.GetHook(out Hook: TPropertyEditorHook
  280. ): boolean;
  281. begin
  282. Hook := nil;
  283. result := false;
  284. end;
  285. { TBGRASVGTheme }
  286. procedure TBGRASVGTheme.SetCheckBoxUnchecked(AValue: TStringList);
  287. begin
  288. CheckEmptyResourceException(AValue.Text);
  289. if (AValue <> FCheckBoxUnchecked) then
  290. begin
  291. FCheckBoxUnchecked.Assign(AValue);
  292. InvalidateThemedControls;
  293. end;
  294. end;
  295. procedure TBGRASVGTheme.SetColorizeActive(AValue: string);
  296. begin
  297. if FColorizeActive = AValue then
  298. Exit;
  299. FColorizeActive := AValue;
  300. InvalidateThemedControls;
  301. end;
  302. procedure TBGRASVGTheme.SetColorizeActiveOp(AValue: TBlendOperation);
  303. begin
  304. if FColorizeActiveOp=AValue then Exit;
  305. FColorizeActiveOp:=AValue;
  306. InvalidateThemedControls;
  307. end;
  308. procedure TBGRASVGTheme.SetColorizeDisabled(AValue: string);
  309. begin
  310. if FColorizeDisabled = AValue then
  311. Exit;
  312. FColorizeDisabled := AValue;
  313. InvalidateThemedControls;
  314. end;
  315. procedure TBGRASVGTheme.SetColorizeDisabledOp(AValue: TBlendOperation);
  316. begin
  317. if FColorizeDisabledOp=AValue then Exit;
  318. FColorizeDisabledOp:=AValue;
  319. InvalidateThemedControls;
  320. end;
  321. procedure TBGRASVGTheme.SetColorizeHover(AValue: string);
  322. begin
  323. if FColorizeHover = AValue then
  324. Exit;
  325. FColorizeHover := AValue;
  326. InvalidateThemedControls;
  327. end;
  328. procedure TBGRASVGTheme.SetColorizeHoverOp(AValue: TBlendOperation);
  329. begin
  330. if FColorizeHoverOp=AValue then Exit;
  331. FColorizeHoverOp:=AValue;
  332. InvalidateThemedControls;
  333. end;
  334. procedure TBGRASVGTheme.SetColorizeNormal(AValue: string);
  335. begin
  336. if FColorizeNormal = AValue then
  337. Exit;
  338. FColorizeNormal := AValue;
  339. InvalidateThemedControls;
  340. end;
  341. procedure TBGRASVGTheme.SetColorizeNormalOp(AValue: TBlendOperation);
  342. begin
  343. if FColorizeNormalOp=AValue then Exit;
  344. FColorizeNormalOp:=AValue;
  345. InvalidateThemedControls;
  346. end;
  347. procedure TBGRASVGTheme.SetGlyphTextSpacing(AValue: integer);
  348. begin
  349. if FGlyphTextSpacing=AValue then Exit;
  350. FGlyphTextSpacing:=AValue;
  351. InvalidateThemedControls;
  352. end;
  353. procedure TBGRASVGTheme.SetRadioButtonChecked(AValue: TStringList);
  354. begin
  355. CheckEmptyResourceException(AValue.Text);
  356. if (AValue <> FRadioButtonChecked) then
  357. begin
  358. FRadioButtonChecked.Assign(AValue);
  359. InvalidateThemedControls;
  360. end;
  361. end;
  362. procedure TBGRASVGTheme.SetRadioButtonUnchecked(AValue: TStringList);
  363. begin
  364. CheckEmptyResourceException(AValue.Text);
  365. if (AValue <> FRadioButtonUnchecked) then
  366. begin
  367. FRadioButtonUnchecked.Assign(AValue);
  368. InvalidateThemedControls;
  369. end;
  370. end;
  371. procedure TBGRASVGTheme.LoadDefaultTheme;
  372. begin
  373. FCheckBoxUnchecked.Text := RES_CHECKBOXUNCHECKED;
  374. FCheckBoxChecked.Text := RES_CHECKBOXCHECKED;
  375. FCheckboxTextSpacing:= DEFAULT_CHECKBOX_TEXT_SPACING;
  376. FRadioButtonUnchecked.Text := RES_RADIOBUTTONUNCHECKED;
  377. FRadioButtonChecked.Text := RES_RADIOBUTTONCHECKED;
  378. FButtonNormal.Text := RES_BUTTON;
  379. FButtonHover.Text := '';
  380. FButtonActive.Text := '';
  381. FButtonSliceScalingLeft := 10;
  382. FButtonSliceScalingTop := 10;
  383. FButtonSliceScalingRight := 10;
  384. FButtonSliceScalingBottom := 10;
  385. FGlyphTextSpacing := DEFAULT_GLYPH_TEXT_SPACING;
  386. FButtonTextSpacing := DEFAULT_BUTTON_TEXT_SPACING;
  387. FColorizeNormal := RES_COLORIZENORMAL;
  388. FColorizeHover := RES_COLORIZEHOVER;
  389. FColorizeActive := RES_COLORIZEACTIVE;
  390. FColorizeDisabled := RES_COLORIZEDISABLED;
  391. FColorizeNormalOp := boTransparent;
  392. FColorizeHoverOp := boTransparent;
  393. FColorizeActiveOp := boTransparent;
  394. FColorizeDisabledOp := boTransparent;
  395. end;
  396. procedure TBGRASVGTheme.LoadTheme(const XMLConf: TXMLConfig);
  397. begin
  398. try
  399. XMLConf.RootName := 'BGRASVGTheme';
  400. // Button
  401. FButtonActive.Text := XMLConf.GetValue('Button/Active/SVG', RES_BUTTON){%H-};
  402. FButtonHover.Text := XMLConf.GetValue('Button/Hover/SVG', ''){%H-};
  403. FButtonNormal.Text := XMLConf.GetValue('Button/Normal/SVG', ''){%H-};
  404. FButtonSliceScalingBottom := XMLConf.GetValue('Button/SliceScaling/Bottom', 10);
  405. FButtonSliceScalingLeft := XMLConf.GetValue('Button/SliceScaling/Left', 10);
  406. FButtonSliceScalingRight := XMLConf.GetValue('Button/SliceScaling/Right', 10);
  407. FButtonSliceScalingTop := XMLConf.GetValue('Button/SliceScaling/Top', 10);
  408. FGlyphTextSpacing := XMLConf.GetValue('Button/GlyphSpacing', DEFAULT_GLYPH_TEXT_SPACING);
  409. FButtonTextSpacing := XMLConf.GetValue('Button/TextSpacing', DEFAULT_BUTTON_TEXT_SPACING);
  410. // CheckBox
  411. FCheckBoxChecked.Text := XMLConf.GetValue('CheckBox/Checked/SVG',
  412. RES_CHECKBOXCHECKED){%H-};
  413. FCheckBoxUnchecked.Text := XMLConf.GetValue('CheckBox/Unchecked/SVG',
  414. RES_CHECKBOXUNCHECKED){%H-};
  415. FCheckBoxTextSpacing := XMLConf.GetValue('CheckBox/TextSpacing', DEFAULT_CHECKBOX_TEXT_SPACING);
  416. // Colorize
  417. FColorizeActive := XMLConf{%H-}.GetValue('Colorize/Active', RES_COLORIZEACTIVE);
  418. FColorizeDisabled := XMLConf{%H-}.GetValue('Colorize/Disabled', RES_COLORIZEDISABLED);
  419. FColorizeHover := XMLConf{%H-}.GetValue('Colorize/Hover', RES_COLORIZEHOVER);
  420. FColorizeNormal := XMLConf{%H-}.GetValue('Colorize/Normal', RES_COLORIZENORMAL);
  421. FColorizeActiveOp := StrToBlendOperation(XMLConf{%H-}.GetValue('Colorize/ActiveOp', BlendOperationStr[boTransparent]));
  422. FColorizeDisabledOp := StrToBlendOperation(XMLConf{%H-}.GetValue('Colorize/DisabledOp', BlendOperationStr[boTransparent]));
  423. FColorizeHoverOp := StrToBlendOperation(XMLConf{%H-}.GetValue('Colorize/HoverOp', BlendOperationStr[boTransparent]));
  424. FColorizeNormalOp := StrToBlendOperation(XMLConf{%H-}.GetValue('Colorize/NormalOp', BlendOperationStr[boTransparent]));
  425. // RadioButton
  426. FRadioButtonChecked.Text :=
  427. XMLConf.GetValue('RadioButton/Checked/SVG', RES_RADIOBUTTONCHECKED{%H-}){%H-};
  428. FRadioButtonUnchecked.Text :=
  429. XMLConf.GetValue('RadioButton/Unchecked/SVG', RES_RADIOBUTTONUNCHECKED{%H-}){%H-};
  430. finally
  431. InvalidateThemedControls;
  432. end;
  433. end;
  434. procedure TBGRASVGTheme.SaveTheme(const XMLConf: TXMLConfig);
  435. begin
  436. XMLConf.RootName := 'BGRASVGTheme';
  437. // Button
  438. XMLConf.SetValue('Button/Active/SVG', FButtonActive.Text{%H-});
  439. XMLConf.SetValue('Button/Hover/SVG', FButtonHover.Text{%H-});
  440. XMLConf.SetValue('Button/Normal/SVG', FButtonNormal.Text{%H-});
  441. XMLConf.SetValue('Button/SliceScaling/Bottom', FButtonSliceScalingBottom);
  442. XMLConf.SetValue('Button/SliceScaling/Left', FButtonSliceScalingLeft);
  443. XMLConf.SetValue('Button/SliceScaling/Right', FButtonSliceScalingRight);
  444. XMLConf.SetValue('Button/SliceScaling/Top', FButtonSliceScalingTop);
  445. XMLConf.SetValue('Button/GlyphSpacing', FGlyphTextSpacing);
  446. XMLConf.SetValue('Button/TextSpacing', FButtonTextSpacing);
  447. // CheckBox
  448. XMLConf.SetValue('CheckBox/Checked/SVG', FCheckBoxChecked.Text{%H-});
  449. XMLConf.SetValue('CheckBox/Unchecked/SVG', FCheckBoxUnchecked.Text{%H-});
  450. XMLConf.SetValue('CheckBox/TextSpacing', FCheckboxTextSpacing);
  451. // Colorize
  452. XMLConf.SetValue('Colorize/Active', FColorizeActive{%H-});
  453. XMLConf.SetValue('Colorize/Disabled', FColorizeDisabled{%H-});
  454. XMLConf.SetValue('Colorize/Hover', FColorizeHover{%H-});
  455. XMLConf.SetValue('Colorize/Normal', FColorizeNormal{%H-});
  456. XMLConf.SetValue('Colorize/ActiveOp', BlendOperationStr[FColorizeActiveOp{%H-}]);
  457. XMLConf.SetValue('Colorize/DisabledOp', BlendOperationStr[FColorizeDisabledOp{%H-}]);
  458. XMLConf.SetValue('Colorize/HoverOp', BlendOperationStr[FColorizeHoverOp{%H-}]);
  459. XMLConf.SetValue('Colorize/NormalOp', BlendOperationStr[FColorizeNormalOp{%H-}]); // RadioButton
  460. XMLConf.SetValue('RadioButton/Checked/SVG', FRadioButtonChecked.Text{%H-});
  461. XMLConf.SetValue('RadioButton/Unchecked/SVG', FRadioButtonUnchecked.Text{%H-});
  462. end;
  463. procedure TBGRASVGTheme.CheckEmptyResourceException(const aResource: string);
  464. begin
  465. if Trim(aResource).IsEmpty then
  466. raise Exception.Create('Resource must not be empty.');
  467. end;
  468. procedure TBGRASVGTheme.SliceScalingDraw(const Source: TBGRASVG;
  469. const marginLeft, marginTop, marginRight, marginBottom: integer;
  470. const Dest: TBGRABitmap; DestDPI: integer);
  471. var
  472. svgBox: TSVGViewBox;
  473. svgTopLeft, svgBottomRight: TPointF;
  474. sourcePosX, sourcePosY: array[1..4] of single;
  475. destPosX, destPosY: array[1..4] of integer;
  476. y, x: integer;
  477. procedure DrawPart(sourceRect: TRectF; destRect: TRect);
  478. var
  479. zoom: TPointF;
  480. begin
  481. if sourceRect.IsEmpty or destRect.IsEmpty then
  482. exit;
  483. dest.ClipRect := destRect;
  484. zoom := PointF(destRect.Width / sourceRect.Width, destRect.Height /
  485. sourceRect.Height);
  486. Source.Draw(dest.Canvas2D, -sourceRect.Left * zoom.x + destRect.Left,
  487. -sourceRect.Top * zoom.y + destRect.Top, Source.DefaultDpi * zoom);
  488. end;
  489. begin
  490. svgBox := Source.ViewBoxInUnit[cuPixel];
  491. svgTopLeft := svgBox.min;
  492. svgBottomRight := svgBox.min + svgBox.size;
  493. sourcePosX[1] := svgTopLeft.x;
  494. sourcePosX[2] := svgTopLeft.x + marginLeft;
  495. sourcePosX[3] := svgBottomRight.x - marginRight;
  496. sourcePosX[4] := svgBottomRight.x;
  497. sourcePosY[1] := svgTopLeft.y;
  498. sourcePosY[2] := svgTopLeft.y + marginTop;
  499. sourcePosY[3] := svgBottomRight.y - marginBottom;
  500. sourcePosY[4] := svgBottomRight.y;
  501. if sourcePosX[2] > sourcePosX[3] then
  502. begin
  503. sourcePosX[2] := (sourcePosX[1] + sourcePosX[4]) / 2;
  504. sourcePosX[3] := sourcePosX[2];
  505. end;
  506. if sourcePosY[2] > sourcePosY[3] then
  507. begin
  508. sourcePosY[2] := (sourcePosY[1] + sourcePosY[4]) / 2;
  509. sourcePosY[3] := sourcePosY[2];
  510. end;
  511. destPosX[1] := 0;
  512. destPosX[2] := round(marginLeft * DestDPI / 96);
  513. destPosX[3] := dest.Width - round(marginRight * DestDPI / 96);
  514. destPosX[4] := dest.Width;
  515. destPosY[1] := 0;
  516. destPosY[2] := round(marginTop * DestDPI / 96);
  517. destPosY[3] := dest.Height - round(marginBottom * DestDPI / 96);
  518. destPosY[4] := dest.Height;
  519. if destPosX[2] > destPosX[3] then
  520. begin
  521. destPosX[2] := round((destPosX[1] + destPosX[4]) / 2);
  522. destPosX[3] := destPosX[2];
  523. end;
  524. if destPosY[2] > destPosY[3] then
  525. begin
  526. destPosY[2] := round((destPosY[1] + destPosY[4]) / 2);
  527. destPosY[3] := destPosY[2];
  528. end;
  529. for y := 1 to 3 do
  530. for x := 1 to 3 do
  531. DrawPart(RectF(sourcePosX[x], sourcePosY[y], sourcePosX[x + 1], sourcePosY[y + 1]),
  532. Rect(destPosX[x], destPosY[y], destPosX[x + 1], destPosY[y + 1]));
  533. Dest.NoClip;
  534. end;
  535. procedure TBGRASVGTheme.ColorizeSurface(ASurface: TBGRAThemeSurface;
  536. AState: TBGRAThemeButtonState);
  537. var
  538. color: String;
  539. op: TBlendOperation;
  540. begin
  541. case AState of
  542. btbsNormal: begin color := FColorizeNormal; op := FColorizeNormalOp; end;
  543. btbsHover: begin color := FColorizeHover; op := FColorizeHoverOp; end;
  544. btbsActive: begin color := FColorizeActive; op := FColorizeActiveOp; end;
  545. else {btbsDisabled} begin color := FColorizeDisabled; op := FColorizeDisabledOp; end;
  546. end;
  547. ASurface.BitmapColorOverlay(StrToBGRA(color), op);
  548. end;
  549. constructor TBGRASVGTheme.Create(AOwner: TComponent);
  550. begin
  551. inherited Create(AOwner);
  552. FOwner := AOwner;
  553. FCheckBoxUnchecked := TStringList.Create;
  554. FCheckBoxChecked := TStringList.Create;
  555. FRadioButtonUnchecked := TStringList.Create;
  556. FRadioButtonChecked := TStringList.Create;
  557. FButtonNormal := TStringList.Create;
  558. FButtonHover := TStringList.Create;
  559. FButtonActive := TStringList.Create;
  560. LoadDefaultTheme;
  561. end;
  562. destructor TBGRASVGTheme.Destroy;
  563. begin
  564. FCheckBoxUnchecked.Free;
  565. FCheckBoxChecked.Free;
  566. FRadioButtonUnchecked.Free;
  567. FRadioButtonChecked.Free;
  568. FButtonNormal.Free;
  569. FButtonHover.Free;
  570. FButtonActive.Free;
  571. inherited Destroy;
  572. end;
  573. function TBGRASVGTheme.PreferredButtonHeight(const hasGlyph: boolean): Integer;
  574. begin
  575. Result := (FButtonTextSpacing * 2);
  576. end;
  577. function TBGRASVGTheme.PreferredButtonWidth(const hasGlyph: boolean): Integer;
  578. begin
  579. Result := (FButtonTextSpacing * 2);
  580. if (hasGlyph) then
  581. Result := Result + FGlyphTextSpacing;
  582. end;
  583. procedure TBGRASVGTheme.DrawButton(Caption: string;
  584. State: TBGRAThemeButtonState; Focused: boolean; ARect: TRect;
  585. ASurface: TBGRAThemeSurface; AImageIndex: Integer;
  586. AImageList: TBGRASVGImageList);
  587. var
  588. svg: TBGRASVG;
  589. svgCode: String;
  590. gs: TSize;
  591. bcFont: TBCFont;
  592. actualCaption: string;
  593. r, rGlyph: TRect;
  594. drawText: boolean = True;
  595. begin
  596. with ASurface do
  597. begin
  598. case State of
  599. btbsNormal: svg := TBGRASVG.CreateFromString(FButtonNormal.Text);
  600. btbsHover:
  601. begin
  602. svgCode := FButtonHover.Text;
  603. if trim(svgCode) = '' then svgCode := FButtonNormal.Text;
  604. svg := TBGRASVG.CreateFromString(svgCode);
  605. end;
  606. btbsActive:
  607. begin
  608. svgCode := FButtonActive.Text;
  609. if trim(svgCode) = '' then svgCode := FButtonHover.Text;
  610. if trim(svgCode) = '' then svgCode := FButtonNormal.Text;
  611. svg := TBGRASVG.CreateFromString(svgCode);
  612. end;
  613. else {btbsDisabled}
  614. svg := TBGRASVG.CreateFromString(FButtonNormal.Text);
  615. end;
  616. SliceScalingDraw(svg, FButtonSliceScalingLeft, FButtonSliceScalingTop,
  617. FButtonSliceScalingRight, FButtonSliceScalingBottom, Bitmap,
  618. BitmapDPI);
  619. svg.Free;
  620. if Assigned(AImageList) and (AImageIndex > -1) and (AImageIndex < AImageList.Count) then
  621. begin
  622. gs := AImageList.GetScaledSize(BitmapDPI);
  623. if ARect.Width - gs.cx < ScaleForBitmap(GlyphTextSpacing + 2*ButtonTextSpacing) then
  624. drawText := false;
  625. end
  626. else gs := TSize.Create(0, 0);
  627. bcFont := TBCFont.Create(nil);
  628. bcFont.Assign(DestCanvas.Font);
  629. bcFont.Scale(BitmapDPI / DestCanvasDPI, false);
  630. bcFont.WordBreak := true;
  631. bcFont.PaddingBottom:= ScaleForBitmap(ButtonTextSpacing);
  632. bcFont.PaddingTop:= ScaleForBitmap(ButtonTextSpacing);
  633. bcFont.PaddingRight:= ScaleForBitmap(ButtonTextSpacing);
  634. bcFont.PaddingLeft:= ScaleForBitmap(ButtonTextSpacing);
  635. bcFont.TextAlignment:= bcaCenter;
  636. if drawText then
  637. actualCaption := Caption
  638. else actualCaption:= '';
  639. r := ScaleForBitmap(ARect, DestCanvasDPI);
  640. rGlyph := ComputeGlyphPosition(r, gs.cx, gs.cy, bcaCenter,
  641. ScaleForBitmap(GlyphTextSpacing), actualCaption, bcFont);
  642. if not rGlyph.IsEmpty then
  643. AImageList.Draw(AImageIndex, Bitmap, RectF(rGlyph));
  644. RenderText(r, bcFont, actualCaption, Bitmap, State <> btbsDisabled);
  645. bcFont.Free;
  646. ColorizeSurface(ASurface, State);
  647. DrawBitmap;
  648. end;
  649. end;
  650. procedure TBGRASVGTheme.DrawRadioButton(Caption: string;
  651. State: TBGRAThemeButtonState; Focused: boolean; Checked: boolean;
  652. ARect: TRect; ASurface: TBGRAThemeSurface);
  653. var
  654. Style: TTextStyle;
  655. svg: TBGRASVG;
  656. begin
  657. with ASurface do
  658. begin
  659. BitmapRect := RectWithSize(ARect.Left, ARect.Top, ARect.Height, ARect.Height);
  660. if Checked then
  661. svg := TBGRASVG.CreateFromString(FRadioButtonChecked.Text)
  662. else
  663. svg := TBGRASVG.CreateFromString(FRadioButtonUnchecked.Text);
  664. svg.StretchDraw(Bitmap.Canvas2D, 0, 0, Bitmap.Width, Bitmap.Height);
  665. svg.Free;
  666. ColorizeSurface(ASurface, State);
  667. DrawBitmap;
  668. if Caption <> '' then
  669. begin
  670. fillchar(Style, sizeof(Style), 0);
  671. Style.Alignment := taLeftJustify;
  672. Style.Layout := tlCenter;
  673. Style.Wordbreak := True;
  674. DestCanvas.TextRect(
  675. Rect(Arect.Height + ScaleForCanvas(CheckBoxTextSpacing), 0,
  676. ARect.Right, ARect.Bottom),
  677. ARect.Height + ScaleForCanvas(CheckBoxTextSpacing), 0, Caption, Style);
  678. end;
  679. end;
  680. end;
  681. procedure TBGRASVGTheme.SetCheckBoxChecked(AValue: TStringList);
  682. begin
  683. CheckEmptyResourceException(AValue.Text);
  684. if (AValue <> FCheckBoxChecked) then
  685. begin
  686. FCheckBoxChecked.Assign(AValue);
  687. InvalidateThemedControls;
  688. end;
  689. end;
  690. procedure TBGRASVGTheme.SetCheckboxTextSpacing(AValue: integer);
  691. begin
  692. if FCheckboxTextSpacing=AValue then Exit;
  693. FCheckboxTextSpacing:=AValue;
  694. InvalidateThemedControls;
  695. end;
  696. procedure TBGRASVGTheme.SetButtonActive(AValue: TStringList);
  697. begin
  698. if (AValue <> FButtonActive) then
  699. begin
  700. FButtonActive.Assign(AValue);
  701. InvalidateThemedControls;
  702. end;
  703. end;
  704. procedure TBGRASVGTheme.SetButtonHover(AValue: TStringList);
  705. begin
  706. if (AValue <> FButtonHover) then
  707. begin
  708. FButtonHover.Assign(AValue);
  709. InvalidateThemedControls;
  710. end;
  711. end;
  712. procedure TBGRASVGTheme.SetButtonNormal(AValue: TStringList);
  713. begin
  714. CheckEmptyResourceException(AValue.Text);
  715. if (AValue <> FButtonNormal) then
  716. begin
  717. FButtonNormal.Assign(AValue);
  718. InvalidateThemedControls;
  719. end;
  720. end;
  721. procedure TBGRASVGTheme.SetButtonSliceScalingBottom(AValue: integer);
  722. begin
  723. if FButtonSliceScalingBottom = AValue then
  724. Exit;
  725. FButtonSliceScalingBottom := AValue;
  726. InvalidateThemedControls;
  727. end;
  728. procedure TBGRASVGTheme.SetButtonSliceScalingLeft(AValue: integer);
  729. begin
  730. if FButtonSliceScalingLeft = AValue then
  731. Exit;
  732. FButtonSliceScalingLeft := AValue;
  733. InvalidateThemedControls;
  734. end;
  735. procedure TBGRASVGTheme.SetButtonSliceScalingRight(AValue: integer);
  736. begin
  737. if FButtonSliceScalingRight = AValue then
  738. Exit;
  739. FButtonSliceScalingRight := AValue;
  740. InvalidateThemedControls;
  741. end;
  742. procedure TBGRASVGTheme.SetButtonSliceScalingTop(AValue: integer);
  743. begin
  744. if FButtonSliceScalingTop = AValue then
  745. Exit;
  746. FButtonSliceScalingTop := AValue;
  747. InvalidateThemedControls;
  748. end;
  749. procedure TBGRASVGTheme.SetButtonTextSpacing(AValue: integer);
  750. begin
  751. if FButtonTextSpacing=AValue then Exit;
  752. FButtonTextSpacing:=AValue;
  753. InvalidateThemedControls;
  754. end;
  755. procedure TBGRASVGTheme.DrawCheckBox(Caption: string; State: TBGRAThemeButtonState;
  756. Focused: boolean; Checked: boolean; ARect: TRect; ASurface: TBGRAThemeSurface);
  757. var
  758. Style: TTextStyle;
  759. svg: TBGRASVG;
  760. r: TRect;
  761. begin
  762. with ASurface do
  763. begin
  764. BitmapRect := RectWithSize(ARect.Left, ARect.Top, ARect.Height, ARect.Height);
  765. if Checked then
  766. svg := TBGRASVG.CreateFromString(FCheckBoxChecked.Text)
  767. else
  768. svg := TBGRASVG.CreateFromString(FCheckBoxUnchecked.Text);
  769. svg.StretchDraw(Bitmap.Canvas2D, 0, 0, Bitmap.Width, Bitmap.Height);
  770. svg.Free;
  771. ColorizeSurface(ASurface, State);
  772. DrawBitmap;
  773. if Caption <> '' then
  774. begin
  775. fillchar(Style, sizeof(Style), 0);
  776. Style.Alignment := taLeftJustify;
  777. Style.Layout := tlCenter;
  778. Style.Wordbreak := True;
  779. DestCanvas.TextRect(
  780. Rect(Arect.Height + ScaleForCanvas(CheckBoxTextSpacing), 0,
  781. ARect.Right, ARect.Bottom),
  782. ARect.Height + ScaleForCanvas(CheckBoxTextSpacing), 0, Caption, Style);
  783. end;
  784. if Focused then
  785. begin
  786. DestCanvas.Pen.Color := DestCanvas.Font.Color;
  787. DestCanvas.Pen.Style := psDash;
  788. DestCanvas.Brush.Style := bsClear;
  789. r := ARect;
  790. DestCanvas.Rectangle(r);
  791. DestCanvas.Pen.Style := psSolid;
  792. end;
  793. end;
  794. end;
  795. procedure TBGRASVGTheme.SaveToFile(AFileName: string);
  796. var
  797. FXMLConf: TXMLConfig;
  798. begin
  799. FXMLConf := TXMLConfig.Create(Self);
  800. try
  801. FXMLConf.Filename := AFileName;
  802. SaveTheme(FXMLConf);
  803. FXMLConf.Flush;
  804. finally
  805. FXMLConf.Free;
  806. end;
  807. end;
  808. procedure TBGRASVGTheme.LoadFromFile(AFileName: string);
  809. var
  810. FXMLConf: TXMLConfig;
  811. begin
  812. FXMLConf := TXMLConfig.Create(Self);
  813. try
  814. FXMLConf.Filename := AFileName;
  815. LoadTheme(FXMLConf);
  816. finally
  817. FXMLConf.Free;
  818. end;
  819. end;
  820. procedure TBGRASVGTheme.SaveToStream(AStream: TStream);
  821. var
  822. FXMLConf: TXMLConfig;
  823. begin
  824. FXMLConf := TXMLConfig.Create(Self);
  825. try
  826. SaveTheme(FXMLConf);
  827. FXMLConf.SaveToStream(AStream);
  828. FXMLConf.Flush;
  829. finally
  830. FXMLConf.Free;
  831. end;
  832. end;
  833. procedure TBGRASVGTheme.LoadFromStream(AStream: TStream);
  834. var
  835. FXMLConf: TXMLConfig;
  836. begin
  837. FXMLConf := TXMLConfig.Create(Self);
  838. try
  839. FXMLConf.RootName := 'BGRASVGTheme';
  840. AStream.Position := 0;
  841. FXMLConf.LoadFromStream(AStream);
  842. LoadTheme(FXMLConf);
  843. finally
  844. FXMLConf.Free;
  845. end;
  846. end;
  847. procedure TBGRASVGTheme.LoadFromResource(AResource: string);
  848. var
  849. AStream: TStream;
  850. begin
  851. AStream := BGRAResource.GetResourceStream(AResource);
  852. LoadFromStream(AStream);
  853. AStream.Free;
  854. end;
  855. end.