View Issue Details

IDProjectCategoryView StatusLast Update
0036127LazarusLCLpublic2020-07-22 19:35
ReporterJoeny Ang Assigned ToJuha Manninen  
PrioritynormalSeverityminorReproducibilityalways
Status resolvedResolutionreopened 
Platformx86_64OSArch Linux 
Product Version2.0.4 
Summary0036127: [Patch] TForm's bounds and restored bounds are inconsistent
DescriptionThe attached patch tries to fix the following issues:

When form is designed with WindowState=wsMaximized:
1. Restored dimension is wrong (related to 0027894, 0022771, 0027375)
2. Width/Height in OnShow() and OnActivate() events are wrong (related to 0021119)
     Under Delphi 3:
          - OnCreate(): design time restored width/height
          - OnShow() and OnActivate(): maximized width/height

When form's WindowsState=not maximized
3. Restored bounds in OnResize() and OnChangeBounds() events are wrong or not updated (related to 0032631)

Note: Expanded on the idea of Yuichiro Takahashi from Issue 0008576 to use Application.QueueAsyncCall() to "queue" the procedure to update the restored values, to the main event loop, thus delaying it until the form is properly moved or resized. The first calls to OnShow() and OnActivate() are also moved to this procedure so that querying Width/Height during these events will return the correct values.

Tested on GTK2 and Win32.
TagsNo tags attached.
Fixed in Revisionr61997, r62113, r63218, r63577
LazTarget-
WidgetsetGTK 2, Win32/Win64
Attached Files

Relationships

related to 0027894 closedJuha Manninen Lazarus TForm RestoredWidth and RestoredHeight updated incorrectly if window maximized under Gtk2 
related to 0022771 resolvedJuha Manninen Lazarus When FormStyle = wsMaximized, after restore Widht/Height have wrong values 
related to 0027375 resolvedJuha Manninen Lazarus If a Form is started with wsMaximized (set in OI), it does not restore to designed Size 
related to 0021119 resolvedZeljan Rikalo Lazarus When a form is started as wsMaximized height and width report design time values 
related to 0032631 resolvedJuha Manninen Packages "restoredLeft, restoredTop, restoredWidth, restoredHeight" of TForm returned wrong value 
related to 0008576 closedJuha Manninen Lazarus After Mini/Maximizing window 'RestoredLeft/Top' values changes to "unreal" values 
related to 0036877 closedZeljan Rikalo Lazarus LCL: Regression TCustomForm.OnActivate 

Activities

Joeny Ang

2019-10-02 09:55

reporter  

tform-wrong-bounds-and-restored-bounds.patch (9,834 bytes)   
--- lcl/forms.pp
+++ lcl/forms.pp
@@ -490,6 +490,8 @@
     FRestoredHeight: integer;
     FShowInTaskbar: TShowInTaskbar;
     FWindowState: TWindowState;
+    FMovedCtr: Integer;
+    FIsFirstOnShow, FIsFirstOnActivate: Boolean;
     function GetClientHandle: HWND;
     function GetEffectiveShowInTaskBar: TShowInTaskBar;
     function GetMonitor: TMonitor;
@@ -590,7 +592,6 @@
     procedure VisibleChanged; override;
     procedure WndProc(var TheMessage : TLMessage); override;
     function VisibleIsStored: boolean;
-    procedure DoSendBoundsToInterface; override;
     procedure DoAutoSize; override;
     procedure SetAutoSize(Value: Boolean); override;
     procedure SetAutoScroll(Value: Boolean); override;
@@ -649,7 +650,8 @@
     function CanFocus: Boolean; override;
     procedure SetFocus; override;
     function SetFocusedControl(Control: TWinControl): Boolean ; virtual;
-    procedure SetRestoredBounds(ALeft, ATop, AWidth, AHeight: integer);
+    procedure SetRestoredBounds(ALeft, ATop, AWidth, AHeight: integer; const
+                                ADefaultPosition: Boolean = False);
     procedure Show;
 
     function ShowModal: Integer; virtual;
--- lcl/include/customform.inc
+++ lcl/include/customform.inc
@@ -16,6 +16,8 @@
 
 const
   BorderStylesAllowAutoScroll = [bsSizeable, bsSizeToolWin];
+  ShowCommands: array[TWindowState] of Integer =
+    (SW_SHOWNORMAL, SW_MINIMIZE, SW_SHOWMAXIMIZED, SW_SHOWFULLSCREEN);
 
 { TCustomForm }
 
@@ -68,93 +70,10 @@
   Gets called after the construction of the object
  ------------------------------------------------------------------------------}
 procedure TCustomForm.AfterConstruction;
