NewStaticText.pas 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  1. unit NewStaticText;
  2. {
  3. TNewStaticText - similar to TStaticText but with multi-line AutoSize
  4. support and a WordWrap property, and without a Transparent property.
  5. Define VCLSTYLES for full VCL Styles support, and for transparency support.
  6. }
  7. interface
  8. uses
  9. Windows, Messages, SysUtils, Classes, Controls, Forms,
  10. {$IFDEF VCLSTYLES} Vcl.Themes, {$ELSE} Themes, {$ENDIF}
  11. Graphics;
  12. {$IFDEF VCLSTYLES}
  13. {$DEFINE TRANSPARENCYSUPPORT}
  14. {$ENDIF}
  15. type
  16. TNewStaticText = class(TWinControl)
  17. private
  18. FAutoSize: Boolean;
  19. FFocusControl: TWinControl;
  20. FForceLTRReading: Boolean;
  21. FLastAdjustBoundsRTL: Boolean;
  22. FShowAccelChar: Boolean;
  23. {$IFNDEF TRANSPARENCYSUPPORT}
  24. FTransparent: Boolean;
  25. {$ENDIF}
  26. FWordWrap: Boolean;
  27. class constructor Create;
  28. class destructor Destroy;
  29. {$IFDEF TRANSPARENCYSUPPORT}
  30. procedure CNCtlColorStatic(var Message: TWMCtlColorStatic); message CN_CTLCOLORSTATIC;
  31. {$ENDIF}
  32. procedure CMDialogChar(var Message: TCMDialogChar); message CM_DIALOGCHAR;
  33. procedure CMFontChanged(var Message: TMessage); message CM_FONTCHANGED;
  34. procedure CMParentFontChanged(var Message: TMessage); message CM_PARENTFONTCHANGED;
  35. procedure CMTextChanged(var Message: TMessage); message CM_TEXTCHANGED;
  36. procedure AdjustBounds;
  37. function CalcBounds: TPoint;
  38. function GetDrawTextFlags: UINT;
  39. procedure SetFocusControl(Value: TWinControl);
  40. procedure SetForceLTRReading(Value: Boolean);
  41. procedure SetShowAccelChar(Value: Boolean);
  42. procedure SetTransparent(const Value: Boolean);
  43. procedure SetWordWrap(Value: Boolean);
  44. function GetTransparent: Boolean;
  45. protected
  46. procedure CreateParams(var Params: TCreateParams); override;
  47. procedure Loaded; override;
  48. procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  49. procedure SetAutoSize(Value: Boolean); override;
  50. public
  51. constructor Create(AOwner: TComponent); override;
  52. function AdjustHeight: Integer;
  53. published
  54. property Align;
  55. property Anchors;
  56. property AutoSize: Boolean read FAutoSize write SetAutoSize default True;
  57. property Caption;
  58. property Color;
  59. property DragCursor;
  60. property DragMode;
  61. property Enabled;
  62. property FocusControl: TWinControl read FFocusControl write SetFocusControl;
  63. property Font;
  64. property ForceLTRReading: Boolean read FForceLTRReading write SetForceLTRReading
  65. default False;
  66. property ParentColor;
  67. property ParentFont;
  68. property ParentShowHint;
  69. property PopupMenu;
  70. property ShowAccelChar: Boolean read FShowAccelChar write SetShowAccelChar
  71. default True;
  72. property ShowHint;
  73. property StyleElements;
  74. property StyleName;
  75. property TabOrder;
  76. property TabStop;
  77. property Transparent: Boolean read GetTransparent write SetTransparent
  78. default True;
  79. property Visible;
  80. property WordWrap: Boolean read FWordWrap write SetWordWrap default False;
  81. property OnClick;
  82. property OnDblClick;
  83. property OnDragDrop;
  84. property OnDragOver;
  85. property OnEndDrag;
  86. property OnMouseDown;
  87. property OnMouseMove;
  88. property OnMouseUp;
  89. property OnStartDrag;
  90. end;
  91. TNewStaticTextStyleHook = class(TStyleHook)
  92. {$IFDEF VCLSTYLES}
  93. strict protected
  94. procedure Paint(Canvas: TCanvas); override;
  95. public
  96. constructor Create(AControl: TWinControl); override;
  97. {$ENDIF}
  98. end;
  99. procedure Register;
  100. implementation
  101. uses
  102. StdCtrls, Types, BidiUtils;
  103. procedure Register;
  104. begin
  105. RegisterComponents('JR', [TNewStaticText]);
  106. end;
  107. { TNewStaticText }
  108. constructor TNewStaticText.Create(AOwner: TComponent);
  109. begin
  110. inherited Create(AOwner);
  111. ControlStyle := [csCaptureMouse, csClickEvents, csSetCaption,
  112. csReplicatable, csDoubleClicks, csGestures {$IF CompilerVersion >= 35.0}, csNeedsDesignDisabledState{$ENDIF}];
  113. {$IFNDEF TRANSPARENCYSUPPORT}
  114. ControlStyle := ControlStyle + [csOpaque];
  115. FTransparent := True; { Even though Transparent is ignored, we still want it to work as if it isn't at design time }
  116. {$ENDIF}
  117. Width := 65;
  118. Height := 17;
  119. FAutoSize := True;
  120. FShowAccelChar := True;
  121. AdjustBounds;
  122. end;
  123. class constructor TNewStaticText.Create;
  124. begin
  125. TCustomStyleEngine.RegisterStyleHook(TNewStaticText, TNewStaticTextStyleHook);
  126. end;
  127. procedure TNewStaticText.CreateParams(var Params: TCreateParams);
  128. begin
  129. inherited CreateParams(Params);
  130. CreateSubClass(Params, 'STATIC');
  131. with Params do
  132. begin
  133. Style := Style or SS_NOTIFY;
  134. if ExStyle and WS_EX_RIGHT = 0 then begin
  135. { Quirk: No style is set for WordWrap=False in RTL mode; WS_EX_RIGHT
  136. overrides SS_LEFTNOWORDWRAP, and there is no SS_RIGHTNOWORDWRAP style.
  137. WordWrap=False still affects AdjustBounds, though. }
  138. if not FWordWrap then Style := Style or SS_LEFTNOWORDWRAP;
  139. end;
  140. if not FShowAccelChar then Style := Style or SS_NOPREFIX;
  141. if FForceLTRReading then ExStyle := ExStyle and not WS_EX_RTLREADING;
  142. WindowClass.style := WindowClass.style and not CS_VREDRAW;
  143. end;
  144. end;
  145. class destructor TNewStaticText.Destroy;
  146. begin
  147. TCustomStyleEngine.UnregisterStyleHook(TNewStaticText, TNewStaticTextStyleHook);
  148. end;
  149. procedure TNewStaticText.CMDialogChar(var Message: TCMDialogChar);
  150. begin
  151. if (FFocusControl <> nil) and Enabled and ShowAccelChar and
  152. IsAccel(Message.CharCode, Caption) then
  153. with FFocusControl do
  154. if CanFocus then
  155. begin
  156. SetFocus;
  157. Message.Result := 1;
  158. end;
  159. end;
  160. procedure TNewStaticText.CMFontChanged(var Message: TMessage);
  161. begin
  162. inherited;
  163. AdjustBounds;
  164. end;
  165. procedure TNewStaticText.CMParentFontChanged(var Message: TMessage);
  166. begin
  167. inherited;
  168. { What we're really trapping here is changes to Parent. Recalculate size
  169. if the new Parent's RTL setting is different. }
  170. if IsRightToLeft <> FLastAdjustBoundsRTL then
  171. AdjustBounds;
  172. end;
  173. procedure TNewStaticText.CMTextChanged(var Message: TMessage);
  174. begin
  175. inherited;
  176. Invalidate;
  177. AdjustBounds;
  178. end;
  179. procedure TNewStaticText.Loaded;
  180. begin
  181. inherited Loaded;
  182. AdjustBounds;
  183. end;
  184. function TNewStaticText.GetDrawTextFlags: UINT;
  185. begin
  186. Result := DT_EXPANDTABS or DT_NOCLIP;
  187. if FWordWrap then Result := Result or DT_WORDBREAK;
  188. if not FShowAccelChar then Result := Result or DT_NOPREFIX;
  189. if IsRightToLeft then begin
  190. { Note: DT_RTLREADING must be included even when just calculating the
  191. size, since on certain fonts it can affect the width of characters.
  192. (Consider the Hebrew string: 'a '#$F9' b'. On 2000 with Lucida Console
  193. as the font, the spaces aren't drawn as wide with RTLREADING.) }
  194. Result := Result or DT_RIGHT;
  195. if not FForceLTRReading then
  196. Result := Result or DT_RTLREADING;
  197. end;
  198. end;
  199. function TNewStaticText.CalcBounds: TPoint;
  200. var
  201. R: TRect;
  202. S: String;
  203. DC: HDC;
  204. begin
  205. { Note: The calculated width/height is actually one pixel wider/taller
  206. than the size of the text, so that when Enabled=False the white shadow
  207. does not get clipped }
  208. R := Rect(0, 0, Width, 0);
  209. if R.Right > 0 then Dec(R.Right);
  210. S := Caption;
  211. if (S = '') or (FShowAccelChar and (S[1] = '&') and (Length(S) = 1)) then
  212. S := S + ' ';
  213. DC := GetDC(0);
  214. try
  215. SelectObject(DC, Font.Handle);
  216. DrawText(DC, PChar(S), Length(S), R, DT_CALCRECT or GetDrawTextFlags);
  217. finally
  218. ReleaseDC(0, DC);
  219. end;
  220. Result.X := R.Right + 1;
  221. Result.Y := R.Bottom + 1;
  222. end;
  223. procedure TNewStaticText.AdjustBounds;
  224. var
  225. NewBounds: TPoint;
  226. NewLeft, NewWidth: Integer;
  227. begin
  228. if not (csLoading in ComponentState) and FAutoSize then
  229. begin
  230. FLastAdjustBoundsRTL := IsRightToLeft;
  231. NewBounds := CalcBounds;
  232. NewLeft := Left;
  233. NewWidth := Width;
  234. if not FWordWrap then begin
  235. NewWidth := NewBounds.X;
  236. if IsParentFlipped(Self) then
  237. Inc(NewLeft, Width - NewWidth);
  238. end;
  239. SetBounds(NewLeft, Top, NewWidth, NewBounds.Y);
  240. end;
  241. end;
  242. function TNewStaticText.AdjustHeight: Integer;
  243. var
  244. OldHeight: Integer;
  245. begin
  246. OldHeight := Height;
  247. Height := CalcBounds.Y;
  248. Result := Height - OldHeight;
  249. end;
  250. procedure TNewStaticText.Notification(AComponent: TComponent;
  251. Operation: TOperation);
  252. begin
  253. inherited Notification(AComponent, Operation);
  254. if (Operation = opRemove) and (AComponent = FFocusControl) then
  255. FFocusControl := nil;
  256. end;
  257. procedure TNewStaticText.SetAutoSize(Value: Boolean);
  258. begin
  259. if FAutoSize <> Value then
  260. begin
  261. FAutoSize := Value;
  262. if Value then AdjustBounds;
  263. end;
  264. end;
  265. procedure TNewStaticText.SetFocusControl(Value: TWinControl);
  266. begin
  267. FFocusControl := Value;
  268. if Value <> nil then Value.FreeNotification(Self);
  269. end;
  270. procedure TNewStaticText.SetForceLTRReading(Value: Boolean);
  271. begin
  272. if FForceLTRReading <> Value then begin
  273. FForceLTRReading := Value;
  274. RecreateWnd;
  275. AdjustBounds;
  276. end;
  277. end;
  278. procedure TNewStaticText.SetShowAccelChar(Value: Boolean);
  279. begin
  280. if FShowAccelChar <> Value then
  281. begin
  282. FShowAccelChar := Value;
  283. RecreateWnd;
  284. AdjustBounds;
  285. end;
  286. end;
  287. {$IFDEF TRANSPARENCYSUPPORT}
  288. procedure TNewStaticText.CNCtlColorStatic(var Message: TWMCtlColorStatic);
  289. begin
  290. { Vcl.StdCtrls' TCustomStaticText.CNCtlColorStatic uses StyleServices(Self).Enabled
  291. instead of IsCustomStyleActive, but that can return True even when no style is active,
  292. which is undesirable because when no style is active the inherited call is required to
  293. support the Font and Brush colors }
  294. if IsCustomStyleActive and Transparent then
  295. begin
  296. SetBkMode(Message.ChildDC, Windows.TRANSPARENT);
  297. StyleServices(Self).DrawParentBackground(Handle, Message.ChildDC, nil, False);
  298. { Return an empty brush to prevent Windows from overpainting what we just have created. }
  299. Message.Result := LRESULT(GetStockObject(NULL_BRUSH));
  300. end
  301. else
  302. inherited;
  303. end;
  304. {$ENDIF}
  305. procedure TNewStaticText.SetTransparent(const Value: Boolean);
  306. begin
  307. {$IFDEF TRANSPARENCYSUPPORT}
  308. if Transparent <> Value then
  309. begin
  310. if Value then
  311. ControlStyle := ControlStyle - [csOpaque]
  312. else
  313. ControlStyle := ControlStyle + [csOpaque];
  314. Invalidate;
  315. end;
  316. {$ELSE}
  317. FTransparent := Value;
  318. {$ENDIF}
  319. end;
  320. function TNewStaticText.GetTransparent: Boolean;
  321. begin
  322. {$IFDEF TRANSPARENCYSUPPORT}
  323. Result := not (csOpaque in ControlStyle);
  324. {$ELSE}
  325. Result := FTransparent;
  326. {$ENDIF}
  327. end;
  328. procedure TNewStaticText.SetWordWrap(Value: Boolean);
  329. begin
  330. if FWordWrap <> Value then
  331. begin
  332. FWordWrap := Value;
  333. RecreateWnd;
  334. AdjustBounds;
  335. end;
  336. end;
  337. {$IFDEF VCLSTYLES}
  338. { TNewStaticTextStyleHook - same as Vcl.StdCtrls' TStaticTextStyleHook
  339. except that it accesses the Control property as a TNewStaticText instead
  340. of a TCustomStaticText or TStaticText, and that it uses the control's
  341. Color property }
  342. type
  343. TControlAccess = class(TControl);
  344. constructor TNewStaticTextStyleHook.Create(AControl: TWinControl);
  345. begin
  346. inherited;
  347. OverridePaint := True;
  348. OverrideEraseBkgnd := True;
  349. PaintOnEraseBkgnd := True;
  350. DoubleBuffered := True;
  351. end;
  352. procedure TNewStaticTextStyleHook.Paint(Canvas: TCanvas);
  353. const
  354. States: array[Boolean] of TThemedTextLabel = (ttlTextLabelDisabled, ttlTextLabelNormal);
  355. var
  356. Details: TThemedElementDetails;
  357. R: TRect;
  358. S: String;
  359. LStyle: TCustomStyleServices;
  360. begin
  361. LStyle := StyleServices;
  362. if LStyle.Available then begin
  363. R := Control.ClientRect;
  364. {$IFDEF TRANSPARENCYSUPPORT}
  365. if TNewStaticText(Control).Transparent then begin
  366. Details := LStyle.GetElementDetails(tbCheckBoxUncheckedNormal);
  367. LStyle.DrawParentBackground(Handle, Canvas.Handle, Details, False);
  368. Canvas.Brush.Style := bsClear;
  369. end else {$ENDIF} begin
  370. Canvas.Brush.Color := TNewStaticText(Control).Color;
  371. Canvas.FillRect(R);
  372. end;
  373. Details := LStyle.GetElementDetails(States[Control.Enabled]);
  374. S := TNewStaticText(Control).Caption;
  375. if (S = '') or (TNewStaticText(Control).FShowAccelChar and (S[1] = '&') and (S[2] = #0)) then
  376. S := S + ' ';
  377. if seFont in Control.StyleElements then
  378. DrawControlText(Canvas, Details, S, R, TNewStaticText(Control).GetDrawTextFlags)
  379. else begin
  380. Canvas.Font := TNewStaticText(Control).Font;
  381. DrawText(Canvas.Handle, S, Length(S), R, TNewStaticText(Control).GetDrawTextFlags);
  382. end;
  383. end;
  384. end;
  385. {$ENDIF}
  386. end.