Immediate Mode GUI

Previous topic - Next topic

Albert

Update: For the updated IMGUI system jump to: http://www.glbasic.com/forum/index.php?topic=5431.msg69686#msg69686

I followed Sol's tutorial about Immediate Mode GUI http://sol.gfxile.net/imgui/ and expanded it a bit (only the top widget will be highlighted; added a dragable Window widget)

QuoteThe magic here is that the call to button will perform everything the widget would be expected to do at any single time, including the rendering of the visible widget. There's a bit of global data that's shared by all of the widgets that enables them to work.

There's no setup phase, but if a widget needs some data to show it's state (say, a scroll bar, text edit field, etc), the state data is owned by the host application and not the widget. This is surprisingly handy and performance-friendly, as there's no querying and message-sending involved.

There's also no cleanup required, except for the data that you have allocated yourself. The widgets only exist when they're called, so you'll need to ask for them each frame you want to use them.

Nothing is ever simply positive, and IMGUI does have its problems. The most obvious one is that it's just about as anti-OOP as you can get, so it may feel wrong for you. Other problems are related to the fact that the widgets don't really exist except when they are queried, and as such things like automatic layouts or keyboard focus get somewhat more difficult.



Here is the code:
Code (glbasic) Select

// --------------------------------- //
// Project: imgui
// Start: Thursday, December 09, 2010
// IDE Version: 8.174

// Made by Bence Dobos. Based on this great tutorial http://sol.gfxile.net/imgui/
// Widget IDs must be unique!

SETCURRENTDIR("Media") // seperate media and binaries?
SYSTEMPOINTER TRUE
LOADFONT "smalfont.png", 0

//GUI object
GLOBAL gui AS UIState

//variables (what the widgets will be using and changing)
GLOBAL backgroundcolor = RGB(100,100,255)
GLOBAL text$ = "teszt"
GLOBAL showwin=FALSE
GLOBAL winX=300
GLOBAL winY=50
GLOBAL win2X=70
GLOBAL win2Y=250
GLOBAL menustr1$[]
DIMDATA menustr1$[], "File", "Open", "Save", "Save As", "---", "New project", "Open project", "Close project", "---", "Exit"
GLOBAL showmenu1=FALSE
GLOBAL menustr2$[]
DIMDATA menustr2$[], "View", "Output", "Status", "---", "Skins", "Colors", "Look and feel",  "---", "Whitespace", "Wordwrap"
GLOBAL showmenu2=FALSE
GLOBAL toastmsg$="Welcome"
GLOBAL toasttimer=50

//start endless loop
WHILE TRUE
CLEARSCREEN backgroundcolor
//prepare the gui
gui.prepare()

ALPHAMODE 0
PRINT "(SHIFT+)TAB to switch widget, ENTER, UP, DOWN to use widget",5,25,TRUE
//ALPHAMODE -0.5

//buttons (doing nothing)
IF button(1,10,50,60,25,RGB(100,0,200),"Button") THEN settoast("button", 50)
IF button(2,120,50,100,25,RGB(100,0,200),"Button long") THEN settoast("button long", 100)

//button (change background color)
IF button(3,10,100,60,25,RGB(255,0,200),"Color") THEN backgroundcolor = RND(0xffffff)

//button (show/hide window)
IF button(4,120,100,150,25,RGB(100,0,200),"Show/hide Window") THEN showwin = NOT showwin

//button (show popupmenu)
IF button(5,10,0,40,25,RGB(200,200,200),"File") THEN showmenu1=TRUE
//button (show popupmenu)
IF button(6,50,0,50,25,RGB(200,200,200),"View") THEN showmenu2=TRUE

//slider (change background color)
sliderV(7, 550, 20, 430, 0xffffff, backgroundcolor)
textfield(7, 10,200, 20, text$)
sliderH(8, 50, 410, 430, 0xffffff, backgroundcolor)

//window (about window, transparent)
ALPHAMODE -0.7
window( 20, win2X, win2Y, 200, 150, RGB(100,200,70), "About")
ALPHAMODE 0
PRINT "Immediate Mode GUI", win2X+5, win2Y+20, TRUE
PRINT "Written by Bence Dobos", win2X+5, win2Y+50, TRUE
PRINT "Based on Jari Komppa's", win2X+5, win2Y+80, TRUE
PRINT "great tutorial at", win2X+5, win2Y+100, TRUE
PRINT "sol.gfxile.net/imgui/", win2X+5, win2Y+120, TRUE

