Seite 1 von 1

[MODULE] WinHandler - Fenster/Gadget Resize-Manager / Programmdaten-Manager

Verfasst: 31.08.2023 00:02
von Kurzer
Das Modul WinHandler.pbi nimmt euch bei der Erstellung von Fenster-Applikationen einige Routinearbeiten ab. Es sorgt für die automatische Ausrichtung von Gadgets bei Größenänderung von Fenstern und bietet Funktionen die Geometrien aller Programmfenster sowie eigene Userdaten in einer INI Datei zu speichern und auch wieder zu laden.

Für diese Dinge muss man im eigenen Programm dann keinen Code mehr schreiben.

Für wen ist WinHandler gedacht?
Alle, die mit der PureBasic Dialog-Library nicht zurecht kommen (oder sie nicht nutzen möchten) und Fensteroberflächen lieber auf "gewohnte" Weise erstellen möchten.

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.

Hiermit kann die Verwaltung (Speichern/Laden) sämtlicher Einstellungen einer Anwendung durch den WinHandler realisiert werden.

Ein Programm, welches den Winhandler, nutzt sollte folgenden Aufbau haben:

Code: Alles auswählen

	; Alle Fenster, die das Programm nutzt, namentlich am WinHandler registrieren
	; Die Fenster sind zu dem Zeitpunkt noch nicht geöffnet
	wh::whAddWindow("main")
	wh::whAddWindow("settings")
	
	; Laden der Daten aus dem INI File
	wh::whLoadWindowData("C:\Users\...\MeinProgramm.ini)	
	
	; Dann wird das Hauptfenster geöffnet (siehe auch weiter unten)
	If Main_OpenWindow()
		Repeat
			WaitWindowEvent()
		Until iQuit = #True
	EndIf
	
	; Zum Schluss müssen alle Fenster aus dem Winhandler entfernt werden 
	wh::whRemoveWindow("settings")
	wh::whRemoveWindow("main")
	
	
	In der Prozedur die das/die Fenster des Programms öffnet (hier "Main_OpenWindow()") sieht der Minimal-code wie folgt aus:
	
	UseModule WH
	; Das zuvor registrierte Fenster über den WinHandler öffnen
	whOpenWindow("main", #PB_Ignore, #PB_Ignore, 400, 220, "Fenstertitel", #PB_Window_SystemMenu|#PB_Window_TitleBar|#PB_Window_SizeGadget|#PB_Window_Invisible)
	
	; Gadgets über den WinHandler zufügen
	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, "Abbrechen"),             #Anchor_Right | #Anchor_Bottom, @Main_Event_CloseWindow(), #PB_EventType_LeftClick)
			
	BindEvent(#PB_Event_CloseWindow, @Main_Event_CloseWindow(), whGetWindowNumber("main"))
			
	HideWindow(whGetWindowNumber("main"), #False)			


	Zum Schliessen des Fensters wird folgender Befehl genutzt:
	
	whCloseWindow("main")
Wie man sieht, werden Fenster und Gadgets im WinHandler nicht über eine (wie bei PB übliche) Objektnummer addressiert, sondern über einen eindeutigen Namen. Grund hierfür ist die Verwendung einer Map zur internen Verwaltung und die (meiner Meinung nach) einfachere Anwendung. Außerdem spart man sich Variablen in die man die Objektnummern - z.B. bei Verwendung von #PB_Any - speichern müsste.

Das Modul besitzt eine Demo, die ausgeführt wird, wenn ihr das Modul einfach nur mit F5 startet.

Folgende Befehle stellt das Modul zur Verfügung:

Code: Alles auswählen

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      : -
		; +-----------------------------------------------------------------
Hier das Modul WinHandler:

Code: Alles auswählen

;*************************************************************************
;* 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
Bild

Re: [MODULE] WinHandler - Fenster/Gadget Resize-Manager / Programmdaten-Manager

Verfasst: 05.09.2023 13:09
von Benubi
Danke für den Code!
Das muß ich mir erst einmal genauer ansehen. Ich grübel seit Längerem über Sprite basierende GUI Lösungen für "fullscreen" Games usw. Marke Eigenbau hatte zu viele Bugs und Beschränkungen, einen Font-Renderer habe ich noch nicht gebaut obwohl es das Einfachste oder Grundlegensde wäre. Ich bin erst vor Kürzerem auf die immediate mode GUI Techniken aufmerksam geworden, ich würde gerne in diese Richtung arbeiten und hoffe aus deinem Code etwas schlauer zu werden, da er relativ kurz und übersichtlich ist im Vergleich zu anderen Lösungen und Halblösungen (z.B. Import von Dear IMGUI etc.). Bei der mitgelieferten Demo mußte ich erstmal anbeißen :)

Re: [MODULE] WinHandler - Fenster/Gadget Resize-Manager / Programmdaten-Manager

Verfasst: 07.09.2023 17:36
von matbal
Hallo Kurzer,

ich habe auch mal deinen Code getestet. Interessantes Konzept.

Eine Funktion hat mir noch gefehlt bei ersten Tests: Wenn ich in einem Programm mehrere Instanzen eines Fensters öffnen will, brauche ich ja auch verschiedene Namen. Für den Fall wäre es praktisch, wenn es noch die Funktion gäbe, die zu einer WindowsNummer den zugehörigen Windowsnamen zurückgibt. (In der Eventprozedur erhalte ich ja nur die Windowsnummer)

----

Eines, was mich anfangs gestört hat, sind die langen Programmzeilen. Das kann man aber mit Zeilenumbrüchen umgehen. Dann wird es für meinen Geschmack auch übersichtlicher.
Was mich aber noch mehr stört, ist, dass mir die Autovervollständigung nicht mehr beim Zugriff auf die Gadgets helfen kann...

Re: [MODULE] WinHandler - Fenster/Gadget Resize-Manager / Programmdaten-Manager

Verfasst: 07.09.2023 21:09
von Kurzer
Hallo ihr beiden,

danke für euer feedback.

Der WinHandler ist aus der Not heraus geboren. Ich bin gerade dabei da noch ein paar kleine konzeptionelle Änderungen einzuführen (wenn ich Zeit finde neben dem Projekt, welches den WinHandler nötig machte).

So wird man in der nächsten Version beim Zufügen eines Gadgets zum Winhandler angeben können, ob sein Inhalt automatisch in der INI Datei gesichert (und beim nächsten Laden wiederhergestellt) werden soll oder nicht - also ähnlich der "UserDaten" die man an ein Fenster heften kann und die auch in der INI gesichert werden.

@matbal: Was die langen Zeilen angeht... Ich kenne die Problematik selbst, dass einem ein fremder Code nicht so recht schmeckt, weil er nicht in das eigene "Beautify-Konzept" passt. Deswegen ist WinHandler als Modul geschrieben (der Prototyp war ein einfaches Include). So kann man den "fremden Rotz" einfach als Blackbox ansehen und nur mit den öffentlichen Prozeduren arbeiten, ohne sich über den Code aufregen zu müssen (sofern er technisch nicht extrem schlecht programmiert ist).

Das mit der Funktion "Gib mir den WindowNamen zu einer WindowNummer" ist eine gute Idee, die ich aufgreife. Das kann man dann ja auch für die Gadgets analog so umsetzen, da die ja auch mit Namen im WinHandler registriert werden.

Das mit der Autovervollständigung, die dir beim Zugriff auf die Gadgets hilft, musst du mir noch mal genauer erklären.
Normale PB Gadgets legst du ja entweder mit festen Nummern an (z.B. ButtonGadget(1, ...) ) oder mit #PB_Any ( z.B. ExitButton = ButtonGadget(#PB_any, ...) ).

Ich weiß zwar nicht genau was du mit der Hilfe der Autovervollständigung meinst, aber du kannst im WinHandler die Fenster- und Gadget-Namen ja auch als Konstante vordefinieren.
#WIN_Main = "main"
#WIN_Settings = "settings"
#GAD_Exit = "button exit"
#GAD_Cancel = "botton cancel"
usw...

Dann sollte bei Eingabe von #WIN oder #GAD auch schon die Autovervollständigung loslegen und die eine Auswahlliste dazu anzeigen.