Restore after programatically maximizing a form doesn't restore width and height lazarus / 34-Windows ---------------------------------------------------------------------- fatmonk · 2013-05-30 08:35 I'm having real trouble getting an application to remember it's state from when it was last closed. The elements in the form are anchored correctly so that they resize with the form correctly. Resizing, maximizing and restoring in normal use works fine - the problems come on restarting the application seemingly because I am trying to restore the previous state of the form. When the application is closed I am storing Left, Top, Width and Height values of the main form in the registry. I am also storing a boolean value to say whether the form was maximized at the time of closing the application or not. When the application is restarted I am reading the values form the registry and using Self.Left:=lastLeft and Self.Top:=lastTop etc where lastLeft and lastTop are the values retrieved from the registry. That bit is working fine - or at least seems to be. I then also check if the window was maximized when the application was last closed and if it was I am running Code: [Select]Self.WindowState:=wsMaximized which maximizes the window perfectly. If I then click the top right button to restore the form to a wsNormal state, however, the width and height of the form is not restore - the Left and Top position of the form IS though! So, that's the first problem. I thought I had found a way around that by coding to reset the Width ad Height of the form when a change in WindowState is fired for the form and that kind of works except that the elements in the form do not reposition to fit the new form size as they do when maximizing and restoring at any other time. This one is really frustrating me, so any help would be gratefully received. Thanks, FM Edit: I've uploaded a demo project which demonstrates the problem: https://s kydrive.live.com/redir?resid=48696EE8C9FA702B!467&authkey=!AH64AZ_eikf AHko To see the problems, unzip the file, open the project and compile and run, resize, close, re-open, resize, maximize, close, re-open, restore.. etc etc.. ---------------------------------------------------------------------- User137 · 2013-05-30 11:15 1. You have memory leaks all over the place, by creating TRegistry objects without freeing them in same procedure. It would be different the the TRegistry variable was public somewhere, but then you would have to check if it exists first, and not Create it if it does. 2. Excess use of variables makes it difficult to see, what are public, and what local variables. In this case ALL are local variables. 3. You must not read Left, Right, Width, Height values when wsMaximized is set. They should be stored in other windowstates, either written directly to registry, or to public variables to write them later. ---------------------------------------------------------------------- fatmonk · 2013-05-30 12:55 First of all I should point out that the project I uploaded was something knocked together in 5 minutes to demonstrate the problem - this is not the actual project, however: 1. Every procedure that has a registryHook:=TRegistry.Create has a corresponding registryHook.Free - is there something else that I need to do to free up these objects after they've been used? 2. The 'excess' use of local variables in the demo project was intended to avoid any confusion that could arise from global variables not being set or updated by a particular function. My reasoning for the demo being that by defining them locally and setting and reading them every time they are used I can know for sure which procedure is setting or reading them. 3. I don't read Left, Right, Width or Height variables while wsMaximized is set. From the demo project: Code: [Select]if Self.WindowState = wsMaximized then   registryHook.WriteBool('wasMaximized',true) else   begin     registryHook.WriteBool('wasMaximized',false);     registryHook.WriteInteger('lastLeft', lastLeft);     registryHook.WriteInteger('lastTop', lastTop);     registryHook.WriteInteger('lastWidth', lastWidth);     registryHook.WriteInteger('lastHeight', lastHeight);   end;  ie only read the values for writing to the registry if the WindowState is not wsMaximized. When WindowsState IS wsMaximized then only the wasMaximized registry value is set. My code may be far from perfect, but it does demonstrate that I have something very odd going on when the application is maximized on startup and subsequently restored. -FM ---------------------------------------------------------------------- Blaazen · 2013-05-30 13:00 If you mean: - your Form is wsNormal (at some pos. X,Y with size W,H) - you maximize - you close application and start again - your app. is now maximized - you switch to wsNormal and you want previous X,Y,W,H again ? Maybe it will need some hack ... ---------------------------------------------------------------------- Blaazen · 2013-05-30 13:35 I think I got it. Finally, it is simplier. Changes: - OnWindowStateChange is not needed - I removed local variables and I store "normal" bounds in private fields. - Setting bounds in "OnCreate" seems to be too early, "OnActivate" too, therefore I do it in OnShow, which run only once - Method OnChangeBounds is not assigned by default (it is not in OI), I assign it later in OnShow handler Next time, you don't have to attach binary (*.exe) - it's large and useless on Linux. Please test on Windows, I tested only on Linux+Qt. ---------------------------------------------------------------------- fatmonk · 2013-05-30 15:04 Thanks for having a go at this Blaazen, On Windows it seems that the size is not being restored at all when restoring after a close when maximized. - I open the application and resize it a little noting its size and position. - I close the application. - I re-open the application - it opens in the last position. All Good. - I resize the window and move it a little noting its new size and position. - I maximize the window. - I close the application. - I re-open the application and it opens maximized. - I click the top right button to restore and the window 'un- maximizes' in that it becomes a moveable resizable window again with the correct top-right buttons, but the size does not restore - it stays actually slightly bigger than full screen! And I thought this was going to be so easy. -FM PS Re the .exe. I actually did delete, but then must have recompiled before zipping up. Sorry. Uploading a smaller version now... ---------------------------------------------------------------------- User137 · 2013-05-30 17:46 Oops, my "sharp" eyes completely missed the .Free's in the middle of the if block. I'm used to seeing them executed last. Code: [Select]if Self.WindowState = wsMaximized then   registryHook.WriteBool('wasMaximized',true) else   begin     registryHook.WriteBool('wasMaximized',false);     registryHook.WriteInteger('lastLeft', lastLeft);     registryHook.WriteInteger('lastTop', lastTop);     registryHook.WriteInteger('lastWidth', lastWidth);     registryHook.WriteInteger('lastHeight', lastHeight);   end;This block is only place where registry was saved, the onClose event. If window was originally normal size, and then maximized at the end, no size data was written to registry during the program runtime. Only the maximized-flag. ---------------------------------------------------------------------- poiuyt555 · 2013-05-31 11:52 i Do not read all thread, but you may see my bug reports about it - there is big and many problems with windowState: http://bugs.freepascal.org/view.php?id=24055 (bug # 1) http://bugs.freepascal.org/view.php?id=24469 (bug # 2-4) I have found 4 bugs with WindowState (read all my replyies there). Even - event OnWindowStateChange did not occur!!! in some case. ---------------------------------------------------------------------- fatmonk · 2013-05-31 13:00 Quote from: User137 on May 30, 2013, 07:46:13 pmThis block is only place where registry was saved, the onClose event. If window was originally normal size, and then maximized at the end, no size data was written to registry during the program runtime. Only the maximized-flag. That's true of the demo project... arrgghhh I'll mod that a bit to make it a bit more realistic. The actual application does the save in a couple of places (but needs some work on the logic as that bit's not quite right - on this subject, is there a hidden .onMove event for a TForm?). The biggest problem for me, though, is that even when the size of the window is restored properly by my own code, the elements inside the form do not reposition properly. I can't seem to find any way of making that happen. Maybe it's to do with one of the bugs poiuyt555 refers to - I'll go read. -FM ---------------------------------------------------------------------- fatmonk · 2015-10-01 09:53 I decided to revisit this after a couple of years.. (not sure that was such a wise idea, but here goes)... I have done a bit more digging around and I think I have found what the problem is that causes so much trouble with trying to restore a window to the correct size and position after maximizing and closing the application. First of all I'll explain how I believe an application /should/ work (based on how other applications seem to work): 1. Open application. 2. Re-size and position application. 3. Maximize application. 4. Close application. 5. Re-open application. Application opens maximized. 6. Restore application - ie exit wsMaximized and return to wsNormal. The application should return to the size and position set in 2. To achieve this I store the size and position, and also the window state in the registry. The sequence I follow is this: 1. On a Bounds Change (or resize) event for the form I first check that the window IS NOT maximized and only then store the width, height, left and top values for the form in a set of variables (lastWidth, lastHeight, lastLeft and lastRight). 2. On a window state change I check if the window state is wsMaximized and set a variable to true if it is and false otherwise. 3. On exiting the application I write these 5 variables to the registry. It turns out that when doing this, the window size and position that gets stored is the maximized size and position because when clicking the maximize button in the application title bar Lazarus first fires resize event /BEFORE/ setting the windows state to wsMaximized. Therefore the check for the wsMaximized state returns false and so the maximized window size and position values are stored in the variables (and then subsequently written to the registry). Solution: Lazarus should set the windows state to wsMaximized BEFORE resizing and repositioning the window. A workaround that doesn't work! To get around the problem above I decided to also store a 'previous' set of size and position information (previousLastWidth, previousLastHeight, previousLastLeft, previousLastTop). When a resize or bounds change event fires, the first thing that is done is that lastWidth is copied to previous lastWidth, lastHeight to previouslastHeight etc. Then the form size and position are stored in lastWidth, lastHeight etc. On a window state change event, if the form has switched to wsMaximized then the previous values are copied back into lastWidth, lastHeight etc. Doing this successfully stores the correct size and position information in the registry when the application exits - so we're on the right track with the workaround. When the application restarts, however, we hit another oddity... When the application starts the previous size, position and window state values are read from the registry correctly. If the application was previously maximized then the window state is set to wsMaximized, then the forms size and position values are set to those retrieved from the registry. The application correctly opens maximized. When the 'restore' button is clicked in the window title bar, the window returns to the wsNormal state in the correct previous position on the screen, BUT the size values have somehow been lost despite having been set correctly with the values from the registry when the application started. I'm still digging into exactly when the size values go missing and will update when I have more info. I'll also update and upload the .pas I am using so that people can try this out form themselves... it's basic code so should be pretty readable, and it's self-contained so should be easy to add to a test application. [Note: I'm currently testing this on Lazarus 1.4.0 on Windows] -FM ---------------------------------------------------------------------- fatmonk · 2015-10-01 11:38 This is getting very close now... but it's still not quite there. I've attached the .pas file that I'm using (zipped to allow attaching, it's very small and simple) - it can be included in a project and there are instructions at the top for how it is intended to be called. Right now the only problem with this code is as follows: 1. Open application. 2. Resize and move application - note the position and size. 3. Maximize application. 4. Close application. 5. Re-open application - application opens maximized. 6. Restore application - application resizes and repositions correctly BUT the controls within the form do not resize to fit! Resizing the form very slightly causes the controls on the form to resize correctly. Restoring from maximize without closing in between works correctly. And I am calling a TForm.Paint after resetting the size and position of the form... Programatically increasing and decreasing the size of the form DOES NOT trigger a resizing of the controls in the form, it has to be done by dragging the edges of the form.. As I say, close, but still not quite there... Now, something that seems really odd... I've added a Hint/ToolTip to the form when debug is on in the rememberPosition.pas file - the tooltip shows you what values are stored in the size and position variable and also the actual size and position values for the form. After the restore described above where the controls don't resize, you can see that although the form window HAS resized correctly, Lazarus seems to think that the form is actually still the bigger maximized size... how can that be? -FM