//window (showing gui variables)
IF showwin
window( 10, winX, winY, 200, 300, RGB(200,200,200), "GUI states")
PRINT "tab: " + gui.tab ,winX+5, winY+20, TRUE
PRINT "shift: " + gui.shift ,winX+100, winY+20, TRUE
PRINT "ctrl: " + gui.ctrl ,winX+5, winY+40, TRUE
PRINT "alt: " + gui.alt ,winX+100, winY+40, TRUE
PRINT "up: " + gui.up ,winX+5, winY+60, TRUE
PRINT "down: " + gui.down ,winX+100, winY+60, TRUE
PRINT "left: " + gui.left ,winX+5, winY+80, TRUE
PRINT "right: " + gui.right ,winX+100, winY+80, TRUE
PRINT "mousex:" + gui.mousex ,winX+5, winY+100, TRUE
PRINT "mousex:" + gui.mousey ,winX+100, winY+100, TRUE
PRINT "lclick: " + gui.mouselclick ,winX+5, winY+120, TRUE
PRINT "rclick: " + gui.mouserclick ,winX+100, winY+120, TRUE
PRINT "mclick: " + gui.mousemclick ,winX+5, winY+140, TRUE
PRINT "scroll: " + gui.mousescroll ,winX+100, winY+140, TRUE
PRINT "prehot: " + gui.prehotitem ,winX+5, winY+160, TRUE
PRINT "hotitem: " + gui.hotitem ,winX+100, winY+160, TRUE
PRINT "active: " + gui.activeitem ,winX+5, winY+180, TRUE
PRINT "kbditem: " + gui.kbditem ,winX+100, winY+180, TRUE
PRINT "key$: " + gui.keyentered$ ,winX+5, winY+200, TRUE
PRINT "keychar: " + gui.keychar ,winX+100, winY+200, TRUE
PRINT "lastwidget: " + gui.lastwidget ,winX+5, winY+220, TRUE
//a red close button bottom of the window
IF button(11,winX+5,winY+270,190,25,RGB(255,100,100),"Close this window") THEN showwin = NOT showwin
//a red close button top-right of the window
IF button(12,winX+200-16,winY,16,16,RGB(255,100,100),"X") THEN showwin = NOT showwin
ENDIF

IF showmenu1
LOCAL item = popupmenu(30, 10, 0, RGB(200,200,200), menustr1$[])
IF item = -1 THEN showmenu1=FALSE
IF item>0
settoast(menustr1$[item] + " MeniItem selected", 50)
showmenu1=FALSE
ENDIF
ENDIF
IF showmenu2
LOCAL item = popupmenu(30, 50, 0, RGB(200,200,200), menustr2$[])
IF item = -1 THEN showmenu2=FALSE
IF item>0
settoast(menustr2$[item] + " MeniItem selected", 50)
showmenu2=FALSE
ENDIF
ENDIF
IF toasttimer>0
toast(99, 320-LEN(toastmsg$, TRUE)/2, 440, RGB(60, 60, 60), toastmsg$, toasttimer)
DEC toasttimer
ENDIF

//finish the gui
gui.finish()
SHOWSCREEN
WEND

FUNCTION settoast:text$, timer
toastmsg$=text$
toasttimer=timer
ENDFUNCTION

// --------------------------------- //
// Immediate Mode GUI code start here
// Widget IDs must be unique!

FUNCTION getkey: k, BYREF dst
IF KEY(k)
IF dst<>-1
dst = 1
ENDIF
ELSE
dst = 0
ENDIF
ENDFUNCTION

TYPE UIState
//mouse
mousex
mousey
mouselclick
mouserclick
mousemclick
mousescroll

//keyboard
kbditem
keyentered$
keychar
tab
shift
ctrl
alt
up
down
left
right

//widget
prehotitem
hotitem
activeitem
lastwidget

