source: Common/ScaleDPI.pas

Last change on this file was 563, checked in by chronos, 18 months ago
  • Modified: Removed U prefix from all Common package units.
File size: 11.6 KB
Line 
1unit ScaleDPI;
2
3{ See: http://wiki.lazarus.freepascal.org/High_DPI }
4
5interface
6
7uses
8 Classes, Forms, Graphics, Controls, ComCtrls, LCLType, SysUtils,
9 Generics.Collections;
10
11type
12 TControlDimensions = class;
13
14 { TControlDimension }
15
16 TControlDimension = class
17 BoundsRect: TRect;
18 FontHeight: Integer;
19 Controls: TControlDimensions;
20 // Class specifics
21 ButtonSize: TPoint; // TToolBar
22 CoolBandWidth: Integer;
23 ConstraintsMin: TPoint; // TForm
24 ConstraintsMax: TPoint; // TForm
25 constructor Create;
26 destructor Destroy; override;
27 end;
28
29 TControlDimensions = class(TObjectList<TControlDimension>)
30 end;
31
32 { TScaleDPI }
33
34 TScaleDPI = class(TComponent)
35 private
36 FAutoDetect: Boolean;
37 FDesignDPI: TPoint;
38 FDPI: TPoint;
39 procedure SetAutoDetect(AValue: Boolean);
40 procedure SetDesignDPI(AValue: TPoint);
41 procedure SetDPI(AValue: TPoint);
42 public
43 procedure StoreDimensions(Control: TControl; Dimensions: TControlDimension);
44 procedure RestoreDimensions(Control: TControl; Dimensions: TControlDimension);
45 procedure ScaleDimensions(Control: TControl; Dimensions: TControlDimension);
46 procedure ApplyToAll(FromDPI: TPoint);
47 procedure ScaleControl(Control: TControl; FromDPI: TPoint);
48 procedure ScaleImageList(ImgList: TImageList; FromDPI: TPoint);
49 function ScalePoint(APoint: TPoint; FromDPI: TPoint): TPoint;
50 function ScaleRect(ARect: TRect; FromDPI: TPoint): TRect;
51 function ScaleX(Size: Integer; FromDPI: Integer): Integer;
52 function ScaleY(Size: Integer; FromDPI: Integer): Integer;
53 constructor Create(AOwner: TComponent); override;
54 property DesignDPI: TPoint read FDesignDPI write SetDesignDPI;
55 property DPI: TPoint read FDPI write SetDPI;
56 published
57 property AutoDetect: Boolean read FAutoDetect write SetAutoDetect;
58 end;
59
60procedure Register;
61
62
63implementation
64
65resourcestring
66 SWrongDPI = 'Wrong DPI [%d,%d]';
67
68procedure Register;
69begin
70 RegisterComponents('Common', [TScaleDPI]);
71end;
72
73{ TControlDimension }
74
75constructor TControlDimension.Create;
76begin
77 Controls := TControlDimensions.Create;
78end;
79
80destructor TControlDimension.Destroy;
81begin
82 FreeAndNil(Controls);
83 inherited;
84end;
85
86procedure TScaleDPI.SetAutoDetect(AValue: Boolean);
87begin
88 if FAutoDetect = AValue then Exit;
89 FAutoDetect := AValue;
90 if AValue then begin
91 DPI := Point(ScreenInfo.PixelsPerInchX, ScreenInfo.PixelsPerInchY);
92 end;
93end;
94
95procedure TScaleDPI.SetDesignDPI(AValue: TPoint);
96begin
97 if (FDesignDPI.X = AValue.X) and (FDesignDPI.Y = AValue.Y) then Exit;
98 if (AValue.X <= 0) or (AValue.Y <= 0) then
99 raise Exception.Create(Format(SWrongDPI, [AValue.X, AValue.Y]));
100 FDesignDPI := AValue;
101end;
102
103procedure TScaleDPI.SetDPI(AValue: TPoint);
104begin
105 if (FDPI.X = AValue.X) and (FDPI.Y = AValue.Y) then Exit;
106 if (AValue.X <= 0) or (AValue.Y <= 0) then
107 raise Exception.Create(Format(SWrongDPI, [AValue.X, AValue.Y]));
108 FDPI := AValue;
109end;
110
111procedure TScaleDPI.StoreDimensions(Control: TControl;
112 Dimensions: TControlDimension);
113var
114 NewControl: TControlDimension;
115 I: Integer;
116begin
117 Dimensions.BoundsRect := Control.BoundsRect;
118 Dimensions.FontHeight := Control.Font.GetTextHeight('Hg');
119 Dimensions.Controls.Clear;
120 if Control is TToolBar then
121 Dimensions.ButtonSize := Point(TToolBar(Control).ButtonWidth, TToolBar(Control).ButtonHeight);
122 if Control is TForm then begin
123 Dimensions.ConstraintsMin := Point(TForm(Control).Constraints.MinWidth,
124 TForm(Control).Constraints.MinHeight);
125 Dimensions.ConstraintsMax := Point(TForm(Control).Constraints.MaxWidth,
126 TForm(Control).Constraints.MaxHeight);
127 end;
128 if Control is TWinControl then
129 for I := 0 to TWinControl(Control).ControlCount - 1 do begin
130 if TWinControl(Control).Controls[I] is TControl then
131 // Do not scale docked forms twice
132 if not (TWinControl(Control).Controls[I] is TForm) then begin
133 NewControl := TControlDimension.Create;
134 Dimensions.Controls.Add(NewControl);
135 StoreDimensions(TWinControl(Control).Controls[I], NewControl);
136 end;
137 end;
138end;
139
140procedure TScaleDPI.RestoreDimensions(Control: TControl;
141 Dimensions: TControlDimension);
142var
143 I: Integer;
144begin
145 Control.BoundsRect := Dimensions.BoundsRect;
146 Control.Font.Height := Dimensions.FontHeight;
147 if Control is TToolBar then begin
148 TToolBar(Control).ButtonWidth := Dimensions.ButtonSize.X;
149 TToolBar(Control).ButtonHeight := Dimensions.ButtonSize.Y;
150 end;
151 if Control is TForm then begin
152 TForm(Control).Constraints.MinWidth := Dimensions.ConstraintsMin.X;
153 TForm(Control).Constraints.MinHeight := Dimensions.ConstraintsMin.Y;
154 TForm(Control).Constraints.MaxWidth := Dimensions.ConstraintsMax.X;
155 TForm(Control).Constraints.MaxHeight := Dimensions.ConstraintsMax.Y;
156 end;
157 if Control is TWinControl then
158 for I := 0 to TWinControl(Control).ControlCount - 1 do begin
159 if TWinControl(Control).Controls[I] is TControl then
160 // Do not scale docked forms twice
161 if not (TWinControl(Control).Controls[I] is TForm) then begin
162 RestoreDimensions(TWinControl(Control).Controls[I], TControlDimension(Dimensions.Controls[I]));
163 end;
164 end;
165end;
166
167procedure TScaleDPI.ScaleDimensions(Control: TControl;
168 Dimensions: TControlDimension);
169var
170 I: Integer;
171begin
172 Control.BoundsRect := ScaleRect(Dimensions.BoundsRect, DesignDPI);
173 Control.Font.Height := ScaleY(Dimensions.FontHeight, DesignDPI.Y);
174 if Control is TToolBar then begin
175 TToolBar(Control).ButtonWidth := ScaleX(Dimensions.ButtonSize.X, DesignDPI.X);
176 TToolBar(Control).ButtonHeight := ScaleY(Dimensions.ButtonSize.Y, DesignDPI.Y);
177 end;
178 if Control is TCoolBar then begin
179 with TCoolBar(Control) do
180 for I := 0 to Bands.Count - 1 do
181 with TCoolBand(Bands[I]) do begin
182 MinWidth := ScaleX(Dimensions.ButtonSize.X, DesignDPI.X);
183 MinHeight := ScaleY(Dimensions.ButtonSize.Y, DesignDPI.Y);
184 //Width := ScaleX(Dimensions.BoundsRect.Left -
185 end;
186 end;
187 if Control is TForm then begin
188 TForm(Control).Constraints.MinWidth := ScaleX(Dimensions.ConstraintsMin.X, DesignDPI.X);
189 TForm(Control).Constraints.MaxWidth := ScaleX(Dimensions.ConstraintsMax.X, DesignDPI.X);
190 TForm(Control).Constraints.MinHeight := ScaleY(Dimensions.ConstraintsMin.Y, DesignDPI.Y);
191 TForm(Control).Constraints.MaxHeight := ScaleY(Dimensions.ConstraintsMax.Y, DesignDPI.Y);
192 end;
193 if Control is TWinControl then
194 for I := 0 to TWinControl(Control).ControlCount - 1 do begin
195 if TWinControl(Control).Controls[I] is TControl then
196 // Do not scale docked forms twice
197 if not (TWinControl(Control).Controls[I] is TForm) then begin
198 ScaleDimensions(TWinControl(Control).Controls[I], TControlDimension(Dimensions.Controls[I]));
199 end;
200 end;
201end;
202
203procedure TScaleDPI.ApplyToAll(FromDPI: TPoint);
204var
205 I: Integer;
206begin
207 for I := 0 to Screen.FormCount - 1 do begin
208 ScaleControl(Screen.Forms[I], FromDPI);
209 end;
210end;
211
212procedure TScaleDPI.ScaleImageList(ImgList: TImageList; FromDPI: TPoint);
213var
214 TempBmp: TBitmap;
215 Temp: array of TBitmap;
216 NewWidth: Integer;
217 NewHeight: Integer;
218 I: Integer;
219begin
220 ImgList.BeginUpdate;
221 try
222 NewWidth := ScaleX(ImgList.Width, FromDPI.X);
223 NewHeight := ScaleY(ImgList.Height, FromDPI.Y);
224
225 Temp := nil;
226 SetLength(Temp, ImgList.Count);
227 for I := 0 to ImgList.Count - 1 do
228 begin
229 TempBmp := TBitmap.Create;
230 try
231 TempBmp.PixelFormat := pf32bit;
232 ImgList.GetBitmap(I, TempBmp);
233 Temp[I] := TBitmap.Create;
234 Temp[I].SetSize(NewWidth, NewHeight);
235 {$IFDEF UNIX}
236 Temp[I].PixelFormat := pf24bit;
237 {$ELSE}
238 Temp[I].PixelFormat := pf32bit;
239 {$ENDIF}
240 Temp[I].TransparentColor := TempBmp.TransparentColor;
241 //Temp[I].TransparentMode := TempBmp.TransparentMode;
242 Temp[I].Transparent := True;
243 Temp[I].Canvas.Brush.Style := bsSolid;
244 Temp[I].Canvas.Brush.Color := Temp[I].TransparentColor;
245 Temp[I].Canvas.FillRect(0, 0, Temp[I].Width, Temp[I].Height);
246
247 if (Temp[I].Width = 0) or (Temp[I].Height = 0) then Continue;
248 Temp[I].Canvas.StretchDraw(Rect(0, 0, Temp[I].Width, Temp[I].Height), TempBmp);
249 finally
250 TempBmp.Free;
251 end;
252 end;
253
254 ImgList.Clear;
255 ImgList.Width := NewWidth;
256 ImgList.Height := NewHeight;
257
258 for I := 0 to High(Temp) do
259 begin
260 ImgList.Add(Temp[I], nil);
261 Temp[i].Free;
262 end;
263 finally
264 ImgList.EndUpdate;
265 end;
266end;
267
268function TScaleDPI.ScaleX(Size: Integer; FromDPI: Integer): Integer;
269begin
270 Result := MulDiv(Size, DPI.X, FromDPI);
271end;
272
273function TScaleDPI.ScaleY(Size: Integer; FromDPI: Integer): Integer;
274begin
275 Result := MulDiv(Size, DPI.Y, FromDPI);
276end;
277
278function TScaleDPI.ScalePoint(APoint: TPoint; FromDPI: TPoint): TPoint;
279begin
280 Result.X := ScaleX(APoint.X, FromDPI.X);
281 Result.Y := ScaleY(APoint.Y, FromDPI.Y);
282end;
283
284function TScaleDPI.ScaleRect(ARect: TRect; FromDPI: TPoint): TRect;
285begin
286 Result.TopLeft := ScalePoint(ARect.TopLeft, FromDPI);
287 Result.BottomRight := ScalePoint(ARect.BottomRight, FromDPI);
288end;
289
290constructor TScaleDPI.Create(AOwner: TComponent);
291begin
292 inherited;
293 DPI := Point(96, 96);
294 DesignDPI := Point(96, 96);
295end;
296
297procedure TScaleDPI.ScaleControl(Control: TControl; FromDPI: TPoint);
298var
299 I: Integer;
300 WinControl: TWinControl;
301 ToolBarControl: TToolBar;
302 //OldAnchors: TAnchors;
303 //OldAutoSize: Boolean;
304begin
305 //if not (Control is TCustomPage) then
306 // Resize childs first
307 if Control is TWinControl then begin
308 WinControl := TWinControl(Control);
309 if WinControl.ControlCount > 0 then begin
310 for I := 0 to WinControl.ControlCount - 1 do begin
311 if WinControl.Controls[I] is TControl then begin
312 ScaleControl(WinControl.Controls[I], FromDPI);
313 end;
314 end;
315 end;
316 end;
317
318 //if Control is TMemo then Exit;
319 //if Control is TForm then
320 // Control.DisableAutoSizing;
321 with Control do begin
322 //OldAutoSize := AutoSize;
323 //AutoSize := False;
324 //Anchors := [];
325 Left := ScaleX(Left, FromDPI.X);
326 Top := ScaleY(Top, FromDPI.Y);
327 //if not (akRight in Anchors) then
328 Width := ScaleX(Width, FromDPI.X);
329 //if not (akBottom in Anchors) then
330 Height := ScaleY(Height, FromDPI.Y);
331 {$IFDEF LCL Qt}
332 Font.Size := 0;
333 {$ELSE}
334 Font.Height := ScaleY(Font.GetTextHeight('Hg'), FromDPI.Y);
335 {$ENDIF}
336 //Anchors := OldAnchors;
337 //AutoSize := OldAutoSize;
338 end;
339
340 if Control is TCoolBar then
341 with TCoolBar(Control) do begin
342 BeginUpdate;
343 try
344 for I := 0 to Bands.Count - 1 do
345 with Bands[I] do begin
346 MinWidth := ScaleX(MinWidth, FromDPI.X);
347 MinHeight := ScaleY(MinHeight, FromDPI.Y);
348 // Workaround to bad band width auto sizing
349 //Width := ScaleX(Width, FromDPI.X);
350 Width := ScaleX(Control.Width + 28, FromDPI.X);
351 //Control.Invalidate;
352 end;
353 // Workaround for bad autosizing of coolbar
354 if AutoSize then begin
355 AutoSize := False;
356 Height := ScaleY(Height, FromDPI.Y);
357 AutoSize := True;
358 end;
359 finally
360 EndUpdate;
361 end;
362 end;
363
364 if Control is TToolBar then begin
365 ToolBarControl := TToolBar(Control);
366 with ToolBarControl do begin
367 ButtonWidth := ScaleX(ButtonWidth, FromDPI.X);
368 ButtonHeight := ScaleY(ButtonHeight, FromDPI.Y);
369 end;
370 end;
371
372 //if Control is TForm then
373 // Control.EnableAutoSizing;
374end;
375
376end.
Note: See TracBrowser for help on using the repository browser.