unit ImageSizeForm;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls, Vcl.ComCtrls;

type
  TImageSizeFrm = class(TForm)
    Shape1: TShape;
    Label1: TLabel;
    Shape2: TShape;
    Label2: TLabel;
    Label3: TLabel;
    btnOk: TButton;
    btnCancel: TButton;
    eWidth: TEdit;
    eHeight: TEdit;
    udWidth: TUpDown;
    udHeight: TUpDown;
    cbMaintainAspectRatio: TCheckBox;
    eWidthAspect: TEdit;
    eHeightAspect: TEdit;
    procedure cbMaintainAspectRatioClick(Sender: TObject);
    procedure AREditExit(Sender: TObject);
    procedure eWidthAspectChange(Sender: TObject);
    procedure eWidthChange(Sender: TObject);
    procedure eHeightChange(Sender: TObject);
  private
    FAspect: Double;
    procedure UpdateAspect;
    procedure ValidateOK;
    procedure SetAspect(const Aspect: Double);
  public
  end;

function ImageSizeDialog(ADialogOwner: TCustomForm; var W, H: Integer;
  var Aspect: Double): Boolean;

implementation

uses
  Math, ASNum;

{$R *.dfm}

function IntToPrettyStr(AValue: Integer): string;
begin
  var PrettyIntFS := TFormatSettings.Invariant;
  PrettyIntFS.ThousandSeparator := #32;
  Result := Format('%.0n', [Double(AValue)], PrettyIntFS);
end;

function TryStrToInt(const AText: string; out AInt: Integer): Boolean;
begin
  Result := System.SysUtils.TryStrToInt(AText.Replace(#32, '').Replace(#$A0, ''), AInt);
end;

function StrToInt(const AText: string): Integer;
begin
  Result := System.SysUtils.StrToInt(AText.Replace(#32, '').Replace(#$A0, ''));
end;

procedure TImageSizeFrm.AREditExit(Sender: TObject);
begin
  if Sender is TEdit then
    if TEdit(Sender).Text = '' then
      TEdit(Sender).Text := '1';
end;

procedure TImageSizeFrm.cbMaintainAspectRatioClick(Sender: TObject);
begin
  eWidthAspect.Enabled := cbMaintainAspectRatio.Checked;
  eHeightAspect.Enabled := cbMaintainAspectRatio.Checked;
  UpdateAspect;
end;

procedure TImageSizeFrm.eHeightChange(Sender: TObject);
var
  W, H: Integer;
begin

  if
    (FAspect <> 0.0)
      and
    TryStrToInt(eWidth.Text, W)
      and
    TryStrToInt(eHeight.Text, H)
      and
    (W <> Round(H * FAspect))
  then
    udWidth.Position := Max(1, Round(H * FAspect));

  ValidateOK;

end;

procedure TImageSizeFrm.eWidthAspectChange(Sender: TObject);
begin
  UpdateAspect;
end;

procedure TImageSizeFrm.eWidthChange(Sender: TObject);
var
  W, H: Integer;
begin

  if
    (FAspect <> 0.0)
      and
    TryStrToInt(eWidth.Text, W)
      and
    TryStrToInt(eHeight.Text, H)
      and
    (H <> Round(W / FAspect))
  then
    udHeight.Position := Max(1, Round(W / FAspect));

  ValidateOK;

end;

procedure TImageSizeFrm.SetAspect(const Aspect: Double);
begin

  if Aspect = 0.0 then
  begin
    cbMaintainAspectRatio.Checked := False;
    cbMaintainAspectRatioClick(Self);
  end
  else
  begin
    cbMaintainAspectRatio.Checked := True;
    var WA, HA: Integer;
    with ASNum.ToFraction(Aspect) do
    begin
      WA := Numerator;
      HA := Denominator;
    end;
    eWidthAspect.Text := IntToPrettyStr(WA);
    eHeightAspect.Text := IntToPrettyStr(HA);
    cbMaintainAspectRatioClick(Self);
  end;

end;

procedure TImageSizeFrm.UpdateAspect;
var
  W, H, WA, HA: Integer;
begin

  if
    cbMaintainAspectRatio.Checked
      and
    TryStrToInt(eWidthAspect.Text,  WA) and (WA > 0)
      and
    TryStrToInt(eHeightAspect.Text, HA) and (HA > 0)
  then
    FAspect := WA / HA
  else
    FAspect := 0.0;

  if
    (FAspect <> 0.0)
      and
    TryStrToInt(eWidth.Text, W)
      and
    TryStrToInt(eHeight.Text, H)
      and
    (W <> Round(H * FAspect))
  then
    udWidth.Position := Max(1, Round(H * FAspect));

  ValidateOK;

end;

procedure TImageSizeFrm.ValidateOK;
var
  W, H: Integer;
begin

  btnOk.Enabled := TryStrToInt(eWidth.Text, W) and TryStrToInt(eHeight.Text, H)
    and InRange(W, 1, 64000) and InRange(H, 1, 64000)
    and
    (
      not cbMaintainAspectRatio.Checked
       or
      (Abs(W - H * FAspect) <= 1.5)
    );

end;

function ImageSizeDialog(ADialogOwner: TCustomForm; var W, H: Integer;
  var Aspect: Double): Boolean;
begin
  var frm := TImageSizeFrm.Create(ADialogOwner);
  try
    frm.udWidth.Position := W;
    frm.udHeight.Position := H;
    frm.SetAspect(Aspect);
    Result := frm.ShowModal = mrOk;
    if Result then
    begin
      W := frm.udWidth.Position;
      H := frm.udHeight.Position;
      Aspect := frm.FAspect;
    end;
  finally
    frm.Free;
  end;
end;

end.
