PngBitBtn.pas 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  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. strict private
  9. class constructor Create;
  10. class destructor Destroy;
  11. {$IFEND}
  12. private
  13. FPngImage: TPngImage;
  14. FPngOptions: TPngOptions;
  15. FCanvas: TCanvas;
  16. FLastKind: TBitBtnKind;
  17. FImageFromAction: Boolean;
  18. FMouseInControl: Boolean;
  19. IsFocused: Boolean;
  20. function PngImageStored: Boolean;
  21. procedure SetPngImage(const Value: TPngImage);
  22. procedure SetPngOptions(const Value: TPngOptions);
  23. procedure CNDrawItem(var Message: TWMDrawItem); message CN_DRAWITEM;
  24. procedure CMMouseEnter(var Message: TMessage); message CM_MOUSEENTER;
  25. procedure CMMouseLeave(var Message: TMessage); message CM_MOUSELEAVE;
  26. protected
  27. procedure ActionChange(Sender: TObject; CheckDefaults: Boolean); override;
  28. procedure SetButtonStyle(ADefault: Boolean); override;
  29. public
  30. constructor Create(AOwner: TComponent); override;
  31. destructor Destroy; override;
  32. published
  33. property PngImage: TPngImage read FPngImage write SetPngImage stored PngImageStored;
  34. property PngOptions: TPngOptions read FPngOptions write SetPngOptions default [pngBlendOnDisabled];
  35. property Glyph stored False;
  36. property NumGlyphs stored False;
  37. end;
  38. {$IF RTLVersion >= 24.0 }
  39. TPngBitBtnStyleHook = class(TBitBtnStyleHook)
  40. strict protected
  41. procedure DrawButton(ACanvas: TCanvas; AMouseInControl: Boolean); override;
  42. end;
  43. {$IFEND}
  44. implementation
  45. uses
  46. ActnList, Themes, PngButtonFunctions, PngImageList;
  47. const
  48. WordBreakFlag: array[Boolean] of Integer = (DT_SINGLELINE, DT_WORDBREAK);
  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. class constructor TPngBitBtn.Create;
  77. begin
  78. TCustomStyleEngine.RegisterStyleHook(TPngBitBtn, TPngBitBtnStyleHook);
  79. end;
  80. class destructor TPngBitBtn.Destroy;
  81. begin
  82. TCustomStyleEngine.UnRegisterStyleHook(TPngBitBtn, TPngBitBtnStyleHook);
  83. end;
  84. {$IFEND}
  85. constructor TPngBitBtn.Create(AOwner: TComponent);
  86. begin
  87. inherited Create(AOwner);
  88. FPngImage := TPngImage.Create;
  89. FPngOptions := [pngBlendOnDisabled];
  90. FCanvas := TCanvas.Create;
  91. FLastKind := bkCustom;
  92. FImageFromAction := False;
  93. end;
  94. destructor TPngBitBtn.Destroy;
  95. begin
  96. inherited Destroy;
  97. FPngImage.Free;
  98. FCanvas.Free;
  99. end;
  100. procedure TPngBitBtn.ActionChange(Sender: TObject; CheckDefaults: Boolean);
  101. begin
  102. inherited ActionChange(Sender, CheckDefaults);
  103. if Sender is TCustomAction then begin
  104. with TCustomAction(Sender) do begin
  105. //Copy image from action's imagelist
  106. if (PngImage.Empty or FImageFromAction) and (ActionList <> nil) and
  107. (ActionList.Images <> nil) and (ImageIndex >= 0) and (ImageIndex <
  108. ActionList.Images.Count) then begin
  109. CopyImageFromImageList(FPngImage, ActionList.Images, ImageIndex);
  110. FImageFromAction := True;
  111. end;
  112. end;
  113. end;
  114. end;
  115. procedure TPngBitBtn.SetButtonStyle(ADefault: Boolean);
  116. begin
  117. inherited SetButtonStyle(ADefault);
  118. if ADefault <> IsFocused then begin
  119. IsFocused := ADefault;
  120. Refresh;
  121. end;
  122. end;
  123. function TPngBitBtn.PngImageStored: Boolean;
  124. begin
  125. Result := not FImageFromAction;
  126. end;
  127. procedure TPngBitBtn.SetPngImage(const Value: TPngImage);
  128. begin
  129. //This is all neccesary, because you can't assign a nil to a TPngImage
  130. if Value = nil then begin
  131. FPngImage.Free;
  132. FPngImage := TPngImage.Create;
  133. end
  134. else begin
  135. FPngImage.Assign(Value);
  136. end;
  137. //To work around the gamma-problem
  138. with FPngImage do
  139. if not Empty and (Header.ColorType in [COLOR_RGB, COLOR_RGBALPHA, COLOR_PALETTE]) then
  140. Chunks.RemoveChunk(Chunks.ItemFromClass(TChunkgAMA));
  141. FImageFromAction := False;
  142. Repaint;
  143. end;
  144. procedure TPngBitBtn.SetPngOptions(const Value: TPngOptions);
  145. begin
  146. if FPngOptions <> Value then begin
  147. FPngOptions := Value;
  148. Repaint;
  149. end;
  150. end;
  151. procedure TPngBitBtn.CNDrawItem(var Message: TWMDrawItem);
  152. var
  153. R, PaintRect: TRect;
  154. GlyphPos, TextPos: TPoint;
  155. IsDown, IsDefault: Boolean;
  156. Flags: Cardinal;
  157. Button: TThemedButton;
  158. Details: TThemedElementDetails;
  159. dtFlags: Integer;
  160. begin
  161. dtFlags := DrawTextBiDiModeFlags(0) or WordBreakFlag[WordWrap];
  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, dtFlags);
  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, DT_TOP or DT_LEFT or dtFlags);
  237. OffsetRect(PaintRect, -1, -1);
  238. FCanvas.Font.Color := clBtnShadow;
  239. end;
  240. DrawText(FCanvas.Handle, PChar(Caption), -1, PaintRect, DT_TOP or DT_LEFT or dtFlags);
  241. end;
  242. //Draw the focus rectangle
  243. if IsFocused and IsDefault then begin
  244. if not StyleServices.Enabled then begin
  245. R := ClientRect;
  246. InflateRect(R, -3, -3);
  247. end;
  248. FCanvas.Pen.Color := clWindowFrame;
  249. FCanvas.Brush.Color := clBtnFace;
  250. DrawFocusRect(FCanvas.Handle, R);
  251. end;
  252. FLastKind := Kind;
  253. FCanvas.Handle := 0;
  254. end;
  255. procedure TPngBitBtn.CMMouseEnter(var Message: TMessage);
  256. begin
  257. inherited;
  258. if StyleServices.Enabled and not FMouseInControl and not (csDesigning in ComponentState) then begin
  259. FMouseInControl := True;
  260. Repaint;
  261. end;
  262. end;
  263. procedure TPngBitBtn.CMMouseLeave(var Message: TMessage);
  264. begin
  265. inherited;
  266. if StyleServices.Enabled and FMouseInControl then begin
  267. FMouseInControl := False;
  268. Repaint;
  269. end;
  270. end;
  271. { TPngBitBtnStyleHook }
  272. {$IF RTLVersion >= 24.0 }
  273. procedure TPngBitBtnStyleHook.DrawButton(ACanvas: TCanvas;
  274. AMouseInControl: Boolean);
  275. var
  276. Details: TThemedElementDetails;
  277. DrawRect, PaintRect, TextRect: TRect;
  278. State: TButtonState;
  279. btn : TPngBitBtn;
  280. dtFlags: Integer;
  281. GlyphPos, TextPos: TPoint;
  282. LColor: TColor;
  283. LFormats: TTextFormat;
  284. begin
  285. dtFlags := btn.DrawTextBiDiModeFlags(0) or WordBreakFlag[btn.WordWrap];
  286. if not (Control is TPngBitBtn) then
  287. begin
  288. inherited;
  289. Exit;
  290. end;
  291. if FPressed then
  292. Details := StyleServices.GetElementDetails(tbPushButtonPressed)
  293. else if AMouseInControl then
  294. Details := StyleServices.GetElementDetails(tbPushButtonHot)
  295. else if Focused or TPngBitBtn(Control).Default then
  296. Details := StyleServices.GetElementDetails(tbPushButtonDefaulted)
  297. else if Control.Enabled then
  298. Details := StyleServices.GetElementDetails(tbPushButtonNormal)
  299. else
  300. Details := StyleServices.GetElementDetails(tbPushButtonDisabled);
  301. DrawRect := Control.ClientRect;
  302. StyleServices.DrawElement(ACanvas.Handle, Details, DrawRect);
  303. btn := Control as TPngBitBtn;
  304. ACanvas.Font := btn.Font;
  305. if not btn.Enabled then State := bsDisabled
  306. else if FPressed then State := bsDown
  307. else State := bsUp;
  308. //Calculate the position of the PNG glyph
  309. CalcButtonLayout(ACanvas, btn.FPngImage, btn.ClientRect, FPressed, False, btn.Caption,
  310. btn.Layout, btn.Margin, btn.Spacing, GlyphPos, TextPos, dtFlags);
  311. //Draw the image
  312. if (btn.FPngImage <> nil) and (btn.Kind = bkCustom) and not btn.FPngImage.Empty then begin
  313. PaintRect := Bounds(GlyphPos.X, GlyphPos.Y, btn.FPngImage.Width, btn.FPngImage.Height);
  314. if btn.Enabled then
  315. DrawPNG(btn.FPngImage, ACanvas, PaintRect, [])
  316. else
  317. DrawPNG(btn.FPngImage, ACanvas, PaintRect, btn.FPngOptions);
  318. end;
  319. ACanvas.Brush.Style := bsClear;
  320. if (State = bsDisabled) or (not StyleServices.IsSystemStyle and (seFont in btn.StyleElements)) then
  321. begin
  322. if not StyleServices.GetElementColor(Details, ecTextColor, LColor) or (LColor = clNone) then
  323. LColor := ACanvas.Font.Color;
  324. end
  325. else
  326. LColor := ACanvas.Font.Color;
  327. LFormats := TTextFormatFlags(DT_NOCLIP or DT_CENTER or DT_VCENTER or dtFlags);
  328. if Length(btn.Caption) > 0 then begin
  329. TextRect := Rect(0, 0, btn.ClientRect.Right - btn.ClientRect.Left, 0);
  330. DrawText(ACanvas.Handle, PChar(btn.Caption), Length(btn.Caption), TextRect, DT_CALCRECT or dtFlags);
  331. end
  332. else begin
  333. TextRect := Rect(0, 0, 0, 0);
  334. end;
  335. OffsetRect(TextRect, TextPos.X + btn.ClientRect.Left, TextPos.Y + btn.ClientRect.Top);
  336. StyleServices.DrawText(ACanvas.Handle, Details, btn.Caption, TextRect, LFormats, LColor);
  337. end;
  338. {$IFEND}
  339. end.