-var
-  NewWidth, NewHeight: Integer;
-  OldWindowState: TWindowState;
-
-  procedure ChangeFormDimensions(AIsBeforeOnCreate: Boolean);
-  begin
-    if (WindowState = wsMaximized) and (FormStyle <> fsMDIChild) then
-    begin
-      {$IFDEF DEBUG_SM_LCLMAXIMIZED}
-      DebugLn('TCustomForm.AfterConstruction: SM_CYCAPTION ',
-        dbgs(GetSystemMetrics(SM_CYCAPTION)),
-      ' SM_CYSIZEFRAME ',dbgs(GetSystemMetrics(SM_CYSIZEFRAME)),
-      ' SM_CXMAXIMIZED ',dbgs(GetSystemMetrics(SM_CXMAXIMIZED)),
-      ' SM_CYMAXIMIZED ',dbgs(GetSystemMetrics(SM_CYMAXIMIZED)),
-      ' SM_LCLMAXIMIZEDHEIGHT ',dbgs(GetSystemMetrics(SM_LCLMAXIMIZEDHEIGHT)),
-      ' SM_LCLMAXIMIZEDWIDTH ',dbgs(GetSystemMetrics(SM_LCLMAXIMIZEDWIDTH)),
-      ' AIsBeforeOnCreate ',dbgs(AIsBeforeOnCreate));
-      {$ENDIF}
-
-      if (BorderStyle <> bsNone) and (FormStyle <> fsSplash) then
-      begin
-        NewHeight := GetSystemMetrics(SM_LCLMAXIMIZEDHEIGHT);
-        NewWidth := GetSystemMetrics(SM_LCLMAXIMIZEDWIDTH);
-        // if some ws does not implement this then provide normal metrics.
-        if NewHeight <= 0 then
-          NewHeight := GetSystemMetrics(SM_CYMAXIMIZED);
-        if NewWidth <= 0 then
-          NewHeight := GetSystemMetrics(SM_CXMAXIMIZED);
-      end else
-      begin
-        NewHeight := GetSystemMetrics(SM_CYMAXIMIZED);
-        NewWidth := GetSystemMetrics(SM_CXMAXIMIZED);
-      end;
-
-      if Constraints.MaxWidth > 0 then
-        NewWidth := Min(Constraints.MaxWidth, NewWidth);
-      if Constraints.MaxHeight > 0 then
-        NewHeight := Min(Constraints.MaxHeight, NewHeight);
-
-      // for unknown reasons on some systems SM_*MAXIMIZED* system metrics
-      // (tested xubuntu,64bits) return 0 or negative values, in this case
-      // a maximized window is expected to have at least WorkArea width/height.
-      //
-      // Reproduced again under Debian Wheezy.
-      // mistery solved, it ocurrs under gtk2/64-bit, fixed at the place
-      // the checks doesn't hurt though
-      //
-      // see bug #21634
-      if NewWidth<=0 then
-        NewWidth := Screen.WorkAreaWidth;
-      if NewHeight<=0 then
-        NewHeight := Screen.WorkAreaHeight;
-
-      if NewWidth>0 then
-        Width := NewWidth;
-      if NewHeight>0 then
-        Height := NewHeight;
-    end;
-
-    if (WindowState = wsFullScreen) and (FormStyle <> fsMDIChild) then
-    begin
-      NewWidth := LCLIntf.GetSystemMetrics(SM_CXFULLSCREEN);
-      NewHeight := LCLIntf.GetSystemMetrics(SM_CYFULLSCREEN);
-      if Constraints.MaxWidth > 0 then
-        NewWidth := Min(Constraints.MaxWidth, NewWidth);
-      if Constraints.MaxHeight > 0 then
-        NewHeight := Min(Constraints.MaxHeight, NewHeight);
-      Width := NewWidth;
-      Height := NewHeight;
-    end;
-  end;
-begin
-  // issue #21119, prepare maximized or fullscreen form to accurate dimensions.
-  // we avoid flickering also in this case.
-  if not (csDesigning in ComponentState) then
-    ChangeFormDimensions(True);
-
-  OldWindowState := WindowState;
+begin
+  SetRestoredBounds(Left, Top, Width, Height, True);
+
   DoCreate;
-
-  // if we change WindowState in constructor and handle isn't allocated
-  // then change our dimensions to accurate one
-  if not (csDesigning in ComponentState) and not HandleAllocated and
-    (OldWindowState <> WindowState) and
-    not (OldWindowState in [wsMaximized, wsFullScreen]) and
-    (WindowState in [wsMaximized, wsFullScreen]) then
-      ChangeFormDimensions(False);
 
   EndFormUpdate; // the BeginFormUpdate is in CreateNew
   inherited AfterConstruction;
@@ -677,6 +596,9 @@
  ------------------------------------------------------------------------------}
 procedure TCustomForm.Activate;
 begin
+  if FIsFirstOnActivate and (WindowState = wsMaximized) then
+    Exit;
+  FIsFirstOnActivate := False;
   if Assigned(FOnActivate) then FOnActivate(Self);
 end;
 
@@ -732,28 +654,47 @@
   end;
 
   inherited WMSize(Message);
-
-  if (Message.SizeType and not SIZE_SourceIsInterface) = SIZE_RESTORED then
-  begin
-    FRestoredWidth := Width;
-    FRestoredHeight := Height;
-    //DebugLn('[TCustomForm.WMSize] saving restored bounds ',DbgSName(Self),' ',dbgs(FRestoredWidth),'x',dbgs(FRestoredHeight));
-  end;
+  Inc(FMovedCtr);
+  Application.QueueAsyncCall(@Moved, 1);  // 1 = resized
 end;
 
 procedure TCustomForm.WMMove(var Message: TLMMove);
 begin
   inherited WMMove(Message);
-  Application.QueueAsyncCall(@Moved, 0);
+  Inc(FMovedCtr);
+  Application.QueueAsyncCall(@Moved, 0);  // 0 = moved
 end;
 
 procedure TCustomForm.Moved(Data: PtrInt);
 begin
