Frage zu WindowCallbackProc & eigenem Gadget mittels Canvas

Windowsspezifisches Forum , API ,..
Beiträge, die plattformübergreifend sind, gehören ins 'Allgemein'-Forum.
Benutzeravatar
Kurzer
Beiträge: 1617
Registriert: 25.04.2006 17:29
Wohnort: Nähe Hamburg

Frage zu WindowCallbackProc & eigenem Gadget mittels Canvas

Beitrag von Kurzer »

Hallo,

ich versuche mir aktuell ein komplexes, eigenes Gadget mittels des Canvas Gadgets zu schreiben.
Es soll nach dem Erzeugen mit MeinGadget() auch wieder entfernbar sein. Und zwar mit der PB-eigenen Prozedur FreeGadget(MeinGadget) .

Mein Anspruch ist, dass die Verwendung sich möglichst nahtlos in die vorhandene Logik der PB Gadgets einfügt. Auch was die Eventbearbeitung angeht.

Die bisherigen Beispiele, die ich im Forum gefunden habe, sind entweder nicht auf dem Canvas basierend oder aber nutzen eigene Prozeduren zum Freigeben der Instanzen und des Speichers. Das wäre dann z.B. FreeMeinGadget() statt des PB-Befehls FreeGadget().

Bei der Bearbeitung von Events gehen die Beispiele so vor, dass im Eventloop nur geprüft wird, ob das jeweilige Canvasgadget (auf welchem das eigene Gadget aufbaut) einen Event erzeugt hat.
Danach müssen dann die Details mit einem weiteren Prozeduraufruf ermittelt werden (z.B. MeinGadget_Getevent(), welches dann weitere Eventkonstanten liefert. Bei einem Drehknopf z.B. ein Event "Knopf wurde gedreht") Ich würde mein eigenes Gadget gern direkt eigene Events in die PB Eventqeue legen. So dass der PB Befehl EventType() im Beispiel oben ein #MEINGADGET_KNOBDRAGGED liefert.

Leider scheitert es schon bei den ersten Tests mit einer WindowsCallback Prozedur.

Anbei ist ein Testcode, der ein Fenster öffnet und ein eigenes Gadget unter Verwendung eines Canvasgadgets und einer Callback Prozedur erzeugt.

Mir ist bewusst, dass man zur Verwaltung von verschiedenen Gadgetinstanzen weitere Struktureinträge benötigt und diese Daten pro Instanz in einem eigenen Memorybuffer speichert, deren Adresse man am besten mittels SetGadgetData() an das Canvasgadget heftet.
Das habe ich bei diesem Testcode weggelassen, da es mir erstmal um die richtige Anwendung des Callbacks geht. Ich habe leider keine C/C++/C# oder sonstigen "Mainstreamprogrammiersprachenkenntnisse", daher wohl auch das fehlende Verständnis speziell in diesem Bereich.

Folgende Fragen habe ich bzgl. des Testcodes:

- Mit aktiver Callbackprozedur wird das Canvasgadget nicht mehr neu gezeichnet, wenn man das Fenster verkleinert/vergrößert und damit Teile des Gadgets verdeckt. Ich dachte die Bearbeitung der Gadgetevents durch PureBasic wird weiterhin durchgeführt, wenn der Rückgabewert #PB_ProcessPureBasicEvents lautet.

Nachtrag:
Hier habe ich folgende Lösung gefunden:
Statt iResult = #PB_ProcessPureBasicEvents am Anfang der Prozedur
nehme man iResult = DefWindowProc_(iWindow, iMessage, wParam, lParam) vor dem ProcedureReturn
Dann wird zumindest das Canvasgadget (die Ränder) neu gezeichnet. Der selbst gezeichnete Inhalt wird allerdings damit nicht wieder restauriert.


- Der Teil "Case #WM_LBUTTONUP" im der Callbackprozedur wird nicht ausgeführt, wenn ich in das Gadget klicke. Warum? Gegenprobe: Wenn ich das Fenster schließe und das Canvasgadget durch PureBasic entfernt wird, dann wir der Teil "Case #WM_NCDESTROY" sehr wohl ausgeführt.

- Ist es die korrekte Art einen Callback wieder zu entfernen, wenn man dies innerhalb der betreffenden Callbackprozedur bei Auftreten von #WM_NCDESTROY tut?

Code: Alles auswählen

		Case #WM_NCDESTROY
			SetWindowCallback(0, Settings\iGadgetNr)  ; Callback wird wieder entfernt
Das Gadget selbst soll interaktiv reagieren. Es wird ein Sudokufeld darstellen, in welches man hineinklicken kann.

Bild

Bei einem Mausklick in eines der Zahlenfelder soll das Gadget eigenständig die Tastatureingabe von Ziffern bzw. die Auswahl von Ziffern aus einer aufklappenden Liste managen.
Hier ist mir auch nicht nicht ganz klar wie man das realisiert, denn im Grunde benötigt das Gadget selbst einen Eventloop und muss auf die Eingaben reagieren.
Bei dem Beispiel mit der Eingabe soll das Gadget dann ein "Es wurde ein Feld geändert"-Message in die PB-Eventqeue legen und kein "Linke Masutaste wurde gedrückt".

