PngBitBtn.pas 12 KB

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