NewStaticText.pas 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. unit NewStaticText;
  2. {
  3. TNewStaticText - similar to TStaticText but with multi-line AutoSize
  4. support and a WordWrap property
  5. }
  6. interface
  7. uses
  8. Windows, Messages, SysUtils, Classes, Controls, Forms;
  9. type
  10. TNewStaticText = class(TWinControl)
  11. private
  12. FAutoSize: Boolean;
  13. FFocusControl: TWinControl;
  14. FForceLTRReading: Boolean;
  15. FLastAdjustBoundsRTL: Boolean;
  16. FShowAccelChar: Boolean;
  17. FWordWrap: Boolean;
  18. procedure CMDialogChar(var Message: TCMDialogChar); message CM_DIALOGCHAR;
  19. procedure CMFontChanged(var Message: TMessage); message CM_FONTCHANGED;
  20. procedure CMParentFontChanged(var Message: TMessage); message CM_PARENTFONTCHANGED;
  21. procedure CMTextChanged(var Message: TMessage); message CM_TEXTCHANGED;
  22. procedure AdjustBounds;
  23. function CalcBounds: TPoint;
  24. function GetDrawTextFlags: UINT;
  25. procedure SetFocusControl(Value: TWinControl);
  26. procedure SetForceLTRReading(Value: Boolean);
  27. procedure SetShowAccelChar(Value: Boolean);
  28. procedure SetWordWrap(Value: Boolean);
  29. protected
  30. procedure CreateParams(var Params: TCreateParams); override;
  31. procedure Loaded; override;
  32. procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  33. procedure SetAutoSize(Value: Boolean); override;
  34. procedure WMEraseBkgnd(var Message: TWMEraseBkgnd); message WM_ERASEBKGND;
  35. procedure WMPaint(var Message: TWMPaint); message WM_PAINT;
  36. public
  37. constructor Create(AOwner: TComponent); override;
  38. function AdjustHeight: Integer;
  39. published
  40. property Align;
  41. property Anchors;
  42. property AutoSize: Boolean read FAutoSize write SetAutoSize default True;
  43. property Caption;
  44. property Color;
  45. property DragCursor;
  46. property DragMode;
  47. property Enabled;
  48. property FocusControl: TWinControl read FFocusControl write SetFocusControl;
  49. property Font;
  50. property ForceLTRReading: Boolean read FForceLTRReading write SetForceLTRReading
  51. default False;
  52. property ParentColor;
  53. property ParentFont;
  54. property ParentShowHint;
  55. property PopupMenu;
  56. property ShowAccelChar: Boolean read FShowAccelChar write SetShowAccelChar
  57. default True;
  58. property ShowHint;
  59. property StyleElements;
  60. property StyleName;
  61. property TabOrder;
  62. property TabStop;
  63. property Visible;
  64. property WordWrap: Boolean read FWordWrap write SetWordWrap default False;
  65. property OnClick;
  66. property OnDblClick;
  67. property OnDragDrop;
  68. property OnDragOver;
  69. property OnEndDrag;
  70. property OnMouseDown;
  71. property OnMouseMove;
  72. property OnMouseUp;
  73. property OnStartDrag;
  74. end;
  75. procedure Register;
  76. implementation
  77. uses
  78. Graphics, Themes, Types,
  79. BidiUtils;
  80. procedure Register;
  81. begin
  82. RegisterComponents('JR', [TNewStaticText]);
  83. end;
  84. { TNewStaticText }
  85. procedure TNewStaticText.WMEraseBkgnd(var Message: TWMEraseBkgnd);
  86. begin;
  87. if IsCustomStyleActive and (seClient in StyleElements) then
  88. Message.Result := 1
  89. else
  90. inherited;
  91. end;
  92. procedure TNewStaticText.WMPaint(var Message: TWMPaint);
  93. const
  94. CStates: array[Boolean] of TThemedTextLabel = (ttlTextLabelDisabled, ttlTextLabelNormal);
  95. begin
  96. { Based on Vcl.StdCtrl's TCustomLabel.DoDrawThemeTextEx and its callers. Only the
  97. DrawParentBackground call is new compared to it. }
  98. if IsCustomStyleActive and (seClient in StyleElements) then begin
  99. const LStyle = StyleServices(Self);
  100. var DC := Message.DC;
  101. var PS: TPaintStruct;
  102. if DC = 0 then
  103. DC := BeginPaint(Handle, PS);
  104. try
  105. var R := ClientRect;
  106. const Details = LStyle.GetElementDetails(CStates[Enabled]);
  107. LStyle.DrawParentBackground(Handle, DC, Details, False, @R);
  108. var Text: String := Caption;
  109. if (Text = '') or (FShowAccelChar and (Text[1] = '&') and (Length(Text) = 1)) then
  110. Text := Text + ' ';
  111. const TextFlags = GetDrawTextFlags;
  112. const OldFont = SelectObject(DC, Font.Handle);
  113. try
  114. LStyle.DrawText(DC, Details, Text, R, TTextFormat(TextFlags), Font.Color);
  115. finally
  116. SelectObject(DC, OldFont);
  117. end;
  118. finally
  119. if Message.DC = 0 then
  120. EndPaint(Handle, PS);
  121. end;
  122. end else
  123. inherited;
  124. end;
  125. constructor TNewStaticText.Create(AOwner: TComponent);
  126. begin
  127. inherited Create(AOwner);
  128. ControlStyle := [csCaptureMouse, csClickEvents, csSetCaption,
  129. csReplicatable, csDoubleClicks];
  130. if not (StyleServices.Enabled and not StyleServices.IsSystemStyle) then
  131. ControlStyle := ControlStyle + [csOpaque];
  132. Width := 65;
  133. Height := 17;
  134. FAutoSize := True;
  135. FShowAccelChar := True;
  136. AdjustBounds;
  137. end;
  138. procedure TNewStaticText.CreateParams(var Params: TCreateParams);
  139. begin
  140. inherited CreateParams(Params);
  141. CreateSubClass(Params, 'STATIC');
  142. with Params do
  143. begin
  144. Style := Style or SS_NOTIFY;
  145. if not SetBiDiStyles(Self, Params) then begin
  146. { Quirk: No style is set for WordWrap=False in RTL mode; WS_EX_RIGHT
  147. overrides SS_LEFTNOWORDWRAP, and there is no SS_RIGHTNOWORDWRAP style.
  148. WordWrap=False still affects AdjustBounds, though. }
  149. if not FWordWrap then Style := Style or SS_LEFTNOWORDWRAP;
  150. end;
  151. if not FShowAccelChar then Style := Style or SS_NOPREFIX;
  152. if FForceLTRReading then ExStyle := ExStyle and not WS_EX_RTLREADING;
  153. end;
  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. Invalidate;
  170. AdjustBounds;
  171. end;
  172. procedure TNewStaticText.CMParentFontChanged(var Message: TMessage);
  173. begin
  174. inherited;
  175. Invalidate;
  176. { What we're really trapping here is changes to Parent. Recalculate size
  177. if the new Parent's RTL setting is different. }
  178. if IsParentRightToLeft(Self) <> FLastAdjustBoundsRTL then
  179. AdjustBounds;
  180. end;
  181. procedure TNewStaticText.CMTextChanged(var Message: TMessage);
  182. begin
  183. inherited;
  184. Invalidate;
  185. AdjustBounds;
  186. end;
  187. procedure TNewStaticText.Loaded;
  188. begin
  189. inherited Loaded;
  190. AdjustBounds;
  191. end;
  192. function TNewStaticText.GetDrawTextFlags: UINT;
  193. begin
  194. Result := DT_EXPANDTABS or DT_NOCLIP;
  195. if FWordWrap then Result := Result or DT_WORDBREAK;
  196. if not FShowAccelChar then Result := Result or DT_NOPREFIX;
  197. if IsParentRightToLeft(Self) then begin
  198. { Note: DT_RTLREADING must be included even when just calculating the
  199. size, since on certain fonts it can affect the width of characters.
  200. (Consider the Hebrew string: 'a '#$F9' b'. On 2000 with Lucida Console
  201. as the font, the spaces aren't drawn as wide with RTLREADING.) }
  202. Result := Result or DT_RIGHT;
  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 := IsParentRightToLeft(Self);
  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.SetAutoSize(Value: Boolean);
  266. begin
  267. if FAutoSize <> Value then
  268. begin
  269. FAutoSize := Value;
  270. if Value then AdjustBounds;
  271. end;
  272. end;
  273. procedure TNewStaticText.SetFocusControl(Value: TWinControl);
  274. begin
  275. FFocusControl := Value;
  276. if Value <> nil then Value.FreeNotification(Self);
  277. end;
  278. procedure TNewStaticText.SetForceLTRReading(Value: Boolean);
  279. begin
  280. if FForceLTRReading <> Value then begin
  281. FForceLTRReading := Value;
  282. RecreateWnd;
  283. AdjustBounds;
  284. end;
  285. end;
  286. procedure TNewStaticText.SetShowAccelChar(Value: Boolean);
  287. begin
  288. if FShowAccelChar <> Value then
  289. begin
  290. FShowAccelChar := Value;
  291. RecreateWnd;
  292. AdjustBounds;
  293. end;
  294. end;
  295. procedure TNewStaticText.SetWordWrap(Value: Boolean);
  296. begin
  297. if FWordWrap <> Value then
  298. begin
  299. FWordWrap := Value;
  300. RecreateWnd;
  301. AdjustBounds;
  302. end;
  303. end;
  304. end.