View Issue Details

IDProjectCategoryView StatusLast Update
0036127LazarusLCLpublic2019-10-07 08:00
ReporterJoeny AngAssigned ToJuha Manninen 
PrioritynormalSeverityminorReproducibilityalways
Status assignedResolutionopen 
Platformx86_64OSArch LinuxOS Version
Product Version2.0.4Product Build 
Target VersionFixed in Version 
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 Revision
LazTarget-
WidgetsetGTK 2, Win32/Win64
Attached Files
  • 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)
  • 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;
     
    
    
  • 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);
    
    

Relationships

related to 0027894 new Lazarus TForm RestoredWidth and RestoredHeight updated incorrectly if window maximized under Gtk2 
related to 0022771 assignedOndrej Pokorny Lazarus When FormStyle = wsMaximized, after restore Widht/Height have wrong values 
related to 0027375 new Lazarus If a Form is started with wsMaximized (set in OI), it does not restore to designed Size 
related to 0021119 confirmedZeljan Rikalo Lazarus When a form is started as wsMaximized height and width report design time values 
related to 0032631 new 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 

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.

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