//checking if mouse position is in specified region
FUNCTION inregion: x, y, w, h
IF (self.mousex<x OR self.mousey<y OR self.mousex>=x+w OR self.mousey>=y+h) THEN RETURN FALSE
RETURN TRUE
ENDFUNCTION

//prepare the IMGUI. Must call at the start of your gameloop
FUNCTION prepare:
//widget
self.hotitem = self.prehotitem
self.prehotitem = 0
//mouse
MOUSESTATE self.mousex, self.mousey, self.mouselclick, self.mouserclick
self.mousemclick = MOUSEAXIS(5)
self.mousescroll = MOUSEAXIS(2)
//keyboard
self.keyentered$ = INKEY$()
self.keychar = ASC(self.keyentered$)
getkey(15, self.tab)
getkey(42, self.shift)
getkey(29, self.ctrl)
getkey(56, self.alt)
getkey(200, self.up)
getkey(205, self.right)
getkey(208, self.down)
getkey(203, self.left)
//PRINT "key "+self.keyentered$+" "+ASC(self.keyentered$),360,5,TRUE
ENDFUNCTION

//finish the IMGUI. Must call at the end of your gameloop
FUNCTION finish:
//mouse
IF self.mouselclick = FALSE
self.activeitem = 0
ELSE
IF self.activeitem = 0 THEN self.activeitem = -1
ENDIF
//keyboard
IF gui.tab = 1
self.kbditem = 0
ENDIF
self.keyentered$ = ""
self.keychar = 0
ENDFUNCTION
ENDTYPE //TYPE IMGUI

//Button widget (clickable)
FUNCTION button: id, x, y, width, height, color, text$
LOCAL fw, fh
GETFONTSIZE fw, fh

IF gui.hotitem = id
IF (gui.activeitem = 0 AND gui.mouselclick)
gui.activeitem = id
ENDIF
ENDIF
IF gui.inregion(x,y,width,height)
gui.prehotitem = id
ENDIF
IF gui.kbditem = 0
gui.kbditem = id
ENDIF

IF gui.kbditem = id
DRAWRECT x-1, y-1, width+4, height+4, RGB(255, 0, 0)
ENDIF

DRAWRECT x+1, y+1, width, height, RGB(0, 0, 0)
IF gui.hotitem = id
IF gui.activeitem = id
DRAWRECT x+2, y+2, width, height, RGB(255, 255, 255)
ELSE
DRAWRECT x, y, width, height, RGB(255, 255, 255)
ENDIF
ELSE
DRAWRECT x, y, width, height, color
ENDIF

PRINT text$, x+5, y+(height-fh)/2, TRUE

//KBDITEM stb...
IF gui.kbditem = id
IF gui.tab = 1
gui.kbditem = 0
IF gui.shift THEN gui.kbditem = gui.lastwidget
gui.tab = -1
ENDIF
IF gui.keyentered$="\n" //ENTER
RETURN 1
ENDIF
ENDIF

gui.lastwidget = id

IF gui.mouselclick = 0 AND gui.hotitem = id AND gui.activeitem = id THEN RETURN 1

RETURN 0
ENDFUNCTION

//slider widget (vertical)
FUNCTION sliderV: id, x, y, height, maximum, BYREF value
STATIC marginH = 4
STATIC marginW = 4
LOCAL ypos = ((height-16) * value) / maximum

IF gui.hotitem = id
IF (gui.activeitem = 0 AND gui.mouselclick)
gui.activeitem = id
ENDIF
ENDIF
IF (gui.inregion(x+marginW,y+marginH, 16, height))
gui.prehotitem = id
ENDIF
IF gui.kbditem = 0
gui.kbditem = id
ENDIF

IF gui.kbditem = id
DRAWRECT x-1, y-1, 16+2, height+marginH*2+2, RGB(255, 0, 0)
ENDIF

DRAWRECT x, y, 16, height + marginH*2, RGB(50, 50, 50)

IF (gui.activeitem = id OR gui.hotitem = id)
DRAWRECT x+marginW, y+marginH + ypos, 16-marginW*2, 16, RGB(255,255,255)
ELSE
DRAWRECT x+marginW, y+marginH + ypos, 16-marginW*2, 16, RGB(200,200,200)
ENDIF