+  Dec(FMovedCtr);
+  if FMovedCtr > 0 then
+    Exit;
   if WindowState = wsNormal then
   begin
-    FRestoredLeft := Left;
-    FRestoredTop := Top;
-  end;
+    if Data = 0 then
+      begin
+        FRestoredLeft := Left;
+        FRestoredTop := Top;
+      end
+    else
+      begin
+        FRestoredWidth := Width;
+        FRestoredHeight := Height;
+        DoOnResize;
+      end;
+    DoOnChangeBounds;
+  end;
+  if FIsFirstOnShow then
+    begin
+      FIsFirstOnShow := False;
+      DoShow;
+    end;
+  if FIsFirstOnActivate then
+    begin
+      FIsFirstOnActivate := False;
+      Activate;
+    end;
 end;
 
 procedure TCustomForm.WMWindowPosChanged(var Message: TLMWindowPosChanged);
@@ -1016,6 +957,9 @@
  ------------------------------------------------------------------------------}
 procedure TCustomForm.DoShow;
 begin
+  if FIsFirstOnShow and (WindowState = wsMaximized) then
+    Exit;
+  FIsFirstOnShow := False;
   if Assigned(FOnShow) then FOnShow(Self);
 end;
 
@@ -1338,7 +1282,7 @@
           AForm := TCustomForm(Owner)
         else
           AForm := Application.MainForm;
-        if (Self <> AForm) then
+        if (Self <> AForm) and Assigned(AForm) then
         begin
           if FormStyle = fsMDIChild then
           begin
@@ -1487,18 +1431,6 @@
   Result := (Color <> {$ifdef UseCLDefault}clDefault{$else}clBtnFace{$endif});
 end;
 
-procedure TCustomForm.DoSendBoundsToInterface;
-begin
-  inherited DoSendBoundsToInterface;
-  if WindowState = wsNormal then
-  begin
-    FRestoredLeft := Left;
-    FRestoredTop := Top;
-    FRestoredWidth := Width;
-    FRestoredHeight := Height;
-  end;
-end;
-
 procedure TCustomForm.GetPreferredSize(var PreferredWidth,
   PreferredHeight: integer; Raw: boolean; WithThemeSpace: boolean);
 begin
@@ -1851,9 +1783,6 @@
        TCustomForm SetWindowState
 ------------------------------------------------------------------------------}
 procedure TCustomForm.SetWindowState(Value : TWindowState);
-const
-  ShowCommands: array[TWindowState] of Integer =
-    (SW_SHOWNORMAL, SW_MINIMIZE, SW_SHOWMAXIMIZED, SW_SHOWFULLSCREEN);
 begin
   if FWindowState <> Value then
   begin
@@ -1864,7 +1793,8 @@
   end;
 end;
 
-procedure TCustomForm.SetRestoredBounds(ALeft, ATop, AWidth, AHeight: integer);
+procedure TCustomForm.SetRestoredBounds(ALeft, ATop, AWidth, AHeight: integer;
+  const ADefaultPosition: Boolean);
 var
   prevWindowState: TWindowState;
 begin
@@ -1874,7 +1804,14 @@
   prevWindowState := WindowState;
   WindowState := wsNormal;
   SetBounds(ALeft, ATop, AWidth, AHeight);
+  // override
+  if ADefaultPosition then
+    MoveToDefaultPosition;
   WindowState := prevWindowState;
+  FRestoredLeft := Left;
+  FRestoredTop := Top;
+  FRestoredWidth := Width;
+  FRestoredHeight := Height;
 end;
 
 procedure TCustomForm.SetScaled(const AScaled: Boolean);
@@ -2047,6 +1984,9 @@
 ------------------------------------------------------------------------------}
 constructor TCustomForm.Create(AOwner: TComponent);
 begin
+  FMovedCtr := 0;
+  FIsFirstOnShow := True;
+  FIsFirstOnActivate := True;
   GlobalNameSpace.BeginWrite;
   try
     CreateNew(AOwner, 1); // this calls BeginFormUpdate, which is ended in AfterConstruction
@@ -2328,6 +2268,9 @@
       Width, MulDiv(Width, Monitor.PixelsPerInch, PixelsPerInch));
 
   Visible := True;
+  { wxMaximized secondary forms are not being shown maximized }
+  if (not (csDesigning in ComponentState)) and Showing then
+    ShowWindow(Handle, ShowCommands[WindowState]);
   BringToFront;
 end;
 

tform-restoredbounds.zip (133,630 bytes)

Joeny Ang

2019-10-03 03:52

reporter   ~0118263

Updated the patch (v2) with the following fixes:
- because the queued procedure is shared between WMMove and WMSize events, an event is sometimes missed because duplicate calls are discarded.
tform-wrong-bounds-and-restored-bounds-v2.patch (10,758 bytes)   
--- lcl/forms.pp
+++ lcl/forms.pp
@@ -490,6 +490,9 @@
     FRestoredHeight: integer;
     FShowInTaskbar: TShowInTaskbar;
     FWindowState: TWindowState;
+    FDelayedEventCtr: Integer;
+    FDelayedWMMove, FDelayedWMSize: Boolean;
+    FIsFirstOnShow, FIsFirstOnActivate: Boolean;
     function GetClientHandle: HWND;
     function GetEffectiveShowInTaskBar: TShowInTaskBar;
     function GetMonitor: TMonitor;