Über weitere Hinweise und Korrekturen würde ich mich freuen.

Testcode:

Code: Alles auswählen

Structure MeinGadget_Attributes
	iGadgetNr.i
	iGadgetInitalized.i
EndStructure

Global Settings.MeinGadget_Attributes

Procedure _WindowProcCallback(iWindow.i, iMessage.i, wParam.i, lParam.i) 
	Protected iResult.i	
	
	iResult = #PB_ProcessPureBasicEvents
	
	Select iMessage
		Case #WM_LBUTTONUP
			Debug "#WM_LBUTTONUP"
		Case #WM_NCDESTROY
			Debug "WM_NCDESTROY"
			SetWindowCallback(0, Settings\iGadgetNr)
	EndSelect 
	
	ProcedureReturn iResult
	
EndProcedure 

Procedure _DrawGadget()
	If Settings\iGadgetInitalized = 1
		If StartDrawing(CanvasOutput(Settings\iGadgetNr))
			Box(0, 0, 100, 100, $FF8844)
			DrawText(10, 10, "Test", $FFFFFF, $FF8844)
			StopDrawing()
		EndIf
	EndIf
EndProcedure

Procedure.i MeinGadget(iGadgetNr, x.i, y.i)
	
	If iGadgetNr = #PB_Any
		Settings\iGadgetNr = CanvasGadget(#PB_Any, x, y, 100, 100, #PB_Canvas_Border)
	Else	
		Settings\iGadgetNr = CanvasGadget(iGadgetNr, x, y, 100, 100, #PB_Canvas_Border)
	EndIf
	
	If Settings\iGadgetNr <> 0 And Settings\iGadgetInitalized = 0
		Settings\iGadgetInitalized = 1
		_DrawGadget()
		SetWindowCallback(@_WindowProcCallback(), Settings\iGadgetNr)
	EndIf
	
	ProcedureReturn Settings\iGadgetNr
	
EndProcedure

If OpenWindow(0, 0, 0, 200, 200, "Test", #PB_Window_ScreenCentered | #PB_Window_MinimizeGadget | #PB_Window_MaximizeGadget | #PB_Window3D_SizeGadget) 
	
	MeinGadget(#PB_Any, 40, 40)
	
	Repeat 
		Select WaitWindowEvent() 
			Case #PB_Event_CloseWindow 
				End 
		EndSelect 
	ForEver 
EndIf 
"Never run a changing system!" | "Unterhalten sich zwei Alleinunterhalter... Paradox, oder?"
PB 6.12 x64, OS: Win 11 24H2 x64, Desktopscaling: 150%, CPU: I7 12700 H, RAM: 32 GB, GPU: Intel(R) Iris(R) Xe Graphics | NVIDIA GeForce RTX 3070
Useralter in 2025: 57 Jahre.
Benutzeravatar
hjbremer
Beiträge: 822
Registriert: 27.02.2006 22:30
Computerausstattung: von gestern
Wohnort: Neumünster

Re: Frage zu WindowCallbackProc & eigenem Gadget mittels Can

Beitrag von hjbremer »

Hier mal ein Beispiel, hoffe es hilft und ist verständlich

Code: Alles auswählen

;PB 4.6 Windows Vista X86 32 Bit

EnableExplicit

;2 Hilfsstrukturen um wParam bzw. lParam zu zerlegen
Structure TwoWords
   Hiword.w
   Loword.w
EndStructure

Structure DoubleWord
   StructureUnion       
      param.i        ; Parameter welcher in Hi + Low zerlegt wird
      p.TwoWords     ; aufgeteiltes Param 
      pos.points     ; aufgeteiltes Param, nur andere Namen für Mouseposi
   EndStructureUnion
EndStructure

Structure MeinGadget_Struktur
   gadgetNr.i
   gadgetId.i
   gadgetPtr.i
   gadgetname.s
   
   gadgetbr.i
   gadgethh.i
   
   xyzbrush.i
   ;usw
   
   mouse.DoubleWord
        
EndStructure

#MeinGadget_PropId = "MeinGadget_Struktur"

;Windows http://msdn.microsoft.com/en-us/library/windows/desktop/ms644931(v=vs.85).aspx
#mymessage1 = #WM_USER + $1001   ;nicht zu niedrig, dann ev. Kollision mit Windows
#mymessage2 = #WM_USER + $1002   ;nicht zu hoch, dann ev. Kollision mit PB

;in PB 4.6 funktioniert es noch
Import "" 
   PB_Gadget_SendGadgetCommand(id, pbEventType) ;am besten hohe Werte 
EndImport

#myeventtype1 = 15001
#myeventtype2 = 15002

Enumeration 1
   #mainwindow   
   #killbutton
   #testbutton
   #canvasgadget1
   #canvasgadget2
EndEnumeration

Procedure.i MeinGadget_Callback(hwnd, msg, wParam, lParam) 
   ;hwnd ist hier gleich der ID vom Gadget
   
   Protected *mg.MeinGadget_Struktur = GetProp_(hwnd, #MeinGadget_PropId)
   
   With *mg
      
      ;wichtig: *mg\gadgetPtr sollte man in eine Variable schieben.
      ;Sonst kann man mit #WM_NCDESTROY das Memory nicht löschen
      ;der Pointer wird nach #WM_NCDESTROY noch einmal gebraucht.
      
      ;den Callback wieder zu entfernen kann man, muß man aber nicht.
      ;Glaube ich zumindest und mache es meistens auch nicht. 
      ;entfernen mit: SetWindowLongPtr_(id, #GWL_WNDPROC, oldproc)
      
      Protected oldproc = \gadgetPtr   
      Protected keystate
      
      Select msg  
            
         Case #WM_HELP: Debug "F1 gedrückt"
            
         Case #WM_NCDESTROY: Debug "WM_NCDESTROY"
            ;SetWindowLongPtr_(\gadgetId, #GWL_WNDPROC, oldproc)
            RemoveProp_(\gadgetId, #MeinGadget_PropId)
            DeleteObject_(\xyzbrush) ;falls existiert
            FreeMemory(*mg)
            ProcedureReturn 0
            
         Case #WM_MOUSEFIRST
            \mouse\param = lparam    ;Mouseposi 
            ;Debug "->Callback: #WM_MOUSEFIRST"
            ;Debug "  Mausposi im " + \gadgetname + " " + Str(\mouse\pos\x) + "/" + Str(\mouse\pos\y)
            
         Case #WM_MOUSEMOVE   ;gibt es nicht, stattdessen #WM_MOUSEFIRST
            
         Case #WM_KILLFOCUS, #WM_SETFOCUS
            
         Case #WM_CHAR         
         Case #WM_KEYUP 
            
         Case #WM_KEYDOWN
            Select wparam
               Case #VK_0 To #VK_9
                  Debug "->Callback: #WM_KEYDOWN im " + \gadgetname
                  Debug "  eine Zahl gedrückt und PB merkt nix !!! ausser mymessage"
                  keystate = GetAsyncKeyState_(#VK_CONTROL)
                  If keystate < 0
                     Debug "  Control + eine Zahl gedrückt"
                  EndIf
                  PostMessage_(hwnd, #mymessage1, wparam, lparam)
                  PB_Gadget_SendGadgetCommand(hwnd, #myeventtype1)
                  ProcedureReturn 0    ;darum merkt PB nix ausser mymessage !!!!
                  
               Case #VK_A To #VK_Z
                  Debug "->Callback: #WM_KEYDOWN im " + \gadgetname
                  Debug "  ein Buchstabe gedrückt und PB merkt es"
                  PostMessage_(hwnd, #mymessage2, wparam, lparam)
                  
            EndSelect
            ;alle Keydowns ins Nirwana
            ;ProcedureReturn 0    
            
         Case #WM_LBUTTONDOWN
            Debug "->Callback: #WM_LBUTTONDOWN im " + \gadgetname
            
         Case #WM_LBUTTONUP
            
         Case #WM_MBUTTONDOWN
            ;simuliert Buttonpress , wparam und lparam sollten null sein
            PostMessage_(GadgetID(#testbutton), #WM_LBUTTONDOWN, 0, 0)
            PostMessage_(GadgetID(#testbutton), #WM_LBUTTONUP, 0, 0)
            
         Case #WM_MBUTTONUP
            
         Case #WM_RBUTTONDOWN, #WM_RBUTTONUP
         Case #WM_MOUSEWHEEL            
         Case #WM_SETCURSOR
         Case #WM_MOUSEACTIVATE, #WM_MOUSELEAVE
         Case #WM_NCMOUSEMOVE, #WM_NCHITTEST, #WM_CAPTURECHANGED
         Case #WM_PAINT, #WM_NCPAINT, #WM_ERASEBKGND
            
      EndSelect 
      
   EndWith
   
   ProcedureReturn CallWindowProc_(oldproc, hwnd, msg, wParam, lParam) 
   
EndProcedure 

Procedure.i DrawGadget(nr)
   
   Protected *mg.MeinGadget_Struktur = GetProp_(GadgetID(nr), #MeinGadget_PropId)
   
   ;mit GetProp stehen nun alle Variablen der Struktur 
   ;für ein bestimmtes Gadget zur Verfügung
   
   Protected dc
   
   dc = StartDrawing(CanvasOutput(*mg\gadgetNr))
   If dc
      Box(0, 0, *mg\gadgetbr, *mg\gadgethh, $FF8844)
      DrawText(10, 10, "Test", $FFFFFF, $FF8844)
      
      StopDrawing()
   EndIf
   
EndProcedure

Procedure.i MeinGadget(gadgetNr, x, y, br, hh, name$, xyz = 0)
   
   Protected *mg.MeinGadget_Struktur = AllocateMemory(SizeOf(MeinGadget_Struktur)) 
   
   Protected flags = #PB_Canvas_Border|#PB_Canvas_Keyboard
   
   With *mg 
      
      If gadgetNr = #PB_Any
         \gadgetNr = CanvasGadget(#PB_Any, x, y, br, hh, flags)
         \gadgetId = GadgetID(\gadgetNr) 
      Else   
         \gadgetNr = gadgetNr
         \gadgetId = CanvasGadget(gadgetNr, x, y, br, hh, flags)
      EndIf
      
      \gadgetPtr = GetWindowLongPtr_(\gadgetId, #GWL_WNDPROC)            ;ProcPointer holen
      SetProp_(\gadgetId, #MeinGadget_PropId, *mg)                       ;Memory setzen
      SetWindowLongPtr_(\gadgetId, #GWL_WNDPROC, @MeinGadget_Callback()) ;eigene WinProc setzen
      
      ;Sonstiges 
      \gadgetname = name$
      \gadgetbr = br
      \gadgethh = hh
      
      ;falls benötigt
      \xyzbrush = CreateSolidBrush_($A3E7F0)
      
      ProcedureReturn \gadgetNr
   EndWith
EndProcedure

Procedure.i MainWindow()
   
   Protected flags, canvasgadget2
   
   flags = #PB_Window_MinimizeGadget | #PB_Window_MaximizeGadget
   flags | #PB_Window_ScreenCentered 
   flags | #PB_Window_SizeGadget
   
   If OpenWindow(#mainwindow, 0, 0, 300, 200, "Test", flags) 
      
      ButtonGadget(#killbutton, 2, 2, 122, 22,"kill gadget links")
      ButtonGadget(#testbutton, 2, 165, 122, 22, "tue nix")
      
      MeinGadget(#canvasgadget1, 20, 40, 100, 100, "Gadget links")
      DrawGadget(#canvasgadget1)
      
      canvasgadget2 = MeinGadget(#PB_Any, 140, 20, 150, 150, "Gadget rechts")
      DrawGadget(canvasgadget2)
      
      Protected event, eventtype, eventwindow
      
      Repeat
         event = WaitWindowEvent()      
         eventwindow = EventWindow()
         
         If event = #PB_Event_Gadget          
            eventtype = EventType() 
            Select EventGadget()               
               Case #testbutton
                  Debug "testbutton gedrückt"
                  
               Case #killbutton
                  FreeGadget(#canvasgadget1)
                  
               Case #canvasgadget1
                  Select eventtype                        
                     Case #myeventtype1: Debug "Mainschleife #canvasgadget1 dies ist #myeventtype1"
                     Case #PB_EventType_LeftClick: Debug "Mainschleife #canvasgadget1 #PB_EventType_LeftClick"
                     Case #PB_EventType_KeyDown:   Debug "Mainschleife #canvasgadget1 #PB_EventType_KeyDown"
                  EndSelect
                  
               Case canvasgadget2
                  Select eventtype
                     Case #myeventtype1: Debug "Mainschleife canvasgadget2 dies ist #myeventtype1"
                     Case #PB_EventType_LeftClick: Debug "Mainschleife canvasgadget2 #PB_EventType_LeftClick"
                     Case #PB_EventType_KeyDown:   Debug "Mainschleife canvasgadget2 #PB_EventType_KeyDown "
                  EndSelect
                  
            EndSelect         
            
         ElseIf event = #mymessage1
            Debug "dies ist #mymessage1 " + Str(#mymessage1)
            
         ElseIf event = #mymessage2
            Debug "dies ist #mymessage2 " + Str(#mymessage2)
            
         ElseIf event = #PB_Event_CloseWindow
            If eventwindow = #mainwindow
               Break
            EndIf   
         EndIf    
         
      ForEver   
   EndIf
   
EndProcedure

MainWindow()
Purebasic 5.70 x86 5.72 X 64 - Windows 10

Der Computer hat dem menschlichen Gehirn gegenüber nur einen Vorteil: Er wird benutzt
grüße hjbremer
Benutzeravatar
edel
Beiträge: 3667
Registriert: 28.07.2005 12:39
Computerausstattung: GameBoy
Kontaktdaten:

Re: Frage zu WindowCallbackProc & eigenem Gadget mittels Can

Beitrag von edel »

Hier mal ein einfaches Beispiel fuer einen ON/OFF Schalter. (Nur Windows)

Code: Alles auswählen

; PB internes Zeug
Structure PB_GadgetVT
	GadgetType.l
	SizeOf.l
	GadgetCallback.i
	FreeGadget.i
	GetGadgetState.i
	SetGadgetState.i
EndStructure

ImportC "" 
	PB_Event_AddWithData(a,b,c,d,e,f)   
EndImport

; Eigene Nachrichten
#CHANGESTATE    = #WM_USER
#GETSTATE       = #WM_USER + 1
#SETSTATE       = #WM_USER + 2

Structure mygadget
	Gadget.i
	OldProc.i
	State.i	
EndStructure

;///////////////////////////////////////////////////
Procedure VT__GetGadgetState(*Gadget.integer)
	ProcedureReturn SendMessage_(*Gadget\i, #GETSTATE, 0, 0)
EndProcedure

;///////////////////////////////////////////////////
Procedure VT__SetGadgetState(*Gadget.integer, state)	
	SendMessage_(*Gadget\i, #SETSTATE, 0, state)	
EndProcedure

;///////////////////////////////////////////////////
Procedure mygadgetProc(hWnd, Msg, wParam, lParam)
	Protected *mg.mygadget = GetWindowLongPtr_(hWnd, #GWLP_USERDATA)
	Protected x , y, color, hdc, rect.rect
	Protected Proc
	
	proc = *mg\OldProc
	
	If Msg = #WM_PAINT
		
		hdc = StartDrawing(CanvasOutput(*mg\Gadget))
			
			If *mg\State = 0				
				color = $7779E9
			Else
				color = $9CE3AA
			EndIf
			
			Box(0, 0, GadgetWidth(*mg\Gadget), GadgetHeight(*mg\Gadget), color)
			
			rect\right = GadgetWidth(*mg\Gadget) 
			rect\bottom = GadgetHeight(*mg\Gadget) 
			
			DrawEdge_(hdc, rect, #BDR_SUNKENINNER, #BF_RECT)
			
			If *mg\State = 0
				rect\right = GadgetWidth(*mg\Gadget) 	/ 2            
			Else
				rect\left  = GadgetWidth(*mg\Gadget)  / 2      
				rect\right = GadgetWidth(*mg\Gadget) 	/ 2 + rect\left         
			EndIf
			
			rect\left 	+ 1
			rect\top 		= 1
			rect\bottom - 1
			
			DrawFrameControl_(hdc, rect, #DFC_BUTTON, #DFCS_BUTTONPUSH)         
			
		StopDrawing()
	EndIf
	
	If Msg = #GETSTATE
		ProcedureReturn *mg\State
	EndIf
	
	If Msg = #SETSTATE
		*mg\State = lparam
		SendMessage_(hWnd, #WM_PAINT, 0, 0)	
	EndIf   
	
	If Msg = #WM_LBUTTONUP		
		*mg\State ! 1		
		PB_Event_AddWithData($332C, *mg\Gadget, 0, #PB_EventType_Change, 0, 0)      
		SendMessage_(hWnd, #WM_PAINT, 0, 0)		
	EndIf      
	
	If Msg = #WM_DESTROY
		SetWindowLongPtr_(hWnd, #GWLP_WNDPROC, proc)
		FreeMemory(*mg)
	EndIf
	
	ProcedureReturn CallWindowProc_(Proc, hWnd, Msg, wParam, lParam)
EndProcedure


;///////////////////////////////////////////////////
Procedure MyGadget(id, x, y, width, height)
	
	Protected gadget
	Protected *mg.mygadget
	Protected *vt.PB_GadgetVT
	
	*mg = AllocateMemory(SizeOf(mygadget))
	
	gadget = CanvasGadget(id, x, y, width, height, #PB_Canvas_Keyboard | #PB_Canvas_ClipMouse)
	
	If id = #PB_Any
		*mg\Gadget = gadget
	Else
		*mg\Gadget = id
	EndIf	
	
	;Gadget Daten im Gadget speichern
	SetWindowLongPtr_(GadgetID(*mg\Gadget), #GWLP_USERDATA, *mg)
	
	;Callback umbiegen 
	*mg\OldProc = SetWindowLongPtr_(GadgetID(*mg\Gadget), #GWLP_WNDPROC, @mygadgetProc())
	
	;setzen von GetGadgetState und SetGadgetState
	*vt = PeekI(IsGadget(*mg\Gadget) + SizeOf(Integer))
	
	*vt\GetGadgetState    = @VT__GetGadgetState()   
	*vt\SetGadgetState    = @VT__SetGadgetState()
	
	ProcedureReturn gadget
EndProcedure

;///////////////////////////////////////////////////
;///////////////////////////////////////////////////
;///////////////////////////////////////////////////
Procedure main()
	
	Protected window
	Protected gadget
	Protected event
	
	window = OpenWindow(#PB_Any, 0, 0, 300, 280, "", #PB_Window_SystemMenu|#PB_Window_ScreenCentered)   
	
	gadget = MyGadget(#PB_Any, 10, 10, 35, 18)
	text   = TextGadget(#PB_Any, 50, 12, 20, 20, "OFF")
	
	gadget2 = MyGadget(#PB_Any, 10, 30, 35, 18)
	text2   = TextGadget(#PB_Any, 50, 32, 20, 20, "ON")
	
	SetGadgetState(Gadget2, 1)
	
	Repeat
		
		event = WaitWindowEvent()
		
		If event = #PB_Event_CloseWindow
			Break
		EndIf
		
		If event = #PB_Event_Gadget
			
			If EventType() = #PB_EventType_Change
				Select EventGadget()
					Case gadget
						If GetGadgetState(gadget)
							SetGadgetText(text, "ON")
						Else
							SetGadgetText(text, "OFF")
						EndIf         
					Case gadget2
						If GetGadgetState(gadget2)
							SetGadgetText(text2, "ON")
						Else
							SetGadgetText(text2, "OFF")
						EndIf         
				EndSelect            
			EndIf
			
		EndIf      
		
	ForEver
	
EndProcedure:End main()
Benutzeravatar
Kurzer
Beiträge: 1617
Registriert: 25.04.2006 17:29
Wohnort: Nähe Hamburg

Re: Frage zu WindowCallbackProc & eigenem Gadget mittels Can

Beitrag von Kurzer »

Vielen Dank euch beiden. Eure Beispiele helfen mir auf jeden Fall weiter. :allright:

Beide Codes kann ich von der Funktion her verstehen, technisch allerdings teilweise nicht vollständig.

In Edels Beispiel werden offenbar die interen PB-Befehle "SetGadgetState()" und "GetGadgetState()" auf eigene Prozeduren umgebogen. Bzw. sind diese Befehle für ein CanvasGadget gar nicht verfügbar. Insofern wurden sie mit...

Code: Alles auswählen

   *vt = PeekI(IsGadget(*mg\Gadget) + SizeOf(Integer))
   *vt\GetGadgetState    = @VT__GetGadgetState()   
   *vt\SetGadgetState    = @VT__SetGadgetState()
...erstmals gesetzt.

VT_ steht vermutlich für "virtual table"... die Funktionalität dahinter kann ich nur erahnen.

Ich plauder mal meine Gedanken frei von der Leber weg. Sollte Nonsens dabei sein, dann berichtigt mich bitte gern.

Code: Alles auswählen

*vt = PeekI(IsGadget(*mg\Gadget) + SizeOf(Integer))
IsGadget() liefert offenbar die Basisadresse einer Gadgetstruktur zurück die PB intern verwaltet. In dem Fall die Struktur des Canvasgadgets.
An 4. Stelle steht vermutlich die Adresse der zu dem Gadget gehörenden "virtuellen Tabelle", in der die Funktionsadressen (Befehls-Prozeduren) für dieses Gadget verwaltet werden (wie man an diese Internas herankommt weiß ich nicht, ich nehme es erstmal nur als "blackbox" an).

Da diese Gadgetstruktur nur PB intern vorliegt, kann man auch nicht strukturiert darauf zugreifen, solange man sie nicht manuell im Source definiert. Daher...

Code: Alles auswählen

Structure PB_GadgetVT
   GadgetType.l
   SizeOf.l
   GadgetCallback.i
   FreeGadget.i
   GetGadgetState.i
   SetGadgetState.i
EndStructure
Woher diese Info herkommt, also welcher Funktionspointer an welcher Stelle steht, weiß ich ebenfalls nicht... diese Info wird von mir wieder als "blackbox" behandelt. ;)

Der Rest ist dann wieder eindeutig. Mit...

Code: Alles auswählen

   *vt\GetGadgetState    = @VT__GetGadgetState()   
   *vt\SetGadgetState    = @VT__SetGadgetState()
... werden die Funktionspointer ausgetauscht, so dass PB bei Aufruf der entsprechenden Befehle intern in unsere eigenen Prozeduren springt. Coole Sache eigentlich.

Bei zwei Sachen bin ich mir auch nicht nicht ganz sicher.

1) Das Zwischenspeichern der eigenen Gadgetstruktur (inkl. der Adresse der alten Callbackprozedur):

Edel nutzt folgendes: SetWindowLongPtr_(hWnd, #GWLP_WNDPROC, proc)
HJBremer nutzt dagegen: SetProp_(\gadgetId, #MeinGadget_PropId, *mg)

Beides funktioniert und beides ist wohl auch für diesen Zweck benutzbar (lt. WIn32-API Hilfe).

Frage: Welche Methode ist sinnvoller oder besser?

Ich persönlich würde SetWindowLongPtr_ bevorzugen, da ich mir vorstellen kann, dass der Zugriff bei SetProp_/GetProp_ langsamer ist, da dort der betreffende Eintrag über eine Zeichenkette indiziert ist. Das Auffinden des Wertes in der Propertyliste von Windows wird vermutlich langsamer sein als bei GetWindowLongPtr_(). Okay, ist vermutlich irrelevant bei den paar Events, die da pro Sekunde bearbeitet werden, aber es geht in dem Fall einfach nur ums Bauchgefühl. ;)

2) Das Einfügen eigener Events in die Eventqeue:

Edel nutzt dafür: PB_Event_AddWithData(a,b,c,d,e,f)
HJBremer nutzt: PB_Gadget_SendGadgetCommand(id, pbEventType)

Auch hier gilt wieder: Beides funktioniert, doch welches sollte man sinnvollerweise nutzen?

Persönlich tendiere ich zu HJBremers Methode, da ich mir diese eher erklären kann. Die Parameter id und pbEventType sind selbsterklärend, wo hingegen ich bei a,b,c,d,e und f keine Ahnung habe was das für Parameter sind.

Das

Code: Alles auswählen

ImportC "" 
   PB_Event_AddWithData(a,b,c,d,e,f)   
EndImport
und

Code: Alles auswählen

Import "" 
   PB_Gadget_SendGadgetCommand(id, pbEventType) ;am besten hohe Werte 
EndImport
ist für mich auch noch die reinste "blackbox".
Anm.: Ich erinnere mich, dass ich vor langer Zeit auch mal von ts-soft ein Beispiel/Fragment für einen ownerdrawn Button bekommen habe (da gab es das CanvasGadget noch nicht) und dort auch diese Imports ohne Pfadangabe drin vorkamen. Ich bin seinerzeit aber nicht weitergekommen und hatte das Vorhaben "ownerdrawn Buttons" dann erstmal beendet.

Vielleicht könnt ihr auch hierzu noch ein bisschen aus dem Nähkästchen plaudern? ;)
Ich werde das Customgadget-Gerüst, das aus diesen Infos entsteht, als auch das Sudoku-Gadget hier im Forum veröffentlichen.
"Never run a changing system!" | "Unterhalten sich zwei Alleinunterhalter... Paradox, oder?"
PB 6.12 x64, OS: Win 11 24H2 x64, Desktopscaling: 150%, CPU: I7 12700 H, RAM: 32 GB, GPU: Intel(R) Iris(R) Xe Graphics | NVIDIA GeForce RTX 3070
Useralter in 2025: 57 Jahre.
Benutzeravatar
ts-soft
Beiträge: 22292
Registriert: 08.09.2004 00:57
Computerausstattung: Mainboard: MSI 970A-G43
CPU: AMD FX-6300 Six-Core Processor
GraKa: GeForce GTX 750 Ti, 2 GB
Memory: 16 GB DDR3-1600 - Dual Channel
Wohnort: Berlin

Re: Frage zu WindowCallbackProc & eigenem Gadget mittels Can

Beitrag von ts-soft »

Die Blackbox befindet sich im SDK Ordner von PB, z.B. die "PB_GadgetStructure" in der Gadget.h Datei.

Code: Alles auswählen

typedef struct PB_GadgetStructure
{
  HWND         Gadget;
  PB_GadgetVT *VT;
  integer      UserData;           // for Get/SetGadgetData
  WNDPROC      OldCallback;    // for PB_Gadget_RegisterDestroy
  integer      Data[4];            // for gadget internal data. (mostly used for front/backcolor and such).
} PB_Gadget;
Der Code von HJBremer baut komplett auf WinAPI auf, bis auf das PBInterne "PB_Gadget_SendGadgetCommand",
wobei der Code von edel auf WinAPI und PureBasic SDK aufbaut, was eine bessere Verzahnung bietet.
"PB_Event_AddWithData" ist ähnlich dem PostEvent() (PB5.10) und wahrscheinlich die zukunftssicherere Variante.

Ich hoffe mal ein bißchen den grauen Schleier Deiner schwarzen Boxen gelüftet zu haben.

Gruß
Thomas
PureBasic 5.73 LTS | SpiderBasic 2.30 | Windows 10 Pro (x64) | Linux Mint 20.1 (x64)
Nutella hat nur sehr wenig Vitamine. Deswegen muss man davon relativ viel essen.
Bild
Benutzeravatar
Kurzer
Beiträge: 1617
Registriert: 25.04.2006 17:29
Wohnort: Nähe Hamburg

Re: Frage zu WindowCallbackProc & eigenem Gadget mittels Can

Beitrag von Kurzer »

Danke ts-soft, die Strukturen habe ich gefunden. Soweit lichtet sich der Schleier.

Aber was bedeutet folgender Hinweis in der Event.h (am Ende der Datei)?

Code: Alles auswählen

// When using PB_Event_AddWithData, the caller of PB_Event_Get must ensure that
// the FreeData function is called when it received a non-zero free function pointer

void PB_Event_Add(int ID, integer ObjectID, integer WindowID, integer Type);
void PB_Event_AddWithData(int ID, integer ObjectID, integer WindowID, integer Type, void *Data, EventDataFreeFunction FreeData);
Müsste in Edels code noch ein Freedata vorkommen?

Auch die ID von $332C in Edels code erschliesst sich mir nicht. Ist es egal welche ID man einsetzt? WindowID = 0 ist auch unklar. Vermutung: Der Teil muss nur gefüllt sein, wenn den Event ein Fenster sendet und kein Gadget.
"Never run a changing system!" | "Unterhalten sich zwei Alleinunterhalter... Paradox, oder?"
PB 6.12 x64, OS: Win 11 24H2 x64, Desktopscaling: 150%, CPU: I7 12700 H, RAM: 32 GB, GPU: Intel(R) Iris(R) Xe Graphics | NVIDIA GeForce RTX 3070
Useralter in 2025: 57 Jahre.
Benutzeravatar
Kurzer
Beiträge: 1617
Registriert: 25.04.2006 17:29
Wohnort: Nähe Hamburg

Re: Frage zu WindowCallbackProc & eigenem Gadget mittels Can

Beitrag von Kurzer »

Ich sitze mal wieder an dem CanvasGadget.

Mit fällt auf, dass Canvas das Fokusrechteck, wenn es denn aktiviert wird, innerhalb der Zeichenfläche platziert. Ist mir leider erst jetzt aufgefallen nachdem die Zeichenroutinen für das CustomGadget geschrieben wurden.
Das Fokusrechteck liegt also genau über meiner CustomGadget-Grafik.

Bild

Kann man dem Canvas beibringen, dass es den Fokusrahmen nach außen legt?

Noch besser wäre natürlich, wenn ich eine eigene Prozedur zum Zeichnen des Fokusrechtecks verwenden könnte, dann würde ich z.B. die Hintergrundfarbe ändern, um das aktive Gadget hervorzuheben.
Gibt es da Möglichkeiten?
"Never run a changing system!" | "Unterhalten sich zwei Alleinunterhalter... Paradox, oder?"
PB 6.12 x64, OS: Win 11 24H2 x64, Desktopscaling: 150%, CPU: I7 12700 H, RAM: 32 GB, GPU: Intel(R) Iris(R) Xe Graphics | NVIDIA GeForce RTX 3070
Useralter in 2025: 57 Jahre.
Sirius-2337
Beiträge: 71
Registriert: 29.05.2010 20:55

Re: Frage zu WindowCallbackProc & eigenem Gadget mittels Can

Beitrag von Sirius-2337 »

Kurzer hat geschrieben: Noch besser wäre natürlich, wenn ich eine eigene Prozedur zum Zeichnen des Fokusrechtecks verwenden könnte, dann würde ich z.B. die Hintergrundfarbe ändern, um das aktive Gadget hervorzuheben.
Gibt es da Möglichkeiten?
Dafür brauchts du eigentlich nur den "#PB_Canvas_DrawFocus"-Flag weglassen und dann bei einem "#PB_EventType_Focus"-Event deinen eigenen Focus zeichnen und bei "#PB_EventType_LostFocus" wieder normal zeichnen.
Benutzeravatar
Kurzer
Beiträge: 1617
Registriert: 25.04.2006 17:29
Wohnort: Nähe Hamburg

Re: Frage zu WindowCallbackProc & eigenem Gadget mittels Can

Beitrag von Kurzer »

Stimmt Sirius, ist eigentlich gar nicht so schwierig.

Ich habe es zwischenzeitlich so gemacht, dass ich während des #WM_PAINT Events mit "GetActiveGadget()" prüfe, ob gerade das aktive Gadget gezeichnet wird. Wenn das der Fall ist, dann wird es mit einer anderen Hintergrundfarbe hervorgehoben. Das Flag #PB_Canvas_DrawFocus beim erzeugen des Gadgets wird dabei weggelassen, so wie Du es schon gesagt hattest.
"Never run a changing system!" | "Unterhalten sich zwei Alleinunterhalter... Paradox, oder?"
PB 6.12 x64, OS: Win 11 24H2 x64, Desktopscaling: 150%, CPU: I7 12700 H, RAM: 32 GB, GPU: Intel(R) Iris(R) Xe Graphics | NVIDIA GeForce RTX 3070
Useralter in 2025: 57 Jahre.
Benutzeravatar
Kurzer
Beiträge: 1617
Registriert: 25.04.2006 17:29
Wohnort: Nähe Hamburg

Re: Frage zu WindowCallbackProc & eigenem Gadget mittels Can

Beitrag von Kurzer »

Und schon habe ich wieder eine Frage.
Es geht wieder um das oben abgebildete "Zahlencodefeld".

Das Gadget soll nach dem Anklicken eines Zahlenfeldes dieses farblich hervorheben und die Farbe dann innerhalb von 1-2 Sekunden langsam wieder "wegdimmen".

Dazu brauche ich eigentlich eine Art Timer, aber Timer funktionieren nur mit Fenstern, nicht mit Gadgets.
Ich würde es im nächsten Schritt mit einem Thread versuchen, kommt mir aber irgendwie wie mit Kanonen auf Spatzen geschossen vor (außerdem muss das Programm, welches das Gadget verwendet dann ja "threadsafe" compiliert werden. Ist mir alles noch nicht so genehm).

Gibt es dazu Gadget-intern etwas eleganteres, was mir regelmäßig einen Event liefert?
Für meinen Fall kann das ruhig API sein, das Gadget ist durch den Callback eh Win-only.
"Never run a changing system!" | "Unterhalten sich zwei Alleinunterhalter... Paradox, oder?"
PB 6.12 x64, OS: Win 11 24H2 x64, Desktopscaling: 150%, CPU: I7 12700 H, RAM: 32 GB, GPU: Intel(R) Iris(R) Xe Graphics | NVIDIA GeForce RTX 3070
Useralter in 2025: 57 Jahre.
Antworten