IF gui.kbditem = id
IF gui.tab = 1
gui.kbditem = 0
IF gui.shift THEN gui.kbditem = gui.lastwidget
gui.tab = -1
ENDIF
IF gui.up = 1
IF (value>0)
DEC value
RETURN 1
ENDIF
ENDIF
IF gui.down = 1
IF (value<maximum)
INC value
RETURN 1
ENDIF
ENDIF
ENDIF

gui.lastwidget = id

IF gui.activeitem = id
LOCAL mousepos = gui.mousey - (y+marginH)
IF (mousepos < 0) THEN mousepos = 0
IF (mousepos > height) THEN mousepos = height
LOCAL v = (mousepos*maximum) / height
IF (v <> value)
value = v
RETURN 1
ENDIF
ENDIF

RETURN 0
ENDFUNCTION

//slider widget (vertical)
FUNCTION sliderH: id, x, y, width, maximum, BYREF value
STATIC marginH = 4
STATIC marginW = 4
LOCAL xpos = ((width-16) * value) / maximum

IF gui.hotitem = id
IF (gui.activeitem = 0 AND gui.mouselclick)
gui.activeitem = id
ENDIF
ENDIF
IF (gui.inregion(x+marginH,y+marginW, width, 16))
gui.prehotitem = id
ENDIF
IF gui.kbditem = 0
gui.kbditem = id
ENDIF

IF gui.kbditem = id
DRAWRECT x-1, y-1, width+marginH*2+2, 16+2, RGB(255, 0, 0)
ENDIF

DRAWRECT x, y, width + marginH*2, 16, RGB(50, 50, 50)

IF (gui.activeitem = id OR gui.hotitem = id)
DRAWRECT x+marginW + xpos, y+marginH, 16, 16-marginW*2, RGB(255,255,255)
ELSE
DRAWRECT x+marginW + xpos, y+marginH, 16, 16-marginW*2, RGB(200,200,200)
ENDIF

IF gui.kbditem = id
IF gui.tab = 1
gui.kbditem = 0
IF gui.shift THEN gui.kbditem = gui.lastwidget
gui.tab = -1
ENDIF
IF gui.left = 1
IF (value>0)
DEC value
RETURN 1
ENDIF
ENDIF
IF gui.right = 1
IF (value<maximum)
INC value
RETURN 1
ENDIF
ENDIF
ENDIF

gui.lastwidget = id

IF gui.activeitem = id
LOCAL mousepos = gui.mousex - (x+marginH)
IF (mousepos < 0) THEN mousepos = 0
IF (mousepos > width) THEN mousepos = width
LOCAL v = (mousepos*maximum) / width
IF (v <> value)
value = v
RETURN 1
ENDIF
ENDIF

RETURN 0
ENDFUNCTION


//textfield widget (could type text in it)
FUNCTION textfield: id, x, y, width, BYREF buffer$

LOCAL length = LEN(buffer$)
LOCAL changed = 0
LOCAL fw, fh
GETFONTSIZE fw, fh

IF gui.hotitem = id
IF (gui.activeitem = 0 AND gui.mouselclick)
gui.activeitem = id
ENDIF
ENDIF
IF (gui.inregion(x-4,y-4, width*fw+8, fh+8))
gui.prehotitem = id
ENDIF
IF gui.kbditem = 0
gui.kbditem = id
ENDIF

IF gui.kbditem = id
DRAWRECT x-6, y-6, width*fw+12, fh+12, RGB(255,0,0)
ENDIF

IF (gui.activeitem = id OR gui.hotitem = id)
DRAWRECT x-4, y-4, width*fw+8, fh+8, RGB(200,200,200)
ELSE
DRAWRECT x-4, y-4, width*fw+8, fh+8, RGB(100,100,100)
ENDIF

IF gui.kbditem = id AND MOD(GETTIMERALL(),1000) < 500
PRINT buffer$+"_", x, y
ELSE
PRINT buffer$, x, y
ENDIF

IF gui.kbditem = id
IF gui.tab = 1
gui.kbditem = 0
IF gui.shift THEN gui.kbditem = gui.lastwidget
gui.tab = -1
ENDIF
IF ASC(gui.keyentered$) = 8 AND length > 0
buffer$ = MID$(buffer$, 0, length-1)
changed = 1
ENDIF
ENDIF
IF (ASC(gui.keyentered$)>=32 AND ASC(gui.keyentered$)<=127 AND length<width)
buffer$ = buffer$ + gui.keyentered$
changed = 1
ENDIF