@@ -499,7 +502,7 @@
     procedure CloseModal;
     procedure FreeIconHandles;
     procedure IconChanged(Sender: TObject);
-    procedure Moved(Data: PtrInt);
+    procedure DelayedEvent(Data: PtrInt);
     procedure SetActive(AValue: Boolean);
     procedure SetActiveControl(AWinControl: TWinControl);
     procedure SetActiveDefaultControl(AControl: TControl);
@@ -590,7 +593,6 @@
     procedure VisibleChanged; override;
     procedure WndProc(var TheMessage : TLMessage); override;
     function VisibleIsStored: boolean;
-    procedure DoSendBoundsToInterface; override;
     procedure DoAutoSize; override;
     procedure SetAutoSize(Value: Boolean); override;
     procedure SetAutoScroll(Value: Boolean); override;
@@ -649,7 +651,8 @@
     function CanFocus: Boolean; override;
     procedure SetFocus; override;
     function SetFocusedControl(Control: TWinControl): Boolean ; virtual;
-    procedure SetRestoredBounds(ALeft, ATop, AWidth, AHeight: integer);
+    procedure SetRestoredBounds(ALeft, ATop, AWidth, AHeight: integer; const
+                                ADefaultPosition: Boolean = False);
     procedure Show;
 
     function ShowModal: Integer; virtual;
--- lcl/include/customform.inc
+++ lcl/include/customform.inc
@@ -16,6 +16,8 @@
 
 const
   BorderStylesAllowAutoScroll = [bsSizeable, bsSizeToolWin];
+  ShowCommands: array[TWindowState] of Integer =
+    (SW_SHOWNORMAL, SW_MINIMIZE, SW_SHOWMAXIMIZED, SW_SHOWFULLSCREEN);
 
 { TCustomForm }
 
@@ -68,93 +70,10 @@
   Gets called after the construction of the object
  ------------------------------------------------------------------------------}
 procedure TCustomForm.AfterConstruction;
-var
-  NewWidth, NewHeight: Integer;
-  OldWindowState: TWindowState;
-
-  procedure ChangeFormDimensions(AIsBeforeOnCreate: Boolean);
-  begin
-    if (WindowState = wsMaximized) and (FormStyle <> fsMDIChild) then
-    begin
-      {$IFDEF DEBUG_SM_LCLMAXIMIZED}
-      DebugLn('TCustomForm.AfterConstruction: SM_CYCAPTION ',
-        dbgs(GetSystemMetrics(SM_CYCAPTION)),
-      ' SM_CYSIZEFRAME ',dbgs(GetSystemMetrics(SM_CYSIZEFRAME)),
-      ' SM_CXMAXIMIZED ',dbgs(GetSystemMetrics(SM_CXMAXIMIZED)),
-      ' SM_CYMAXIMIZED ',dbgs(GetSystemMetrics(SM_CYMAXIMIZED)),
-      ' SM_LCLMAXIMIZEDHEIGHT ',dbgs(GetSystemMetrics(SM_LCLMAXIMIZEDHEIGHT)),
-      ' SM_LCLMAXIMIZEDWIDTH ',dbgs(GetSystemMetrics(SM_LCLMAXIMIZEDWIDTH)),
-      ' AIsBeforeOnCreate ',dbgs(AIsBeforeOnCreate));
-      {$ENDIF}
-
-      if (BorderStyle <> bsNone) and (FormStyle <> fsSplash) then
-      begin
-        NewHeight := GetSystemMetrics(SM_LCLMAXIMIZEDHEIGHT);
-        NewWidth := GetSystemMetrics(SM_LCLMAXIMIZEDWIDTH);
-        // if some ws does not implement this then provide normal metrics.
-        if NewHeight <= 0 then
-          NewHeight := GetSystemMetrics(SM_CYMAXIMIZED);
-        if NewWidth <= 0 then
-          NewHeight := GetSystemMetrics(SM_CXMAXIMIZED);
-      end else
-      begin
-        NewHeight := GetSystemMetrics(SM_CYMAXIMIZED);
-        NewWidth := GetSystemMetrics(SM_CXMAXIMIZED);
-      end;
-
-      if Constraints.MaxWidth > 0 then
-        NewWidth := Min(Constraints.MaxWidth, NewWidth);
-      if Constraints.MaxHeight > 0 then
-        NewHeight := Min(Constraints.MaxHeight, NewHeight);
-
-      // for unknown reasons on some systems SM_*MAXIMIZED* system metrics
-      // (tested xubuntu,64bits) return 0 or negative values, in this case
-      // a maximized window is expected to have at least WorkArea width/height.
-      //
-      // Reproduced again under Debian Wheezy.
-      // mistery solved, it ocurrs under gtk2/64-bit, fixed at the place
-      // the checks doesn't hurt though
-      //
-      // see bug #21634
-      if NewWidth<=0 then
-        NewWidth := Screen.WorkAreaWidth;
-      if NewHeight<=0 then
-        NewHeight := Screen.WorkAreaHeight;
-
-      if NewWidth>0 then
-        Width := NewWidth;
-      if NewHeight>0 then
-        Height := NewHeight;
-    end;
-
-    if (WindowState = wsFullScreen) and (FormStyle <> fsMDIChild) then
-    begin
-      NewWidth := LCLIntf.GetSystemMetrics(SM_CXFULLSCREEN);
-      NewHeight := LCLIntf.GetSystemMetrics(SM_CYFULLSCREEN);
-      if Constraints.MaxWidth > 0 then
-        NewWidth := Min(Constraints.MaxWidth, NewWidth);
-      if Constraints.MaxHeight > 0 then
-        NewHeight := Min(Constraints.MaxHeight, NewHeight);
-      Width := NewWidth;
-      Height := NewHeight;
-    end;
-  end;
-begin
-  // issue #21119, prepare maximized or fullscreen form to accurate dimensions.
-  // we avoid flickering also in this case.
-  if not (csDesigning in ComponentState) then
-    ChangeFormDimensions(True);
-
-  OldWindowState := WindowState;
+begin
+  SetRestoredBounds(Left, Top, Width, Height, True);
+
   DoCreate;
