2
0

NewStaticText.pas 13 KB

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