IF (gui.mouselclick=FALSE AND gui.hotitem = id AND gui.activeitem = id)
gui.kbditem = id
ENDIF

gui.lastwidget = id

RETURN changed

ENDFUNCTION

//window widget (only a rectangle, with draggable header)
FUNCTION window: id, BYREF x, BYREF y, width, height, color, name$
STATIC headerH = 16
LOCAL changed = 0
LOCAL fw, fh
GETFONTSIZE fw, fh

IF gui.hotitem = id
IF (gui.activeitem = 0 AND gui.mouselclick)
gui.activeitem = id
ENDIF
ENDIF

IF (gui.inregion(x, y, width, height))
gui.prehotitem = -2 //window area
ENDIF
IF (gui.inregion(x, y, width, headerH))
gui.prehotitem = id //header
ENDIF

//move window
IF gui.mouselclick = 1 AND gui.activeitem = id
x = gui.mousex-width/2
y = gui.mousey-headerH/2
changed = 1
ENDIF

IF (gui.activeitem = id OR gui.hotitem = id)
DRAWRECT x, y, width, headerH, RGB(255, 255, 255)
ELSE
DRAWRECT x, y, width, headerH, color
ENDIF

PRINT name$, x+5, y+(headerH-fh)/2, TRUE

LOCAL namelen = LEN(name$, TRUE)
FOR i=1 TO 4
DRAWLINE x + 10 + namelen, y+headerH*i/4.0, x + width - 5, y+headerH*i/4.0, RGB(255, 255, 255)
NEXT

IF (gui.activeitem = id OR gui.hotitem = id)
DRAWRECT x, y+headerH, width, height-headerH, color //RGB(255,255,255)
ELSE
DRAWRECT x, y+headerH, width, height-headerH, color
ENDIF

RETURN changed
ENDFUNCTION

//popupmenu widget (only active while hot, return -1 if )
FUNCTION popupmenu: id, x, y, color, menuitems$[]
LOCAL fw, fh
GETFONTSIZE fw, fh
LOCAL width=0
LOCAL height=0
LOCAL numitems=0
LOCAL item=0
FOREACH mi$ IN menuitems$[]
width = MAX(width, LEN(mi$, TRUE))
IF mi$<>"---" THEN INC numitems
NEXT
height = numitems*fh
INC width, 10

IF (gui.inregion(x, y+fh, width, height-fh) OR gui.inregion(x, y, LEN(menuitems$[0],TRUE)+10, fh))
gui.prehotitem = id
gui.hotitem = id
IF (gui.activeitem = 0)
gui.activeitem = id
ENDIF
ENDIF
IF gui.hotitem<>id AND gui.mouselclick
RETURN -1
ENDIF

DRAWRECT x, y, LEN(menuitems$[0],TRUE)+10, fh, 0xffffff-color
DRAWRECT x, y+fh, width, height-fh, color
IF (gui.hotitem = id)
item = INTEGER((gui.mousey - y) / fh)
IF item>0
DRAWRECT x, y+item*fh, width, fh, RGB(255, 255, 255)
ENDIF
ENDIF

LOCAL ypos=0
FOREACH mi$ IN menuitems$[]
IF mi$<>"---"
PRINT mi$, x+5, y+ypos, TRUE
INC ypos, fh
ELSE
DRAWLINE x, y+ypos, x+width, y+ypos, 0
ENDIF
NEXT

IF (item>0 AND gui.activeitem = id AND gui.mouselclick)
numitems=0
LOCAL i=0
FOREACH mi$ IN menuitems$[]
IF mi$<>"---"
IF item=numitems THEN RETURN i
INC numitems
ENDIF
INC i
NEXT
ENDIF
RETURN 0
ENDFUNCTION

//toads widget (show a message and fade away after a period)
FUNCTION toast: id, x, y, color, text$, BYREF timer
LOCAL fw, fh
GETFONTSIZE fw, fh
LOCAL alpha=timer/10.0
IF alpha<0.1 THEN RETURN -1
IF alpha>1.0 THEN alpha = 0.8
LOCAL width = LEN(text$, TRUE)