-
-  // if we change WindowState in constructor and handle isn't allocated
-  // then change our dimensions to accurate one
-  if not (csDesigning in ComponentState) and not HandleAllocated and
-    (OldWindowState <> WindowState) and
-    not (OldWindowState in [wsMaximized, wsFullScreen]) and
-    (WindowState in [wsMaximized, wsFullScreen]) then
-      ChangeFormDimensions(False);
 
   EndFormUpdate; // the BeginFormUpdate is in CreateNew
   inherited AfterConstruction;
@@ -677,6 +596,9 @@
  ------------------------------------------------------------------------------}
 procedure TCustomForm.Activate;
 begin
+  if FIsFirstOnActivate and (WindowState = wsMaximized) then
+    Exit;
+  FIsFirstOnActivate := False;
   if Assigned(FOnActivate) then FOnActivate(Self);
 end;
 
@@ -733,27 +655,55 @@
 
   inherited WMSize(Message);
 
-  if (Message.SizeType and not SIZE_SourceIsInterface) = SIZE_RESTORED then
-  begin
-    FRestoredWidth := Width;
-    FRestoredHeight := Height;
-    //DebugLn('[TCustomForm.WMSize] saving restored bounds ',DbgSName(Self),' ',dbgs(FRestoredWidth),'x',dbgs(FRestoredHeight));
-  end;
+  FDelayedWMSize := True;
+  Inc(FDelayedEventCtr);
+  Application.QueueAsyncCall(@DelayedEvent, 0);
 end;
 
 procedure TCustomForm.WMMove(var Message: TLMMove);
 begin
   inherited WMMove(Message);
-  Application.QueueAsyncCall(@Moved, 0);
-end;
-
-procedure TCustomForm.Moved(Data: PtrInt);
-begin
+
+  FDelayedWMMove := True;
+  Inc(FDelayedEventCtr);
+  Application.QueueAsyncCall(@DelayedEvent, 0);
+end;
+
+procedure TCustomForm.DelayedEvent(Data: PtrInt);
+begin
+  { discard duplicate calls, accept last call only }
+  Dec(FDelayedEventCtr);
+  if FDelayedEventCtr > 0 then
+    Exit;
+  { update restored bounds }
   if WindowState = wsNormal then
-  begin
-    FRestoredLeft := Left;
-    FRestoredTop := Top;
-  end;
+    begin
+      if FDelayedWMMove then
+        begin
+          FRestoredLeft := Left;
+          FRestoredTop := Top;
+        end;
+      if FDelayedWMSize then
+        begin
+          FRestoredWidth := Width;
+          FRestoredHeight := Height;
+          DoOnResize;     // delayed onResize()
+        end;
+      DoOnChangeBounds;     // delayed onChangeBounds()
+    end;
+  FDelayedWMMove := False;
+  FDelayedWMSize := False;
+  { call onShow() or onActivate() for the first time }
+  if FIsFirstOnShow then
+    begin
+      FIsFirstOnShow := False;
+      DoShow;
+    end;
+  if FIsFirstOnActivate then
+    begin
+      FIsFirstOnActivate := False;
+      Activate;
+    end;
 end;
 
 procedure TCustomForm.WMWindowPosChanged(var Message: TLMWindowPosChanged);
@@ -1016,6 +966,9 @@
  ------------------------------------------------------------------------------}
 procedure TCustomForm.DoShow;
 begin
+  if FIsFirstOnShow and (WindowState = wsMaximized) then
+    Exit;
+  FIsFirstOnShow := False;
   if Assigned(FOnShow) then FOnShow(Self);
 end;
 
@@ -1338,7 +1291,7 @@
           AForm := TCustomForm(Owner)
         else
           AForm := Application.MainForm;
-        if (Self <> AForm) then
+        if (Self <> AForm) and Assigned(AForm) then
         begin
           if FormStyle = fsMDIChild then
           begin
@@ -1487,18 +1440,6 @@
   Result := (Color <> {$ifdef UseCLDefault}clDefault{$else}clBtnFace{$endif});
 end;
 
-procedure TCustomForm.DoSendBoundsToInterface;
-begin
-  inherited DoSendBoundsToInterface;
-  if WindowState = wsNormal then
-  begin
-    FRestoredLeft := Left;
-    FRestoredTop := Top;
-    FRestoredWidth := Width;
-    FRestoredHeight := Height;
-  end;
-end;
-
 procedure TCustomForm.GetPreferredSize(var PreferredWidth,
   PreferredHeight: integer; Raw: boolean; WithThemeSpace: boolean);
 begin
