"You are my master and i have your slave"
Writing a GUI system
- Fluid Byte
- Addict

- Posts: 2336
- Joined: Fri Jul 21, 2006 4:41 am
- Location: Berlin, Germany
Well, you know where this is going!
"You are my master and i have your slave"

"You are my master and i have your slave"
Windows 10 Pro, 64-Bit / Whose Hoff is it anyway?
- Fluid Byte
- Addict

- Posts: 2336
- Joined: Fri Jul 21, 2006 4:41 am
- Location: Berlin, Germany
Here you go!
Code: Select all
; Window Z-Order for Screen GUI (V3)
; Fluid Byte
; August 05, 2008
InitSprite() : InitKeyboard() : InitMouse()
; Screen-GUI Stuff
Structure GUI_WINDOW
X.w
Y.w
Width.w
Height.w
Title.s
Active.b
Dragging.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()
MX = MouseX() : MY = MouseY()
StartDrawing(ScreenOutput())
DrawingMode(#PB_2DDrawing_Transparent)
ForEach gwin()
WX = gwin()\X : WY = gwin()\Y : WW = gwin()\Width : WH = gwin()\Height
; // Drag Windows //
If gwin()\Dragging
gwin()\X = MouseX() + MTX
gwin()\Y = MouseY() + MTY
EndIf
If MouseButton(1) = 0 : gwin()\Dragging = #False : EndIf
; // Draw Windows //
; * 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)
; * Detect Titlebar click
If MY < (WY + 30)
MTX = gwin()\X - MX
MTY = gwin()\Y - MY
gwin()\Dragging = #True
EndIf
; * 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
EndIf
If 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
EndDataSectionWindows 10 Pro, 64-Bit / Whose Hoff is it anyway?
- Fluid Byte
- Addict