IF gui.hotitem = id
IF (gui.activeitem = 0 AND gui.mouselclick)
gui.activeitem = id
timer = 0
ENDIF
ENDIF

IF (gui.inregion(x-4, y-4, width+8, fh+8))
gui.prehotitem = id
ENDIF

ALPHAMODE -alpha
DRAWRECT x-4, y-4, width+8, fh+8, color
PRINT text$, x,y, TRUE
ALPHAMODE 0
ENDFUNCTION


Also you can download the whole project.

EDIT:
- added SliderH (horizontal slider)
- added PopupMenu
- added Toaster (fade away message)

[attachment deleted by admin]

Wampus

Well...this is superb=D

Immediate Mode GUIs, eh? I am going to look up more about them. I would like to use something like this for a complex map, character and AI testing program I need to write. Sometimes I will need information boxes up and be able to change settings on the fly while the game is running.

Are you going to develop it further? Add other features?

Kitty Hello

Wow, that's very little code you're using.  :blink:

Did I do anything wrong with DDgui?

Albert

Hello!
I'm developing it further, adding new widgets to it, as I'm using IMGUI to improve my leveleditor.
As it is told in the tutorial, it's not a complete GUI system as DDGUI is, and a lot of thing cannot be do with IMGUI. But if you want to add a quick button or a slider into your game, then you can do it without too much inicialization.

Kitty Hello

The menu is ace!

Wampus

Albert I think I will do what you did and follow those tutorials about Immediate Mode GUIs, or use what you've done and add what I need.

By the New Year I want to have a GUI ready where I can turn windows off & on and move them about that will have virtual screens for the iPad (1024x768), iPhone Retina (960x640) and older iPhone (480x320) all running in realtime. Since the code to a game I'm writing for these systems will completely separate display, input and the game engine mechanics it should be possible to run the game displays and inputs inside virtual windows. I also want windows I can turn off and on that allow me to edit maps, game agents, reset the game to the beginning or jump to other parts and also edit other settings while the game is running in realtime. An immediate-mode GUI is exactly what I need for this kind of thing.

Gernot, as far as I'm aware these Immediate-Mode GUIs work very differently to conventional GUIs. They're simplistic but very good for realtime processing - great for games.

Albert

#6
Ragaril, please keep in mind, that it's not designed to work with Windows. I added a window widget just for fun, but you can't bring a window to front or back, unless you write a little windows manager.
If you see Sol's other tutorials, you may find a PDF about IMGUI in J2ME devices. You can see that IMGUI is good for different screen sizes, because IMGUI is a dynamic system. You also may found in that PDF some J2ME screenshot about listboxs and other goodness, but there are only little information about this.
In the PDF there was a formula to complex, scrollable listbox:
gui.StartListbox(1, ...)
gui.ListItem(1, ...)
gui.ListItem(1, ...)
.....
gui.EndListbox(1, Count)
If you crate ListBox or some another cool widget please share with us.
In the PDF there are also some links, I followed one to a forum, where some guy developed IMGUI further.

Wampus

Thanks for the tips Albert. If I do create a listbox or whatnot, I'll post it. I just need some way to create an extensive testing environment where I can change many variables without having to change and recompile code or alter an ini file.

DmitryK

Albert, thank you for sharing that!

this is my five cents, hope it'll be useful:

1. I changed "button" function for sprite support
if you don't send last param "image" (sprite id) the function works as before
also text is horizontal centered now

2. I joined sliderh and sliderv fuctions to one - slider
now slider can be any width (not only 16), both width and height are sent as params
also added sprite support - two last params

Code (glbasic) Select
//Button widget (clickable)
FUNCTION button: id, x, y, _width, _height, color, text$, image=-1 //new   image - sprite id
LOCAL fw, fh
LOCAL width, height // new

//new---------------------------------
IF image=-1
  width = _width
height = _height
ELSE
GETSPRITESIZE image, width, height
ENDIF
//end new-----------------------------

GETFONTSIZE fw, fh

