NewStaticText.pas 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  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. if StyleServices(Self).Enabled and Transparent then
  291. begin
  292. SetBkMode(Message.ChildDC, Windows.TRANSPARENT);
  293. StyleServices(Self).DrawParentBackground(Handle, Message.ChildDC, nil, False);
  294. { Return an empty brush to prevent Windows from overpainting what we just have created. }
  295. Message.Result := GetStockObject(NULL_BRUSH);
  296. end
  297. else
  298. inherited;
  299. end;
  300. {$ENDIF}
  301. procedure TNewStaticText.SetTransparent(const Value: Boolean);
  302. begin
  303. {$IFDEF TRANSPARENCYSUPPORT}
  304. if Transparent <> Value then
  305. begin
  306. if Value then
  307. ControlStyle := ControlStyle - [csOpaque]
  308. else
  309. ControlStyle := ControlStyle + [csOpaque];
  310. Invalidate;
  311. end;
  312. {$ELSE}
  313. FTransparent := Value;
  314. {$ENDIF}
  315. end;
  316. function TNewStaticText.GetTransparent: Boolean;
  317. begin
  318. {$IFDEF TRANSPARENCYSUPPORT}
  319. Result := not (csOpaque in ControlStyle);
  320. {$ELSE}
  321. Result := FTransparent;
  322. {$ENDIF}
  323. end;
  324. procedure TNewStaticText.SetWordWrap(Value: Boolean);
  325. begin
  326. if FWordWrap <> Value then
  327. begin
  328. FWordWrap := Value;
  329. RecreateWnd;
  330. AdjustBounds;
  331. end;
  332. end;
  333. {$IFDEF VCLSTYLES}
  334. { TNewStaticTextStyleHook - same as Vcl.StdCtrls' TStaticTextStyleHook
  335. except that it accesses the Control property as a TNewStaticText instead
  336. of a TCustomStaticText or TStaticText }
  337. type
  338. TControlAccess = class(TControl);
  339. constructor TNewStaticTextStyleHook.Create(AControl: TWinControl);
  340. begin
  341. inherited;
  342. OverridePaint := True;
  343. OverrideEraseBkgnd := True;
  344. PaintOnEraseBkgnd := True;
  345. DoubleBuffered := True;
  346. end;
  347. procedure TNewStaticTextStyleHook.Paint(Canvas: TCanvas);
  348. const
  349. States: array[Boolean] of TThemedTextLabel = (ttlTextLabelDisabled, ttlTextLabelNormal);
  350. var
  351. Details: TThemedElementDetails;
  352. R: TRect;
  353. S: String;
  354. LStyle: TCustomStyleServices;
  355. begin
  356. LStyle := StyleServices;
  357. if LStyle.Available then begin
  358. R := Control.ClientRect;
  359. {$IFDEF TRANSPARENCYSUPPORT}
  360. if TNewStaticText(Control).Transparent then begin
  361. Details := LStyle.GetElementDetails(tbCheckBoxUncheckedNormal);
  362. LStyle.DrawParentBackground(Handle, Canvas.Handle, Details, False);
  363. Canvas.Brush.Style := bsClear;
  364. end else {$ENDIF} begin
  365. Canvas.Brush.Color := LStyle.GetStyleColor(scWindow);
  366. Canvas.FillRect(R);
  367. end;
  368. Details := LStyle.GetElementDetails(States[Control.Enabled]);
  369. S := TNewStaticText(Control).Caption;
  370. if (S = '') or (TNewStaticText(Control).FShowAccelChar and (S[1] = '&') and (S[2] = #0)) then
  371. S := S + ' ';
  372. if seFont in Control.StyleElements then
  373. DrawControlText(Canvas, Details, S, R, TNewStaticText(Control).GetDrawTextFlags)
  374. else begin
  375. Canvas.Font := TNewStaticText(Control).Font;
  376. DrawText(Canvas.Handle, S, Length(S), R, TNewStaticText(Control).GetDrawTextFlags);
  377. end;
  378. end;
  379. end;
  380. {$ENDIF}
  381. end.