2
0

NewCtrls.pas 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  1. unit NewCtrls;
  2. {
  3. Inno Setup
  4. Copyright (C) 1997-2026 Jordan Russell
  5. Portions by Martijn Laan
  6. For conditions of distribution and use, see LICENSE.TXT.
  7. Previously this unit had RTL-capable versions of standard controls
  8. But now standard controls are RTL-capable already, and there's not much code left here
  9. Define VCLSTYLES to include an improved version TButtonStyleHook.DrawButton for command
  10. link buttons
  11. }
  12. interface
  13. uses
  14. Windows, SysUtils, Messages, Classes, Graphics, Controls, Forms, Dialogs,
  15. {$IFDEF VCLSTYLES} Vcl.Themes, {$ELSE} Themes, {$ENDIF}
  16. StdCtrls, ExtCtrls;
  17. type
  18. TNewEdit = class(TEdit);
  19. TNewPathEdit = class(TNewEdit)
  20. private
  21. FAutoCompleteFiles: Boolean;
  22. procedure SetAutoCompleteFiles(const Value: Boolean);
  23. protected
  24. procedure CreateWnd; override;
  25. public
  26. constructor Create(AOwner: TComponent); override;
  27. published
  28. property AutoCompleteFiles: Boolean read FAutoCompleteFiles write SetAutoCompleteFiles;
  29. end;
  30. TNewMemo = class(TMemo);
  31. TNewComboBox = class(TComboBox);
  32. TNewListBox = class(TListBox);
  33. TNewButton = class(TButton)
  34. private
  35. class constructor Create;
  36. class destructor Destroy;
  37. protected
  38. procedure CreateParams(var Params: TCreateParams); override;
  39. public
  40. function AdjustHeightIfCommandLink: Integer;
  41. end;
  42. TNewButtonStyleHook = class(TButtonStyleHook)
  43. {$IFDEF VCLSTYLES}
  44. private
  45. class function DrawOrMeasureCommandLink(const Draw: Boolean;
  46. const Control: TNewButton; const LStyle: TCustomStyleServices; const FPressed: Boolean;
  47. const ACanvas: TCanvas; const AMouseInControl: Boolean): Integer; static;
  48. class function GetIdealHeightIfCommandLink(const Control: TNewButton; const Style: TCustomStyleServices): Integer; static;
  49. protected
  50. procedure DrawButton(ACanvas: TCanvas; AMouseInControl: Boolean); override;
  51. {$ENDIF}
  52. end;
  53. TNewCheckBox = class(TCheckBox);
  54. TNewRadioButton = class(TRadioButton);
  55. TNewLinkLabel = class(TLinkLabel)
  56. public
  57. function AdjustHeight: Integer;
  58. end;
  59. procedure Register;
  60. implementation
  61. uses
  62. CommCtrl, ShLwApi,
  63. Types,
  64. BidiUtils, UnsignedFunc;
  65. procedure Register;
  66. begin
  67. RegisterComponents('JR', [TNewEdit, TNewPathEdit, TNewMemo, TNewComboBox, TNewListBox,
  68. TNewButton, TNewCheckBox, TNewRadioButton]);
  69. end;
  70. { TNewPathEdit }
  71. constructor TNewPathEdit.Create(AOwner: TComponent);
  72. begin
  73. inherited;
  74. ParentBidiMode := False;
  75. BidiMode := bdLeftToRight;
  76. end;
  77. procedure TNewPathEdit.CreateWnd;
  78. const
  79. SHACF_FILESYS_DIRS = $00000020;
  80. begin
  81. inherited;
  82. if not(csDesigning in ComponentState) then begin
  83. var Flags: DWORD;
  84. if FAutoCompleteFiles then
  85. Flags := SHACF_FILESYS_ONLY
  86. else
  87. Flags := SHACF_FILESYS_DIRS;
  88. SHAutoComplete(Handle, Flags);
  89. end;
  90. end;
  91. procedure TNewPathEdit.SetAutoCompleteFiles(const Value: Boolean);
  92. begin
  93. if FAutoCompleteFiles <> Value then begin
  94. FAutoCompleteFiles := Value;
  95. RecreateWnd;
  96. end;
  97. end;
  98. { TNewButton }
  99. class constructor TNewButton.Create;
  100. begin
  101. TCustomStyleEngine.RegisterStyleHook(TNewButton, TNewButtonStyleHook);
  102. end;
  103. procedure TNewButton.CreateParams(var Params: TCreateParams);
  104. begin
  105. inherited;
  106. if (Style = bsCommandLink) and IsRightToLeft then begin
  107. { Command link buttons need to have WS_EX_LAYOUTRTL enabled for full RTL, in addition to
  108. WS_EX_RTLREADING and WS_EX_LEFTSCROLLBAR, but not WS_EX_RIGHT. This can be confirmed by
  109. inspecting the style of a task dialog command link button. However, if VCL Styles is
  110. active, this should not be done since the style hook does not expect it at all. }
  111. if not IsCustomStyleActive then
  112. Params.ExStyle := Params.ExStyle or WS_EX_LAYOUTRTL;
  113. end;
  114. end;
  115. class destructor TNewButton.Destroy;
  116. begin
  117. TCustomStyleEngine.UnregisterStyleHook(TNewButton, TNewButtonStyleHook);
  118. end;
  119. function TNewButton.AdjustHeightIfCommandLink: Integer;
  120. begin
  121. Result := 0;
  122. if Style = bsCommandLink then begin
  123. var OldHeight := Height;
  124. {$IFDEF VCLSTYLES}
  125. var LStyle := StyleServices(Self);
  126. if not LStyle.Enabled or LStyle.IsSystemStyle then
  127. LStyle := nil;
  128. if LStyle <> nil then begin
  129. const IdealHeight = TNewButtonStyleHook.GetIdealHeightIfCommandLink(Self, LStyle);
  130. if IdealHeight <> 0 then begin
  131. Height := IdealHeight;
  132. Exit(Height - OldHeight);
  133. end;
  134. end;
  135. {$ENDIF}
  136. var IdealSize: TSize;
  137. IdealSize.cx := Width;
  138. IdealSize.cy := 0; { Not needed according to docs and tests, but clearing anyway }
  139. if SendMessage(Handle, BCM_GETIDEALSIZE, 0, LPARAM(@IdealSize)) <> 0 then begin
  140. Height := IdealSize.cy;
  141. Result := Height - OldHeight;
  142. end;
  143. end;
  144. end;
  145. { TNewLinkLabel }
  146. function TNewLinkLabel.AdjustHeight: Integer;
  147. begin
  148. var OldHeight := Height;
  149. var IdealSize: TSize;
  150. SendMessage(Handle, LM_GETIDEALSIZE, Width, LPARAM(@IdealSize));
  151. Height := IdealSize.cy;
  152. Result := Height - OldHeight;
  153. end;
  154. {$IFDEF VCLSTYLES}
  155. { TNewButtonStyleHook - same as Vcl.StdCtrls' TButtonStyleHook except that for command links it:
  156. -Adds support for measuring height
  157. -Fixes RTL support for CommandLinkHint
  158. -Actually flips the text and icons on RTL
  159. -Improves alignment of shield icons, especially at high dpi
  160. -Avoids drawing empty notes
  161. -Respects the font of the control
  162. -Properly centers glyphs vertically on the first text line
  163. For other button styles it just calls the original code, and the code for those styles is not copied here }
  164. class function TNewButtonStyleHook.DrawOrMeasureCommandLink(const Draw: Boolean;
  165. const Control: TNewButton; const LStyle: TCustomStyleServices; const FPressed: Boolean;
  166. const ACanvas: TCanvas; const AMouseInControl: Boolean): Integer;
  167. var
  168. Details: TThemedElementDetails;
  169. LParentRect, DrawRect, R, RSingleLine: TRect;
  170. LIsRightToLeft: Boolean;
  171. IL: BUTTON_IMAGELIST;
  172. IW, IH: Integer;
  173. TextFormat: TTextFormatFlags;
  174. ThemeTextColor: TColor;
  175. Buffer: string;
  176. BufferLength: Integer;
  177. ImgIndex: Integer;
  178. BCaption: String;
  179. IsDefault: Boolean;
  180. IsElevationRequired: Boolean;
  181. LPPI: Integer;
  182. begin
  183. const Handle = Control.Handle;
  184. LPPI := Control.CurrentPPI;
  185. LParentRect := Control.ClientRect;
  186. LIsRightToLeft := Control.IsRightToLeft;
  187. BCaption := Control.Caption;
  188. ImgIndex := 0;
  189. IsDefault := Control.Active;
  190. IsElevationRequired := CheckWin32Version(6, 0) and Control.ElevationRequired;
  191. if not Control.Enabled then
  192. begin
  193. Details := LStyle.GetElementDetails(tbPushButtonDisabled);
  194. ImgIndex := 3;
  195. end
  196. else
  197. if FPressed then
  198. begin
  199. Details := LStyle.GetElementDetails(tbPushButtonPressed);
  200. ImgIndex := 2;
  201. end
  202. else if AMouseInControl then
  203. begin
  204. Details := LStyle.GetElementDetails(tbPushButtonHot);
  205. ImgIndex := 1;
  206. end
  207. else if Control.Focused or IsDefault then
  208. begin
  209. Details := LStyle.GetElementDetails(tbPushButtonDefaulted);
  210. ImgIndex := 4;
  211. end
  212. else if Control.Enabled then
  213. Details := LStyle.GetElementDetails(tbPushButtonNormal);
  214. DrawRect := LParentRect;
  215. if Draw then
  216. LStyle.DrawElement(ACanvas.Handle, Details, DrawRect);
  217. IW := MulDiv(35, LPPI, Screen.DefaultPixelsPerInch);
  218. Inc(DrawRect.Left, IW);
  219. Inc(DrawRect.Top, 15);
  220. Inc(DrawRect.Left, 5);
  221. ACanvas.Font := Control.Font;
  222. R := DrawRect;
  223. TextFormat := TTextFormatFlags(UDrawTextBiDiModeFlags(Control, DT_LEFT or DT_WORDBREAK or DT_CALCRECT));
  224. LStyle.DrawText(ACanvas.Handle, Details, BCaption, R, TextFormat, ACanvas.Font.Color); { R is used directly below for measuring, and later also for the note }
  225. Result := R.Bottom;
  226. if Draw then begin
  227. RSingleLine := DrawRect;
  228. TextFormat := TTextFormatFlags(UDrawTextBiDiModeFlags(Control, DT_LEFT or DT_SINGLELINE or DT_CALCRECT));
  229. LStyle.DrawText(ACanvas.Handle, Details, BCaption, RSingleLine, TextFormat, ACanvas.Font.Color); { RSingleLine is used below for the glyphs }
  230. { Following does not use any DT_CALCRECT results }
  231. TextFormat := TTextFormatFlags(UDrawTextBiDiModeFlags(Control, DT_LEFT or DT_WORDBREAK));
  232. if (seFont in Control.StyleElements) and LStyle.GetElementColor(Details, ecTextColor, ThemeTextColor) then
  233. ACanvas.Font.Color := ThemeTextColor;
  234. var R2 := DrawRect;
  235. FlipRect(R2, LParentRect, LIsRightToLeft);
  236. LStyle.DrawText(ACanvas.Handle, Details, BCaption, R2, TextFormat, ACanvas.Font.Color);
  237. end;
  238. SetLength(Buffer, Button_GetNoteLength(Handle) + 1);
  239. if Length(Buffer) > 1 then
  240. begin
  241. BufferLength := Length(Buffer);
  242. if Button_GetNote(Handle, PChar(Buffer), BufferLength) then
  243. begin
  244. Inc(DrawRect.Top, R.Height + 2); { R is the DT_CALCRECT result }
  245. ACanvas.Font.Height := MulDiv(ACanvas.Font.Height, 2, 3);
  246. R := DrawRect;
  247. TextFormat := TTextFormatFlags(UDrawTextBiDiModeFlags(Control, DT_LEFT or DT_WORDBREAK or DT_CALCRECT));
  248. LStyle.DrawText(ACanvas.Handle, Details, Buffer, R, TextFormat, ACanvas.Font.Color); { R is used directly below for measuring }
  249. if R.Bottom > Result then
  250. Result := R.Bottom;
  251. if Draw then begin
  252. { Following does not use any DT_CALCRECT results }
  253. TextFormat := TTextFormatFlags(UDrawTextBiDiModeFlags(Control, DT_LEFT or DT_WORDBREAK));
  254. FlipRect(DrawRect, LParentRect, LIsRightToLeft);
  255. LStyle.DrawText(ACanvas.Handle, Details, Buffer, DrawRect, TextFormat, ACanvas.Font.Color);
  256. end;
  257. end;
  258. end;
  259. Inc(Result, 15);
  260. if not Draw then
  261. Exit;
  262. if Button_GetImageList(handle, IL) and (IL.himl <> 0) and
  263. ImageList_GetIconSize(IL.himl, IW, IH) then
  264. begin
  265. R.Left := 2;
  266. R.Top := RSingleLine.Top + (RSingleLine.Height - IH) div 2;
  267. if IsElevationRequired then
  268. begin
  269. ImgIndex := 0;
  270. Inc(R.Left, MulDiv(8, LPPI, Screen.DefaultPixelsPerInch));
  271. end;
  272. R.Right := R.Left + IW;
  273. R.Bottom := R.Top + IH;
  274. if Draw then begin
  275. FlipRect(R, LParentRect, LIsRightToLeft);
  276. ImageList_Draw(IL.himl, ImgIndex, ACanvas.Handle, R.Left, R.Top, ILD_NORMAL);
  277. end;
  278. end else begin
  279. if not Control.Enabled then
  280. Details := LStyle.GetElementDetails(tbCommandLinkGlyphDisabled)
  281. else
  282. if FPressed then
  283. Details := LStyle.GetElementDetails(tbCommandLinkGlyphPressed)
  284. else if Control.Focused then
  285. Details := LStyle.GetElementDetails(tbCommandLinkGlyphDefaulted)
  286. else if AMouseInControl then
  287. Details := LStyle.GetElementDetails(tbCommandLinkGlyphHot)
  288. else
  289. Details := LStyle.GetElementDetails(tbCommandLinkGlyphNormal);
  290. DrawRect.Right := IW;
  291. DrawRect.Left := 3;
  292. DrawRect.Top := RSingleLine.Top + (RSingleLine.Height - IW) div 2;
  293. DrawRect.Bottom := DrawRect.Top + IW;
  294. if Draw then begin
  295. if LIsRightToLeft then begin
  296. FlipRect(DrawRect, LParentRect, True);
  297. var FlipBitmap := TBitmap.Create;
  298. try
  299. FlipBitmap.Width := DrawRect.Width;
  300. FlipBitmap.Height := DrawRect.Height;
  301. BitBlt(FlipBitmap.Canvas.Handle, 0, 0, DrawRect.Width, DrawRect.Height, ACanvas.Handle, DrawRect.Left, DrawRect.Top, SRCCOPY);
  302. LStyle.DrawElement(FlipBitmap.Canvas.Handle, Details, Rect(0, 0, DrawRect.Width, DrawRect.Height), nil, LPPI);
  303. StretchBlt(ACanvas.Handle, DrawRect.Left, DrawRect.Top, DrawRect.Width, DrawRect.Height,
  304. FlipBitmap.Canvas.Handle, FlipBitmap.Width-1, 0, -FlipBitmap.Width, FlipBitmap.Height, SRCCOPY);
  305. finally
  306. FlipBitmap.Free;
  307. end;
  308. end else
  309. LStyle.DrawElement(ACanvas.Handle, Details, DrawRect, nil, LPPI);
  310. end;
  311. end;
  312. end;
  313. class function TNewButtonStyleHook.GetIdealHeightIfCommandLink(const Control: TNewButton; const Style: TCustomStyleServices): Integer;
  314. begin
  315. const Canvas = TCanvas.Create;
  316. try
  317. Canvas.Handle := GetDC(0);
  318. try
  319. Result := DrawOrMeasureCommandLink(False, Control, Style, False, Canvas, False);
  320. finally
  321. ReleaseDC(0, Canvas.Handle);
  322. end;
  323. finally
  324. Canvas.Handle := 0;
  325. Canvas.Free;
  326. end;
  327. end;
  328. procedure TNewButtonStyleHook.DrawButton(ACanvas: TCanvas; AMouseInControl: Boolean);
  329. begin
  330. const LControlStyle = GetWindowLong(Handle, GWL_STYLE);
  331. if (LControlStyle and BS_COMMANDLINK) <> BS_COMMANDLINK then begin
  332. inherited;
  333. Exit;
  334. end;
  335. DrawOrMeasureCommandLink(True, TNewButton(Control), StyleServices, FPressed, ACanvas, AMouseInControl);
  336. end;
  337. {$ENDIF}
  338. end.