source: trunk/Packages/Common/UScaleDPI.pas

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