- Posts: 2336
- Joined: Fri Jul 21, 2006 4:41 am
- Location: Berlin, Germany
I just updated the code and fixed a click issue, should be fine now. 
Windows 10 Pro, 64-Bit / Whose Hoff is it anyway?
Hehe thanks for your help FB, it keeps me going 
I've been tinkering all afternoon with this, and gotten *somewhere*, but my code has become much more complicated than your example so it's still giving me headaches
I've gotten the panels to drag at least, but for some reason if I click one after another then they are both drawn at the same point, unless I click away from them all and deactivate all the panels first. I'm assuming that it's something to do with the linked list, being at the wrong element... it's all a bit too hefty to post any source though... agh!!!
I've been tinkering all afternoon with this, and gotten *somewhere*, but my code has become much more complicated than your example so it's still giving me headaches
I've gotten the panels to drag at least, but for some reason if I click one after another then they are both drawn at the same point, unless I click away from them all and deactivate all the panels first. I'm assuming that it's something to do with the linked list, being at the wrong element... it's all a bit too hefty to post any source though... agh!!!
Solved! Thanks again FB!
Hehe, I don't know if my implementation is the most efficient and it'll probably be broken again by tomorrow but I'm just glad it works
The problem was - my panels are no longer just simply rectangular sprites - they comprise of 10 sprites: the top, bottom, left, and right borders, then the top left, top right, bottom left and bottom right corners, then the actual "client area" (I simply refer to this as the area because I'm awkward
) and finally the header/title bar. This number will probably grow as icons, close buttons etc are added...
Anyways, the problem was that I got it moving the whole panel (borders, title bar, corners etc with the mouse offset variable (as all these are created and drawn relative to the Panel X and Y positions), but the area was being drawn relative to the panel coordinates... and in the structure the area has it's own X/Y coordinates rather than being relative to the panel like the rest... so I just created two more X/Y offset floats specifically for the area and did everything again and voila
The other issue was down to not telling it to set Dragging to false when the mouse button was released..
At this rate you'll have your slave back in no time
Cheers
Hehe, I don't know if my implementation is the most efficient and it'll probably be broken again by tomorrow but I'm just glad it works
The problem was - my panels are no longer just simply rectangular sprites - they comprise of 10 sprites: the top, bottom, left, and right borders, then the top left, top right, bottom left and bottom right corners, then the actual "client area" (I simply refer to this as the area because I'm awkward
Anyways, the problem was that I got it moving the whole panel (borders, title bar, corners etc with the mouse offset variable (as all these are created and drawn relative to the Panel X and Y positions), but the area was being drawn relative to the panel coordinates... and in the structure the area has it's own X/Y coordinates rather than being relative to the panel like the rest... so I just created two more X/Y offset floats specifically for the area and did everything again and voila
The other issue was down to not telling it to set Dragging to false when the mouse button was released..
At this rate you'll have your slave back in no time
Cheers
- Fluid Byte
- Addict

- Posts: 2336
- Joined: Fri Jul 21, 2006 4:41 am
- Location: Berlin, Germany
Actually I have to thank you for asking the Window Z-Order question. You know, I have come along way with BASIC dialects. Starting with RealBASIC on the MAC to DarkBASIC, BlitzBasic and finally PureBasic on the PC. In all of them I created a some sort of Screen GUI wich I never finished. Though I created a scrollbar wich is 100% identical to the Windows one I never got the Z-Order thing to work.
Anyway, that slave is yours now. Take it as a sign of my generosity.
Anyway, that slave is yours now. Take it as a sign of my generosity.
Windows 10 Pro, 64-Bit / Whose Hoff is it anyway?
That's really cool - I started with DarkBASIC, then DBPro, then PureBasic, then IBasic - about 3 years ago I gave up on my dream of being an indie game designer and grew up a bit
I just started coding again a month or so ago, IBasic had vanished (just as I suspected it would) and good old PB was still going strong. My problem is my attention span is too short to stick with a project usually but this one is coming on in leaps and bounds which is a great motivator.
But when I started this I was looking at C++ GUI libraries and since I can grasp some C++ when reading it, that's where my knowledge ends
So I started to look for DarkBASIC and BlitzBASIC code because obviously it's easier to tell what's going on. With your help the most difficult parts have become clear...
How far did you get with yours then? I'm about to start thinking about how to implement gadgets... buttons will be easy... keeping track of and rendering them all in the right order will be a challenge too.
I'm thinking a structure of
But obviously the number of gadgets may vary... in one Blitz library I think a static array was used in the window structure with a limit of 25 I think, but I'm sure there are better ways 
Oh and thanks for the slave
But when I started this I was looking at C++ GUI libraries and since I can grasp some C++ when reading it, that's where my knowledge ends
How far did you get with yours then? I'm about to start thinking about how to implement gadgets... buttons will be easy... keeping track of and rendering them all in the right order will be a challenge too.
I'm thinking a structure of
Code: Select all
ForEach PanelLinkedList()
Draw panel
Draw all child gadgets
Next
Oh and thanks for the slave
- Fluid Byte
- Addict

- Posts: 2336
- Joined: Fri Jul 21, 2006 4:41 am
- Location: Berlin, Germany
Heh, I got about 200 PB projects lying here and like 5 of them are finished. So I think we share the same kind of disease.untune wrote:My problem is my attention span is too short to stick with a project usually but this one is coming on in leaps and bounds which is a great motivator.
Buttons? Check. Scrollbars? Check. Progressbars? Check. Windows without Z-Order? Check indeed. I also got some statusbar and menubar code but this is in early beta stage like some other controls.untune wrote:How far did you get with yours then?
But I am just asking myself, will your windows be resizable? If so, that would be pain in the ass to code because you would have to take care of the clipping of all gadgets. I know this could be done by using sprites to draw the windows but I guess there would be quite some stuff in my code to adopt to this method.
Windows 10 Pro, 64-Bit / Whose Hoff is it anyway?
Haha well at least I'm not the only one 
The gadgets are the important thing on my list now: specifically - buttons, edit boxes/text fields, scrollbars, progress bars, sliders, frames, checkboxes, radioboxes, labels, listviews (that incorporate scrolling) and maybe comboboxes and tabs. The majority won't be all that important but they need to be there for versatility.
I had designed my panels to incorporate borders and corners for resizing but I hadn't thought about the clipping issue - after giving it more thought I've decided that I don't really intend to use this GUI for anything really complex, and fixed sizes should be fine - the borders look good anyways and the implementation should allow resizing to be added further down the line if it needs it. I don't know how difficult it would be considering HGE is using flat 3d quads a la Sprite3d - I'm guessing very difficult
At the end of the day I just wanted a nice looking GUI for games with a more menu based interface - 4X games and managerial for example... Once I've added something to load a GUI "theme" from an XML or INI file or something, everything should be good.
The gadgets are the important thing on my list now: specifically - buttons, edit boxes/text fields, scrollbars, progress bars, sliders, frames, checkboxes, radioboxes, labels, listviews (that incorporate scrolling) and maybe comboboxes and tabs. The majority won't be all that important but they need to be there for versatility.
I had designed my panels to incorporate borders and corners for resizing but I hadn't thought about the clipping issue - after giving it more thought I've decided that I don't really intend to use this GUI for anything really complex, and fixed sizes should be fine - the borders look good anyways and the implementation should allow resizing to be added further down the line if it needs it. I don't know how difficult it would be considering HGE is using flat 3d quads a la Sprite3d - I'm guessing very difficult
At the end of the day I just wanted a nice looking GUI for games with a more menu based interface - 4X games and managerial for example... Once I've added something to load a GUI "theme" from an XML or INI file or something, everything should be good.
Wehey! I went and broke it 
Bit of an odd one this, because it seems a bit illogical...
I hadn't really tested my create function out other than in the initial setup so I just stuck in a few lines that let me spawn a new panel at the mouse coordinates when the right mouse button is clicked. Nice and simple.
The problem seems to lie in the code that brings panels to the front -
The behaviour is like this, After creating 2 additional panels, and clicking on the others randomly, moving them in and out of focus, the first thing that happens is that the header text in one of the newly spawned panels will go iffy - sometimes it will display as "???", one time it was "?J?", another time it appeared to copy the titlebar text from another panel, one time it borrowed a portion of it... seems very random 
Then a few more cliks and I get an invalid memory access error... on this line:
Strings aren't my forte so am I missing something here? It's pretty much exactly as Fluid Byte posted it and I find it a bit odd that this only seems to have started since I added this little bit to spawn new panels....
Baffling
Bit of an odd one this, because it seems a bit illogical...
I hadn't really tested my create function out other than in the initial setup so I just stuck in a few lines that let me spawn a new panel at the mouse coordinates when the right mouse button is clicked. Nice and simple.
The problem seems to lie in the code that brings panels to the front -
Code: Select all
; // Get the info from the structure, create temporary structure to store it
GUI_PANEL_TEMPHEADER = GUI_LIST_PANEL()\Header
GUI_PANEL_TEMPBUFFER = AllocateMemory(SizeOf(_GUI_PANEL))
CopyMemory(GUI_LIST_PANEL(), GUI_PANEL_TEMPBUFFER, SizeOf(_GUI_PANEL))
; // Remove the element from the list
DeleteElement(GUI_LIST_PANEL())
; // Go to the front (end) of the list and add a new element
LastElement(GUI_LIST_PANEL())
AddElement(GUI_LIST_PANEL())
; // Copy data from buffer to new element
GUI_LIST_PANEL()\Header = GUI_PANEL_TEMPHEADER
CopyMemory(GUI_PANEL_TEMPBUFFER, GUI_LIST_PANEL(), SizeOf(_GUI_PANEL))
FreeMemory(GUI_PANEL_TEMPBUFFER)
; // Deactivate old panel, activate new panel
ChangeCurrentElement(GUI_LIST_PANEL(), GUI_PANEL_CURRENT)
GUI_LIST_PANEL()\Active = #False
LastElement(GUI_LIST_PANEL())
GUI_LIST_PANEL()\Active = #True
Then a few more cliks and I get an invalid memory access error... on this line:
Code: Select all
; // Copy data from buffer to new element
*** GUI_LIST_PANEL()\Header = GUI_PANEL_TEMPHEADER
CopyMemory(GUI_PANEL_TEMPBUFFER, GUI_LIST_PANEL(), SizeOf(_GUI_PANEL))
FreeMemory(GUI_PANEL_TEMPBUFFER)
Baffling
UPDATE: Well I've partially fixed the problem, first by making the buffer variables protected instead of global, and also by changing the header buffer to GUI_PANEL_TEMPBUFFER$.
I have no idea what the difference is for a fixed length string and a .s string, is it to do with how they are stored?
The remaining problem is that randomly the white panes headers will sometimes change to "e Panel"... the whole header string should be "TEST PANEL: White Panel"... so I assume that the "TEST PANEL: Whit" is getting lost somewhere
[EDIT] And now also "TEST PAN?" - haha
Any ideas?
I have no idea what the difference is for a fixed length string and a .s string, is it to do with how they are stored?
The remaining problem is that randomly the white panes headers will sometimes change to "e Panel"... the whole header string should be "TEST PANEL: White Panel"... so I assume that the "TEST PANEL: Whit" is getting lost somewhere
[EDIT] And now also "TEST PAN?" - haha
Any ideas?
- Fluid Byte
- Addict

- Posts: 2336
- Joined: Fri Jul 21, 2006 4:41 am
- Location: Berlin, Germany
Let me get some coffee and chill for a while ...
I will look into this problem later.
I will look into this problem later.
Windows 10 Pro, 64-Bit / Whose Hoff is it anyway?
Protected probably puts em "out of scope" and the copymemory cant access the full structure data which again could render the pointers invalid or respectivelly cause an IMA since its trying to access a protected memoryarea from inside a procedure. 
Just an idea.
Cheers,
Thalius
Just an idea.
Cheers,
Thalius
"In 3D there is never enough Time to do Things right,
but there's always enough Time to make them *look* right."
"psssst! i steal signatures... don't tell anyone!
"
but there's always enough Time to make them *look* right."
"psssst! i steal signatures... don't tell anyone!
- Fluid Byte
- Addict

- Posts: 2336
- Joined: Fri Jul 21, 2006 4:41 am
- Location: Berlin, Germany
I think it's because the Windows are not created during runtime but before entering the main loop. If you create and delete Windows at runtime you have to reset the focus for each window otherwise things seem to get messed up.
I'm too lazy atm so I will try to figure this out when getting home again.
I'm too lazy atm so I will try to figure this out when getting home again.
Windows 10 Pro, 64-Bit / Whose Hoff is it anyway?