IF gui.hotitem = id
IF (gui.activeitem = 0 AND gui.mouselclick)
gui.activeitem = id
ENDIF
ENDIF
IF gui.inregion(x,y,width,height)
gui.prehotitem = id
ENDIF
IF gui.kbditem = 0
gui.kbditem = id
ENDIF

    // selected outline draw
IF gui.kbditem = id AND image=-1 //change -> AND image=-1
DRAWRECT x-1, y-1, width+4, height+4, RGB(255, 0, 0)
ENDIF

    IF gui.kbditem = id AND image=-1 //change
DRAWRECT x+1, y+1, width, height, RGB(0, 0, 0)
ENDIF

IF gui.hotitem = id
IF gui.activeitem = id
  IF image=-1 //new
  DRAWRECT x+2, y+2, width, height, RGB(255, 255, 255)
  ELSE // new
    ALPHAMODE -0.8
DRAWSPRITE image, x+2, y+2 //new
  ENDIF // new
ELSE
  IF image=-1 //new
DRAWRECT x, y, width, height, RGB(255, 255, 255)
  ELSE
    ALPHAMODE -1
    DRAWSPRITE image, x, y //new
  ENDIF
ENDIF
ELSE
  IF image=-1 //new
DRAWRECT x, y, width, height, color
  ELSE
    ALPHAMODE -0.8
    DRAWSPRITE image, x, y //new
  ENDIF
ENDIF

PRINT text$,  x+(width-LEN(text$,TRUE))/2, y+(height-fh)/2, TRUE //change -> x+(width-LEN(text$,TRUE))/2

//KBDITEM stb...
IF gui.kbditem = id
IF gui.tab = 1
gui.kbditem = 0
IF gui.shift THEN gui.kbditem = gui.lastwidget
gui.tab = -1
ENDIF
IF gui.keyentered$="\n" //ENTER
RETURN 1
ENDIF
ENDIF

gui.lastwidget = id

IF gui.mouselclick = 0 AND gui.hotitem = id AND gui.activeitem = id THEN RETURN 1

RETURN 0
ENDFUNCTION

//slider widget (vertical and horizontal)
FUNCTION slider: id, x, y, _width, _height, maximum, BYREF value, slider_image=-1, pointer_image=-1
STATIC marginH = 2
STATIC marginW = 2

//new-----------------------------------
LOCAL width, height, val, mousepos, xpos, ypos, slider_size, pointer_size, slider_h // true, false

IF slider_image=-1
  width = _width
height = _height
ELSE
GETSPRITESIZE slider_image, width, height
GETSPRITESIZE pointer_image, pointer_size, pointer_size
ENDIF

IF width >= height // horizontal slider
  slider_h = TRUE
  slider_size = width
  IF slider_image=-1 THEN pointer_size = height - marginH*2
  xpos = ((width-pointer_size -marginW*2) * value) / maximum
  ypos = 0
ELSE               // vertical slider
  slider_h = FALSE
  slider_size = height
  IF slider_image=-1 THEN pointer_size = width - marginW*2
  xpos = 0
  ypos = (height-pointer_size - marginH*2) - ((height-pointer_size - marginH*2) * value) / maximum
ENDIF
//end new ------------------------------

IF gui.hotitem = id
IF (gui.activeitem = 0 AND gui.mouselclick)
gui.activeitem = id
ENDIF
ENDIF
IF (gui.inregion(x,y, width, height))
gui.prehotitem = id
ENDIF
IF gui.kbditem = 0
gui.kbditem = id
ENDIF

    //selected outline draw
IF gui.kbditem = id AND slider_image=-1 THEN DRAWRECT x-1, y-1, width+2, height+2, RGB(255, 0, 0)

    //slider itself draw
    IF slider_image=-1 THEN DRAWRECT x, y, width, height, RGB(50, 50, 50)

    // pointer draw
IF (gui.activeitem = id OR gui.hotitem = id)
  IF slider_image=-1
DRAWRECT x+marginW + xpos, y+marginH + ypos, pointer_size, pointer_size, RGB(255,255,255) //change
  ELSE
    ALPHAMODE -1
    DRAWSPRITE slider_image, x, y
    DRAWSPRITE pointer_image, x+marginW + xpos, y+ypos
  ENDIF
ELSE
  IF slider_image=-1
