[MODULE] WinHandler - Window/Gadget Resize Manager / Program Data Manager

Share your advanced PureBasic knowledge/code with the community.
User avatar
Kurzer
Enthusiast
Enthusiast
Posts: 670
Joined: Sun Jun 11, 2006 12:07 am
Location: Near Hamburg

[MODULE] WinHandler - Window/Gadget Resize Manager / Program Data Manager

Post by Kurzer »

The WinHandler.pbi module does some routine work for you when creating window applications. It takes care of the automatic alignment of gadgets when resizing windows and offers functions to save the geometries of all program windows as well as own user data in an INI file and to load them again.

For these things you don't have to write code in your own program anymore.

For whom is WinHandler intended?
All those who don't get along with the PureBasic dialog library (or don't want to use it) and prefer to create window interfaces in the "usual" way.

Automatic alignment:
PureBasic gadgets can be anchored to one or more sides of the window with so-called "anchors" during creation. When the window is resized, the gadgets are automatically repositioned or resized according to their anchor points.

Saving of window geometry and variables:
Furthermore, the module offers functions to save and reload the position and size of all windows of an application in an INI file. Furthermore, any amount of data can be stored at windows managed by the WinHandler for management (currently only strings). These data are stored also automatically in the INI file.

Herewith the management (save/load) of all settings of an application can be realized by the WinHandler.

A program, which uses the WinHandler, should have the following structure:

