NewStaticText.pas 13 KB

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