Brian Frost
Brian Frost

Reputation: 13454

Is there a scaled Delphi FireMonkey layout strategy that preserves font text aspect ratios?

For years I've use a VCL forms application layout strategy that auto-sizes the form controls on the main form. Users can maximise or resize the application at will with the same general look and feel of button layouts, charts etc. To do this I wrote code to manipulate the BoundsRect of child controls based on the FormResize event of the form, and this simple technique preserves the text font size and aspect ratio (eg of buttons). I have further simple code to raise and lower the font size to track the expanding and reducing button / label sizes of main controls, but many controls e.g a TChart are left with readable albeit small fonts when the application is minimised - I'm very happy with this.

I'd like to migrate my layout to use FireMonkey which has TScaledLayout which, when you align it alClient in form neatly resizes all of its contained controls. Unfortunately the resizing extends to all text fonts too, which is really not desirable. Setting the TScaledLayout to alFit keeps the original aspect ratio of the contents but can result in empty padding at the top or sides to achieve this.

Is there any way in which I could 'turn off' the resizing of all fonts under TScaledLayout action? I've explored the info in FireMonkey Layouts Strategies and also tried each of its vastly increased 'Align' settings without success. Eploying full screen while moving between various display devices would need this ability surely?

Upvotes: 3

Views: 3363

Answers (2)

George Birbilis
George Birbilis

Reputation: 2940

One could try the following strategy:

  • Use a ScaledLayout and set its Align property to Fit. I suppose you also have to use Align := None and no Anchors at its Children.
  • Place it inside a Panel and set that to Align:=Client.
  • Place the Panel in a Form

In my case I wanted to make a zoomable panel (with aspect ratio preservation) inside a TScrollBox layout, so I use Align:=Center at that panel and set its scale programmatically for zooming.

Note that Embarcadero's ScaledLayout sample is misleading in that case since it sets the scale of the ScaledLayout (which is irrelevant to the children scaling capabilities of ScaledLayout upon its resizing - they could have set the Scale at a TPanel or any other container)

Another confusing aspect is that Align := Scale at controls isn't related to Scaling, but to child controls resizing when parent resizes keeping their relative position and relative size (that is they don't scale their display, they actually resize).

Update:

I found that when zooming in/out with the above strategy I'd get scrollbars behave the inverse than expected. I realized it was related to the outer panel not getting resized but only its scale being set (note that since it's in a scrollbox in my case I have it set to Align:=Center, not Align:=Client like you'd do in a form)

So instead of an outer panel I used yet another TScaleLayout since it has extra useful properties OriginalWidth/OriginalHeight that keep the original size of the control. This is needed for calculation of the new size based on the new scale one applies when zooming in/out (not using the Scale property of it as Embarcadero's sample misleadingly suggests, apart from Unit/-Unit axes scale values to support negative scaling [aka axis flipping]):

procedure TForm2.FormCreate(Sender: TObject);
begin
  BeginUpdate;
  Zoomer.Align := TAlignLayout.Center; //at design mode we have it set to TAlignLayout.Client
  UpdateZoomFromTrackbars;
  EndUpdate;
end;

procedure TForm2.SetZoom(const Value: Single);
begin
  SetZoom(Value, Value);
end;

procedure TForm2.SetZoom(const ValueX, ValueY: Single);
begin
  if (ValueX <> 0) and (ValueY <>0) then //FMX has bug where Scale won't work anymore if set to 0
    begin
    BeginUpdate;

    //update track bars
    trackZoomX.BeginUpdate; trackZoomX.ValueRange.Value := ValueX; trackZoomX.EndUpdate;
    trackZoomY.BeginUpdate; trackZoomY.ValueRange.Value := ValueY; trackZoomY.EndUpdate;

    with Zoomer do
    begin
      Size.Size := TSizeF.Create(OriginalWidth * abs(ValueX), OriginalHeight * abs(ValueY)); //don't use Scale to resize (won't work well here), ScaledLayout scales its contents automatically
      Scale.Point := TPointF.Create(sign(ValueX), sign(ValueY));
    end;
    //ScrollBox.InvalidateContentSize;

    EndUpdate;
    end;
end;

procedure TForm2.UpdateZoomFromTrackbars;
begin
  SetZoom(trackZoomX.Value, trackZoomY.Value);
end;

procedure TForm2.ScrollBoxResize(Sender: TObject);
begin
  var scrollBoxSize := ScrollBox.Size.Size;
  if not scrollBoxSize.IsZero then
    begin
    BeginUpdate;
    Zoomer.Size.Size := scrollBoxSize;
    with Zoomer do
    begin
      OriginalWidth := Width;
      OriginalHeight := Height;
    end;
    UpdateZoomFromTrackbars;
    //ScrollBox.InvalidateContentSize;
    EndUpdate;
    end;
end;

procedure TForm2.trackZoomXTracking(Sender: TObject);
begin
  if trackZoomX.IsUpdating then exit;

  BeginUpdate;
  if switchSyncAxes.IsChecked then
    trackZoomY.Value := trackZoomX.Value;

  UpdateZoomFromTrackbars;
  EndUpdate;
end;

procedure TForm2.trackZoomYTracking(Sender: TObject);
begin
  if trackZoomY.IsUpdating then exit;

  BeginUpdate;
  if switchSyncAxes.IsChecked then
    trackZoomX.Value := trackZoomY.Value;

  UpdateZoomFromTrackbars;
  EndUpdate;
end;

Upvotes: 1

Freddie Bell
Freddie Bell

Reputation: 2287

Think transparent layers like on an overhead projector (remember those?). Put your text labels on a panel that is on the form. Put the scaledlayout on the panel. Put the controls on the scaledlayout. Now when the form resizes, the controls also do, but the labels do not.

Upvotes: 1

Related Questions