[WIP] MODUL::CanvasPopupImageMenu (für Touch)

Hier könnt Ihr gute, von Euch geschriebene Codes posten. Sie müssen auf jeden Fall funktionieren und sollten möglichst effizient, elegant und beispielhaft oder einfach nur cool sein.
Derren
Beiträge: 557
Registriert: 23.07.2011 02:08

[WIP] MODUL::CanvasPopupImageMenu (für Touch)

Beitrag von Derren »

Guten Abend :coderselixir:

Für eine Touch-Anwendung habe ich mir gerade ein PopupImageMenu auf Canvas-Basis gebastelt (Hintergrund: Kein Rechtsklick und pfriemelige Minimenüs, die man nicht mal mit einem Stift richtig trifft. Da es ein Hybrid-Tablet/Laptop ist, will ich auch nicht die Anzeigeeinstellungen auf Rentner-Modus stellen. Daher, für die 2 Anwendungen, die ich für dne Touch/Tablet/Finger Modus entwickle, eigene, große GUI-Elemente)


edit: Modularisiert und Code ein wenig aufgeräumt (gibt aber trotzdem sicher noch einiges zu verbessern). Man kann jetzt mehrere, verschiedene Menüs erstellen.

Bild

Include:

Code: Alles auswählen

EnableExplicit
DeclareModule CanvasPopupMenu
	#Event_PopupMenu         = #PB_Event_FirstCustomValue     + 1 ;Bei Kollision mit anderen Custom-Events anpassen!
	#EventType_EntrySelected = #PB_EventType_FirstCustomValue + 1 ;Bei Kollision mit anderen Custom-Events anpassen!
	
	
	Declare Create(menuID, entryHeight.i, backgroundColor=0, fontID=#PB_Default)  ;Gibt #False zurück, falls Menü schon existiert. Anderfalls #True	
	Declare AddMenuItem(menuID, imageID, text.s, textcolor=$FFFFFF)				  ; Gibt ID des erstellen Eintrags zurück: entryID.i = AddCanvasPopupMenuEntry(...)	
	Declare Display(menuID, parentWindow, x=0, y=0)								  ; Wie PB-Befehl: DisplayPopupMenu()		
	Declare CanvasEventMenu()													  ;Äquivalent zu EventMenu(), gibt die ID des Menus zurück in welchem ein Eintrag gewählt wurde	
	Declare GetPopupEntryID()													  ;Gibt die ID des Menu-Eintrags zurück, welcher angeklickt wurde 
																				  ;(Achtung, anders als PB können mehrere Menüs Einträge mit der gleichen ID haben. 
																				  ;Bei Verwedung mehrerer Menüs ist unbedingt CanvasEventMenu() vorzuschalten
EndDeclareModule



Module CanvasPopupMenu
	
	Define popupMenuWindowHandle
	Define currentPopupWindowHandle ;-1 if no popup is currently open
	
	Structure struct_canvaspopupmenuentry
		imageID.i
		text.s
		textColor.i	
	EndStructure	
	
	Structure struct_canvaspopupmenu
		menuID.i
		parentWindow.i
		fontID.i
		backColor.i
		entryHeight.i
		List entries.struct_canvaspopupmenuentry()
	EndStructure 
	
	NewList Menus.struct_canvaspopupmenu()		
	Procedure __closepopup()
		Shared popupMenuWindowHandle
		Shared currentPopupWindowHandle
		currentPopupWindowHandle = -1
		CloseWindow(popupMenuWindowHandle)
	EndProcedure 
	
	Procedure __canvaspopupentry_clicked()
		Shared currentPopupWindowHandle
		Shared popupMenuWindowHandle
		Shared Menus()
		
		Protected entryheight		
		If currentPopupWindowHandle > -1		
			ForEach Menus()
				If Menus()\menuID = currentPopupWindowHandle
					entryheight = Menus()\entryheight
					PostEvent(#Event_PopupMenu, EventWindow(), currentPopupWindowHandle, #EventType_EntrySelected, WindowMouseY(popupMenuWindowHandle)/entryheight )
					__closepopup()
					Break 
				EndIf 
			Next 
		EndIf 
	EndProcedure 
	
	
	Procedure Create(menuID, entryHeight.i, backgroundColor=0, fontID=#PB_Default)
		Shared Menus()
		
		ForEach Menus()
			If Menus()\menuID =menuID
				ProcedureReturn #False
			EndIf 
		Next 
		
		AddElement(Menus())
		Menus()\menuID=menuID
		Menus()\backColor = backgroundColor
		Menus()\fontID = fontID
		Menus()\entryHeight = entryHeight
		
		ProcedureReturn #True 
	EndProcedure 
	
	Procedure AddMenuItem(menuID, imageID, text.s, textcolor=$FFFFFF)
		Shared Menus()
		
		AddElement(Menus()\entries())
		With Menus()\entries()
			\textColor = textcolor
			\imageID = imageID
			\text = text
		EndWith 	
		ProcedureReturn ListSize(Menus()\entries()) - 1
	EndProcedure
	
	Procedure Display(menuID, parentWindow, x=0, y=0)
		Shared popupMenuWindowHandle 
		Shared currentPopupWindowHandle
		
		Protected c, numof_entries, ypos, maxtextwidth
		Shared Menus()
		ForEach Menus()
			
			If Menus()\menuID = menuID				
				
				popupMenuWindowHandle=OpenWindow(#PB_Any, WindowX(parentwindow, #PB_Window_InnerCoordinate)+WindowMouseX(parentwindow)+x,WindowY(parentwindow, #PB_Window_InnerCoordinate)+WindowMouseY(parentwindow)+y, 0,0,"POPUP",#PB_Window_BorderLess, WindowID(parentwindow))
				currentPopupWindowHandle = menuID
				
				BindEvent(#PB_Event_DeactivateWindow, @__closepopup(), popupMenuWindowHandle)
				
				c=CanvasGadget(#PB_Any, 0, 0, 2000, 2000)
				
				StartDrawing(CanvasOutput(c))
				
				Box(0, 0, 2000,2000, Menus()\backcolor)
				
				If Menus()\fontid <> #PB_Default 
					DrawingFont(Menus()\fontid)
				EndIf 
				DrawingMode(#PB_2DDrawing_Transparent)
				ForEach Menus()\entries()
					
					numof_entries = numof_entries + 1
					With Menus()\entries()
						DrawImage(\imageID, 0, ypos, Menus()\entryheight, Menus()\entryheight)
						DrawText(Menus()\entryheight+5, ypos + Menus()\entryheight/2 - TextHeight(\text)/2, \text,\textColor)
						If TextWidth(\text)>maxtextwidth
							maxtextwidth = TextWidth(\text)
						EndIf 
					EndWith	
					ypos = ypos + Menus()\entryheight
				Next 
				StopDrawing()
				
				BindEvent(#PB_Event_Gadget, @__canvaspopupentry_clicked(), popupMenuWindowHandle, c, #PB_EventType_LeftClick )
				ResizeWindow(popupMenuWindowHandle, #PB_Ignore, #PB_Ignore, Menus()\entryheight+10+maxtextwidth, numof_entries * Menus()\entryheight)
				ResizeGadget(c, #PB_Ignore, #PB_Ignore, Menus()\entryheight+10+maxtextwidth, numof_entries * Menus()\entryheight)
				Break
			EndIf 
			
			
		Next 
	EndProcedure 
	
	Procedure GetPopupEntryID()
		ProcedureReturn EventData()
	EndProcedure 
	
	Procedure CanvasEventMenu()
		ProcedureReturn EventGadget()
	EndProcedure
	
EndModule 
DisableExplicit

Beispiel:

Code: Alles auswählen

;-
;-
;- BEGIN EXAMPLE
;-

If #PB_Compiler_IsMainFile = #True 
	
	;Erstelle einige Testbilder als Icons für die Menüeinträge
	CreateImage(0, 50, 50):StartDrawing(ImageOutput(0)):Circle(25,25,24,$FF6600):StopDrawing()
	CreateImage(1, 50, 50):StartDrawing(ImageOutput(1)):Circle(25,25,24,$0000FF):StopDrawing()
	CreateImage(2, 50, 50):StartDrawing(ImageOutput(2)):Circle(25,25,24,$00FF00):StopDrawing()
	CreateImage(3, 50, 50):StartDrawing(ImageOutput(3)):Circle(25,25,24,$FFFFFF):StopDrawing()
	
	
	;Öffne Testfenster mit Button
	#Win = 0
	OpenWindow(#Win, 0, 0, 400, 400, "Canvas Popup Test", #PB_Window_ScreenCentered|#PB_Window_SystemMenu)
	ButtonGadget(0, 10, 10, 150, 40, "TEST")
	ButtonGadget(1, 10, 110, 150, 40, "TEST2")
	
	
	;Lade Fonts für das CanvasMenu
	LoadFont(0, "Times New Roman", 14, #PB_Font_Italic)
	LoadFont(1, "Wingdings", 16)	
	
	
	;Erstelle Popup-Menu ("Equivalent" zu CreatePopupImageMenu())
	;CanvasPopupMenu::Create(MenuID, Größe der Icons (zugleich die Höhe ein Einträge), [Hintergrundfarbe des Menüs], [Font])
	CanvasPopupMenu::Create(1, 50, $FFcc99, FontID(0))
	
	;Füge Einträge hinzu (in der Reihenfolge des Codes. Hier gibt es keine IDs zu vergeben. Die IDs werden von der Funktion zurückgegeben)
	;CanvasPopupMenu::AddMenuItem(Eltern-Menu, ImageID für das Icon, Text, Textfarbe)
	CanvasPopupMenu::AddMenuItem(1, ImageID(1), "Rot", 0)
	CanvasPopupMenu::AddMenuItem(1, ImageID(0), "Blau blau blau blau........lj 8u9 gg7 vbnun97", $FF00FF)
	CanvasPopupMenu::AddMenuItem(1, ImageID(2), "Grün", $009900)
	myEntryID = CanvasPopupMenu::AddMenuItem(1, ImageID(3), "Klick hier und lass dich vom Debugger überraschen", $00FFFF) ;returns ID
	
	
	;Erstelle zweites Menu
	CanvasPopupMenu::Create(2, 30, $FFcc99, FontID(1))
	CanvasPopupMenu::AddMenuItem(2, ImageID(1), "ABC", 0)
	CanvasPopupMenu::AddMenuItem(2, ImageID(0), "47r", $990000)	
	
	
	Repeat
		e=WaitWindowEvent()
		If e=#PB_Event_Gadget
			If EventGadget()=0	
				;Ähnlich wie in PB: DisplayPopupMenu(menuID, parentWindow, [x, y])
				CanvasPopupMenu::Display(1, #Win)	
				
			ElseIf EventGadget()=1
				CanvasPopupMenu::Display(2, #Win)			
			EndIf 
		EndIf 	
		
		If e=CanvasPopupMenu::#Event_PopupMenu ;Wenn unser Custom-Event auftritt...
			
			Debug "EventMenu: "+CanvasPopupMenu::CanvasEventMenu()
			Debug "EntryID:   "+CanvasPopupMenu::GetPopupEntryID() 
			Debug ""
			
			
			If CanvasPopupMenu::CanvasEventMenu() = 1 And CanvasPopupMenu::GetPopupEntryID()  = myEntryID
				Debug ">>>"
				Debug ">>> ÜBERRASCHUNG!! :)"
				Debug ">>>"
			EndIf 
			
			
		EndIf 
	Until e=#PB_Event_CloseWindow
EndIf 
Zuletzt geändert von Derren am 21.02.2016 17:07, insgesamt 4-mal geändert.
Signatur und so
Benutzeravatar
Macros
Beiträge: 1314
Registriert: 23.12.2005 15:00
Wohnort: Olching(bei FFB)
Kontaktdaten:

Re: [WIP] Quick & Dirty: CanvasPopupImageMenu (für Touch)

Beitrag von Macros »

Guten Abend zurück :coderselixir:

Wenn du in Zeile 79 den Code folgendermaßen änderst:

Code: Alles auswählen

         h=OpenWindow(#PB_Any, WindowX(parentwindow, #PB_Window_InnerCoordinate)+x,WindowY(parentwindow, #PB_Window_InnerCoordinate)+y, 0,0,"POPUP",#PB_Window_BorderLess|#PB_Window_Tool,WindowID(parent))
         _id = id
         BindEvent(#PB_Event_DeactivateWindow, @__closepopup(), h)
Läuft es sogar unter Linux.
Allerdings sollte man momentan nicht aus versehen auf den Button klicken, wenn das Popup Menü schon da ist ;)

Gut Nacht
Bild
Derren
Beiträge: 557
Registriert: 23.07.2011 02:08

Re: [WIP] Quick & Dirty: CanvasPopupImageMenu (für Touch)

Beitrag von Derren »

Unter Linux sollte man nicht auf den button drücken?
Unter Windows gibt's bei mir keine Probleme. Das Event "#PB_Event_DeactivateWindow" wird bei einem Klick außerhalb des "Menüs" gefeuert und schließt das Fenster.

Unter Windows funktioniert auch die Kombi der PB-Flags ToolWindow & Borderless nicht.
Eine ParentID anzugeben kam mir allerdings nicht in den Sinn. Damit entfällt das ToolWindow dann eh (war nur dazu da um den Taskbareintrag zu unterdrücken).
Merci :allright:
Signatur und so
Benutzeravatar
Macros
Beiträge: 1314
Registriert: 23.12.2005 15:00
Wohnort: Olching(bei FFB)
Kontaktdaten:

Re: [WIP] Quick & Dirty: CanvasPopupImageMenu (für Touch)

Beitrag von Macros »

Problem gefunden. Siehe Zeile 85 und 86. Erklärt sich selbst ;)

Code: Alles auswählen

         BindEvent(#PB_Event_DeactivateWindow, @__closepopup(), h)
         BindEvent(#PB_Event_DeactivateWindow, @__closepopup(), h)
Linux lässt wohl zwei BindEvent mit selben Parametern zu und führt dann beide aus.
Bei Nummer zwei gibts das Fenster bereits nimmer. --> Crash

Nachdem das behoben wurde, darf der Code nun in meine Codesammlung.
Danke fürs schreiben :)
Wenn ich ihn mal brauche, wird er als Modul umgebaut. Poste ich dann hier.
Bild
Derren
Beiträge: 557
Registriert: 23.07.2011 02:08

Re: [WIP] Quick & Dirty: CanvasPopupImageMenu (für Touch)

Beitrag von Derren »

Oi. Da muss was beim Kopieren schief gelaufen sein. In meinem Code steht die Zeile nur 1mal da.
Wenn ich sie 2mal schreibe, crasht es unter Windows auch, so wie du gesagt hast, wenn man den Button drückt, während das Popup offen ist.
Ich pass es oben an :)
Danke :allright:
Signatur und so
Benutzeravatar
Sicro
Beiträge: 955
Registriert: 11.08.2005 19:08
Kontaktdaten:

Re: [WIP] MODUL::CanvasPopupImageMenu (für Touch)

Beitrag von Sicro »

Ich habe deinen Code gerade mal unter Linux ausprobiert und festgestellt, dass beim Klicken auf die Einträge nichts in der Debug-Ausgabe angezeigt wird.
Verursacht wird diese Fehlfunktion, weil du nach dem Klicken auf einen Eintrag das Fenster schließt und unter Linux dadurch auch die zum Fenster gehörenden Events aus der Nachrichtenwarteschlange [WaitWindowEvent() / WindowEvent()] gelöscht werden. Unter Linux gibt es in Wirklichkeit nämlich keine Haupt-Nachrichtenwarteschlange, sondern dort hat jedes Element seinen Callback, wie es PB auch mit den Befehlen BindEvent() und BindGadgetEvent() bereitstellt.
Die Lösung des Problems: Sende die Events per PostEvent() nicht - wie es aktuell ist - an das Popup-Fenster, sondern an das Parent-Fenster.

Code: Alles auswählen

Structure struct_canvaspopupmenu
  menuID.i
  parentWindow.i
  fontID.i
  backColor.i
  entryHeight.i
  List entries.struct_canvaspopupmenuentry()
EndStructure
parentWindow verwendest du nirgends im Code.

Code: Alles auswählen

If #PB_Compiler_IsMainFile = #True
Besser so:

Code: Alles auswählen

CompilerIf #PB_Compiler_IsMainFile = #True
Bild
Warum OpenSource eine Lizenz haben sollte :: PB-CodeArchiv-Rebirth :: Pleasant-Dark (Syntax-Farbschema) :: RegEx-Engine (kompiliert RegExes zu NFA/DFA)
Manjaro Xfce x64 (Hauptsystem) :: Windows 10 Home (VirtualBox) :: Neueste PureBasic-Version
Antworten