Writing a GUI system

Advanced game related topics
untune
User
User
Posts: 56
Joined: Sun Jul 06, 2008 10:07 pm
Location: Bolton, UK

Writing a GUI system

Post by untune »

Hi all

I'm really hoping someone can help me out here - I'm hoping to write a fairly generic, not very complicated GUI system for use in games... I'm thinking of a little mini windows type setup - ie windows, buttons, listboxes, etc etc

All I'm looking for at the moment are ideas on how best to implement it. I've made a start using the HGE engine to render it. Seems like a good idea since it's DX 8 and hardware accelerated.

So far I've managed to get basic "panes" (just blank boxes really) rendering but things such as getting focus, moving windows to the front when clicked etc are a bit baffling. I've almost managed to implement a "MouseOver" function that checks which pane the pointer is currently over, but it doesnt take into consideration which window is the frontmost when 2 or more are overlapped.

I'm currently storing the info relating to a window in the following struct:

Code: Select all

Structure Pane
    
    ID.l ; // Handle to pane
    *sprite ; // sprite pointer
    tl.mVector2 ; // top left x/y coordinate
    br.mVector2 ; // bottom right x/y coordinate
    s.mVector2 ; // Size of pane - x width * y height
    z.b ; // Depth of the pane - will help with focus and render sorting
    colour.l ; // colour of pane
    
EndStructure
Any input at all is most welcome, I've been hunting all over for code examples and tutorials but nothing is much help.

Cheers
untune
User
User
Posts: 56
Joined: Sun Jul 06, 2008 10:07 pm
Location: Bolton, UK

Post by untune »

If I'm storing each pane in a structured array like the structure shown above, does anyone have any ideas on the best way to do depth sorting? I've used a boolean to store the z depth which would limit to 256 "layers". I'm halfway there but I can't seem to get it to sort properly...

ahh I'm stuck and I can't think, take a look and hopefully somebody will be interested

Cheers

Code: Select all

; // Include the HGEWrapper include file
XIncludeFile #PB_Compiler_Home + "Includes\HGEWrapper_include.pbi"
XIncludeFile "math.pbi"

; // Constants
#SCREEN_WIDTH     =  800
#SCREEN_HEIGHT    =  600
#SCREEN_BPP       =  32
#SCREEN_WINDOWED  =  #True


; // Structures

Structure Pane
    
    ID.l ; // Handle to pane (pointer)
    *sprite ; // sprite pointer
    tl.mVector2 ; // top left x/y coordinate
    br.mVector2 ; // bottom right x/y coordinate
    s.mVector2 ; // Size of pane - x width * y height
    z.b ; // Depth of the pane - will help with focus and render sorting
    colour.l ; // colour of pane
    
EndStructure


; // Declares
Declare GUI_Pane_Create(x.f, y.f, w.f, h.f, colour.l)
Declare GUI_Pane_Delete()
Declare.l GUI_Pane_MouseOver()
Declare GUI_Pane_Update()
Declare GUI_Pane_Sort()
Declare GUI_Pane_BringToFront(*PaneID.Pane)


; // Globals
;Global pane.Pane
Global mx.f, my.f ; // Mouse X & Y
Global Dim Panes.Pane(0)
Global numPanes.l = 0
;Global PaneID.l = 0


ProcedureCDLL FrameFunc()

  ; // Delata time variable - keeps everything at the correct speed
  ; // [Time elapsed since last call of FrameFunc()]
  dt.f = hge_Timer_GetDelta()
  
  hge_Input_GetMousePos(@mx, @my)
  
  ; // If the escape key is pressed, exit the function
  Select hge_Input_GetKey()
  
    Case #HGEK_ESCAPE
        
        ProcedureReturn #True
        
    Case #HGEK_LBUTTON
    
        Debug "Mouse X: " + Str(mx) + "; Mouse Y: " + Str (my)
        
        Define *CurrentPane.Pane
        *CurrentPane = GUI_Pane_MouseOver()
        If *CurrentPane
    
            Debug "Pane ID: " + Str(*CurrentPane)
            If *CurrentPane\z < numPanes
                GUI_Pane_BringToFront(*CurrentPane)
            EndIf
            
        Else
        
            Debug "Pane ID = NULL" 
    
        EndIf
        
        ;GUI_Pane_Sort()
        
    Case #HGEK_N
    
        Debug "No. of panes: " + Str(numPanes)
     
        
  EndSelect