@@ -1851,9 +1792,6 @@
        TCustomForm SetWindowState
 ------------------------------------------------------------------------------}
 procedure TCustomForm.SetWindowState(Value : TWindowState);
-const
-  ShowCommands: array[TWindowState] of Integer =
-    (SW_SHOWNORMAL, SW_MINIMIZE, SW_SHOWMAXIMIZED, SW_SHOWFULLSCREEN);
 begin
   if FWindowState <> Value then
   begin
@@ -1864,7 +1802,8 @@
   end;
 end;
 
-procedure TCustomForm.SetRestoredBounds(ALeft, ATop, AWidth, AHeight: integer);
+procedure TCustomForm.SetRestoredBounds(ALeft, ATop, AWidth, AHeight: integer;
+  const ADefaultPosition: Boolean);
 var
   prevWindowState: TWindowState;
 begin
@@ -1874,7 +1813,14 @@
   prevWindowState := WindowState;
   WindowState := wsNormal;
   SetBounds(ALeft, ATop, AWidth, AHeight);
+  // override
+  if ADefaultPosition then
+    MoveToDefaultPosition;
   WindowState := prevWindowState;
+  FRestoredLeft := Left;
+  FRestoredTop := Top;
+  FRestoredWidth := Width;
+  FRestoredHeight := Height;
 end;
 
 procedure TCustomForm.SetScaled(const AScaled: Boolean);
@@ -2047,6 +1993,11 @@
 ------------------------------------------------------------------------------}
 constructor TCustomForm.Create(AOwner: TComponent);
 begin
+  FDelayedEventCtr := 0;
+  FDelayedWMMove := False;
+  FDelayedWMSize := False;
+  FIsFirstOnShow := True;
+  FIsFirstOnActivate := True;
   GlobalNameSpace.BeginWrite;
   try
     CreateNew(AOwner, 1); // this calls BeginFormUpdate, which is ended in AfterConstruction
@@ -2328,6 +2279,9 @@
       Width, MulDiv(Width, Monitor.PixelsPerInch, PixelsPerInch));
 
   Visible := True;
+  { wxMaximized secondary forms are not being shown maximized }
+  if (not (csDesigning in ComponentState)) and Showing then
+    ShowWindow(Handle, ShowCommands[WindowState]);
   BringToFront;
 end;
 

Juha Manninen

2019-10-03 16:01

developer   ~0118278

Interesting! How many of the related issues this would fix?
I think this should be applied and tested in trunk.

Juha Manninen

2019-10-05 22:28

developer   ~0118358

I applied the patch in r61997 for everybody to test. Let's see if somebody finds bugs in it.

Joeny Ang

2019-10-07 08:00

reporter  

delayedevent-wait-for-wmsize-before-onshow-onactivate.patch (922 bytes)   
--- lcl/include/customform.inc
+++ lcl/include/customform.inc
@@ -691,19 +691,22 @@
         end;
       DoOnChangeBounds;     // delayed onChangeBounds()
     end;
+  { call onShow() or onActivate() for the first time, after first WMSize }
+  if FDelayedWMSize then
+    begin
+      if FIsFirstOnShow then
+        begin
+          FIsFirstOnShow := False;
+          DoShow;
+        end;
+      if FIsFirstOnActivate then
+        begin
+          FIsFirstOnActivate := False;
+          Activate;
+        end;
+    end;
   FDelayedWMMove := False;
   FDelayedWMSize := False;
-  { call onShow() or onActivate() for the first time }
-  if FIsFirstOnShow then
-    begin
-      FIsFirstOnShow := False;
-      DoShow;
-    end;
-  if FIsFirstOnActivate then
-    begin
-      FIsFirstOnActivate := False;
-      Activate;
-    end;
 end;
 
 procedure TCustomForm.WMWindowPosChanged(var Message: TLMWindowPosChanged);

Joeny Ang

2019-10-07 08:00

reporter   ~0118386

Another one :)

GTK2 only:
When a form is designed so as part of it is clipped off the screen, and WindowState=wsMaximized, width and height during onShow()/onActivate() is wrong.
Reason: After creation (?), the window manager automatically moves the form so that the whole form is visible. This will trigger WMMove() events, thus triggering the delayed onShow()/onActivate() even before the form is maxmimized.
Solution is to wait for the first WMSize() before triggering onShow()/onActivate().

Apply patch after the v2 patch.

Juha Manninen

2019-10-25 00:37

developer   ~0118793

I applied the new patch in r62113. Thanks. Please test everybody.

Juha Manninen

2020-03-16 19:29

developer   ~0121628

Resolving.
Thank you Joeny Ang for the patches.

Michl

2020-05-24 15:56

developer   ~0123037

Last edited: 2020-05-24 17:33

View 3 revisions

At least it breaks the usage of SpartaDockedFormEditor. After revision 61997 it isn't possible to change the bounds of a form anymore.

Too bad, just Windows seems to be affected. Just tried GTK2 and QT, both are working fine.

I'll try to look at it.

Michl

2020-05-25 15:17

developer   ~0123059

I fixed the problem with SpartaDockedFormEditor in revision 63218

Michl

2020-05-25 21:05

developer   ~0123062

Last edited: 2020-05-25 21:08

View 3 revisions

I have to reopen the issue. I could fix one problem with SpartaDockedFormEditor, but there is a other one, what can't be fixed there. If there are more than one form opened and resized, the frame got corrupted. Before I can search for the problem, one main problem has to be fixed:

