Based on MS: Positioning Objects on a Multiple Display Setup
It's not new, there are probably other codes around...
Code: Select all
;-Top
; -----------------------------------------------------------------------------
; Title: Keep Window position, size and state (Normal, Minimize, Maximize) between run
; Description: Save the Window position, size and state (Normal, Minimize, Maximize) on closing. To then initialize the window size and position
; on the same monitor, at the same size, state and position (optionally, clip to the edge, default), the next time the program run.
; Based on: https://learn.microsoft.com/en-us/windows/win32/gdi/positioning-objects-on-a-multiple-display-setup
; Source Name: KeepWindowSize.pbi
; Author: ChrisR
; Creation Date: 2024-04-05
; Modification Date: 2024-10-15 - Use GetWindowPlacement to save Window position & size
; Version: 1.1.0
; PB-Version: 6.0 or other
; OS: Windows Only
; Forum: https://www.purebasic.fr/english/viewtopic.php?t=83983
; -----------------------------------------------------------------------------
;
; (*) How tu use:
; Add: XIncludeFile "KeepWindowSize.pbi"
; And optionaly UseModule KeepWindowSize
; Then use the 2 Functions:
; InitWindowSize(#Window[, Flag]) ; Optional Flag parameter: #MONITOR_CENTER | #MONITOR_CLIP | #MONITOR_WORKAREA | #MONITOR_AREA | #MONITOR_NOMOVE
; - Call it after opening the window to initialize its size and position with the previous values saved in the function below.
; SaveWindowSize(#Window)
; - Call it when you close the window to save its size and position. Note that saved values are in pixel, without scaling.
; - The name of the preference file saved is the same as the executable file name without extension +".pref", next to it, in the same path.
; And the section name (group) is "Size_Window_" + Str(#Window), if a Window constant is used (for multi-window support), else for variables only "Size_Window"
;
; -----------------------------------------------------------------------------
CompilerIf (#PB_Compiler_IsMainFile)
EnableExplicit
CompilerEndIf
;
;------------------------------------------------------------------------------
;- Declare Module KeepWindowSize
;------------------------------------------------------------------------------
;
DeclareModule KeepWindowSize
#MONITOR_CENTER = 1 ; Center window to monitor
#MONITOR_CLIP = 0 ; Clip window to monitor
#MONITOR_WORKAREA = 2 ; Use monitor work area (without the taskbar)
#MONITOR_AREA = 0 ; Use monitor entire area
#MONITOR_NOMOVE = 4 ; Keep saved window position
Declare InitWindowSize(Window, Flags = #MONITOR_CLIP | #MONITOR_WORKAREA)
Declare SaveWindowSize(Window)
EndDeclareModule
;
;------------------------------------------------------------------------------
;- Module KeepWindowSize
;------------------------------------------------------------------------------
;
Module KeepWindowSize
EnableExplicit
Structure MONITORINFOEX2 Align #PB_Structure_AlignC
cbSize.l
rcMonitor.RECT
rcWork.RECT
dwFlags.l
szDevice.c[#CCHDEVICENAME]
EndStructure
Declare Min(ValueA, ValueB)
Declare Max(ValueA, ValueB)
Procedure Min(ValueA, ValueB)
If ValueA < ValueB
ProcedureReturn ValueA
EndIf
ProcedureReturn ValueB
EndProcedure
Procedure Max(ValueA, ValueB)
If ValueA > ValueB
ProcedureReturn ValueA
EndIf
ProcedureReturn ValueB
EndProcedure
Procedure InitWindowSize(Window, Flags = #MONITOR_CLIP | #MONITOR_WORKAREA)
Protected PrefFile.s = GetPathPart(ProgramFilename()) + GetFilePart(ProgramFilename(), #PB_FileSystem_NoExtension) + ".pref"
Protected X, Y, Width, Height, State
Protected GroupName.s, hMonitor, WinRect.RECT, MonitorRect.RECT, Mi.MONITORINFOEX2
If OpenPreferences(PrefFile, #PB_Preference_GroupSeparator)
If Window < 10000 ; Use the constante Window number in the Group section name to differentiate them in multi-windows mode
GroupName = "Size_Window_" + Str(Window)
Else
GroupName = "Size_Window"
EndIf
If PreferenceGroup(GroupName)
X = ReadPreferenceLong("X", 0)
Y = ReadPreferenceLong("Y", 0)
Width = ReadPreferenceLong("Width", 0)
Height = ReadPreferenceLong("Height", 0)
State = ReadPreferenceLong("State", #PB_Window_Normal)
ClosePreferences()
;Debug "Window pref . . : " +#TAB$+ X + "," + Y + "," + Width + "," + Height
SetWindowPos_(WindowID(Window), #Null, X, Y, Width, Height, #SWP_NOZORDER | #SWP_NOACTIVATE)
If Not (Flags & #MONITOR_NOMOVE)
GetWindowRect_(WindowID(Window), @WinRect)
; Correct width and height are below the minimum size for any reason, X and Y do not change: X = WinRect\left : Y = WinRect\top
Width = WinRect\right - WinRect\left
Height = WinRect\bottom - WinRect\top
hMonitor = MonitorFromRect_(WinRect, #MONITOR_DEFAULTTONEAREST)
If hMonitor
Mi\cbSize = SizeOf(Mi)
If GetMonitorInfo_(hMonitor, @Mi)
If Flags & #MONITOR_WORKAREA
MonitorRect = Mi\rcWork
Else
MonitorRect = Mi\rcMonitor
EndIf
;Debug "Monitor nearest : " +#TAB$+ MonitorRect\left + "," + MonitorRect\top + "," + Str(MonitorRect\right-MonitorRect\left) + "," + Str(MonitorRect\bottom-MonitorRect\top)
; Center or Clip the window rect to the monitor rect
If Flags & #MONITOR_CENTER
X = MonitorRect\left + (MonitorRect\right - MonitorRect\left - Width) / 2
Y = MonitorRect\top + (MonitorRect\bottom - MonitorRect\top - Height) / 2
Else
X = Max(MonitorRect\left, Min(MonitorRect\right - Width, X))
Y = Max(MonitorRect\top, Min(MonitorRect\bottom - Height, Y))
EndIf
SetWindowPos_(WindowID(Window), #Null, X, Y, 0, 0, #SWP_NOSIZE | #SWP_NOZORDER | #SWP_NOACTIVATE)
;Debug "Window Pos . . : " +#TAB$+ X + "," + Y + "," + Width + "," + Height
EndIf
EndIf
EndIf
SetWindowState(Window, State)
EndIf
EndIf
EndProcedure
Procedure SaveWindowSize(Window)
Protected PrefFile.s = GetPathPart(ProgramFilename()) + GetFilePart(ProgramFilename(), #PB_FileSystem_NoExtension) + ".pref"
Protected GroupName.s, lpwndpl.WINDOWPLACEMENT
If OpenPreferences(PrefFile, #PB_Preference_GroupSeparator) = 0
If CreatePreferences(PrefFile, #PB_Preference_GroupSeparator)
ClosePreferences()
EndIf
EndIf
If OpenPreferences(PrefFile, #PB_Preference_GroupSeparator)
If Window < 10000 ; Use the constante Window number in the Group section name to differentiate them in multi-windows mode
GroupName = "Size_Window_" + Str(Window)
Else
GroupName = "Size_Window"
EndIf
If PreferenceGroup(GroupName)
GetWindowPlacement_(WindowID(Window), @lpwndpl)
WritePreferenceLong("X", lpwndpl\rcNormalPosition\left)
WritePreferenceLong("Y", lpwndpl\rcNormalPosition\top)
WritePreferenceLong("Width", lpwndpl\rcNormalPosition\right - lpwndpl\rcNormalPosition\left)
WritePreferenceLong("Height", lpwndpl\rcNormalPosition\bottom - lpwndpl\rcNormalPosition\top)
WritePreferenceLong("State", GetWindowState(Window))
ClosePreferences()
EndIf
EndIf
EndProcedure
EndModule
;
;------------------------------------------------------------------------------
;- Demo
;------------------------------------------------------------------------------
;
CompilerIf (#PB_Compiler_IsMainFile)
UseModule KeepWindowSize
Enumeration Window
#MainWindow
EndEnumeration
Enumeration Gadgets
#Button
EndEnumeration
; Required to receive the #PB_Event_CloseWindow event, when the window is minimized and closed from the taskbar (right-click). Not required, fixed in PB6.10
CompilerIf #PB_Compiler_Version < 610
Procedure Callback_MainWindow(hWnd, uMsg, wParam, lParam)
If uMsg = #WM_CLOSE
PostEvent(#PB_Event_Gadget, GetDlgCtrlID_(hWnd), 0, #PB_Event_CloseWindow)
EndIf
ProcedureReturn #PB_ProcessPureBasicEvents
EndProcedure
CompilerEndIf
Procedure Resize_MainWindow()
Protected MainWindow_WidthIni = 340, MainWindow_HeightIni = 180
Protected ScaleX.f, ScaleY.f
ScaleX = WindowWidth(#MainWindow) / MainWindow_WidthIni : ScaleY = WindowHeight(#MainWindow) / MainWindow_HeightIni
ResizeGadget(#Button, ScaleX * 40, ScaleY * 40, ScaleX * 260, ScaleY * 100)
EndProcedure
Procedure Open_MainWindow(X = 0, Y = 0, Width = 340, Height = 180)
If OpenWindow(#MainWindow, X, Y, Width, Height, "Keep position & Size between Run", #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_MaximizeGadget | #PB_Window_SizeGadget | #PB_Window_ScreenCentered)
ButtonGadget(#Button, 40, 40, 260, 100, "Proportional Button")
CompilerIf #PB_Compiler_Version < 610 : SetWindowCallback(@Callback_MainWindow(), #MainWindow) : CompilerEndIf
BindEvent(#PB_Event_SizeWindow, @Resize_MainWindow(), #MainWindow)
PostEvent(#PB_Event_SizeWindow, #MainWindow, 0)
ProcedureReturn #True
EndIf
EndProcedure
If Open_MainWindow()
InitWindowSize(#MainWindow) ; Optional Flag parameter: #MONITOR_CENTER | #MONITOR_CLIP | #MONITOR_WORKAREA | #MONITOR_AREA | #MONITOR_NOMOVE. Default value = #MONITOR_CLIP | #MONITOR_WORKAREA
Repeat : Until WaitWindowEvent() = #PB_Event_CloseWindow
SaveWindowSize(#MainWindow)
EndIf
CompilerEndIf