CnSkinMenu.pas 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. {******************************************************************************}
  2. { CnPack For Delphi/C++Builder }
  3. { 中国人自己的开放源码第三方开发包 }
  4. { (C)Copyright 2001-2018 CnPack 开发组 }
  5. { ------------------------------------ }
  6. { }
  7. { 本开发包是开源的自由软件,您可以遵照 CnPack 的发布协议来修 }
  8. { 改和重新发布这一程序。 }
  9. { }
  10. { 发布这一开发包的目的是希望它有用,但没有任何担保。甚至没有 }
  11. { 适合特定目的而隐含的担保。更详细的情况请参阅 CnPack 发布协议。 }
  12. { }
  13. { 您应该已经和开发包一起收到一份 CnPack 发布协议的副本。如果 }
  14. { 还没有,可访问我们的网站: }
  15. { }
  16. { 网站地址:http://www.cnpack.org }
  17. { 电子邮件:master@cnpack.org }
  18. { }
  19. {******************************************************************************}
  20. unit CnSkinMenu;
  21. interface
  22. uses
  23. Windows, SysUtils, Classes, Menus, Forms, Graphics, ImgList,
  24. CnSkinTheme, CnCommon;
  25. type
  26. TCnSkinMenu = class(TComponent)
  27. private
  28. FMaxWidth: Integer;
  29. FColor: TColor;
  30. FHighLightColor: TColor;
  31. FForm: TForm;
  32. FSaveFormCreate: TNotifyEvent;
  33. procedure FormCreate(Sender: TObject);
  34. function GetImageList(MenuItem: TMenuItem): TCustomImageList;
  35. procedure AdvancedDrawMenuItem(Sender: TObject; ACanvas: TCanvas;
  36. ARect: TRect; State: TOwnerDrawState);
  37. procedure MeasureMenuItem(Sender: TObject; ACanvas: TCanvas;
  38. var Width, Height: Integer);
  39. protected
  40. procedure DrawBorder(Handle: HWND);
  41. procedure DoDrawText(MenuItem: TMenuItem; ACanvas: TCanvas;
  42. const ACaption: string; var Rect: TRect; Selected: Boolean; Flags: Longint);
  43. public
  44. constructor Create(AOwner: TComponent); override;
  45. destructor Destroy; override;
  46. procedure InstallMenuSkin;
  47. procedure UnInstallMenuSkin;
  48. published
  49. property Color: TColor read FColor write FColor;
  50. property HighLightColor: TColor read FHighLightColor write FHighLightColor;
  51. property MaxWidth: Integer read FMaxWidth write FMaxWidth;
  52. end;
  53. implementation
  54. var
  55. CnSkinMenus: TList;
  56. constructor TCnSkinMenu.Create(AOwner: TComponent);
  57. begin
  58. inherited Create(AOwner);
  59. FColor := clMenu;
  60. FHighLightColor := clMenuHighLight;
  61. FMaxWidth := -1;
  62. CnSkinMenus.Add(Self);
  63. FForm := AOwner as TForm;
  64. if not (csDesigning in FForm.ComponentState) then
  65. begin
  66. if Assigned(FForm.OnCreate) then
  67. FSaveFormCreate := FForm.OnCreate;
  68. FForm.OnCreate := FormCreate;
  69. end;
  70. end;
  71. destructor TCnSkinMenu.Destroy;
  72. begin
  73. CnSkinMenus.Remove(Self);
  74. inherited Destroy;
  75. end;
  76. procedure TCnSkinMenu.FormCreate(Sender: TObject);
  77. begin
  78. InstallMenuSkin;
  79. if Assigned(FSaveFormCreate) then
  80. FSaveFormCreate(Sender);
  81. end;
  82. function TCnSkinMenu.GetImageList(MenuItem: TMenuItem): TCustomImageList;
  83. var
  84. LItem: TMenuItem;
  85. LMenu: TMenu;
  86. begin
  87. Result := nil;
  88. LItem := MenuItem.Parent;
  89. while (LItem <> nil) and (LItem.SubMenuImages = nil) do
  90. LItem := LItem.Parent;
  91. if LItem <> nil then
  92. Result := LItem.SubMenuImages
  93. else
  94. begin
  95. LMenu := MenuItem.GetParentMenu;
  96. if LMenu <> nil then
  97. Result := LMenu.Images;
  98. end;
  99. end;
  100. procedure TCnSkinMenu.DrawBorder(Handle: HWND);
  101. var
  102. Canvas: TCanvas;
  103. RC, RW: TRect;
  104. X, Y: Integer;
  105. begin
  106. Canvas := TCanvas.Create;
  107. Canvas.Handle := GetWindowDC(Handle);
  108. try
  109. GetClientRect(Handle, RC);
  110. GetWindowRect(Handle, RW);
  111. OffsetRect(RW, -RW.Left, -RW.Top);
  112. X := (RW.Right - RC.Right) div 2;
  113. Y := (RW.Bottom - RC.Bottom) div 2;
  114. ExcludeClipRect(Canvas.Handle, X, Y, RW.Right - X, RW.Bottom - Y);
  115. Canvas.Pen.Color := CnSkinThemes.CurrentSkin.ShadowColor;
  116. Canvas.Brush.Color := FColor;
  117. Canvas.Rectangle(RW);
  118. finally
  119. ReleaseDC(Handle, Canvas.Handle);
  120. Canvas.Free;
  121. end;
  122. end;
  123. procedure TCnSkinMenu.DoDrawText(MenuItem: TMenuItem; ACanvas: TCanvas;
  124. const ACaption: string; var Rect: TRect; Selected: Boolean; Flags: Longint);
  125. begin
  126. with ACanvas do
  127. begin
  128. Brush.Style := bsClear;
  129. if MenuItem.Default then
  130. Font.Style := Font.Style + [fsBold];
  131. if not MenuItem.Enabled then
  132. begin
  133. OffsetRect(Rect, 1, 1);
  134. Font.Color := CnSkinThemes.CurrentSkin.LightColor;
  135. DrawText(Handle, PChar(ACaption), -1, Rect, Flags);
  136. OffsetRect(Rect, -1, -1);
  137. Font.Color := CnSkinThemes.CurrentSkin.ShadowColor;
  138. end
  139. else if Selected then
  140. Font.Color := CnSkinThemes.CurrentSkin.LightColor;
  141. DrawText(Handle, PChar(ACaption), -1, Rect, Flags);
  142. end;
  143. end;
  144. procedure TCnSkinMenu.AdvancedDrawMenuItem(Sender: TObject; ACanvas: TCanvas;
  145. ARect: TRect; State: TOwnerDrawState);
  146. var
  147. MenuItem: TMenuItem;
  148. Y: Integer;
  149. Image: TCustomImageList;
  150. Selected: Boolean;
  151. Text: string;
  152. procedure DrawTopLevelMenuItem;
  153. begin
  154. Selected := Selected or (odHotLight in State);
  155. if Selected then
  156. begin
  157. ACanvas.Brush.Color := FHighLightColor;
  158. ACanvas.Pen.Color := CnSkinThemes.CurrentSkin.ShadowColor;
  159. end else
  160. begin
  161. ACanvas.Brush.Color := clMenuBar;
  162. ACanvas.Pen.Color := clMenuBar;
  163. end;
  164. ACanvas.Rectangle(ARect);
  165. DoDrawText(MenuItem, ACanvas, MenuItem.Caption, ARect, Selected,
  166. DT_CENTER or DT_VCENTER or DT_SINGLELINE);
  167. end;
  168. begin
  169. MenuItem := Sender as TMenuItem;
  170. Image := GetImageList(MenuItem);
  171. Selected := odSelected in State;
  172. if MenuItem.GetParentComponent is TMainMenu then
  173. DrawTopLevelMenuItem
  174. else
  175. begin
  176. DrawBorder(WindowFromDC(ACanvas.Handle));
  177. with ACanvas do
  178. begin
  179. Pen.Color := CnSkinThemes.CurrentSkin.ShadowColor;
  180. if Selected and MenuItem.Enabled then
  181. begin
  182. Brush.Color := FHighLightColor;
  183. Rectangle(ARect);
  184. end else
  185. begin
  186. Brush.Color := FColor;
  187. FillRect(ARect);
  188. end;
  189. if MenuItem.Caption = '-' then
  190. begin
  191. Y := ARect.Top + (ARect.Bottom - ARect.Top -1) div 2;
  192. MoveTo(ARect.Left, Y);
  193. LineTo(ARect.Right, Y);
  194. end else
  195. begin
  196. if Assigned(Image) and (MenuItem.ImageIndex >= 0) then
  197. Image.Draw(ACanvas, ARect.Left + 1, ARect.Top + 1,
  198. MenuItem.ImageIndex, MenuItem.Enabled)
  199. else
  200. begin
  201. if MenuItem.Enabled then
  202. Pen.Color := clBlack else
  203. Pen.Color := clGray;
  204. if MenuItem.Checked then
  205. begin
  206. MoveTo(ARect.Left + 3, ARect.Top + 8);
  207. LineTo(ARect.Left + 7, ARect.Top + 12);
  208. LineTo(ARect.Left + 14, ARect.Top + 4);
  209. MoveTo(AREct.Left + 2, ARect.Top + 9);
  210. LineTo(ARect.Left + 6, ARect.Top + 13);
  211. LineTo(ARect.Left + 15, ARect.Top + 5);
  212. end;
  213. end;
  214. if Assigned(Image) then
  215. Inc(ARect.Left, Image.Width + 4) else
  216. Inc(ARect.Left, 20);
  217. Dec(ARect.Right, 16);
  218. Text := ShortCutToText(MenuItem.ShortCut);
  219. if Text <> '' then
  220. begin
  221. DoDrawText(MenuItem, ACanvas, Text, ARect, Selected,
  222. DT_VCENTER or DT_SINGLELINE or DT_RIGHT);
  223. Dec(ARect.Right, ACanvas.TextWidth(Text) + 5);
  224. end;
  225. DoDrawText(MenuItem, ACanvas, MenuItem.Caption, ARect, Selected,
  226. DT_VCENTER or DT_SINGLELINE or DT_LEFT or DT_END_ELLIPSIS);
  227. end;
  228. end;
  229. end;
  230. end;
  231. procedure TCnSkinMenu.MeasureMenuItem(Sender: TObject; ACanvas: TCanvas;
  232. var Width, Height: Integer);
  233. var
  234. MenuItem: TMenuItem;
  235. begin
  236. MenuItem := Sender as TMenuItem;
  237. if MenuItem.GetParentComponent is TMainMenu then Exit;
  238. if GetImageList(MenuItem) = nil then
  239. begin
  240. if Assigned(Menuitem.Bitmap) and not MenuItem.Bitmap.Empty then
  241. Inc(Width, 23) else
  242. Inc(Width, 38);
  243. end;
  244. if (FMaxWidth >= 0) and (Width > FMaxWidth) then
  245. Width := FMaxWidth;
  246. end;
  247. procedure TCnSkinMenu.InstallMenuSkin;
  248. var
  249. I: Integer;
  250. MenuItem: TMenuItem;
  251. begin
  252. if FForm = nil then
  253. Exit;
  254. for I := 0 to FForm.ComponentCount -1 do
  255. if FForm.Components[I] is TMenuItem then
  256. begin
  257. MenuItem := FForm.Components[I] as TMenuItem;
  258. MenuItem.OnMeasureItem := MeasureMenuItem;
  259. MenuItem.OnAdvancedDrawItem := AdvancedDrawMenuItem;
  260. end
  261. else if FForm.Components[I] is TMenu then
  262. (FForm.Components[I] as TMenu).OwnerDraw := True;
  263. end;
  264. procedure TCnSkinMenu.UnInstallMenuSkin;
  265. var
  266. I: Integer;
  267. MenuItem: TMenuItem;
  268. begin
  269. if FForm = nil then
  270. Exit;
  271. for I := 0 to FForm.ComponentCount -1 do
  272. if FForm.Components[I] is TMenuItem then
  273. begin
  274. MenuItem := FForm.Components[I] as TMenuItem;
  275. if TMethod(MenuItem.OnMeasureItem).Code = @TCnSkinMenu.MeasureMenuItem then
  276. MenuItem.OnMeasureItem := nil;
  277. if TMethod(MenuItem.OnAdvancedDrawItem).Code = @TCnSkinMenu.AdvancedDrawMenuItem then
  278. MenuItem.OnAdvancedDrawItem := nil;
  279. end
  280. else if FForm.Components[I] is TMenu then
  281. (FForm.Components[I] as TMenu).OwnerDraw := False;
  282. end;
  283. initialization
  284. CnSkinMenus := TList.Create;
  285. finalization
  286. CnSkinMenus.Free;
  287. end.