After the revision 61997, OnChangeBounds of a form is called twice. See added project TestChangeBoundsEvent.zip. (tested on Windows 7)

Joeny Ang

2020-07-15 10:26

reporter   ~0124030

Last edited: 2020-07-15 10:27

View 2 revisions

Changes made by this patch:
  - fixed sequence of events (based on Delphi 3... create, show, activate, resize)
  - OnResize() and OnChangeBounds() are being called twice
      :: moved code queueing delayed events to overriden DoOnResize() and DoOnChangeBounds()
  - removed TCustomForm.WMMove()
  - added check for wsFullScreen in TCustomForm.Activate() and TCustomForm.DoShow()
  - renamed some variables
fix-duplicate-onresize-onchangebounds.patch (4,523 bytes)   
--- lcl/forms.pp
+++ lcl/forms.pp
@@ -491,7 +491,7 @@
     FShowInTaskbar: TShowInTaskbar;
     FWindowState: TWindowState;
     FDelayedEventCtr: Integer;
-    FDelayedWMMove, FDelayedWMSize: Boolean;
+    FDelayedOnChangeBounds, FDelayedOnResize: Boolean;
     FIsFirstOnShow, FIsFirstOnActivate: Boolean;
     function GetClientHandle: HWND;
     function GetEffectiveShowInTaskBar: TShowInTaskBar;
@@ -535,7 +535,6 @@
     procedure WMActivate(var Message : TLMActivate); message LM_ACTIVATE;
     procedure WMCloseQuery(var message: TLMessage); message LM_CLOSEQUERY;
     procedure WMHelp(var Message: TLMHelp); message LM_HELP;
-    procedure WMMove(var Message: TLMMove); message LM_MOVE;
     procedure WMShowWindow(var message: TLMShowWindow); message LM_SHOWWINDOW;
     procedure WMSize(var message: TLMSize); message LM_Size;
     procedure WMWindowPosChanged(var Message: TLMWindowPosChanged); message LM_WINDOWPOSCHANGED;
@@ -581,6 +580,8 @@
     procedure Resizing(State: TWindowState); override;
     procedure CalculatePreferredSize(var PreferredWidth,
            PreferredHeight: integer; WithThemeSpace: Boolean); override;
+    procedure DoOnResize; override;
+    procedure DoOnChangeBounds; override;
     procedure SetZOrder(Topmost: Boolean); override;
     procedure SetParent(NewParent: TWinControl); override;
     procedure MoveToDefaultPosition; virtual;
--- lcl/include/customform.inc
+++ lcl/include/customform.inc
@@ -604,7 +604,7 @@
  ------------------------------------------------------------------------------}
 procedure TCustomForm.Activate;
 begin
-  if FIsFirstOnActivate and (WindowState = wsMaximized) then
+  if FIsFirstOnActivate and (WindowState in [wsMaximized, wsFullScreen]) then
     Exit;
   FIsFirstOnActivate := False;
   if Assigned(FOnActivate) then FOnActivate(Self);
@@ -662,22 +662,23 @@
   end;
 
   inherited WMSize(Message);
-
+end;
+
+procedure TCustomForm.DoOnResize;
+begin
   if not (csDestroying in ComponentState) then
   begin
-    FDelayedWMSize := True;
+    FDelayedOnResize := True;
     Inc(FDelayedEventCtr);
     Application.QueueAsyncCall(@DelayedEvent, 0);
   end;
 end;
 
-procedure TCustomForm.WMMove(var Message: TLMMove);
-begin
-  inherited WMMove(Message);
-
+procedure TCustomForm.DoOnChangeBounds;
+begin
   if not (csDestroying in ComponentState) then
   begin
-    FDelayedWMMove := True;
+    FDelayedOnChangeBounds := True;
     Inc(FDelayedEventCtr);
     Application.QueueAsyncCall(@DelayedEvent, 0);
   end;
@@ -692,21 +693,20 @@
   { update restored bounds }
   if WindowState = wsNormal then
     begin
-      if FDelayedWMMove then
+      if FDelayedOnChangeBounds then
         begin
           FRestoredLeft := Left;
           FRestoredTop := Top;
         end;
-      if FDelayedWMSize then
+      if FDelayedOnResize then
         begin
           FRestoredWidth := Width;
           FRestoredHeight := Height;
-          DoOnResize;     // delayed onResize()
         end;
-      DoOnChangeBounds;     // delayed onChangeBounds()
-    end;
-  { call onShow() or onActivate() for the first time, after first WMSize }
-  if FDelayedWMSize then
+    end;
+  { call onShow() or onActivate() for the first time, after first OnResize()
+    and OnChangeBounds() }
+  if FDelayedOnResize and FDelayedOnChangeBounds then
     begin
       if FIsFirstOnShow then
         begin
@@ -720,8 +720,14 @@
             Activate;
         end;
     end;
-  FDelayedWMMove := False;
-  FDelayedWMSize := False;
+  { delayed onResize() }
+  if FDelayedOnResize then
+    inherited DoOnResize;
+  { delayed onChangeBounds() }
+  if FDelayedOnResize or FDelayedOnChangeBounds then
+    inherited DoOnChangeBounds;
+  FDelayedOnChangeBounds := False;
+  FDelayedOnResize := False;
 end;
 
 procedure TCustomForm.WMWindowPosChanged(var Message: TLMWindowPosChanged);