Code: Select all

	; Register all windows used by the program by name at WinHandler.
	; The windows are not yet open at that time
	wh::whAddWindow("main")
	wh::whAddWindow("settings")
	
	; Load the data from the INI file
	wh::whLoadWindowData("C:\Users\...\MyProgramm.ini)	
	
	; Then the main window is opened (see also below)
	If Main_OpenWindow()
		Repeat
			WaitWindowEvent()
		Until iQuit = #True
	EndIf
	
	; Finally, all windows must be removed from the Winhandler 
	wh::whRemoveWindow("settings")
	wh::whRemoveWindow("main")
	
	
	In the procedure that opens the window(s) of the program (here "Main_OpenWindow()") the minimal code looks like this:
	
	UseModule WH
	; Open the previously registered window via the WinHandler
	whOpenWindow("main", #PB_Ignore, #PB_Ignore, 400, 220, "Windowtitel", #PB_Window_SystemMenu|#PB_Window_TitleBar|#PB_Window_SizeGadget|#PB_Window_Invisible)
	
	; Add gadgets via the WinHandler
	whAddGadget("main", "frame",         FrameGadget(#PB_Any, 5, 5, 390, 165, "Frame"),                    #Anchor_Left | #Anchor_Top | #Anchor_Right | #Anchor_Bottom)
	whAddGadget("main", "button ok",     ButtonGadget(#PB_Any, 5, 180, 90, 35, "Ok", #PB_Button_Default),  #Anchor_Left | #Anchor_Bottom, @Main_Event_CloseWindow(), #PB_EventType_LeftClick)
	whAddGadget("main", "button cancel", ButtonGadget(#PB_Any, 305, 180, 90, 35, "Cancel"),                #Anchor_Right | #Anchor_Bottom, @Main_Event_CloseWindow(), #PB_EventType_LeftClick)
			
	BindEvent(#PB_Event_CloseWindow, @Main_Event_CloseWindow(), whGetWindowNumber("main"))
			
	HideWindow(whGetWindowNumber("main"), #False)			


	The following command is used to close the window:
	
	whCloseWindow("main")
As you can see, windows and gadgets in WinHandler are not addressed by an object number (as usual in PB), but by a unique name. The reason for this is the use of a map for internal administration and the (in my opinion) simpler use. In addition it saves variables into which one would have to store the object numbers - e.g. when using #PB_Any.

The module has a demo, which is executed if you just start the module with F5.

The module provides the following commands:

Code: Select all

whAddWindow(sWindowName.s, iSaveGeometry.i=#True, iSaveUserData.i=#True)
		; +-----------------------------------------------------------------
		; |Description  : Registers a new window with its name in the WinHandler and sets the flags iSaveGeometry and iSaveUserData
		; |Arguments    : sWindowName  : Name under which the window is to be addressed
		; |             : iSaveGeometry: Defines whether this window should be taken into account when saving the window geometries to the INI file
		; |             : iSaveUserData: Determines whether the attached user data of this window should be taken into account when saving to the INI file
		; |Results      : #True/#False
		; |Remarks      : If whAddWindow() is not called by the programmer, then this is automatically made up for by whOpenWindow()
		; +-----------------------------------------------------------------

whRemoveWindow(sWindowName.s)
		; +-----------------------------------------------------------------
		; |Description  : Removes a window previously registered by whAddWindow() from WinHandler
		; |Arguments    : sWindowName: Name under which the window was registered
		; |Results      : #True/#False
		; |Remarks      : -
		; +-----------------------------------------------------------------

whOpenWindow(sWindowName.s, ix.i=#PB_Ignore, iy.i=#PB_Ignore, iWidth.i=600, iHeight.i=800, sTitel.s="", iFlags.i=-2, iParentWindow=-2)
		; +-----------------------------------------------------------------
		; |Description  : Opens a PureBasic window (same parameter sequence, except window name)
		; |             : and registers the events #PB_Event_SizeWindow and #PB_Event_MoveWindow in the WinHandler
		; |Arguments    : sWindowName: Name under which the window was registered
		; |             : For all other parameters see OpenWindow() in the PureBasic help file
		; |Results      : Returns the PB object number of the opened window or 0 in case of errors
		; |Remarks      : -
		; +-----------------------------------------------------------------

whCloseWindow(sWindowName.s)
		; +-----------------------------------------------------------------
		; |Description  : Closes a window managed by WinHandler
		; |Arguments    : sWindowName: Name under which the window was registered
		; |Results      : #True/#False
		; |Remarks      : -
		; +-----------------------------------------------------------------

whGetWindowNumber(sWindowName.s)
		; +-----------------------------------------------------------------
		; |Description  : Returns the PB object number of a window registered in WinHandler
		; |Arguments    : sWindowName: Name under which the window was registered
		; |Results      : Returns the PB object number of the window or 0 in case of errors
		; |Remarks      : -
		; +-----------------------------------------------------------------

whSetWindowData(sWindowName.s, sDataKey.s, sData.s)
		; +-----------------------------------------------------------------
		; |Description  : Stores a user-defined string at the window
		; |Arguments    : sWindowName: Name of the window where the data string is to be stored
		; |             : sDataKey   : Name under which the data string is to be identified
		; |             : sData      : The data string
		; |Results      : #True/#False
		; |Remarks      : -
		; +-----------------------------------------------------------------

whGetWindowData(sWindowName.s, sDataKey.s)
		; +-----------------------------------------------------------------
		; |Description  : Returns the value of a user-defined string previously stored at a window
		; |Arguments    : sWindowName: Name of the window with which the data string is associated
		; |             : sDataKey   : Name of the data string
		; |Results      : The user-defined string or an empty string, in case of errors
		; |Remarks      : -
		; +-----------------------------------------------------------------

whRemoveWindowData(sWindowName.s, sDataKey.s="")
		; +-----------------------------------------------------------------
		; |Description  : Removes a user-defined string previously stored at a window
		; |Arguments    : sWindowName: Name of the window with which the data string is associated
		; |             : sDataKey   : Name of the data string
		; |Results      : #True/#False
		; |Remarks      : If sDataKey="", then all data strings associated with this window will be removed
		; +-----------------------------------------------------------------

whSaveWindowData(sFileName.s, iIncludeUserData=#True, iDeleteOldFile=#False)
		; +-----------------------------------------------------------------
		; |Description  : Saves the geometry and the data strings of all windows to an INI file
		; |Arguments    : sFileName     : Path and filename of the INI file
		; |             : iIncludeUserData: Determines whether the data strings should also be saved
		; |             :                   This overrides the iSaveGeometry option on whAddWindow(), even if iSaveGeometry = #true
		; |             :                   for some windows. If iIncludeUserData = #False, then no data strings are saved at all,
		; |             :                   only the window geometries are saved
		; |             : iDeleteOldFile: If #True, then the file will be completely rebuilt instead of updating it
		; |             :                 (thus removing old data strings as well. Useful if they are no longer used in the program)
		; |Results      : #True/#False
		; |Remarks      : -
		; +-----------------------------------------------------------------

whLoadWindowData(sFileName.s, iIncludeUserData=#True)
		; +-----------------------------------------------------------------
		; |Description  : Loads the geometry and data strings of all windows from an INI file and updates the window structures in WinHandler
		; |Arguments    : sFileName       : Path and file name of the INI file
		; |             : iIncludeUserData: Determines whether the data strings should also be read
		; |Results      : #True/#False
		; |Remarks      : -
		; +-----------------------------------------------------------------

whAddGadget(sWindowName.s, sGadgetName.s, iGadgetNumber.i, iAnchor.i=#Anchor_Left, *Eventprocedure=0, iEventType.i=#PB_All)
		; +-----------------------------------------------------------------
		; |Description  : Adds a (previously created) PureBasic gadget to a window registered in WinHandler
		; |Arguments    : sWindowName    : Name under which the window was registered
		; |             : sGadgetName    : Name under which the gadget is to be addressed
		; |             : iGadgetNumber  : Object number of the PureBasic gadget
		; |             : iAnchor        : With bitwise OR (|) linked values of anchor constants for this gadget (see gadget anchor constants)
		; |             : *Eventprocedure: Address of a (standard) event procedure to be linked to the gadget.
		; |             : iEventType     : EventType for the specified event procedure
		; |Results      : Returns the PB object number of the added gadget or 0 in case of errors
		; |Remarks      : Besides the possibility to assign a (standard) event procedure to the gadget "on the fly" with this command, 
		; |               you can link further events to the gadget via the normal PureBasic commands. But then you have to release
		; |               them yourself!
		; +-----------------------------------------------------------------

whFreeGadget(sWindowName.s, sGadgetName.s)
		; +-----------------------------------------------------------------
		; |Description  : Frees a gadget previously registered with whAddGadget() and removes it from the window
		; |Arguments    : sWindowName: Name under which the window was registered
		; |             : sGadgetName: Name under which the gadget was registered
		; |Results      : -
		; |Remarks      : This function also detaches a (standard) event from the gadget that may have been previously assigned by whAddGadget()
		; +-----------------------------------------------------------------

whGetGadgetNumber(sWindowName.s, sGadgetName.s)
		; +-----------------------------------------------------------------
		; |Description  : Returns the PB object number of a gadget registered in WinHandler
		; |Arguments    : sWindowName: Name under which the window was registered
		; |             : sGadgetName: Name under which the gadget was registered
		; |Results      : Returns the PB object number of the gadget or 0 in case of errors
		; |Remarks      : -
		; +-----------------------------------------------------------------
Here the module WinHandler:

Code: Select all

;*************************************************************************
;* WinHandler (c) Kurzer - Markus Haacke
;*************************************************************************
;*
;* Modulname         : WinHandler
;* Filename          : mod_WinHandler.pbi
;* Filetype          : Module [MainApp, Formular, Include, Module, Data]
;* Programming lang. : Purebasic 5.60+
;* String-Format     : Unicode [Ascii, Unicode, All]
;* Platform          : Windows [Windows, Mac, Linux, All]
;* Processor         : All [x86, x64, All]
;* Compileroptions   : -
;* Version           : 1.00
;* Date              : 30.08.2023
;* Autor             : Markus Haacke
;* Dependencies      : -
;* -----------------------------------------------------------------------
;* Description (english):
;* 
;* The WinHandler module provides functions to automate the alignment of
;* gadgets within window applications and to save and reload program variables
;* and window geometries in/from an INI file.
;* 
;* If you don't get along with the PureBasic dialog library and prefer to
;* create window interfaces in the "usual" way, WinHandler takes a bit of
;* work off your hands.
;* 
;* Automatic alignment:
;* PureBasic gadgets can be anchored to one or more sides of the window with
;* so-called "anchors" when they are created. When the window is resized, the
;* gadgets are automatically repositioned or resized according to their anchor
;* points.
;* 
;* Saving window geometry and variables:
;* Furthermore, the module offers functions to save the position and size of
;* all windows of an application in an INI file and to reload them next start.
;* In addition, windows managed by the WinHandler can have any amount of data
;* stored for management (currently only strings). These data are also auto-
;* matically stored in the INI file.
;* 
;* By these functions the management (save/load) of all settings of an
;* application can be realized by the WinHandler.
;* 
;* Description (deutsch):
;* 
;* Das Modul WinHandler stellt Funktionen bereit, um die Ausrichtung von
;* Gadgets innerhalb von Fensteranwendungen zu automatisieren sowie
;* Programmvariablen und Fenstergeometrien in einer INI Datei zu speichern
;* und wieder zu laden.
;* 
;* Wer mit der PureBasic Dialog-Library nicht zurecht kommt und Fenster-
;* oberflächen lieber auf "gewohnte" Weise erstellt, dem nimmt WinHandler
;* ein bisschen Arbeit ab.
;* 
;* Automatische Ausrichtung:
;* PureBasic Gadgets können bei der Erstellung mit sogenannten "Ankern" an
;* einer oder mehreren Seiten des Fensters verankert werden. Bei einer Größen-
;* änderung des Fensters werden die Gadgets automatisch entsprechend ihrer
;* Ankerpunkte neu positioniert bzw. in der Größe angepasst.
;* 
;* Speichern von Fenstergeometrie und Variablen:
;* Weiterhin bietet das Modul Funktionen an, um die Position und Größe aller
;* Fenster einer Anwendung in einer INI Datei zu speichern und wieder zu laden.
;* Darüber hinaus können an Fenstern, die durch den WinHandler gemanagt werden,
;* beliebig viele Daten zur Verwaltung hinterlegt werden (derzeit nur Strings).
;* Diese Daten werden ebenfalls automatisch in der INI-Datei gespeichert.
;* 
;* Durch diese Funktionen kann die Verwaltung (Speichern/Laden) sämtlicher
;* Einstellungen einer Anwendung durch den WinHandler realisiert werden.
;*
;* -----------------------------------------------------------------------
;* License: MIT License
;* 
;* Copyright (c) 2023 Kurzer - Markus Haacke
;* 
;* Permission is hereby granted, free of charge, to any person obtaining a copy
;* of this software and associated documentation files (the "Software"), to deal
;* in the Software without restriction, including without limitation the rights
;* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
;* copies of the Software, and to permit persons to whom the Software is
;* furnished to do so, subject to the following conditions:
;*
;* The above copyright notice and this permission notice shall be included in all
;* copies or substantial portions of the Software.
;* 
;* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
;* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
;* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
;* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
;* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
;* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
;* SOFTWARE.
;* 
;* ---------------- German translation of the MIT License ----------------
;*
;* MIT Lizenz:
;*
;* Hiermit wird unentgeltlich jeder Person, die eine Kopie der Software und der
;* zugehörigen Dokumentationen (die "Software") erhält, die Erlaubnis erteilt,
;* sie uneingeschränkt zu nutzen, inklusive und ohne Ausnahme mit dem Recht, sie
;* zu verwenden, zu kopieren, zu verändern, zusammenzufügen, zu veröffentlichen,
;* zu verbreiten, zu unterlizenzieren und/oder zu verkaufen, und Personen, denen
;* diese Software überlassen wird, diese Rechte zu verschaffen, unter den folgenden
;* Bedingungen:
;* 
;* Der obige Urheberrechtsvermerk und dieser Erlaubnisvermerk sind in allen Kopien
;* oder Teilkopien der Software beizulegen.
;* 
;* DIE SOFTWARE WIRD OHNE JEDE AUSDRÜCKLICHE ODER IMPLIZIERTE GARANTIE BEREITGESTELLT,
;* EINSCHLIEßLICH DER GARANTIE ZUR BENUTZUNG FÜR DEN VORGESEHENEN ODER EINEM BESTIMMTEN
;* ZWECK SOWIE JEGLICHER RECHTSVERLETZUNG, JEDOCH NICHT DARAUF BESCHRÄNKT. IN KEINEM
;* FALL SIND DIE AUTOREN ODER COPYRIGHTINHABER FÜR JEGLICHEN SCHADEN ODER SONSTIGE
;* ANSPRÜCHE HAFTBAR ZU MACHEN, OB INFOLGE DER ERFÜLLUNG EINES VERTRAGES, EINES DELIKTES
;* ODER ANDERS IM ZUSAMMENHANG MIT DER SOFTWARE ODER SONSTIGER VERWENDUNG DER SOFTWARE
;* ENTSTANDEN.
;* -----------------------------------------------------------------------
;* Changelog:
;* 1.00 - rel 30.08.2023:
;*            First release
;*************************************************************************

;- --- [WinHandler Modul] -----------------------------------------------------
;-

DeclareModule WH
	;- --- [Module declaration / public elements] ------------------------------------------
	;-
	
	;*************************************************************************
	;- Macros
	;*************************************************************************
	
	;*************************************************************************
	;- Constants
	;*************************************************************************
	
	; Gadget-Anchor-Modes
	#Anchor_Left      = %000001
	#Anchor_Top       = %000010
	#Anchor_Right     = %000100
	#Anchor_Bottom    = %001000
	#Anchor_CenterHor = %010000
	#Anchor_CenterVer = %100000
	
	; Gadget-Spacing-Constants
	Enumeration 
		#Spacing_Left
		#Spacing_Top
		#Spacing_Right
		#Spacing_Bottom
	EndEnumeration
	
	;*************************************************************************
	;- Structures
	;*************************************************************************
	
	Structure Gadgets																; Mapkey is the name of the gadget, e.g. "OkButton" or "OpenFile"
		iNumber.i																			; Gadegtnumber
		iAnchor.i																			; Anchor points
		iSpacing.i[4]																	; Initial distances of the gadgets from the window border (left, top, right, bottom)
		*Eventprocedure
		iEventType.i
	EndStructure
	
	Structure Windows																; Mapkey is the name of the window, e.g. "Main" or "Settings"
		iNumber.i																			; The PureBasic window number
		iState.i																			; Window state (see #WinState_XYZ constants)
		iSaveGeometry.i																; Indicates if the window dimensions are saved in the prefsfile (#True/#False)
		iSaveUserData.i																; Specifies whether the window user data will be saved in the prefsfile (#True/#False)
		iPosX.i																				; Window dimensions at runtime
		iPosY.i
		iWidth.i
		iHeight.i
		iOrgWidth.i																		; Window dimensions at design time (when first opened)
		iOrgHeight.i																	; Needed to perform resizing of gadgets correctly
		Map mpGadgets.Gadgets()
		Map mpUserData.s()														; Contains the user data stored at the window and saved to the prefs file
	EndStructure
	
	;*************************************************************************
	;- Variables, Arrays, Linked Lists, Maps
	;*************************************************************************
	
	Global NewMap mpWindows.Windows(30)							; Management list for windows and associated gadgets
	
	;*************************************************************************
	;- Interfaces, Prototypes
	;*************************************************************************
	
	;*************************************************************************
	;- Public procedures (dec)
	;*************************************************************************
	Declare.i whAddWindow(sWindowName.s, iSaveGeometry.i=#True, iSaveUserData.i=#True)
	Declare.i whRemoveWindow(sWindowName.s)
	Declare.i whOpenWindow(sWindowName.s, ix.i=#PB_Ignore, iy.i=#PB_Ignore, iWidth.i=600, iHeight.i=800, sTitel.s="", iFlags.i=-2, iParentWindow=-2)
	Declare.i whCloseWindow(sWindowName.s)
	Declare.i whGetWindowNumber(sWindowName.s)
	Declare.i whSetWindowData(sWindowName.s, sDataKey.s, sData.s)
	Declare.s whGetWindowData(sWindowName.s, sDataKey.s)
	Declare.i whRemoveWindowData(sWindowName.s, sDataKey.s="")
	Declare.i whSaveWindowData(sFileName.s, iIncludeUserData=#True, iDeleteOldFile=#False)
	Declare.i whLoadWindowData(sFileName.s, iIncludeUserData=#True)
	Declare.i whAddGadget(sWindowName.s, sGadgetName.s, iGadgetNumber.i, iAnchor.i=#Anchor_Left, *Eventprocedure=0, iEventType.i=#PB_All)
	Declare.i whFreeGadget(sWindowName.s, sGadgetName.s)
	Declare.i whGetGadgetNumber(sWindowName.s, sGadgetName.s)
	
EndDeclareModule

Module WH
	;-
	;- --- [Module implementation / private elements] -----------------------------------------
	;-
	
	;*************************************************************************
	;- Private procedures (imp)
	;*************************************************************************
	Procedure   whResizeGadget()
		; +-----------------------------------------------------------------
		; |Description  : Recalculation of gadget sizes and positions
		; |Arguments    : -
		; |Results      : -
		; |Remarks      : -
		; +-----------------------------------------------------------------
		With mpWindows()
			If \mpGadgets()\iAnchor & (#Anchor_Left | #Anchor_Right) = #Anchor_Left | #Anchor_Right
				ResizeGadget(\mpGadgets()\iNumber, #PB_Ignore, #PB_Ignore, \iWidth - \mpGadgets()\iSpacing[#Spacing_Left] - \mpGadgets()\iSpacing[#Spacing_Right], #PB_Ignore)
			ElseIf \mpGadgets()\iAnchor & #Anchor_Right
				ResizeGadget(\mpGadgets()\iNumber, \iWidth - \mpGadgets()\iSpacing[#Spacing_Right] - GadgetWidth(\mpGadgets()\iNumber), #PB_Ignore, #PB_Ignore, #PB_Ignore)
			EndIf
			
			If \mpGadgets()\iAnchor & (#Anchor_Top | #Anchor_Bottom) = #Anchor_Top | #Anchor_Bottom
				ResizeGadget(\mpGadgets()\iNumber, #PB_Ignore, #PB_Ignore, #PB_Ignore, \iHeight - \mpGadgets()\iSpacing[#Spacing_Top] - \mpGadgets()\iSpacing[#Spacing_Bottom])
			ElseIf \mpGadgets()\iAnchor & #Anchor_Bottom
				ResizeGadget(\mpGadgets()\iNumber, #PB_Ignore, \iHeight - \mpGadgets()\iSpacing[#Spacing_Bottom] - GadgetHeight(\mpGadgets()\iNumber), #PB_Ignore, #PB_Ignore)
			EndIf
			
			If \mpGadgets()\iAnchor & #Anchor_CenterHor = #Anchor_CenterHor
				ResizeGadget(\mpGadgets()\iNumber, (\iWidth - GadgetWidth(\mpGadgets()\iNumber)) / 2, #PB_Ignore, #PB_Ignore, #PB_Ignore)
			EndIf
			
			If \mpGadgets()\iAnchor & #Anchor_CenterVer = #Anchor_CenterVer
				ResizeGadget(\mpGadgets()\iNumber, #PB_Ignore, (\iHeight - GadgetHeight(\mpGadgets()\iNumber)) / 2, #PB_Ignore, #PB_Ignore)
			EndIf
		EndWith
		
	EndProcedure
	Procedure   whWinHandler()
		; +-----------------------------------------------------------------
		; |Description  : Processing of window events #PB_Event_SizeWindow and #PB_Event_MoveWindow
		; |Arguments    : -
		; |Results      : -
		; |Remarks      : -
		; +-----------------------------------------------------------------
		Protected.i iWindow, iEvent
		Static iLastWindow.i, sLastWindow.s
		
		With mpWindows()
			; Determine the relevant window from the map
			; If it is the same window as in the last event, the map does not need to be searched again.
			iWindow = EventWindow()
			If iLastWindow = iWindow
				FindMapElement(mpWindows(), sLastWindow)
			Else
				ResetMap(mpWindows())
				While NextMapElement(mpWindows())
					If \iNumber = iWindow
						iLastWindow = iWindow
						sLastWindow = MapKey(mpWindows())
						Break
					EndIf
				Wend
			EndIf
			
			If \iNumber = iWindow
				iEvent = Event()
				If iEvent = #PB_Event_SizeWindow
					; Store new window position and size
					\iPosX = WindowX(\iNumber)
					\iPosY = WindowY(\iNumber)
					\iWidth = WindowWidth(\iNumber)
					\iHeight = WindowHeight(\iNumber)
					
					; Calculate new gadget positions and sizes according to #Gadget_Anchor constants
					ResetMap(\mpGadgets())
					While NextMapElement(\mpGadgets())
						If \mpGadgets()\iNumber > 0
							whResizeGadget()
						EndIf
					Wend
				ElseIf 	iEvent = #PB_Event_MoveWindow
					; Store new window position
					\iPosX = WindowX(\iNumber)
					\iPosY = WindowY(\iNumber)				
				EndIf
			EndIf 
		EndWith
		
	EndProcedure	
	
	;*************************************************************************
	;- Public procedures (imp)
	;*************************************************************************
	Procedure.i whAddWindow(sWindowName.s, iSaveGeometry.i=#True, iSaveUserData.i=#True)
		; +-----------------------------------------------------------------
		; |Description  : Registers a new window with its name in the WinHandler and sets the flags iSaveGeometry and iSaveUserData
		; |Arguments    : sWindowName  : Name under which the window is to be addressed
		; |             : iSaveGeometry: Defines whether this window should be taken into account when saving the window geometries to the INI file
		; |             : iSaveUserData: Determines whether the attached user data of this window should be taken into account when saving to the INI file
		; |Results      : #True/#False
		; |Remarks      : If whAddWindow() is not called by the programmer, then this is automatically made up for by whOpenWindow()
		; +-----------------------------------------------------------------
		If sWindowName = ""
			ProcedureReturn #False
		EndIf
		
		sWindowName = LCase(sWindowName)
		
		mpWindows(sWindowName)\iSaveGeometry = iSaveGeometry
		mpWindows(sWindowName)\iSaveUserData = iSaveUserData
		
		ProcedureReturn #True
		
	EndProcedure
	Procedure.i whRemoveWindow(sWindowName.s)
		; +-----------------------------------------------------------------
		; |Description  : Removes a window previously registered by whAddWindow() from WinHandler
		; |Arguments    : sWindowName: Name under which the window was registered
		; |Results      : #True/#False
		; |Remarks      : -
		; +-----------------------------------------------------------------
		If sWindowName = ""
			ProcedureReturn #False
		EndIf
		
		sWindowName = LCase(sWindowName)
		
		; Does the entry exist at all?
		If FindMapElement(mpWindows(), sWindowName) = 0
			ProcedureReturn #False
		EndIf
		
		; Is the associated window still open? Then we close it.
		If IsWindow(mpWindows(sWindowName)\iNumber) <> 0
			whCloseWindow(sWindowName)
		EndIf
		
		
		DeleteMapElement(mpWindows(), sWindowName)	
		ProcedureReturn #True
		
	EndProcedure
	;-
	Procedure.i whOpenWindow(sWindowName.s, ix.i=#PB_Ignore, iy.i=#PB_Ignore, iWidth.i=600, iHeight.i=800, sTitel.s="", iFlags.i=-2, iParentWindow=-2)
		; +-----------------------------------------------------------------
		; |Description  : Opens a PureBasic window (same parameter sequence, except window name)
		; |             : and registers the events #PB_Event_SizeWindow and #PB_Event_MoveWindow in the WinHandler
		; |Arguments    : sWindowName: Name under which the window was registered
		; |             : For all other parameters see OpenWindow() in the PureBasic help file
		; |Results      : Returns the PB object number of the opened window or 0 in case of errors
		; |Remarks      : -
		; +-----------------------------------------------------------------
		If sWindowName = ""
			ProcedureReturn 0
		EndIf
		
		sWindowName = LCase(sWindowName)
		
		; If the window entry does not exist yet (whAddWindow() has not been called yet), then we will make up for it.
		If FindMapElement(mpWindows(), sWindowName) = 0
			If whAddWindow(sWindowName) = #False
				ProcedureReturn 0
			EndIf
		EndIf
		
		If FindMapElement(mpWindows(), sWindowName) <> 0
			If IsWindow(mpWindows()\iNumber) = 0
				If mpWindows()\iWidth = 0 Or mpWindows()\iHeight = 0 Or mpWindows()\iOrgWidth = 0 Or mpWindows()\iOrgHeight = 0
					mpWindows()\iPosX = ix
					mpWindows()\iPosY = iy
					mpWindows()\iWidth = iWidth
					mpWindows()\iHeight = iHeight
					mpWindows()\iOrgWidth = iWidth
					mpWindows()\iOrgHeight = iHeight
				EndIf
				
				If iFlags = -2 And iParentWindow = -2
					mpWindows()\iNumber = OpenWindow(#PB_Any, mpWindows()\iPosX, mpWindows()\iPosY, mpWindows()\iWidth, mpWindows()\iHeight, sTitel)
				ElseIf iFlags <> -2 And iParentWindow = -2
					mpWindows()\iNumber = OpenWindow(#PB_Any, mpWindows()\iPosX, mpWindows()\iPosY, mpWindows()\iWidth, mpWindows()\iHeight, sTitel , iFlags)
				ElseIf iFlags <> -2 And iParentWindow <> -2
					mpWindows()\iNumber = OpenWindow(#PB_Any, mpWindows()\iPosX, mpWindows()\iPosY, mpWindows()\iWidth, mpWindows()\iHeight, sTitel, iFlags , iParentWindow)
				EndIf
				If mpWindows()\iNumber <> 0
					; Connecting window events with the
					BindEvent(#PB_Event_SizeWindow, @whWinHandler(), mpWindows()\iNumber)
					BindEvent(#PB_Event_MoveWindow, @whWinHandler(), mpWindows()\iNumber)
					ProcedureReturn mpWindows()\iNumber
				EndIf
				; If the window is currently open (whOpenWindow() was called multiple times), only the window number is returned.
			ElseIf IsWindow(mpWindows()\iNumber) <> 0
				ProcedureReturn mpWindows()\iNumber
			Else
				ProcedureReturn 0
			EndIf
		Else
			ProcedureReturn 0
		EndIf
		
	EndProcedure
	Procedure.i whCloseWindow(sWindowName.s)
		; +-----------------------------------------------------------------
		; |Description  : Closes a window managed by WinHandler
		; |Arguments    : sWindowName: Name under which the window was registered
		; |Results      : #True/#False
		; |Remarks      : -
		; +-----------------------------------------------------------------
		If sWindowName = ""
			ProcedureReturn #False
		EndIf
		
		sWindowName = LCase(sWindowName)
		
		; If the window entry does not exist (whAddWindow() or whOpenWindow() have not been called yet).
		If FindMapElement(mpWindows(), sWindowName) = 0
			ProcedureReturn #True
		Else
			With mpWindows(sWindowName)
				; When the window is actually open
				If IsWindow(\iNumber) <> 0
					; Detach window events from WinHandler
					UnbindEvent(#PB_Event_MoveWindow, @whWinHandler(), \iNumber)
					UnbindEvent(#PB_Event_SizeWindow, @whWinHandler(), \iNumber)
					; Release all gadgets of the window
					ResetMap(\mpGadgets())
					While NextMapElement(\mpGadgets()) <> 0
						whFreeGadget(sWindowName, MapKey(\mpGadgets()))
						ResetMap(\mpGadgets())
					Wend
					CloseWindow(mpWindows()\iNumber)
				EndIf
				ClearMap(\mpGadgets())
			EndWith
			
			ProcedureReturn #True
		EndIf
		
	EndProcedure
	Procedure.i whGetWindowNumber(sWindowName.s)
		; +-----------------------------------------------------------------
		; |Description  : Returns the PB object number of a window registered in WinHandler
		; |Arguments    : sWindowName: Name under which the window was registered
		; |Results      : Returns the PB object number of the window or 0 in case of errors
		; |Remarks      : -
		; +-----------------------------------------------------------------
		If sWindowName = ""
			ProcedureReturn 0
		EndIf
		
		sWindowName = LCase(sWindowName)
		
		; If the window entry does not exist (whAddWindow() or whOpenWindow() have not been called yet)
		If FindMapElement(mpWindows(), sWindowName) = 0
			ProcedureReturn 0
		EndIf
		
		; If the window is closed
		If IsWindow(mpWindows()\iNumber) = 0
			ProcedureReturn 0
		Else
			ProcedureReturn mpWindows()\iNumber
		EndIf
		
	EndProcedure
	;-
	Procedure.i whSetWindowData(sWindowName.s, sDataKey.s, sData.s)
		; +-----------------------------------------------------------------
		; |Description  : Stores a user-defined string at the window
		; |Arguments    : sWindowName: Name of the window where the data string is to be stored
		; |             : sDataKey   : Name under which the data string is to be identified
		; |             : sData      : The data string
		; |Results      : #True/#False
		; |Remarks      : -
		; +-----------------------------------------------------------------
		If sWindowName = "" Or sDataKey = ""
			ProcedureReturn #False
		EndIf
		
		sWindowName = LCase(sWindowName)
		sDataKey = LCase(sDataKey)
		
		; If the window entry does not exist (whAddWindow() or whOpenWindow() have not been called yet)
		If FindMapElement(mpWindows(), sWindowName) = 0
			ProcedureReturn #False
		EndIf	
		
		mpWindows(sWindowName)\mpUserData(sDataKey) = sData
		
		ProcedureReturn #True
		
	EndProcedure
	Procedure.s whGetWindowData(sWindowName.s, sDataKey.s)
		; +-----------------------------------------------------------------
		; |Description  : Returns the value of a user-defined string previously stored at a window
		; |Arguments    : sWindowName: Name of the window with which the data string is associated
		; |             : sDataKey   : Name of the data string
		; |Results      : The user-defined string or an empty string, in case of errors
		; |Remarks      : -
		; +-----------------------------------------------------------------
		If sWindowName = "" Or sDataKey = ""
			ProcedureReturn ""
		EndIf
		
		sWindowName = LCase(sWindowName)
		sDataKey = LCase(sDataKey)
		
		; If the window entry does not exist (whAddWindow() or whOpenWindow() have not been called yet)
		If FindMapElement(mpWindows(), sWindowName) = 0
			ProcedureReturn ""
		EndIf	
		
		ProcedureReturn mpWindows(sWindowName)\mpUserData(sDataKey)
		
	EndProcedure
	Procedure.i whRemoveWindowData(sWindowName.s, sDataKey.s="")
		; +-----------------------------------------------------------------
		; |Description  : Removes a user-defined string previously stored at a window
		; |Arguments    : sWindowName: Name of the window with which the data string is associated
		; |             : sDataKey   : Name of the data string
		; |Results      : #True/#False
		; |Remarks      : If sDataKey="", then all data strings associated with this window will be removed
		; +-----------------------------------------------------------------
		If sWindowName = ""
			ProcedureReturn #False
		EndIf
		
		sWindowName = LCase(sWindowName)
		sDataKey = LCase(sDataKey)
		
		; If the window entry does not exist (whAddWindow() or whOpenWindow() have not been called yet)
		If FindMapElement(mpWindows(), sWindowName) = 0
			ProcedureReturn #False
		EndIf	
		
		If sDataKey = ""
			ClearMap(mpWindows(sWindowName)\mpUserData())
		Else
			DeleteMapElement(mpWindows(sWindowName)\mpUserData(), sDataKey)
		EndIf
		
		ProcedureReturn #True
		
	EndProcedure
	Procedure.i whSaveWindowData(sFileName.s, iIncludeUserData=#True, iDeleteOldFile=#False)
		; +-----------------------------------------------------------------
		; |Description  : Saves the geometry and the data strings of all windows to an INI file
		; |Arguments    : sFileName     : Path and filename of the INI file
		; |             : iIncludeUserData: Determines whether the data strings should also be saved
		; |             :                   This overrides the iSaveGeometry option on whAddWindow(), even if iSaveGeometry = #true
		; |             :                   for some windows. If iIncludeUserData = #False, then no data strings are saved at all,
		; |             :                   only the window geometries are saved
		; |             : iDeleteOldFile: If #True, then the file will be completely rebuilt instead of updating it
		; |             :                 (thus removing old data strings as well. Useful if they are no longer used in the program)
		; |Results      : #True/#False
		; |Remarks      : -
		; +-----------------------------------------------------------------
		
		; If the prefs file does not exist, we create it
		If OpenPreferences(sFileName) = 0 Or iDeleteOldFile = #True
			CreatePreferences(sFileName)
		EndIf
		
		If OpenPreferences(sFileName, #PB_Preference_GroupSeparator) = 0
			ProcedureReturn #False
		EndIf
		
		With mpWindows()
			ResetMap(mpWindows())
			While NextMapElement(mpWindows())
				If \iSaveGeometry = #True Or \iSaveUserData = #True
					PreferenceGroup("Window_" + MapKey(mpWindows()))
				EndIf
				
				If \iSaveGeometry = #True
					; Fenstergeometrie speichern
					WritePreferenceInteger("PosX", \iPosX)
					WritePreferenceInteger("PosY", \iPosY)
					WritePreferenceInteger("Width", \iWidth)
					WritePreferenceInteger("Height", \iHeight)
					WritePreferenceInteger("OrgWidth", \iOrgWidth)
					WritePreferenceInteger("OrgHeight", \iOrgHeight)
				EndIf
				
				If \iSaveUserData = #True And iIncludeUserData = #True
					; UserDaten speichern
					ResetMap(\mpUserData())
					While NextMapElement(\mpUserData())
						WritePreferenceString(MapKey(\mpUserData()), \mpUserData())
					Wend
				EndIf
			Wend
		EndWith
		
		ClosePreferences()
		ProcedureReturn #True
		
	EndProcedure
	Procedure.i whLoadWindowData(sFileName.s, iIncludeUserData=#True)
		; +-----------------------------------------------------------------
		; |Description  : Loads the geometry and data strings of all windows from an INI file and updates the window structures in WinHandler
		; |Arguments    : sFileName       : Path and file name of the INI file
		; |             : iIncludeUserData: Determines whether the data strings should also be read
		; |Results      : #True/#False
		; |Remarks      : -
		; +-----------------------------------------------------------------
		
		If OpenPreferences(sFileName) = 0
			ProcedureReturn #False
		EndIf
		
		With mpWindows()
			ResetMap(mpWindows())
			While NextMapElement(mpWindows())
				If PreferenceGroup("Window_" + MapKey(mpWindows())) <> 0
					ExaminePreferenceKeys()
					While  NextPreferenceKey()
						Select PreferenceKeyName()
						Case "PosX" : \iPosX = Val(PreferenceKeyValue())
						Case "PosY" : \iPosY = Val(PreferenceKeyValue())
						Case "Width" : \iWidth = Val(PreferenceKeyValue())
						Case "Height" : \iHeight = Val(PreferenceKeyValue())
						Case "OrgWidth" : \iOrgWidth = Val(PreferenceKeyValue())
						Case "OrgHeight" : \iOrgHeight = Val(PreferenceKeyValue())
						Default
							If iIncludeUserData = #True
								\mpUserData(LCase(PreferenceKeyName())) = PreferenceKeyValue()
							EndIf
						EndSelect
					Wend				
				EndIf
			Wend
		EndWith
		
		ClosePreferences()
		ProcedureReturn #True
		
	EndProcedure
	;-
	Procedure.i whAddGadget(sWindowName.s, sGadgetName.s, iGadgetNumber.i, iAnchor.i=#Anchor_Left, *Eventprocedure=0, iEventType.i=#PB_All)
		; +-----------------------------------------------------------------
		; |Description  : Adds a (previously created) PureBasic gadget to a window registered in WinHandler
		; |Arguments    : sWindowName    : Name under which the window was registered
		; |             : sGadgetName    : Name under which the gadget is to be addressed
		; |             : iGadgetNumber  : Object number of the PureBasic gadget
		; |             : iAnchor        : With bitwise OR (|) linked values of anchor constants for this gadget (see gadget anchor constants)
		; |             : *Eventprocedure: Address of a (standard) event procedure to be linked to the gadget.
		; |             : iEventType     : EventType for the specified event procedure
		; |Results      : Returns the PB object number of the added gadget or 0 in case of errors
		; |Remarks      : Besides the possibility to assign a (standard) event procedure to the gadget "on the fly" with this command, 
		; |               you can link further events to the gadget via the normal PureBasic commands. But then you have to release
		; |               them yourself!
		; +-----------------------------------------------------------------
		If sWindowName = "" Or sGadgetName = "" Or iGadgetNumber < 0
			ProcedureReturn 0
		EndIf
		
		sWindowName = LCase(sWindowName)
		sGadgetName = LCase(sGadgetName)
		
		; If the window entry does not exist (whAddWindow() or whOpenWindow() have not been called yet)
		If FindMapElement(mpWindows(), sWindowName) = 0
			ProcedureReturn 0
		EndIf
		
		; If the window is closed
		If IsWindow(mpWindows()\iNumber) = 0
			ProcedureReturn 0
		EndIf
		
		; If the gadget entry already exists
		If FindMapElement(mpWindows(sWindowName)\mpGadgets(), sGadgetName) <> 0
			ProcedureReturn mpWindows(sWindowName)\mpGadgets(sGadgetName)\iNumber
		Else
			With mpWindows(sWindowName)
				\mpGadgets(sGadgetName)\iNumber = iGadgetNumber
				\mpGadgets(sGadgetName)\iAnchor = iAnchor
				
				\mpGadgets(sGadgetName)\iSpacing[#Spacing_Left] = GadgetX(iGadgetNumber)
				\mpGadgets(sGadgetName)\iSpacing[#Spacing_Top] = GadgetY(iGadgetNumber)
				\mpGadgets(sGadgetName)\iSpacing[#Spacing_Right] = \iOrgWidth - GadgetX(iGadgetNumber) - GadgetWidth(iGadgetNumber)
				\mpGadgets(sGadgetName)\iSpacing[#Spacing_Bottom] = \iOrgHeight - GadgetY(iGadgetNumber) - GadgetHeight(iGadgetNumber)
				
				If *Eventprocedure <> 0
					\mpGadgets(sGadgetName)\Eventprocedure = *Eventprocedure
					\mpGadgets(sGadgetName)\iEventType = iEventType
					BindGadgetEvent(iGadgetNumber, *Eventprocedure, iEventType)
				Else
					\mpGadgets(sGadgetName)\Eventprocedure = 0
					\mpGadgets(sGadgetName)\iEventType = #PB_All
				EndIf
				
				ProcedureReturn mpWindows(sWindowName)\mpGadgets()\iNumber
			EndWith
		EndIf
		
	EndProcedure
	Procedure.i whFreeGadget(sWindowName.s, sGadgetName.s)
		; +-----------------------------------------------------------------
		; |Description  : Frees a gadget previously registered with whAddGadget() and removes it from the window
		; |Arguments    : sWindowName: Name under which the window was registered
		; |             : sGadgetName: Name under which the gadget was registered
		; |Results      : -
		; |Remarks      : This function also detaches a (standard) event from the gadget that may have been previously assigned by whAddGadget()
		; +-----------------------------------------------------------------
		If sWindowName = "" Or sGadgetName = ""
			ProcedureReturn #False
		EndIf
		
		; If the window entry does not exist (whAddWindow() or whOpenWindow() have not been called yet)
		If FindMapElement(mpWindows(), sWindowName) = 0
			ProcedureReturn #False
		EndIf
		
		; If the window is closed
		If IsWindow(mpWindows(sWindowName)\iNumber) = 0
			ProcedureReturn #False
		EndIf
		
		; If the gadget entry does not exist
		If FindMapElement(mpWindows(sWindowName)\mpGadgets(), sGadgetName) = 0
			ProcedureReturn #False
		Else
			With mpWindows(sWindowName)
				If \mpGadgets(sGadgetName)\Eventprocedure <> 0
					UnbindGadgetEvent(\mpGadgets(sGadgetName)\iNumber, \mpGadgets(sGadgetName)\Eventprocedure)
				EndIf
				If IsGadget(\mpGadgets(sGadgetName)\iNumber) <> 0
					FreeGadget(\mpGadgets(sGadgetName)\iNumber)
				EndIf
				DeleteMapElement(\mpGadgets(), sGadgetName)
				ProcedureReturn #True
			EndWith
		EndIf
		
	EndProcedure
	Procedure.i whGetGadgetNumber(sWindowName.s, sGadgetName.s)
		; +-----------------------------------------------------------------
		; |Description  : Returns the PB object number of a gadget registered in WinHandler
		; |Arguments    : sWindowName: Name under which the window was registered
		; |             : sGadgetName: Name under which the gadget was registered
		; |Results      : Returns the PB object number of the gadget or 0 in case of errors
		; |Remarks      : -
		; +-----------------------------------------------------------------
		If sWindowName = "" Or sGadgetName = ""
			ProcedureReturn 0
		EndIf
		
		sWindowName = LCase(sWindowName)
		sGadgetName = LCase(sGadgetName)
		
		; If the window entry does not exist (whAddWindow() or whOpenWindow() have not been called yet)
		If FindMapElement(mpWindows(), sWindowName) = 0
			ProcedureReturn 0
		EndIf
		
		; If the window is closed
		If IsWindow(mpWindows(sWindowName)\iNumber) = 0
			ProcedureReturn 0
		Else
			ProcedureReturn mpWindows(sWindowName)\mpGadgets(sGadgetName)\iNumber
		EndIf	
		
	EndProcedure
EndModule


CompilerIf #PB_Compiler_IsMainFile = 1
	;-
	;*************************************************************************
	;- Democode
	;*************************************************************************
	
	EnableExplicit
	
	Global iQuit.i, sIniFile.s
	sIniFile = GetPathPart(ProgramFilename()) + "WinHandler.ini"
	
	Procedure.i Settings_CloseWindow()
		UseModule WH
		
		If IsWindow(whGetWindowNumber("settings")) <> 0
			
			; The registered gadgets and their (standard) events are automatically released when the window is closed
			whCloseWindow("settings")
			
			ProcedureReturn #True
		Else	
			ProcedureReturn #False
		EndIf
		
		UnuseModule WH
	EndProcedure
	Procedure   Settings_Event_Button_Clear()
		UseModule WH
		
		SetGadgetText(whGetGadgetNumber("settings", "string"), "")
	
		UnuseModule WH
	EndProcedure
	Procedure   Settings_Event_Button_Save()
		UseModule WH
		
		whSetWindowData("settings", "text", GetGadgetText(whGetGadgetNumber("settings", "string")))
		whSaveWindowData(sIniFile, #True, #False)
		Settings_CloseWindow()
		
		UnuseModule WH
	EndProcedure
	Procedure   Settings_Event_CloseWindow()
		Settings_CloseWindow()
	EndProcedure	
	Procedure.i Settings_OpenWindow()
		UseModule WH
		
		If IsWindow(whGetWindowNumber("settings")) = 0
			; Open the settings window
			If whOpenWindow("settings", #PB_Ignore, #PB_Ignore, 400, 240, "WinHandler-Demo [settings]", #PB_Window_SystemMenu|#PB_Window_TitleBar|#PB_Window_SizeGadget|#PB_Window_Invisible) <> 0
				; Open the main window
				whAddGadget("settings", "frame",         FrameGadget(#PB_Any, 5, 5, 390, 185, "Program settings"),                           #Anchor_Left | #Anchor_Top | #Anchor_Right | #Anchor_Bottom)
				whAddGadget("settings", "text",          TextGadget(#PB_Any, 15, 30, 260, 20, "Enter some text into the string gadget:"),    #Anchor_Left | #Anchor_Top)
				whAddGadget("settings", "string",        StringGadget(#PB_Any, 15, 50, 370, 25, ""),                                         #Anchor_Left | #Anchor_Top | #Anchor_Right)
				whAddGadget("settings", "text ini",      TextGadget(#PB_Any, 15, 90, 200, 20, "The INI file is located at:"),                #Anchor_Left | #Anchor_Bottom)
				whAddGadget("settings", "editor ini",    EditorGadget(#PB_Any, 15, 110, 370, 70, #PB_Editor_WordWrap | #PB_Editor_ReadOnly), #Anchor_Left | #Anchor_Right | #Anchor_Bottom)
				whAddGadget("settings", "button save",   ButtonGadget(#PB_Any, 5, 200, 90, 35, "Save to INI", #PB_Button_Default),           #Anchor_Left | #Anchor_Bottom, @Settings_Event_Button_Save(), #PB_EventType_LeftClick)
				whAddGadget("settings", "button clear",  ButtonGadget(#PB_Any, 150, 200, 90, 35, "Clear text", #PB_Button_Default),          #Anchor_CenterHor | #Anchor_Bottom, @Settings_Event_Button_Clear(), #PB_EventType_LeftClick)
				whAddGadget("settings", "button cancel", ButtonGadget(#PB_Any, 305, 200, 90, 35, "Cancel"),                                  #Anchor_Right | #Anchor_Bottom, @Settings_Event_CloseWindow(), #PB_EventType_LeftClick)
				
				SetGadgetText(whGetGadgetNumber("settings", "string"), whGetWindowData("settings", "text"))
				SetGadgetText(whGetGadgetNumber("settings", "editor ini"), sIniFile)
				SetGadgetColor(whGetGadgetNumber("settings", "editor ini"), #PB_Gadget_BackColor, $C1B7BC)
				
				BindEvent(#PB_Event_CloseWindow, @Settings_Event_CloseWindow(), whGetWindowNumber("settings"))
				
				; Set some window attributes and finally make the window visible
				SmartWindowRefresh(whGetWindowNumber("settings"), #True)
				WindowBounds(whGetWindowNumber("settings"), 300, 260, 1200, 800)
				HideWindow(whGetWindowNumber("settings"), #False)			
				
				ProcedureReturn #True
			EndIf
		Else	
			ProcedureReturn #False
		EndIf
		UnuseModule WH
	EndProcedure
	

	Procedure   Main_Event_Button_Exit()
		iQuit = #True	
	EndProcedure
	
	Procedure   Main_Event_Button_Settings()
		Settings_OpenWindow()
	EndProcedure
	
	Procedure.i Main_OpenWindow()
		UseModule WH
		
		If IsWindow(whGetWindowNumber("main")) = 0
			; Open the main window
			If whOpenWindow("main", #PB_Ignore, #PB_Ignore, 500, 500, "WinHandler-Demo [main]", #PB_Window_SystemMenu|#PB_Window_MinimizeGadget|#PB_Window_TitleBar|#PB_Window_MaximizeGadget|#PB_Window_SizeGadget|#PB_Window_Invisible) <> 0
				
				; Add some gadgets to  the window
				whAddGadget("main", "button nothing",  ButtonGadget(#PB_Any, 5, 10, 170, 35, "Do nothing"),            #Anchor_Left | #Anchor_Top)
				whAddGadget("main", "button settings", ButtonGadget(#PB_Any, 360, 10, 135, 35, "Settings"),            #Anchor_Top | #Anchor_Right, @Main_Event_Button_Settings(), #PB_EventType_LeftClick)
				whAddGadget("main", "container",       ContainerGadget(#PB_Any, 410, 60, 85, 75),                      #Anchor_Right | #Anchor_CenterVer)
				whAddGadget("main", "button 1",        ButtonGadget(#PB_Any, 0, 0, 85, 35, "Button 1"),                #Anchor_Left)
				whAddGadget("main", "button 2",        ButtonGadget(#PB_Any, 0, 40, 85, 35, "Button 2"),               #Anchor_Left)
				CloseGadgetList()
				whAddGadget("main", "editor",          EditorGadget(#PB_Any, 5, 60, 395, 380, #PB_Editor_WordWrap),    #Anchor_Left | #Anchor_Top | #Anchor_Right | #Anchor_Bottom)
				whAddGadget("main", "button exit",     ButtonGadget(#PB_Any, 395, 455, 100, 35, "Exit"),               #Anchor_CenterHor | #Anchor_Bottom, @Main_Event_Button_Exit(), #PB_EventType_LeftClick)
				
				; Set a text for the editor gadget
				SetGadgetText(whGetGadgetNumber("main", "editor"), "When you resize the window, the gadgets will automatically realign according to their anchor points." + #CRLF$ + #CRLF$ +

"Click the 'Settings' button to enter the Settings dialog. There you can enter a user defined string and save this string as well as all window sizes and positions in an INI file." + #CRLF$ + #CRLF$ +

"At the next start of the program all window sizes and positions will be initialized with these saved values. And also the user defined string will be loaded again and displayed in the Settings dialog." + #CRLF$ + #CRLF$ +

"All necessary program steps are done by WinHandler.")
				
				BindEvent(#PB_Event_CloseWindow, @Main_Event_Button_Exit(), whGetWindowNumber("main"))
				
				; Set some window attributes and finally make the window visible
				SmartWindowRefresh(whGetWindowNumber("main"), #True) 
				WindowBounds(whGetWindowNumber("main"), 320, 200, 9999, 9999)			
				HideWindow(whGetWindowNumber("main"), #False)
				
				ProcedureReturn #True
			EndIf
		Else	
			ProcedureReturn #False
		EndIf
		
		UnuseModule WH
	EndProcedure
	
	Procedure.i Main_CloseWindow()
		UseModule WH
		
		If IsWindow(whGetWindowNumber("main")) <> 0
			
			UnbindEvent(#PB_Event_CloseWindow, @Main_Event_Button_Exit(), whGetWindowNumber("main"))		
			
			; The registered gadgets and their (standard) events are automatically released when the window is closed
			whCloseWindow("main")
			
			ProcedureReturn #True
		Else	
			ProcedureReturn #False
		EndIf
		
		UnuseModule WH
	EndProcedure
	
	
	;*************************************************************************
	;- Mainloop
	;*************************************************************************
	
	; Register all windows of the program to WinHandler
	wh::whAddWindow("main")
	wh::whAddWindow("settings")
	wh::whLoadWindowData(sIniFile)	
	
	If Main_OpenWindow()
		Repeat
			WaitWindowEvent()
		Until iQuit = #True
	EndIf
	
	; Unregister all windows from WinHandler
	wh::whRemoveWindow("settings")
	wh::whRemoveWindow("main")
	
	End 
	
CompilerEndIf
Image
PB 6.02 x64, OS: Win 7 Pro x64 & Win 11 x64, Desktopscaling: 125%, CPU: I7 6500, RAM: 16 GB, GPU: Intel Graphics HD 520, User age in 2024: 56y
"Happiness is a pet." | "Never run a changing system!"