DRAWRECT x+marginW + xpos, y+marginH + ypos, pointer_size, pointer_size, RGB(200,200,200) //change
  ELSE
    ALPHAMODE -0.8
    DRAWSPRITE slider_image, x, y
    DRAWSPRITE pointer_image, x+marginW + xpos, y+ypos
  ENDIF
ENDIF

    //keyboard management
IF gui.kbditem = id
IF gui.tab = 1
gui.kbditem = 0
IF gui.shift THEN gui.kbditem = gui.lastwidget
gui.tab = -1
ENDIF
IF gui.left = 1 OR gui.down = 1 //change
IF (value > 0)
DEC value, maximum/slider_size //change -> maximum/slider_size
IF value < 0 THEN value = 0
RETURN 1
ENDIF
ENDIF
IF gui.right = 1 OR gui.up = 1
IF (value < maximum)
INC value, maximum/slider_size //change -> maximum/slider_size
IF value > maximum THEN value = maximum
RETURN 1
ENDIF
ENDIF
ENDIF

gui.lastwidget = id

    //mouse management
IF gui.activeitem = id
IF slider_h //new
mousepos = gui.mousex - (x+marginW)
ELSE
mousepos = gui.mousey - (y+marginH)
ENDIF
IF (mousepos < 0) THEN mousepos = 0
IF (mousepos > slider_size) THEN mousepos = slider_size
IF slider_h //new
  val = (mousepos*maximum) / slider_size // for horiz
ELSE
  val = (slider_size-(mousepos*maximum)) / slider_size //for vert
ENDIF
IF (val <> value)
value = val
RETURN 1
ENDIF
ENDIF

RETURN 0
ENDFUNCTION




[attachment deleted by admin]
Our life is just a Game!

Albert

Neat! I like it.
I found some bug in my IMGUI code, I will rerelease here when I will have some more spare time :)

Here is a screenshot. I used my IMGUI code in my old jump&run game's level editor:

Schranz0r

I <3 DGArray's :D

PC:
AMD Ryzen 7 3800X 16@4.5GHz, 16GB Corsair Vengeance LPX DDR4-3200 RAM, ASUS Dual GeForce RTX™ 3060 OC Edition 12GB GDDR6, Windows 11 Pro 64Bit, MSi Tomahawk B350 Mainboard

erico


WPShadow

 :blink: looks great  :good:
AMD X2 4600, 2 GB Ram, ATI X1950 XTX, XP PRO SP2: GLB Premium 10.beta_dingsi, <(´.´<) Kirby Dance (>`.`)>
http://lostrevenant.blogspot.com
alea iacta est

MrTAToad

Will you be able to add in radio buttons, tabs and edit boxes ?

Albert

#14
IMGUI is about: "you store all the data, not the GUI object". So you can add radio buttons and tabs, but the control will be not in the widget. I've just added to my program a Graphics details setting with simple radio buttons. I used only the basic IMGUI widget called button()



There is my Settings() function (I deleted the other settings to make smaller this code, and I'm using a big font):
Code (glbasic) Select


GLOBAL GraphicsDetail=2 //High

Settings()

FUNCTION Settings:
LOCAL vege=0
LOCAL mx = 10
LOCAL mxg = 150
LOCAL my
LOCAL tmp, color
WHILE vege<>1
PRINT "Details",mx,my,TRUE
IF GraphicsDetail=0
color=RGB(128,128,128)
ELSE
color=RGB(0,0,0)
ENDIF
tmp=button(5, mxg, my+10, 100, 30, color, "Low")
IF tmp THEN GraphicsDetail=0
IF GraphicsDetail=1
color=RGB(128,128,128)
ELSE
color=RGB(0,0,0)
ENDIF
tmp=button(6, mxg+110, my+10, 100, 30, color, "Medium")
IF tmp THEN GraphicsDetail=1
IF GraphicsDetail=2
color=RGB(128,128,128)
ELSE
color=RGB(0,0,0)
ENDIF
tmp=button(7, mxg+220, my+10, 100, 30, color, "High")
IF tmp THEN GraphicsDetail=2
SHOWSCREEN //
WEND
ENDFUNCTION

Also Tabs is only another radio buttons, but you draw other content if the user click on other tab. You could make nice graphics to your widgets I'm using really simple one (DRAWRECT)