@@ -992,7 +998,7 @@
  ------------------------------------------------------------------------------}
 procedure TCustomForm.DoShow;
 begin
-  if FIsFirstOnShow and (WindowState = wsMaximized) then
+  if FIsFirstOnShow and (WindowState in [wsMaximized, wsFullScreen]) then
     Exit;
   FIsFirstOnShow := False;
   if Assigned(FOnShow) then FOnShow(Self);
@@ -2020,8 +2026,8 @@
 constructor TCustomForm.Create(AOwner: TComponent);
 begin
   FDelayedEventCtr := 0;
-  FDelayedWMMove := False;
-  FDelayedWMSize := False;
+  FDelayedOnChangeBounds := False;
+  FDelayedOnResize := False;
   FIsFirstOnShow := True;
   FIsFirstOnActivate := True;
   GlobalNameSpace.BeginWrite;

Juha Manninen

2020-07-17 01:01

developer   ~0124110

Thanks for the patch. I applied it in r63577.
Michl and others, please test.

Juha Manninen

2020-07-22 19:35

developer   ~0124233

No complaints. I guess this is fixed now. Resolving...

Issue History

Date Modified Username Field Change
2019-10-02 09:55 Joeny Ang New Issue
2019-10-02 09:55 Joeny Ang File Added: tform-wrong-bounds-and-restored-bounds.patch
2019-10-02 09:55 Joeny Ang File Added: tform-restoredbounds.zip
2019-10-03 03:52 Joeny Ang File Added: tform-wrong-bounds-and-restored-bounds-v2.patch
2019-10-03 03:52 Joeny Ang Note Added: 0118263
2019-10-03 14:33 Juha Manninen Relationship added related to 0027894
2019-10-03 14:33 Juha Manninen Relationship added related to 0022771
2019-10-03 14:33 Juha Manninen Relationship added related to 0027375
2019-10-03 14:34 Juha Manninen Relationship added related to 0021119
2019-10-03 14:42 Juha Manninen Relationship added related to 0032631
2019-10-03 14:44 Juha Manninen Relationship added related to 0008576
2019-10-03 14:45 Juha Manninen Description Updated View Revisions
2019-10-03 14:45 Juha Manninen LazTarget => -
2019-10-03 14:45 Juha Manninen Widgetset GTK 2, Win32/Win64 => GTK 2, Win32/Win64
2019-10-03 16:01 Juha Manninen Note Added: 0118278
2019-10-05 22:26 Juha Manninen Assigned To => Juha Manninen
2019-10-05 22:26 Juha Manninen Status new => assigned
2019-10-05 22:28 Juha Manninen Note Added: 0118358
2019-10-07 08:00 Joeny Ang File Added: delayedevent-wait-for-wmsize-before-onshow-onactivate.patch
2019-10-07 08:00 Joeny Ang Note Added: 0118386
2019-10-25 00:37 Juha Manninen Note Added: 0118793
2019-10-25 00:38 Juha Manninen Fixed in Revision => r61997, r62113
2019-10-25 00:38 Juha Manninen Widgetset GTK 2, Win32/Win64 => GTK 2, Win32/Win64
2020-03-16 19:29 Juha Manninen Status assigned => resolved
2020-03-16 19:29 Juha Manninen Resolution open => fixed
2020-03-16 19:29 Juha Manninen Widgetset GTK 2, Win32/Win64 => GTK 2, Win32/Win64
2020-03-16 19:29 Juha Manninen Note Added: 0121628
2020-04-05 18:44 Juha Manninen Relationship added related to 0036877
2020-05-24 15:56 Michl Note Added: 0123037
2020-05-24 15:59 Michl Note Edited: 0123037 View Revisions
2020-05-24 17:33 Michl Note Edited: 0123037 View Revisions
2020-05-25 15:16 Michl Fixed in Revision r61997, r62113 => r61997, r62113, r63218
2020-05-25 15:16 Michl Widgetset GTK 2, Win32/Win64 => GTK 2, Win32/Win64
2020-05-25 15:17 Michl Note Added: 0123059
2020-05-25 21:04 Michl Status resolved => assigned
2020-05-25 21:04 Michl Resolution fixed => reopened
2020-05-25 21:05 Michl Note Added: 0123062
2020-05-25 21:05 Michl File Added: TestChangeBoundsEvent.zip
2020-05-25 21:07 Michl Note Edited: 0123062 View Revisions
2020-05-25 21:08 Michl Note Edited: 0123062 View Revisions
2020-07-15 10:26 Joeny Ang Note Added: 0124030
2020-07-15 10:26 Joeny Ang File Added: fix-duplicate-onresize-onchangebounds.patch
2020-07-15 10:27 Joeny Ang Note Edited: 0124030 View Revisions
2020-07-17 01:01 Juha Manninen Note Added: 0124110
2020-07-17 01:02 Juha Manninen Fixed in Revision r61997, r62113, r63218 => r61997, r62113, r63218, r63577
2020-07-17 01:02 Juha Manninen Widgetset GTK 2, Win32/Win64 => GTK 2, Win32/Win64
2020-07-22 19:35 Juha Manninen Status assigned => resolved
2020-07-22 19:35 Juha Manninen Widgetset GTK 2, Win32/Win64 => GTK 2, Win32/Win64
2020-07-22 19:35 Juha Manninen Note Added: 0124233