Search code examples
delphicontrolsscaling

ScaleBy + trackbar


I need help to implement control scaling (by ScaleBy method) with trackbar.

Here's my OnChange event for trackbar but it does not work perfectly:

procedure TMainForm.tbScaleChange(Sender: TObject);
begin
  if tbScale.Tag = 0 then
  begin
    // this section is to make trackbar's step = 5
    tbScale.Tag := 1;
    if tbScale.Position mod 5 <> 0 then
      tbScale.Position := 5 * ((tbScale.Position + 5 div 2) div 5);
    tbScale.Tag := 0;

    // in FtbScalePrevPos form variable I keep previous trackbar position
    if FtbScalePrevPos > tbScale.Position then
      someControl.ScaleBy(100, 105)
    else if FtbScalePrevPos < tbScale.Position then
      someControl.ScaleBy(105, 100);
    FtbScalePrevPos := tbScale.Position;
  end;
end;

After scaling above control's dimensions are growing too fast with moving trackbar.

EDIT: My trackbar has Min = 50, Max = 200 and these values are scaling factors in percent. Code for increasing/decreasing tbScale.Position by 5 is a bit complicated but works fine and is not related to origin of my problem. The problem is to find function that will calculate proper scaling factor based on trackbar position. In naive approach with passing tbScale.Position directly to ScaleBy method, if I move trackbar to 150 (scaling factor 1.5) and then to 200 (factor 2), my control will not be scaled by 2 as I would like but by 3 (1.5*2). The simple solution would be to save initial dimensions of someControl and then used them with calculated scaling factor. Unfortunately in my case someControl is TTabSheet and in fact I want to scale its many child controls.


Solution

  • Consider the following changes to your code:

    1. Do not change tbScale.Position in the OnChange event, use a local temporary variable instead. Actually, you dont even need the temporary.
    2. Because of point 1, you can remove the code dealing with tbScale.Tag
    3. Making the tbScale step = 5 is not necessary, you are only using it for comparison (<, >) with previous value.

    Thus your code is reduced to the following:

    procedure TForm22.tbScaleChange(Sender: TObject);
    begin
      if FtbScalePrevPos > tbScale.Position then
        Button1.ScaleBy(100, 105)
      else if FtbScalePrevPos < tbScale.Position then
        Button1.ScaleBy(105, 100);
      FtbScalePrevPos := tbScale.Position;
    end;
    

    If incrementing/decrementing with 5% per step goes too fast, reduce 105 to say, 103.

    The TTrackBar has a Min= 0 and Max=10 as default. If you want to be able to reduce the size of the scaled controls, set e.g. Min := -10. If you want to increase the steps, adjust Min and Max.


    Edit after additional information.

    I now understand what the problem is (I think). In the previous the size change was progressive and you are looking for a linear change.

    I still maintain that the calculations you do with the tbScale value can be replaced with assigning min, max and initial position with a 5th smaller values and multiply with 5 in the OnChange procedure.

    With the range of tbScale2 at 10..40 (instead of 50..200) and initial position at 20 (instead of 100), as well as FtbScale2PrevPos initialized at 100, the following changes the operation from progressive to linear.

    procedure TForm22.FormCreate(Sender: TObject);
    begin
      FtbScale2PrevPos := 100;
    end;
    
    procedure TForm22.tbScale2Change(Sender: TObject);
    begin
      Button1.ScaleBy(tbScale2.Position * 5, FtbScale2PrevPos);
    
      FtbScale2PrevPos := tbScale2.Position * 5;
    end;