Creating an in game GUI from scratch...

BlitzMax Forums/BlitzMax Beginners Area/Creating an in game GUI from scratch...

Takis76(Posted 2014) [#1]
I am creating my very beautiful game and I need an in game GUI.

I have ifsoGUI and is very nice but a lots of gadgets are missing. For example there is no canvas gadget and I need it very very much for my game and my level editor.
So I thought if I will create my own GUI from scratch it will be better with more gadgets and will fulfill the needs of my game.
But there is one HUGE problem for now.

I don't know how to create a GUI and the GUI theory in generic.
I know in a GUI there are the windows and gadgets, but each gadget have and events too.

Also I don't know little things about methods and lists.
Any one of you , you have already working on your own GUI , do you have some idea how to setup my base gadget and how to inherit from this and add other objects (the gadgets) and how to link them.
You can understand what I say, I have seen some source code from some guis but I don't understand some things about how the Create method works, how to add some object in a list.

I created this code:

Type Share_Variables Abstract

	Field x:Int
	Field y:Int
	Field width:Int
	Field height:Int
	Field gadget_list:TList = New TList
	
	Global window_list:TList = New TList
	
End Type

Type GUI_window Extends Share_Variables

	Function Create:GUI_window(x, y, width, height)
		Local w:GUI_window = New GUI_window
		w.x = x
		w.y = y
		w.width = width
		w.height = height
		w.gadget_list = New TList
		'w.hidden = True
		Share_Variables.window_list.addlast(w)
		Return w
	EndFunction
	
End Type

Function Create_window:GUI_window(x:Int, y:Int, width:Float, height:Float)
	Return GUI_window.Create(x, y, width, height)
EndFunction

Graphics(640, 480, 0, 60)

Global main_window:GUI_window
main_window = Create_window(25, 25, 111, 122)

While Not AppTerminate() Or KeyHit(KEY_ESCAPE)

	If KeyHit(KEY_ESCAPE) Then Ending()
	If KeyHit(KEY_SPACE) Then Ending()

	DrawText(String main_window.x, 0, 200)
	DrawText(String main_window.y, 0, 210)
	DrawText(String main_window.width, 0, 220)
	DrawText(String main_window.height, 0, 230)
	
	Flip - 1
	
Wend

Function Ending()
	End
End Function


And it compiled.
It returned the position and window dimensions. I think is a good start but I don't know the theory and what I do next.
I will try to draw some graphic in those x,y,width and height variables.


markcw(Posted 2014) [#2]
Have you looked at FryGUI, HighGUI 3 or CEGUI?


Takis76(Posted 2014) [#3]
I was thought to create one gui from scratch to understand how things work.
The FryGUI is a dead link.
Do other guis support canvas gadgets and skinnable interface?
I don't understand how other guis work.
And there is not book for blitzmax which explains all of these about types, methods lists etc...


Hotshot2005(Posted 2014) [#4]
TAKE look at this one

http://www.amazon.com/Programming-Absolute-Beginner-BlitzMax-ebook/dp/B005FCFGM0/ref=sr_1_1?ie=UTF8&qid=1350762993&sr=8-1&keywords=games+programming+for+the+absolute+beginner+with+blitzmax


Hotshot2005(Posted 2014) [#5]
TAKE look at this one

http://www.amazon.com/Programming-Absolute-Beginner-BlitzMax-ebook/dp/B005FCFGM0/ref=sr_1_1?ie=UTF8&qid=1350762993&sr=8-1&keywords=games+programming+for+the+absolute+beginner+with+blitzmax

or this one

http://www.truplo.com/blitzmaxbeginnersguide/wave1.html

Hope that help :)


Brucey(Posted 2014) [#6]
I also recommend the Beginners Guide link that hotshot posted above. Explains a lot, very well.

From this page there's also a link to a PDF.


markcw(Posted 2014) [#7]
Ok, I have uploaded my copy of Fry.mod here. FryGUI says it supports both these things.

CEGUI supports skins but it seems you have to figure out how to render to texture to create a canvas gadget. It has good docs/forum though.

HighGUI 3 (thread here) supports both things too. It seems to be relatively simple so you could customize it if you want.

There is a very detailed OOP/types tutorial by John J here.

There is a linked list demo by fredborg here. There are also good examples posted to the online manual.

"Games Programming for the Absolute Beginner with BlitzMax" is only on Kindle, it was also only sold as a book in N. America but you might find a 2nd hand copy.


Takis76(Posted 2014) [#8]
Thank you very much for you suggestions.
The HighGUI 3 have dead links too.
I am reading the books I found but I have difficulties with lists.


markcw(Posted 2014) [#9]
The HighGUI 3 links work here.

There is another book for BlitzMax called Game Programming for Teens, 3E (3rd edition) by Maneesh Sethi which is basically a beginner's tutorial for how to make an Invaders clone. It does touch on types and lists but not in much depth. There is also 3D Game Programming for Teens, 2nd Edition which is for Blitz3D.

If you are confused about lists then look at the online manual data commands here. The examples posted there show you the basics pretty well.


zoqfotpik(Posted 2014) [#10]
For GUI code look at Brucey's SFXR port, it shows how to code a simple GUI with buttons and sliders.

GUI coding is sort of a conceptual bear at first but it's much simpler than you think.


Takis76(Posted 2014) [#11]
With the resources you gave me , I manage to create 2 windows (Very simple just squares)

I can create as many windows as I want and I can put in any x,y position and make them at any size.
Then I can change their color and their text.

The code so far. (Works).

'Here are the types of the program
Type Base_gadget Abstract

	Field x:Int
	Field y:Int
	Field width:Int
	Field height:Int
	Field parent:Base_gadget
	Field name:String
	
	Field gadget_color_red:Byte = 255
	Field gadget_color_green:Byte = 50
	Field gadget_color_blue:Byte = 255
	
	Global gadget_list:TList = CreateList()
	
EndType
	

Type GUI_Window Extends Base_gadget
	
	Method Create:GUI_Window(wName:String, wX:Int, wY:Int, wW:Int, wH:Int, parent:Base_gadget)
		
		Local new_gadget:GUI_Window
		new_gadget = New GUI_Window

		Self.name = wName
		Self.x = wX
		Self.y = wY
		Self.width = wW
		Self.height = wH
		
		gadget_list.AddLast(new_gadget)
		
	EndMethod
	Method Show()
		SetColor(Self.gadget_color_red, Self.gadget_color_green, Self.gadget_color_blue)
		DrawRect(Self.x, Self.y, Self.width, Self.height)
		
		SetColor(255, 255, 255)
		DrawText(Self.name ,Self.x+2,Self.y+2)
	End Method

	Method Set_Color:GUI_Window(red:Byte, green:Byte, blue:Byte)
		Self.gadget_color_red = red
		Self.gadget_color_green = green
		Self.gadget_color_blue = blue
		
	End Method
EndType


'Windowed
Graphics(640, 480, 0, 60)

'Full Screen
'Graphics(640, 480, 32, 60, 1)


SetMaskColor(255, 0, 255)

SetBlend(ALPHABLEND | MASKBLEND)

Global main_window:GUI_window = New GUI_window
Global main_window2:GUI_window = New GUI_window

main_window.Create("win1", 50, 50, 50, 50, Null)
main_window2.Create("win2", 110, 50, 50, 50, Null)




main_window.Set_Color(255, 0, 0)
main_window2.Set_Color(255, 155, 50)

main_window.Show()
main_window2.Show()



While Not AppTerminate() Or KeyHit(KEY_ESCAPE)

	If KeyHit(KEY_ESCAPE) Then Ending()
	If KeyHit(KEY_SPACE) Then Ending()

	DrawText(String main_window.x, 0, 100)
	DrawText(String main_window2.x, 0, 110)

			
		Flip - 1

Wend

Function Ending()
End
End Function


I still don't know how to select some object from the lists.

In the create method I tried this code:

	Method Create:GUI_Window(wName:String, wX:Int, wY:Int, wW:Int, wH:Int, parent:Base_gadget)
		
		Local new_gadget:GUI_Window
		new_gadget = New GUI_Window

		new_gadget.name = wName
		new_gadget.x = wX
		new_gadget.y = wY
		new_gadget.width = wW
		new_gadget.height = wH
		
		gadget_list.AddLast(new_gadget)
		
	EndMethod


Using new_gadget instead of self and didn't work.
And I don't know how to select each of the new_gadget to change its color , position , size and text. With the self it worked.

If you copy and paste the code above (before this one) and compile it works.


markcw(Posted 2014) [#12]
Your problem is you've got 2 instances of the type a local one in create and the global. Either pass the global one as parameter to create and use it instead of the local or return the local and use a dummy global to access the method.
	Method Create:GUI_Window(wName:String, wX:Int, wY:Int, wW:Int, wH:Int, parent:Base_gadget)
		
		Local new_gadget:GUI_Window
		new_gadget = New GUI_Window

		new_gadget.name = wName
		new_gadget.x = wX
		new_gadget.y = wY
		new_gadget.width = wW
		new_gadget.height = wH
		
		gadget_list.AddLast(new_gadget)
		Return new_gadget
		
	EndMethod
...
Global dummy:GUI_window = New GUI_window

Global main_window:GUI_window = dummy.Create("win1", 50, 50, 50, 50, Null)
Global main_window2:GUI_window = dummy.Create("win2", 110, 50, 50, 50, Null)

Method Create:GUI_Window(wName:String, wX:Int, wY:Int, wW:Int, wH:Int, new_gadget:GUI_Window, parent:Base_gadget)
		
		new_gadget.name = wName
		new_gadget.x = wX
		new_gadget.y = wY
		new_gadget.width = wW
		new_gadget.height = wH
		
		gadget_list.AddLast(new_gadget)
		Return new_gadget
		
	EndMethod
...
Global main_window:GUI_window = New GUI_window
Global main_window2:GUI_window = New GUI_window

main_window.Create("win1", 50, 50, 50, 50, main_window, Null)
main_window2.Create("win2", 110, 50, 50, 50, main_window2, Null)



markcw(Posted 2014) [#13]
Ok, the third way is to not use New in Create and use Self. Also, there is an example of how to look up the list.
	Method Create:GUI_Window(wName:String, wX:Int, wY:Int, wW:Int, wH:Int, parent:Base_gadget)
		
		Self.name = wName
		Self.x = wX
		Self.y = wY
		Self.width = wW
		Self.height = wH
		
		gadget_list.AddLast(Self)
				
	EndMethod
...
	Function Lookup()
		For Local a:GUI_Window=EachIn gadget_list
			Print a.name+","+a.x+","+a.y+":"+a.width+","+a.height
		Next
	End Function
...
Global main_window:GUI_window = New GUI_window
Global main_window2:GUI_window = New GUI_window

main_window.Create("win1", 50, 50, 50, 50, Null)
main_window2.Create("win2", 110, 50, 50, 50, Null)

GUI_window.Lookup()



Takis76(Posted 2014) [#14]
I didn't understand what you mean with the dummy
I will use the third example with self without add local new_gadget.
I am still confused with lists.


markcw(Posted 2014) [#15]
The dummy is just an instance of the type so you can access the methods or fields but apart from that it is not used. So by dummy I meant "An imitation of a real or original object, intended to be used as a practical substitute."

Not sure what you don't understand about lists. A list is a collection of objects, usually you want them of the same type but you can have different objects in the same list and look up any type with an EachIn loop. It's similar to storing data in a one-dimensional array but more flexible. The list uses TLink internally hence the name, but normally you don't need to use links.


Henri(Posted 2014) [#16]
If you want to find a specific object in a list you then have to iterate through the list from first object to the last (or at least until the object is found).
Identify the object by comparing it with the object instance you want to find or with an id like:

SuperStrict

Type a
	Field id:Int
	Field s:String = "Test object of type A"
EndType

Local list:TList = New TList

'Create some test objects and store them in the list
For Local i:Int = 0 To 9
	
	Local obj:a = New a
	obj.id = i	'example of using an id to later identify the object
	
	list.addlast(obj)
Next

'Finding object with id 5
Local found:Int

For Local obj:a = EachIn list
	If obj.id = 5 Then
		found = True
		Print obj.s + " | ID=" + obj.id
	EndIf
Next

If Not found Then Print "Object not found"

-Henri


zoqfotpik(Posted 2014) [#17]
I advise that you really work hard to understand lists because they are unbelievably powerful and make a lot of previously hard things seem easy.

If you run into trouble with them and just want to get your GUI done, you could try an array with a Top variable.

So you would make windowarray:TWindow[100]

Then you initialize it like so

for i = 0 to 99 {because that is the way arrays count}
Windowarray[i]=new TWindow {you have to initialize them or get an exception! NEVER FORGET because otherwise you will run into this again and again and never know why}
Next


Then as you add windows into that array you increment Top.

When a window dies, decrement Top.

When you need to walk your list of windows, do

for i = 0 to top
Windowarray[i].dostuff()
Next


If top ever exceeds your array size - 1, because again arrays count from zero, you can throw an error, or you can resize your array thus:
Windowarray= Windowarray[..Top*2]

This will expand the array as you need it, doubling the size each time, and will give you more space than you will ever need, with an optimally small number of resize operations because those are slow in some implementations.

All that's basically what lists do for you. Also a list can hold anything, even different types of object.

If you don't wanna mess with resizing, even though it's super simple, just allocate a window array as large as you want.

Preallocating is generally sneered at as wasteful of memory but in these days of huge memory spaces that's a bogus concern and preallocation can pay large dividends in speed and smoothness because the garbage collector doesn't have to run at strange intervals.

By doing a list within an array you are reinventing the wheel but that is a great way to learn how wheels work and if you already understand arrays, just use those until you understand how GUIs work. Then you can work on lists. One conceptual headache at a time.

If you want to understand lists read this:
http://en.m.wikibooks.org/wiki/BlitzMax/Modules/Data_structures/Linked_lists
And the Blitzmax OO Tutorial by John.

Type out all the examples and get them to run and you will understand, it's not as hard as it seems.


Derron(Posted 2014) [#18]
instead of keeping references of old "windows" in your array you should null them if not used anymore.

You then could replace
for i = 0 to top
Windowarray[i].dostuff()
Next


with

for local window:TMyWindow = EachIn Windowarray
  window.dostuff()
Next


If you define your Windowarray to contain "guibaseobject" instead of "guiwidget" you could of course store other widgets too.
The "eachin"-loop then skips all items not of type "TMyWindow" (or ancestors of TMyWindow).

bye
Ron


Takis76(Posted 2014) [#19]
Wow there is a lots of work :P
Now I am working to skin my first window. Soon I will post a zip file with the graphics included.
Another one obstacle is and I don't understand , how to add gadgets on this new window I have created and how to create event system.
When I will finish with the skinning of the first window I will try to add one simple button on this new window.
I will post a zip file , because the next code include and graphics too.
Thank you very very much for your valuable information and help you provide me.


zoqfotpik(Posted 2014) [#20]
The way to code an event system of sorts is as follows.

Make a function InRect(x,y, ux,uy,lx,ly). This returns a 1 if x,y is within the rectangle (ux,uy,lx,ly) and a zero otherwise. You are going to be calling this function on every click, for every gui item.

Make a type TClickRect. This will be your parent GUI object. It has an onclicked() method. That is what will get called every time it is clicked.

This gets added to a TList thus:

widgetlist:TList = new TList
TestClickRect:TClickRect = new TClickRect
widgetlist.addlast(TestClickRect)


Then your click checking loop:
if MouseHit(1)
temprect:TClickRect = new TClickRect
for temprect = eachin widgetlist
if inrect(MouseX,MouseY, <widget upperx, uppery, lowerx, lowery>) 
temprect.onclicked()
next
endif


That doesn't take into account whether there are overlapping items. You will need to figure that out yourself but basically you figure out which windows a mouseclick is within, and then of those, figure out which has the highest Z value which means the window that is on top of that window stack.

Now when you make your TWindow, you extend TClickRect.

Type TWindow contains a TList.

TList contains a list of TClickRects. Those are your buttons.

This is probably the simplest way to do windows.

By making lists of TClickRects, you can also make button bars, menubars, all sorts of things. All windowing systems are basically built out of clickrects.

If you really want to go crazy, learn about function pointers, and then assign functions to buttons when you create the buttons. That is a somewhat ghetto way of doing things but if you are doing it the easy way that will give you a lot of power.

But if I WERE YOU I would figure out lists or how to make arrays of buttons, have a function
AddButton(name:string, x:int, y:int, width:int, height:int, functionpointer) that adds buttons to a list or a static array, then use that to build up a button bar, and use that buttonbar within your game.

This is a very simple way of doing things and it's pretty powerful, it lasted for years in the GLUT toolkit. Have a right click bring up a buttonbar right where you clicked, then a left click on an item calls that item's function pointer and makes the menu disappear again.

Writing a GUI is a great learning experience and it's very interesting but learning it is not really trivial and it's really up to you whether you want to fart around with learning that right now or whether you want to work on your game.

You could also do a very simple thing and use my mousewheel menu:
Strict

Global numitems = 7 	
Global currentitem:Int  ' Which item is currently selected 
Global olditem:Int

Global currenttextline:Int
Graphics 640,480

Global ticks = 0
Global namearray:String[100]  ' 100 menu items total
Global menufadeouttimer:Int ' When this has elapsed, the menu disappears automatically

' This is your list of menu item names.
namearray[0]="Thing1"
namearray[1]="Thing2"
namearray[2]="Redthing"
namearray[3]="Bluething"
namearray[4]="BarThing"
namearray[5]="FooThing"
namearray[6]="Exit"

While Not KeyDown(KEY_ESCAPE) And Not (currentitem=6 And menufadeouttimer<0)
	ticks = ticks + 1 
	Cls
	currenttextline=0
	olditem= currentitem
	currentitem= Abs(MouseZ()) Mod numitems ' Mousewheel Switches Tile Types
	If olditem<> currentitem
		menufadeouttimer = 60
	EndIf
	menufadeouttimer:-1		
	If menufadeouttimer > 0 
	drawmenu()
	Else
		SetScale 2,2
		SetColor 255,0,0
		DrawText namearray[currentitem], 0,-2
		SetScale 1,1
	EndIf
	Flip
Wend

Function drawmenu()
	SetBlend alphablend
	SetAlpha .5  ' draw semi-transparent menu background
	SetColor 0,0,0
	DrawRect 0,0,200,1000
	SetAlpha 1
	If menufadeouttimer < 50 And menufadeouttimer > 0 SetAlpha 1*(51/menufadeouttimer)
	'SetColor 255,0,0
	Local recty = 0*12
	DrawRect 0,recty,200,24
	SetScale 2,2
	SetColor 255,0,0
	DrawText namearray[currentitem], 0,-2
	SetScale 1,1
	SetColor 255,255,255
	recty = currentitem*12 + 24
	DrawRect 0,recty,200,12
	For Local i = 0 To numitems 
		If i = currentitem
			SetColor 0,0,0
		Else 
			SetColor 255,255,255
		EndIf
		DrawText namearray[i], 10,i*12 + 24
		SetColor 255,255,255
	Next
	SetAlpha 1
End Function

Function consoleprint(toprint$)
	currenttextline:+12
	DrawText(toprint$,10,currenttextline)
End Function


But this is not complete! It needs to execute code somehow based on the selection you made. Either do that with a function pointer or a case statement.

Here is a bit of code explaining how to bind function pointers to keys. A function pointer can also be bound to a buttonID.
' bindkey - binds key to function
Global functions()[256]  '  This is an array of function pointers, one for each ascii keycode.
						 '  Not all of these will be initialized...
						 '  If you are using this in your menu system, you will execute these on
						 '  a given button press, as down below in the main loop.
Graphics 320,200
Function bind(key:Int, func())  ' Binds a key to a function pointer.
	functions[key]=func
End Function

Function thing1()  ' Example functions.  These could be anything...
DrawText "thing1", 10, 10
End Function

Function thing2()
DrawText "thing2", 10, 10
End Function

Function bail()
End
End Function


' These are the binding statements.  These bind a key to a function.
' Thing1, Thing2 and bail are all references to functions.
bind(KEY_1,thing1)  
bind(KEY_2,thing2)
bind(KEY_ESCAPE,bail)

While(1)
	Repeat
		char% = GetChar()
		If char And functions[Int(char)] 
			Cls
			functions[Int(char)]
			Flip
		EndIf 
		' Or for your button menu system,
		' if somebuttonhit and functions[buttonID]
		   ' in other words, if the button id has had a function assigned to it
	 	' functions[buttonID]    ' Execute whatever function corresponds to that button ID
	    ' endif
	Until char=Null
Wend



Derron(Posted 2014) [#21]
Instead of "checking which widgets think they contain the widget" and afterwards filtering it:

- have a list of widgets. Each time you add a widget, sort the list using a custom compare-function (which returns 1/0/-1 depending on the zindex compared to the other widget)

So your list contains a zordered list - which you need anyway if you have overlapping widgets.

When checking for "clicks" you check from "top to bottom". while rendering is from "bottom to top".

As soon as an item registered a click, no other widget has to get checked.

If you want to inform other widgets too - inform them via the widget itself (it sends out an event à la "widget clicked" with the widget as payload/sender).


You may also consider to have a "GetScreenRect:TRectangle" method in each widget - it returns the visible area for the widget on the screen.

It is calculated this way:
x = parent.GetX() + x
y = parent.GetY() + y
w = max(parent.GetX(), x) - min(parent.GetX2(), GetX())
h = similar to w

x2 is "x + w". I use "getX()" so it checks the parents parent and so on.

Method GetX:Float()
if parent then return parent.GetX() + x
return x
End Method



bye
Ron


Takis76(Posted 2014) [#22]
:) The very first version is ready

GUI Ver 0.0001

It creates 3 windows with simple skin , (you can create as many windows you want) , you can change the color , you can make them
hidden or show and you can change the text of the title bar.

You can't move them yet not drag or select them and change their z order. (But you can initialize the size and position on creation only)
Hmm change the z order and position on run time... I haven't figure out how to do this yet , but I will find it.
The next step is to add one simple button gadget on this window.

Edit: Oooops a lots of new posts added. I will try to make the window dragable and clickable and change the z order of the window. I confused a lot with the new code you sent me.


zoqfotpik(Posted 2014) [#23]
I still think when you're trying to write a game and an editor, writing a windowing system so you can get to that point is way too much effort. It's really not a trivial undertaking and there are easier ways of doing almost anything than that.


Takis76(Posted 2014) [#24]

I still think when you're trying to write a game and an editor, writing a windowing system so you can get to that point is way too much effort. It's really not a trivial undertaking and there are easier ways of doing almost anything than that.



I know there are easier ways to do it just using the ifsogui I am already have , but there is not any complete gui library for blitzmax. Even the high gui is not complete.
Menus are missing , right button menus are missing too. Most of the guis don't have canvas gadget and other more complex gadgets.

Creating a gui (If I will manage to finish it) will give me a lots of experience of object oriented programming and understanding other things that I still haven't understood with other languages too. On thing I wasn't understood in the past was the lists and classes with inheritance. Now I have learnt the inheritance is just the "extends" command in one struct or type and the class was one method or function inside the struct or type.
This took me more than 6 years to understand it. Also I discovered the command eachin always I used the for loop with fixed variables.


Ok I will try to make the window movable and change its z order.
:)


Derron(Posted 2014) [#25]
The whole "drag" thing is not that easy ... it's NOT just "if mouseover and mousehit then setDragged".

In my gui I do not handle "drag n drop" properly, but I handle it. Just check how wxWidgets handle it: a drag event starts not on "mousedown/mouseup" but on "mousedown and moved mouse over a certain distance".

Think of it as if the widgets are glued on the background and you need a specific force to unglue them.


@missing things:
A Menu is just a Canvas + Labels.
A Context Menu is a Menu created as soon as a widget is configured to react that way on a right click.

A GUI Toolkit just needs to provide you with basic widgets.

Eg. is a "Spinner" nothing more than a "Input" + 2 small buttons with a glyph on each of them.

A "Color selector" is a Canvas/Window with colored buttons (with design "inset") etc.
If you want to have a color wheel, then that is a canvas which reacts to your clicks and has a custom render code.

Do not blame the existing toolkits not to have "everything" you could imagine.
There are always multiple ways to combine things to form something new: eg. I combined a "input" and a "list" to a dropdown-widget - using this approach it is even possible to "drag n drop" items from the dropdown widget to a list or other way around. A list is nothing more than a special "panel" (canvas) which listens to some events and contains scrollers (the area to scroll something).


To keep things "easy to modify" you should think about the design before coding ... I have many things I would have better coded differently. I think "stubs" (functions doing nothing) are not that costly compared to the benefit you gain instead of having to copy-paste a lot of code from widget A to B.
So something like DrawBackground() DrawContent() DrawOverlay() DrawDebug() helps to just override things changing between widgets.


Also think a bit about connectors (Field onClick:int() = null) which you could easily attach custom functions too (same is possible for your draw methods) but it is similar to extending types to override behavior, don't know what is the preferable way of doing things.


bye
Ron


Takis76(Posted 2014) [#26]
A new versions is out

GUI Ver 0.0001b

It does the dragging effect but is not so smooth have lag. I don't have understood about the z order yet and when you overlap the windows you dragging and windows behind.
This is just the drag effect , drag the window from the title bar.


Takis76(Posted 2014) [#27]
It seems this .self command do miracles.
I fixed the zorder and now I can move my windows without picking up overlapped ones behind , but I did it little differently.

The next version of my GUI.

GUI Ver 0.0002

The next is to see how to add one gadget (for example a button over the existing window).

I have issues when I move the mouse fast. The pointer goes outside of the window very fast and the window remains unmovable. How do I will fix this?


zoqfotpik(Posted 2014) [#28]
You have it stay selected until the mouseclick is released.

When it is first selected, give the mousepointer a "currentlygrabbedwindow" ID.

If this ID is non-null, always have that window follow the mouse.

When the click is released, give that ID a null or zero value.

Looks like you are almost there.


Derron(Posted 2014) [#29]
For such cases I have a "GUIManager" - it contains the "widget list", helper functions ("addWidget") but also contains the currently focused widget etc.

So you could store the exact widget there (draggedWidget:TGUIWidget) or just the ID - but you then have to make sure that the ID is unique (a GUID) . You then have to "get(id)" each time you compare it (but you save to unset one reference).


@rest
Do like zoqfotpik suggested: split into "grab" and "release" - grab then of course could be like suggested above be a mix of "mousedown" and "moved distance > x".

Similar you will have to handle "button clicks" - you should act on "mouseup" not on "mousedown" (store object on mousedown but emit "click" if mouseup is on the same object).


bye
Ron


Takis76(Posted 2014) [#30]
I don't have window IDs in my code!
Do I need to add extra argument in my window create method?


zoqfotpik(Posted 2014) [#31]
If it's a big problem you could make it a global variable. This gets incremented every time you create a window and in your window create method the window ID gets set to that variable.


Derron(Posted 2014) [#32]
Just have something like this:

TBaseWidget
 Field ID:int
 Global LastID:int = 0

 Method New()
   LastID :+1
   ID = LastID
 End Method
End Type


That "New" Method is also called as soon as you do a "new TMyWidget" - as soon as you extend "TMyWidget" from "TBaseWidget". Even if you override "Method New()" in your custom widgets, it gets "traversed" up to the baseWidget.


bye
Ron


zoqfotpik(Posted 2014) [#33]
Now Derron, is there any other good way other than a global variable?

I use globals. I may not use a *ton* of them but when I'm finished with a project I often have a screenful or more. Is it better to put it inside a type for reasons of modularity, and if you do that, wouldn't "lastID" be running into dangers of namespace incompatibility?


Derron(Posted 2014) [#34]
how could the run into "namespace problems"?


Instead of a global variable you could use a GUID ... eg. based on something like "amount of instances of my type" + nanoseconds since programmestart or so.
You just have to make sure, that you cannot reuse an existing GUID (or think of threading ...).

For predefined things a GUID is really handy (eg "enemy-01" "player-1") ... but for things like particle systems you will better use a global "lastID".

If all your objects extend from a baseobject, you wont even pollute namespaces.


Could you give an example (maybe I just do not understand enough from "namespaces" and problems arising there) ?


bye
Ron


zoqfotpik(Posted 2014) [#35]
I just mean conflicting variable names. Of course there's an obvious way to fix that problem.

Nothing technical regarding ID numbers, just a general question regarding global variables and naming. I usually precede mine with JT in things I package up for reuse.


Derron(Posted 2014) [#36]
To avoid this I just use the globals in a "Manager"-type. The Manager itself is a singleton so its variables are "fields" - this way it could get properly exposed to LUA (reflection only handles Fields - and modded, it handles Const but not Global).


bye
Ron