; // FrameFunc should always return FALSE
ProcedureReturn #False
EndProcedure

ProcedureCDLL RenderFunc()

  ; // Begin rendering - this MUST be called before any rendering occurs
  hge_Gfx_BeginScene(#Null)
  
  ; / Clear screen with black
  hge_Gfx_Clear(0)
  
  ; */
  ; // CODE GOES HERE
  ; /*
  GUI_Pane_Update()

  
  ; End rendering and update screen
  hge_Gfx_EndScene()


; // RenderFunc should always return FALSE
ProcedureReturn #False
EndProcedure

Procedure Main()

  ; // Create instance of HGE
  hge_Create(#HGE_VERSION)
  
  ; // System setup
  hge_System_SetStateFunc(#HGE_FRAMEFUNC, @FrameFunc())
  hge_System_SetStateFunc(#hge_RENDERFUNC, @RenderFunc())
  hge_System_SetStateString(#HGE_TITLE, "HGE Project")
  hge_System_SetStateBool(#HGE_SHOWSPLASH, #False)
  hge_System_SetStateBool(#HGE_HIDEMOUSE, #False)
  
  ; // Video setup
  hge_System_SetStateBool(#HGE_WINDOWED, #SCREEN_WINDOWED)
  hge_System_SetStateInt(#HGE_SCREENWIDTH, #SCREEN_WIDTH)
  hge_System_SetStateInt(#HGE_SCREENHEIGHT, #SCREEN_HEIGHT)
  hge_System_SetStateInt(#HGE_SCREENBPP, #SCREEN_BPP)
  
  ; // Disable sound
  hge_System_SetStateBool(#HGE_USESOUND, #False)
  
  ; Initiate system
  If hge_System_Initiate()
  
    ; */
    ; // LOAD RESOURCES, CREATE OBJECTS ETC...
    ; /*
    GUI_Pane_Create(64, 64, 400, 100, $FF00AADD)
    GUI_Pane_Create(128, 128, 600, 200, $FF00DDDD)
    GUI_Pane_Create(256, 256, 128, 128, $FFAA00DD)
    GUI_Pane_Create(64, 192, 600, 100, $FF00EEA3)
  
    ; // Start running FrameFunc()
    ; // At this point the execution stops and everything is handled within
    ; // the FrameFunc() procedure until it returns #TRUE
    hge_System_Start()
  
  Else
    ; // Error report
    error.s = PeekS(hge_System_GetErrorMessage(), -1, #PB_Ascii)
    MessageRequester("Error", error, #PB_MessageRequester_Ok)
  
  EndIf
  
  ; // Restore video settings, free allocated resources and release the interface
  GUI_Pane_Delete()
  hge_System_Shutdown()
  hge_Release()


EndProcedure


Procedure GUI_Pane_Create(x.f, y.f, w.f, h.f, colour.l)

    ReDim Panes.Pane(numPanes)

    ;*pane = AllocateMemory(SizeOf(Pane))
    Panes(numPanes)\tl\x = x
    Panes(numPanes)\tl\y = y
    Panes(numPanes)\br\x = (x + w)
    Panes(numPanes)\br\y = (y + h)
    Panes(numPanes)\s\x = w
    Panes(numPanes)\s\y = h
    Panes(numPanes)\colour = colour
    Panes(numPanes)\z = numPanes ; Create depth
    Panes(numPanes)\sprite = hge_Sprite(0, x, y, w, h)
    ;hge_Sprite_SetHotSpot(Panes(numPanes)\sprite, x, y)
    hge_Sprite_SetColor(Panes(numPanes)\sprite, colour)
    
    ;PaneID = PaneID + 1
    Panes(numPanes)\ID = @Panes(numPanes)
    ;Panes(numPanes)\ID = PaneID
    numPanes= (numPanes + 1)
    

EndProcedure


Procedure GUI_Pane_Delete()

    For i = 0 To (numPanes - 1)
    ;FreeMemory(*p)
    hge_Sprite_Delete(Panes(i)\sprite)
    Next i

EndProcedure


Procedure.l GUI_Pane_MouseOver()

    Protected i.l
    Protected result.l

    For i = 0 To (numPanes - 1)
    
        hge_Input_GetMousePos(@mx, @my)
        
            If mx >= Panes(i)\tl\x
            
                If mx <= Panes(i)\br\x
            
                    If my >= Panes(i)\tl\y
                    
                        If my <= Panes(i)\br\y
                
                            Panes(i)\ID = @Panes(i)
                            result = Panes(i)\ID
                            Break
                 
                        Else
                 
                            result = #False
                    
                        EndIf
                        
                    Else
                    
                        result = #False
                        
                    EndIf
                     
                    
                Else
                
                    result = #False
                    
                EndIf
                
            Else
            
                result = #False
                
            EndIf
    
    Next i


; // If mouse is over pane, return true, otherwise false
ProcedureReturn result

EndProcedure


Procedure GUI_Pane_Update()

    Protected i.l

    For i = 0 To (numPanes - 1)

        hge_Sprite_Render(Panes(i)\sprite, Panes(i)\tl\x, Panes(i)\tl\y)
    
    Next i

EndProcedure


Procedure GUI_Pane_Sort()

    SortStructuredArray(Panes(), #PB_Sort_Ascending, OffsetOf(Pane\z), #PB_Sort_Byte)

EndProcedure


Procedure GUI_Pane_BringToFront(*PaneID.Pane)
    
    *PaneID\z = (numPanes - 1)
    GUI_Pane_Sort()

EndProcedure


; // Main function
Main()
y3an
User
User
Posts: 56
Joined: Sun Mar 09, 2008 6:06 am

Post by y3an »

Hi,


In my game, i'm dealing with layers for the interface.

I have choose a simple way :q :

I Have an Array for the surface used ( 450*600 for me ), in bytes.

I just have a procedure who fill in the Z (from back to top) order the surface used by the windows, on this array, with the Z number, everytime something moves :q,
so, i just have to test what number is under the mouse.

From there, it's easy to add details of the window (borders, headers.. ) on this array, if you use multiples.

It's fast and not complicated.

Hope i have not missing something in your question, I galley with english some hours :roll:
untune
User
User
Posts: 56
Joined: Sun Jul 06, 2008 10:07 pm
Location: Bolton, UK

Post by untune »

Hi y3an, thanks so much for the reply

I think I understand what you mean, the thing I'm struggling with is checking which one under the mouse is closest. I have a couple of windows overlapping and I need it to detect that I'm clicking on the one nearest to the top but at the moment it just checks whether the mouse is within the rectangle.

Would you be able to provide any code to help me understand how your method works?

Cheers
y3an
User
User
Posts: 56
Joined: Sun Mar 09, 2008 6:06 am

Post by y3an »

Pfff i cant do anything better than an holly mess.. Sorry for the time i get to answer x)

The way to test X,Y .. W,H coordonates is surely better .. The way i use is because my layers arn't cubes... but sprites with many transparences..
So it was dumbtosay that :lol:

I think the thing could help you is linkedlist ! It's easy to swap.
I Use an external array to swap orders of the "windows" here, but it's not... :roll: perfect.. Very basic ..

I'am sure you can find better exemles on use of the linked list lol

Code: Select all







;****************************************************************************************************

If ExamineDesktops() And InitSprite() And InitKeyboard() And InitMouse()
If OpenWindow(0,0,0,DesktopWidth(0),DesktopHeight(0),"Mazeltoff",#PB_Window_BorderLess | #PB_Window_ScreenCentered) 
If OpenWindowedScreen(WindowID(0),0,0,DesktopWidth(0),DesktopHeight(0), 1,0,0)
      
Else : MessageRequester("Erreur", "Impossible d'ouvrir un ecran fenetré")            : End : EndIf 
Else : MessageRequester("Erreur", "Impossible d'ouvrir une fenetre")                 : End : EndIf
Else : MessageRequester("Erreur", "Impossible d'obtenir la resolution de l'ecran")   : End : EndIf









;**************************************** Structures, Dimz ****************************************
Structure constituents

  Width.w
  Height.w
  X.w
  Y.w
  Z.w

  Spe.l ; Option for the win

EndStructure





Global Nbz : NbZ = 6 ; Number of wins

Global Dim Win.constituents(NbZ)
Global Dim Viewable_Wins.b(DesktopWidth(0),DesktopHeight(0) )


Global NewList Liste()   ; <<< Here the linked list used to manage Z indexs.



#Main    = $212121

;***************************************************************************************************



;{ Creation des "windows"

;******************************************** Main window at background..

AddElement(Liste()) : Liste()= 0

                Win(0)\Width  = DesktopWidth(0)  - 20
                Win(0)\Height = DesktopHeight(0) - 20
                
                Win(0)\X      = 10
                Win(0)\Y      = 10
                
                Win(0)\Z      = 0  
                
                Win(0)\Spe    = #Main
 ;************************************************ 


For A = 1 To Nbz ; <<<<<<<<<<<<< Some boxes ...

AddElement(Liste()) : Liste() = A

                Win(A)\Width  = 180
                Win(A)\Height = 120
                
                Win(A)\X      = A*100
                Win(A)\Y      = A*75
                
                Win(A)\Z      = A  
                
                Win(A)\Spe    = RGB(A*20,A*5,A*6.4)
Next A

;}





Procedure Draw_Win( Z )


CreateSprite(Z,Win(Z)\Width, Win(Z)\Height)

StartDrawing(SpriteOutput(Z))
  Box(0,0, Win(Z)\Width, Win(Z)\Height, RGB(10,10,10))
  Box(2,2, Win(Z)\Width-4, Win(Z)\Height-4,   Win(Z)\Spe  )
StopDrawing()

EndProcedure

;<<<<<<<<<<<<<<<<< Here is what is dumb to manage rectangles windows !

Procedure Viewable()

For Nb = 0 To Nbz  ;If the tested Win is bigger, she's "draw", and it is Nb the mouse will point.

SelectElement( Liste(), Nb )
Z = Liste()

    For X = 0 To Win(Z)\Width : For Y = 0 To Win(Z)\Height

        If Viewable_Wins( Win(Z)\X+X, Win(Z)\Y + Y ) < Nb 
           Viewable_Wins( Win(Z)\X+X, Win(Z)\Y + Y ) = Nb
        EndIf

    Next : Next 


Next 

EndProcedure







CreateSprite(10, 20,20 )    ;   A mouse...
StartDrawing(SpriteOutput(10))

LineXY(1,0,20,19, $aa0000 )
LineXY(0,0,20,20, $ff0000 )
LineXY(0,1,19,20, $aa0000 )

StopDrawing()






For Z = 0 To NbZ : Draw_Win( Z ) : Next  ; <<<< Draw the wins on sprites.. Here just basics cubes...

Viewable()  ; < ---- ----- Here I fill the Array of the screen.









    Repeat : Repeat : Event = WindowEvent() :  Until Event = 0 
       
     ClearScreen(RGB(0,0,0)) 
          
         
         For Nb = 0 To NbZ  
         SelectElement( Liste(), Nb )
         Z = Liste()  
         DisplaySprite( Win(Z)\Z,  Win(Z)\X, Win(Z)\Y )
         Next
   

      
      DisplayTransparentSprite(10,MouseX,MouseY)
      
 

   
      ExamineMouse()
   
   
        If MouseDeltaX()<>0 : mouseX = mouseX+ MouseDeltaX() : EndIf
        If MouseDeltaY()<>0 : mouseY = mouseY+ MouseDeltaY() : EndIf
        
        If MouseX > DesktopWidth(0)  : MouseX = DesktopWidth(0)  : EndIf : If MouseX < 0 : MouseX = 0 : EndIf
        If MouseY > DesktopHeight(0) : MouseY = DesktopHeight(0) : EndIf : If MouseY < 0 : MouseY = 0 : EndIf
 
       
       If MouseButton(1) And Click = 0 And Viewable_Wins( mouseX,MouseY ) <> 0
       

;********************** Here is the useful SwapElements... ***********
        
           Focus = Viewable_Wins( mouseX,MouseY )    
                       
                SelectElement(Liste(), Focus)          
                *FirstElement = @Liste()
                SelectElement(Liste(), Nbz ) 
                *SecondElement = @Liste()
  
                SwapElements(Liste(), *FirstElement, *SecondElement)
                
                 Viewable() 
                 click = 1
                    
       EndIf
       
        
       If MouseButton(1) = 0 And click = 1
       Click = 0   
       EndIf 
   

   
   
   
   
      FlipBuffers() 
      
      ExamineKeyboard()
    Until KeyboardPushed(#PB_Key_Escape)

End
;****************************************************************************************************
Last edited by y3an on Mon Jul 28, 2008 6:07 pm, edited 3 times in total.
GBeebe
Enthusiast
Enthusiast
Posts: 263
Joined: Sat Oct 09, 2004 6:52 pm
Location: Franklin, PA - USA
Contact:

try this

Post by GBeebe »

I would just cycle through the panes when a mouse click is detected
zFocus = -1
For a = 0 to numPanes

Check for a collision.
If mx > panes(a)\tl\x ......

Then see if the zOrder is higher than the others.
If panes(a)\z > panes(zFocus)\z
zFocus = a
endif

If this was all in a procedure, you could ProcedureReturn zFocus
and it would let you know what index in the panes array had the click.

PS: I didn't have much time to look at ur code, and can't test it in Linux. If the lower z means it's closer then you need to change the > to a < .
Hope this helps.
y3an
User
User
Posts: 56
Joined: Sun Mar 09, 2008 6:06 am

Post by y3an »

LinkedList are usefull for this kind of job, you should look closer at it.
And i should also, because my code is very far of been perfect >.<
untune
User
User
Posts: 56
Joined: Sun Jul 06, 2008 10:07 pm
Location: Bolton, UK

Post by untune »

Hi y3an, thanks for the code... unfortunately there seems to be a problem... you can only select each window once and after that they stop responding... and also selecting any of the windows on the left makes the 2nd from the right move to the front... just the kind of behaviour that's been giving me headaches :D

I tried to implement this with linked lists already but moved back to using an array, at least for the time being... being able to reference the inxexes is quite helpful when looping through, and with the linked list I had trouble keeping track of exactly where I was in the list :D

I've found this, scroll down to GUI candy:

http://www.x-pressive.com/SpriteCandy/

It's for Blitz3d, it's pretty much everything I need but I don't just want to port it and besides there are bits of the code I don't quite follow, and most of it is just going over my head... I've been away from coding for far too long :D

Maybe it could be of use to you though! :)

@ GBeebe: Thanks for that tip, I'll give it a try :)
User avatar
Fluid Byte
Addict
Addict
Posts: 2336
Joined: Fri Jul 21, 2006 4:41 am
Location: Berlin, Germany

Post by Fluid Byte »

*Removed, see below*
Last edited by Fluid Byte on Wed Jul 30, 2008 10:42 am, edited 1 time in total.
Windows 10 Pro, 64-Bit / Whose Hoff is it anyway?
User avatar
Kaeru Gaman
Addict
Addict
Posts: 4826
Joined: Sun Mar 19, 2006 1:57 pm
Location: Germany

Post by Kaeru Gaman »

didn't test all your codes, just followed the discussion.

one idea that occurred to me was:
if you re-order the LList of windows, you can check top-down or button-up,
the first hit will be the right one, this will spare a lot or time.

re-ordering the list when z-order changes (DeleteElement, InsertElement)
is not that lack of performance because it will only be performed on a change, not always.
oh... and have a nice day.
User avatar
Fluid Byte
Addict
Addict
Posts: 2336
Joined: Fri Jul 21, 2006 4:41 am
Location: Berlin, Germany

Post by Fluid Byte »

You don't mean my code now is it just a general thought, right? If so I think a little snippet will help to understand the method you you have in mind.
Windows 10 Pro, 64-Bit / Whose Hoff is it anyway?
User avatar
Kaeru Gaman
Addict
Addict
Posts: 4826
Joined: Sun Mar 19, 2006 1:57 pm
Location: Germany

Post by Kaeru Gaman »

naw no code nau...
just a bit discussing, not tooo (yas 3 o) intaristad in dis nau...

F'I laik, will provide code tuhmarah but won.

....but hav bettah to do... sry!
oh... and have a nice day.
untune
User
User
Posts: 56
Joined: Sun Jul 06, 2008 10:07 pm
Location: Bolton, UK

Post by untune »

Thanks for the code Fluid Byte, that's clarified things a lot, going to start again tomorrow now that I've planned things a little more and I'm going to use linked lists instead...

Cheers!
y3an
User
User
Posts: 56
Joined: Sun Mar 09, 2008 6:06 am

Post by y3an »

Maybe it could be of use to you though! Smile
Yep, allthrough now i can have a look on the Fluid Bytes code :D


In my app you can't play with Z-index, they are definned by .. the layer infos, thats why I have galley to make this horrible win system xD, sorry for the false hope yesterday :lol:
fortunatly, magic Fluid byte cames and save the situation 8)

naw no code nau...
just a bit discussing, not tooo (yas 3 o) intaristad in dis nau...

F'I laik, will provide code tuhmarah but won.
Holly christ ! He gone sick ! That's crap, this man did seem not uninteresting :cry:
User avatar
Fluid Byte
Addict
Addict
Posts: 2336
Joined: Fri Jul 21, 2006 4:41 am
Location: Berlin, Germany

Post by Fluid Byte »

Untune, forget the code I posted and use this one instead. I made some necessary changes:

- code optimations / improvements
- fixed a memory leak (after second CopyMemory())
- theres no more Z-Order to change because it can be removed completely with this technique. You just deactivate the old window and activate the new one by a flag.
- some more comments

Code: Select all

; Window Z-Order for Screen GUI (v2)
; Fluid Byte
; July 30, 2008

InitSprite() : InitKeyboard() : InitMouse()

; Screen-GUI Stuff
Structure GUI_WINDOW
	X.w
	Y.w
	Width.w
	Height.w
	Title.s
	Active.b
EndStructure

Global NewList gwin.GUI_WINDOW()

Global GUI_LASTID

Procedure GUI_OpenWindow(X,Y,Width,Height,Title.s)
	If GUI_LASTID
		ChangeCurrentElement(gwin(),GUI_LASTID) : gwin()\Active = #False		
	EndIf
	
	AddElement(gwin())
	gwin()\X = X
	gwin()\Y = Y
	gwin()\Width = Width
	gwin()\Height = Height
	gwin()\Title = Title
	gwin()\Active = #True
	
	GUI_LASTID = gwin()
	
	ProcedureReturn GUI_LASTID
EndProcedure

; Open Screen
OpenWindow(0,0,0,640,480,"Z-Order",#PB_Window_SystemMenu | #PB_Window_ScreenCentered)

OpenWindowedScreen(WindowID(0),0,0,640,480,0,0,0)

; GUI Windows
guiWindow1 = GUI_OpenWindow(10,10,320,220,"GUI Window #1")
guiWindow2 = GUI_OpenWindow(150,70,310,220,"GUI Window #2")
guiWindow3 = GUI_OpenWindow(100,150,320,220,"GUI Window #3")
guiWindow4 = GUI_OpenWindow(240,220,320,220,"GUI Window #4")

; Mouse Pointer
lpBuffer = AllocateMemory(630)
UnpackMemory(?Cursor,lpBuffer)
CatchSprite(0,lpBuffer)
TransparentSpriteColor(0,RGB(0,128,128))
FreeMemory(lpBuffer)

; -------------------------------------------------------------------
; MAIN LOOP
; -------------------------------------------------------------------

Repeat
	EventID = WindowEvent()   
	
	ClearScreen(RGB(150,120,50))
	
	ExamineKeyboard() : ExamineMouse()

	; // Draw Windows //
	
	StartDrawing(ScreenOutput())
	DrawingMode(1)
	
	ForEach gwin()
		WX = gwin()\X : WY = gwin()\Y : WW = gwin()\Width : WH = gwin()\Height         
		
		; * Active Window
		If gwin()\Active = #True
			Box(WX,WY,WW,WH,0)
			Box(WX + 1,WY + 1,WW - 2,WH - 2,RGB(255,220,0))
			Box(WX + 1,WY + 1,WW - 2,30,RGB(255,170,0))
			Line(WX,WY + 30,WW,0)   
			DrawText(WX + 10,WY + 8,gwin()\Title,RGB(100,30,0))      
		Else
			; * Inactive Window
			Box(WX,WY,WW,WH,0)
			Box(WX + 1,WY + 1,WW - 2,WH - 2,RGB(200,170,50))
			Box(WX + 1,WY + 1,WW - 2,30,RGB(180,140,0))
			Line(WX,WY + 30,WW,0)
			DrawText(WX + 10,WY + 8,gwin()\Title,$44aacc)
		EndIf
	Next
	
	StopDrawing()
	
	; // Check Mouseclick //
	
	If CountList(gwin()) ! 0 And MouseButton(1) And GUI_MOUSEBLOCK = #False
		GUI_MOUSEBLOCK = #True
		
		MX = MouseX() : MY = MouseY()
		
		; * Cycle through items from back to front
		LastElement(gwin())         
		
		hMemCurrent = gwin() ; address of last element / first window
				
		Repeat
			WX = gwin()\X : WY = gwin()\Y : WW = gwin()\Width : WH = gwin()\Height
			
			If MX >= WX And MX < (WX + WW) And MY >= WY And MY < (WY + WH)
				; * Exit loop and don't do anything if clicked window is the active one
				If gwin() = hMemCurrent : Break : EndIf
			
				; * Retrieve Window title and data from element address in memory
				tmpString$ = gwin()\Title				
				hMemBuffer = AllocateMemory(SizeOf(GUI_WINDOW))            
				CopyMemory(gwin(),hMemBuffer,SizeOf(GUI_WINDOW))
				
				; * Remove selected element (data has been temporarily saved)
				DeleteElement(gwin())
				
				; * Goto end of list and add a new item so it's last and the new topmost window
				LastElement(gwin())            
				AddElement(gwin())
				gwin()\Title = tmpString$ 
				
				; * Copy window data to address of new element
				CopyMemory(hMemBuffer,gwin(),SizeOf(GUI_WINDOW))
				FreeMemory(hMemBuffer)
				
				; * Activte new window, deactivte old window
 				gwin()\Active = #True 				 			
 				ChangeCurrentElement(gwin(),hMemCurrent) 				 			
 				gwin()\Active = #False
				
				Break ; we found our window, get outta here
			EndIf
		Until PreviousElement(gwin()) = 0
	ElseIf MouseButton(1) = 0
		GUI_MOUSEBLOCK = #False
	EndIf
	
	DisplayTransparentSprite(0,MouseX(),MouseY())
	
	FlipBuffers()
Until KeyboardPushed(1) Or EventID = #PB_Event_CloseWindow

; -------------------------------------------------------------------
; DATA SECTION
; -------------------------------------------------------------------

DataSection
	Cursor:
	Data.l $0276434A,$4A720000,$A9B7AACC,$146320D0,$284A6811,$01232023,$9188409D,$F3000461,$20492601,$0A0401E0
	Data.l $E00081C0,$FFC0E015,$09302024,$409C3C04,$66013801,$FE4D02B6,$91FB77FB,$B7C236B7,$BDF63086,$BFEC1EC1
	Data.l $C0F36107,$0F625EF7,$008A083D,$87FFF581,$C4592A11,$4287C926,$3EC90540,$69F6974F,$21E5E328,$DDDE6107
	Data.l $50B353C6,$0FB86C06,$0000D893
	Data.b $90,$48
EndDataSection
Windows 10 Pro, 64-Bit / Whose Hoff is it anyway?
Post Reply