PngBitBtn.pas 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  1. unit PngBitBtn;
  2. interface
  3. uses
  4. Windows, Messages, Classes, Graphics, Controls, Buttons, pngimage, PngFunctions;
  5. type
  6. TPngBitBtn = class(TBitBtn)
  7. {$IF RTLVersion >= 24.0 }
  8. {$IFNDEF BCB}
  9. strict private
  10. class constructor Create;
  11. class destructor Destroy;
  12. {$IFEND}
  13. {$IFEND}
  14. private
  15. FPngImage: TPngImage;
  16. FPngOptions: TPngOptions;
  17. FCanvas: TCanvas;
  18. FLastKind: TBitBtnKind;
  19. FImageFromAction: Boolean;
  20. FMouseInControl: Boolean;
  21. IsFocused: Boolean;
  22. function PngImageStored: Boolean;
  23. procedure SetPngImage(const Value: TPngImage);
  24. procedure SetPngOptions(const Value: TPngOptions);
  25. procedure CNDrawItem(var Message: TWMDrawItem); message CN_DRAWITEM;
  26. procedure CMMouseEnter(var Message: TMessage); message CM_MOUSEENTER;
  27. procedure CMMouseLeave(var Message: TMessage); message CM_MOUSELEAVE;
  28. protected
  29. procedure ActionChange(Sender: TObject; CheckDefaults: Boolean); override;
  30. procedure SetButtonStyle(ADefault: Boolean); override;
  31. public
  32. constructor Create(AOwner: TComponent); override;
  33. destructor Destroy; override;
  34. published
  35. property PngImage: TPngImage read FPngImage write SetPngImage stored PngImageStored;
  36. property PngOptions: TPngOptions read FPngOptions write SetPngOptions default [pngBlendOnDisabled];
  37. property Glyph stored False;
  38. property NumGlyphs stored False;
  39. end;
  40. {$IF RTLVersion >= 24.0 }
  41. TPngBitBtnStyleHook = class(TBitBtnStyleHook)
  42. strict protected
  43. procedure DrawButton(ACanvas: TCanvas; AMouseInControl: Boolean); override;
  44. end;
  45. {$IFEND}
  46. implementation
  47. uses
  48. ActnList, Themes, PngButtonFunctions, PngImageList;
  49. {$IF RTLVersion < 23.0 }
  50. type
  51. TThemeServicesHelper = class helper for TThemeServices
  52. private
  53. function GetEnabled: Boolean;
  54. public
  55. function GetElementContentRect(DC: HDC; Details: TThemedElementDetails; const BoundingRect: TRect;
  56. out ContentRect: TRect): Boolean; overload;
  57. property Enabled: Boolean read GetEnabled;
  58. end;
  59. function TThemeServicesHelper.GetElementContentRect(DC: HDC; Details: TThemedElementDetails; const BoundingRect: TRect;
  60. out ContentRect: TRect): Boolean;
  61. begin
  62. ContentRect := Self.ContentRect(DC, Details, BoundingRect);
  63. Result := true;
  64. end;
  65. function TThemeServicesHelper.GetEnabled: Boolean;
  66. begin
  67. Result := ThemesEnabled;
  68. end;
  69. function StyleServices: TThemeServices;
  70. begin
  71. result := ThemeServices;
  72. end;
  73. {$IFEND}
  74. { TPngBitBtn }
  75. {$IF RTLVersion >= 24.0 }
  76. {$IFNDEF BCB}
  77. class constructor TPngBitBtn.Create;
  78. begin
  79. TCustomStyleEngine.RegisterStyleHook(TPngBitBtn, TPngBitBtnStyleHook);
  80. end;
  81. class destructor TPngBitBtn.Destroy;
  82. begin
  83. TCustomStyleEngine.UnRegisterStyleHook(TPngBitBtn, TPngBitBtnStyleHook);
  84. end;
  85. {$IFEND}
  86. {$IFEND}
  87. constructor TPngBitBtn.Create(AOwner: TComponent);
  88. begin
  89. inherited Create(AOwner);
  90. FPngImage := TPngImage.Create;
  91. FPngOptions := [pngBlendOnDisabled];
  92. FCanvas := TCanvas.Create;
  93. FLastKind := bkCustom;
  94. FImageFromAction := False;
  95. end;
  96. destructor TPngBitBtn.Destroy;
  97. begin
  98. inherited Destroy;
  99. FPngImage.Free;
  100. FCanvas.Free;
  101. end;
  102. procedure TPngBitBtn.ActionChange(Sender: TObject; CheckDefaults: Boolean);
  103. begin
  104. inherited ActionChange(Sender, CheckDefaults);
  105. if Sender is TCustomAction then begin
  106. with TCustomAction(Sender) do begin
  107. //Copy image from action's imagelist
  108. if (PngImage.Empty or FImageFromAction) and (ActionList <> nil) and
  109. (ActionList.Images <> nil) and (ImageIndex >= 0) and (ImageIndex <
  110. ActionList.Images.Count) then begin
  111. CopyImageFromImageList(FPngImage, ActionList.Images, ImageIndex);
  112. FImageFromAction := True;
  113. end;
  114. end;
  115. end;
  116. end;
  117. procedure TPngBitBtn.SetButtonStyle(ADefault: Boolean);
  118. begin
  119. inherited SetButtonStyle(ADefault);
  120. if ADefault <> IsFocused then begin
  121. IsFocused := ADefault;
  122. Refresh;
  123. end;
  124. end;
  125. function TPngBitBtn.PngImageStored: Boolean;
  126. begin
  127. Result := not FImageFromAction;
  128. end;
  129. procedure TPngBitBtn.SetPngImage(const Value: TPngImage);
  130. begin
  131. //This is all neccesary, because you can't assign a nil to a TPngImage
  132. if Value = nil then begin
  133. FPngImage.Free;
  134. FPngImage := TPngImage.Create;
  135. end
  136. else begin
  137. FPngImage.Assign(Value);
  138. end;
  139. //To work around the gamma-problem
  140. with FPngImage do
  141. if not Empty and (Header.ColorType in [COLOR_RGB, COLOR_RGBALPHA, COLOR_PALETTE]) then
  142. Chunks.RemoveChunk(Chunks.ItemFromClass(TChunkgAMA));
  143. FImageFromAction := False;
  144. Repaint;
  145. end;
  146. procedure TPngBitBtn.SetPngOptions(const Value: TPngOptions);
  147. begin
  148. if FPngOptions <> Value then begin
  149. FPngOptions := Value;
  150. Repaint;
  151. end;
  152. end;
  153. procedure TPngBitBtn.CNDrawItem(var Message: TWMDrawItem);
  154. var
  155. R, PaintRect: TRect;
  156. GlyphPos, TextPos: TPoint;
  157. IsDown, IsDefault: Boolean;
  158. Flags: Cardinal;
  159. Button: TThemedButton;
  160. Details: TThemedElementDetails;
  161. begin
  162. R := ClientRect;
  163. FCanvas.Handle := Message.DrawItemStruct^.hDC;
  164. FCanvas.Font := Self.Font;
  165. IsDown := Message.DrawItemStruct^.itemState and ODS_SELECTED <> 0;
  166. IsDefault := Message.DrawItemStruct^.itemState and ODS_FOCUS <> 0;
  167. //Draw the border
  168. if StyleServices.Enabled then begin
  169. //Themed border
  170. if not Enabled then
  171. Button := tbPushButtonDisabled
  172. else if IsDown then
  173. Button := tbPushButtonPressed
  174. else if FMouseInControl then
  175. Button := tbPushButtonHot
  176. else if IsFocused or IsDefault then
  177. Button := tbPushButtonDefaulted
  178. else
  179. Button := tbPushButtonNormal;
  180. //Paint the background, border, and finally get the inner rect
  181. Details := StyleServices.GetElementDetails(Button);
  182. StyleServices.DrawParentBackground(Handle, Message.DrawItemStruct.hDC, @Details, True);
  183. StyleServices.DrawElement(Message.DrawItemStruct.hDC, Details, Message.DrawItemStruct.rcItem);
  184. StyleServices.GetElementContentRect(FCanvas.Handle, Details, Message.DrawItemStruct.rcItem, R);
  185. end
  186. else begin
  187. //Draw the outer border, when focused
  188. if IsFocused or IsDefault then begin
  189. FCanvas.Pen.Color := clWindowFrame;
  190. FCanvas.Pen.Width := 1;
  191. FCanvas.Brush.Style := bsClear;
  192. FCanvas.Rectangle(R.Left, R.Top, R.Right, R.Bottom);
  193. InflateRect(R, -1, -1);
  194. end;
  195. //Draw the inner border
  196. if IsDown then begin
  197. FCanvas.Pen.Color := clBtnShadow;
  198. FCanvas.Pen.Width := 1;
  199. FCanvas.Brush.Color := clBtnFace;
  200. FCanvas.Rectangle(R.Left, R.Top, R.Right, R.Bottom);
  201. InflateRect(R, -1, -1);
  202. end
  203. else begin
  204. Flags := DFCS_BUTTONPUSH or DFCS_ADJUSTRECT;
  205. if Message.DrawItemStruct.itemState and ODS_DISABLED <> 0 then
  206. Flags := Flags or DFCS_INACTIVE;
  207. DrawFrameControl(Message.DrawItemStruct.hDC, R, DFC_BUTTON, Flags);
  208. end;
  209. //Adjust the rect when focused and/or down
  210. if IsFocused then begin
  211. R := ClientRect;
  212. InflateRect(R, -1, -1);
  213. end;
  214. if IsDown then
  215. OffsetRect(R, 1, 1);
  216. end;
  217. //Calculate the position of the PNG glyph
  218. CalcButtonLayout(FCanvas, FPngImage, ClientRect, IsDown, False, Caption,
  219. Layout, Margin, Spacing, GlyphPos, TextPos, DrawTextBiDiModeFlags(0));
  220. //Draw the image
  221. if (FPngImage <> nil) and (Kind = bkCustom) and not FPngImage.Empty then begin
  222. PaintRect := Bounds(GlyphPos.X, GlyphPos.Y, FPngImage.Width, FPngImage.Height);
  223. if Enabled then
  224. DrawPNG(FPngImage, FCanvas, PaintRect, [])
  225. else
  226. DrawPNG(FPngImage, FCanvas, PaintRect, FPngOptions);
  227. end;
  228. //Draw the text
  229. if Length(Caption) > 0 then begin
  230. PaintRect := Rect(TextPos.X, TextPos.Y, Width, Height);
  231. FCanvas.Brush.Style := bsClear;
  232. //grayed Caption when disabled
  233. if not Enabled then begin
  234. OffsetRect(PaintRect, 1, 1);
  235. FCanvas.Font.Color := clBtnHighlight;
  236. DrawText(FCanvas.Handle, PChar(Caption), -1, PaintRect,
  237. DrawTextBiDiModeFlags(0) or DT_TOP or DT_LEFT or DT_SINGLELINE);
  238. OffsetRect(PaintRect, -1, -1);
  239. FCanvas.Font.Color := clBtnShadow;
  240. end;
  241. DrawText(FCanvas.Handle, PChar(Caption), -1, PaintRect,
  242. DrawTextBiDiModeFlags(0) or DT_TOP or DT_LEFT or DT_SINGLELINE);
  243. end;
  244. //Draw the focus rectangle
  245. if IsFocused and IsDefault then begin
  246. if not StyleServices.Enabled then begin
  247. R := ClientRect;
  248. InflateRect(R, -3, -3);
  249. end;
  250. FCanvas.Pen.Color := clWindowFrame;
  251. FCanvas.Brush.Color := clBtnFace;
  252. DrawFocusRect(FCanvas.Handle, R);
  253. end;
  254. FLastKind := Kind;
  255. FCanvas.Handle := 0;
  256. end;
  257. procedure TPngBitBtn.CMMouseEnter(var Message: TMessage);
  258. begin
  259. inherited;
  260. if StyleServices.Enabled and not FMouseInControl and not (csDesigning in ComponentState) then begin
  261. FMouseInControl := True;
  262. Repaint;
  263. end;
  264. end;
  265. procedure TPngBitBtn.CMMouseLeave(var Message: TMessage);
  266. begin
  267. inherited;
  268. if StyleServices.Enabled and FMouseInControl then begin
  269. FMouseInControl := False;
  270. Repaint;
  271. end;
  272. end;
  273. { TPngBitBtnStyleHook }
  274. {$IF RTLVersion >= 24.0 }
  275. procedure TPngBitBtnStyleHook.DrawButton(ACanvas: TCanvas;
  276. AMouseInControl: Boolean);
  277. const
  278. WordBreakFlag: array[Boolean] of Integer = (0, DT_WORDBREAK);
  279. var
  280. Details: TThemedElementDetails;
  281. DrawRect, PaintRect, TextRect: TRect;
  282. State: TButtonState;
  283. btn : TPngBitBtn;
  284. GlyphPos, TextPos: TPoint;
  285. LColor: TColor;
  286. LFormats: TTextFormat;
  287. begin
  288. if not (Control is TPngBitBtn) then
  289. begin
  290. inherited;
  291. Exit;
  292. end;
  293. if FPressed then
  294. Details := StyleServices.GetElementDetails(tbPushButtonPressed)
  295. else if AMouseInControl then
  296. Details := StyleServices.GetElementDetails(tbPushButtonHot)
  297. else if Focused or TPngBitBtn(Control).Default then
  298. Details := StyleServices.GetElementDetails(tbPushButtonDefaulted)
  299. else if Control.Enabled then
  300. Details := StyleServices.GetElementDetails(tbPushButtonNormal)
  301. else
  302. Details := StyleServices.GetElementDetails(tbPushButtonDisabled);
  303. DrawRect := Control.ClientRect;
  304. StyleServices.DrawElement(ACanvas.Handle, Details, DrawRect);
  305. btn := Control as TPngBitBtn;
  306. ACanvas.Font := btn.Font;
  307. if not btn.Enabled then State := bsDisabled
  308. else if FPressed then State := bsDown
  309. else State := bsUp;
  310. //Calculate the position of the PNG glyph
  311. CalcButtonLayout(ACanvas, btn.FPngImage, btn.ClientRect, FPressed, False, btn.Caption,
  312. btn.Layout, btn.Margin, btn.Spacing, GlyphPos, TextPos, btn.DrawTextBiDiModeFlags(0));
  313. //Draw the image
  314. if (btn.FPngImage <> nil) and (btn.Kind = bkCustom) and not btn.FPngImage.Empty then begin
  315. PaintRect := Bounds(GlyphPos.X, GlyphPos.Y, btn.FPngImage.Width, btn.FPngImage.Height);
  316. if btn.Enabled then
  317. DrawPNG(btn.FPngImage, ACanvas, PaintRect, [])
  318. else
  319. DrawPNG(btn.FPngImage, ACanvas, PaintRect, btn.FPngOptions);
  320. end;
  321. ACanvas.Brush.Style := bsClear;
  322. if (State = bsDisabled) or (not StyleServices.IsSystemStyle and (seFont in btn.StyleElements)) then
  323. begin
  324. if not StyleServices.GetElementColor(Details, ecTextColor, LColor) or (LColor = clNone) then
  325. LColor := ACanvas.Font.Color;
  326. end
  327. else
  328. LColor := ACanvas.Font.Color;
  329. LFormats := TTextFormatFlags(DT_NOCLIP or DT_CENTER or DT_VCENTER
  330. or btn.DrawTextBiDiModeFlags(0) or WordBreakFlag[btn.WordWrap]);
  331. if Length(btn.Caption) > 0 then begin
  332. TextRect := Rect(0, 0, btn.ClientRect.Right - btn.ClientRect.Left, 0);
  333. DrawText(ACanvas.Handle, PChar(btn.Caption), Length(btn.Caption), TextRect,
  334. DT_CALCRECT or btn.DrawTextBiDiModeFlags(0));
  335. end
  336. else begin
  337. TextRect := Rect(0, 0, 0, 0);
  338. end;
  339. OffsetRect(TextRect, TextPos.X + btn.ClientRect.Left, TextPos.Y + btn.ClientRect.Top);
  340. StyleServices.DrawText(ACanvas.Handle, Details, btn.Caption, TextRect, LFormats, LColor);
  341. end;
  342. {$IFEND}
  343. end.