Изменить форму и сохранить пропорции
подобный вопрос здесь: изменение размера формы при сохранении соотношения сторон
в основном, я хочу изменить размер формы и сохранить ее соотношение сторон, но я хочу изменить размер, чтобы следовать курсору. Ответ в теме выше предоставил решение, которое наполовину удовлетворительно-оно работает, но изменение размера работает 2x медленнее, чем должно. Когда я начну изменять размер формы по оси X, вы увидите, где находится курсор и какой размер формы есть:
Я думал, что, поскольку он изменяет размер 2x медленнее, я должен опустить 0.5 множитель в коде, и он будет работать, но не кости. Вот код, который я использую в данный момент:
type
TfrmTable = class(TForm)
procedure FormCanResize(Sender: TObject; var NewWidth, NewHeight: Integer; var Resize: Boolean);
procedure FormCreate(Sender: TObject);
private
FAspectRatio: Double;
public
end;
var
frmTable: TfrmTable;
implementation
{$R *.dfm}
procedure TfrmTable.FormCreate(Sender: TObject);
begin
FAspectRatio := Width / Height;
end;
procedure TfrmTable.FormCanResize(Sender: TObject; var NewWidth, NewHeight: Integer; var Resize: Boolean);
begin
NewHeight := Round(0.50 * (NewHeight + NewWidth / FAspectRatio));
NewWidth := Round(NewHeight * FAspectRatio);
end;
я попробовал другой подход, используя что-то вроде этого:
procedure TfrmTable.FormCanResize(Sender: TObject; var NewWidth, NewHeight: Integer; var Resize: Boolean);
begin
if NewWidth <> Width then
NewHeight := Round(NewWidth / FAspectRatio)
else
if NewHeight <> Height then
NewWidth := Round(NewHeight * FAspectRatio);
end;
что это? Ну, я думаю, что я сначала проверю, если NewWidth
отличается от нынешней Width
, и если это так, это означает, что пользователь изменяет размер формы по оси X. Тогда я должен установить NewHeight
на соответствующее значение. В противном случае я проверяю, если NewHeight
отличается от нынешней Height
и set NewWidth
значение для соответствующего значения. Это также приводит к странным результатам, когда я перетаскиваю форму по оси X, кажется, работает, и как только я прекращаю изменять размер, форма возвращается к своему исходному размеру - я пришел к выводу, что как только я прекращаю изменять размер (пусть кнопка мыши вверх), FormCanResize()
событие вызывается со старыми NewHeight
значение, которое затем возвращает форму обратно к ее старому размеру.
4 ответов
правильное сообщение для обработки этого -WM_SIZING
:
обрабатывая это сообщение, приложение может контролировать размер и положение прямоугольника перетаскивания и, при необходимости, изменение его размера или позиция.
procedure TForm1.WMSizing(var Message: TMessage);
begin
case Message.wParam of
WMSZ_LEFT, WMSZ_RIGHT, WMSZ_BOTTOMLEFT:
with PRect(Message.LParam)^ do
Bottom := Top + Round((Right-Left)/FAspectRatio);
WMSZ_TOP, WMSZ_BOTTOM, WMSZ_TOPRIGHT, WMSZ_BOTTOMRIGHT:
with PRect(Message.LParam)^ do
Right := Left + Round((Bottom-Top)*FAspectRatio);
WMSZ_TOPLEFT:
with PRect(Message.LParam)^ do
Top := Bottom - Round((Right-Left)/FAspectRatio);
end;
inherited;
end;
Я не думаю, что можно обойтись без вспомогательных.
здесь я использую простое целое поле для хранения размера по горизонтали, вертикали или вообще нет. Вы также можете объявить перечисление для этого.
...
private
FAspectRatio: Double;
FResizing: Integer;
procedure WMExitSizeMove(var Message: TMessage); message WM_EXITSIZEMOVE;
end;
...
procedure TForm1.FormCanResize(Sender: TObject; var NewWidth,
NewHeight: Integer; var Resize: Boolean);
begin
if FResizing = 0 then
FResizing := Abs(NewHeight - Height) - Abs(NewWidth - Width);
if FResizing < 0 then
NewHeight := Round(NewWidth / FAspectRatio)
else
NewWidth := Round(NewHeight * FAspectRatio);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
FAspectRatio := Width / Height;
end;
procedure TForm1.WMExitSizeMove(var Message: TMessage);
begin
inherited;
FResizing := 0;
end;
вот мой взгляд на это. Здесь я пытаюсь основать размер на ширине или высоте в зависимости от того, какой из них был перемещен больше всего.
type
TMyForm = class(TForm)
procedure FormCreate(Sender: TObject);
private
FAspectRatio: Double;
FWidthAtStartOfSize: Integer;
FHeightAtStartOfSize: Integer;
protected
procedure WMEnterSizeMove(var Message: TMessage); message WM_ENTERSIZEMOVE;
procedure WMSizing(var Message: TMessage); message WM_SIZING;
end;
procedure TMyForm.FormCreate(Sender: TObject);
begin
FAspectRatio := Width / Height;
end;
procedure TMyForm.WMEnterSizeMove(var Message: TMessage);
begin
inherited;
FWidthAtStartOfSize := Width;
FHeightAtStartOfSize := Height;
end;
procedure TMyForm.WMSizing(var Message: TMessage);
var
SizeBasedOnWidth: Boolean;
NewHeight, NewWidth: Integer;
Rect: PRect;
begin
inherited;
Rect := PRect(Message.LParam);
case Message.wParam of
WMSZ_LEFT, WMSZ_RIGHT:
Rect.Bottom := Rect.Top + Round(Rect.Width/FAspectRatio);
WMSZ_TOP, WMSZ_BOTTOM:
Rect.Right := Rect.Left + Round(Rect.Height*FAspectRatio);
WMSZ_TOPLEFT, WMSZ_TOPRIGHT, WMSZ_BOTTOMLEFT, WMSZ_BOTTOMRIGHT:
begin
if Rect.Width>FWidthAtStartOfSize then begin
SizeBasedOnWidth := Rect.Height<MulDiv(FHeightAtStartOfSize, Rect.Width, FWidthAtStartOfSize)
end else begin
SizeBasedOnWidth := Rect.Width>MulDiv(FWidthAtStartOfSize, Rect.Height, FHeightAtStartOfSize);
end;
if SizeBasedOnWidth then begin
NewHeight := Round(Rect.Width/FAspectRatio);
case Message.wParam of
WMSZ_TOPLEFT, WMSZ_TOPRIGHT:
Rect.Top := Rect.Bottom - NewHeight;
WMSZ_BOTTOMLEFT, WMSZ_BOTTOMRIGHT:
Rect.Bottom := Rect.Top + NewHeight;
end;
end else begin
NewWidth := Round(Rect.Height*FAspectRatio);
case Message.wParam of
WMSZ_TOPLEFT, WMSZ_BOTTOMLEFT:
Rect.Left := Rect.Right - NewWidth;
WMSZ_TOPRIGHT, WMSZ_BOTTOMRIGHT:
Rect.Right := Rect.Left + NewWidth;
end;
end;
end;
end;
end;
Мне очень нравится ответ Сертака. Коротко и мило. Я начал свой код, основанный на его. Но код Sertac принимает стороны при изменении размера в углах. Для определенного угла он всегда благоприятствует вертикальному или горизонтальному. Здесь я попытался быть агностиком и разрешить изменение размера угла на основе горизонтальных или вертикальных ребер, в зависимости от того, как вы выполните перетаскивание.
обратите внимание, что ни один алгоритм не будет полностью удовлетворительным. На самом деле, я думаю, что если бы я делал это, я бы очень подумал об отключении изменения размера угла.
Если вы нацелены на OSX с помощью firemonkey, соответствующий API предоставляет простой способ заблокировать соотношение сторон:
uses {...} FMX.Platform.Mac, Macapi.AppKit, Macapi.CocoaTypes;
// ...
procedure TMyForm.FormShow(Sender: TObject);
var Window: NSWindow;
begin
if not FRunOnce then
begin
FRunOnce := true;
Window := WindowHandleToPlatform(Handle).Wnd;
Window.setContentAspectRatio(NSSize(TPointF.Create(ClientWidth, ClientHeight)));
end;
end;