Modding Discussion IMGUI Library for Interface API

Discussion in 'Starbound Modding' started by severedskullz, Oct 15, 2014.

?

Interested?

  1. Yes

    77.8%
  2. No

    0 vote(s)
    0.0%
  3. What the hell is an IMGUI?

    22.2%
  1. Hello everyone... If you have not heard already, there was a particular feature I have been waiting on for almost a year now... Lua GUIs! My first project is to implement a IMGUI Hybrid library to help everyone else (And myself of course) in developing these GUIs.

    I already plan on implementing the following:
    • Button
    • Text Box
    • Image
    • Check Boxes
    • Radio Buttons
    If anyone else has any particular selection or suggestions for a GUI element that they would like added / feel that they need then be sure to make a suggestion!

    I also would like any feedback/suggestions on how these elements should behave... Should they hold to the tried-and-true IMGUI, or should they be more of a OOP/Form like system. Any other implementation suggestions are highly encouraged. I can do whatever I feel like to meet my own needs, but if this is going to be a starter library to help people prototype their GUI's it needs to be easy, quick, and understandable.

    Again... comments/suggestions strongly encouraged.

    Since we have 3 votes on "What the hell is an IMGUI" I might as well explain it...

    And IMGUI stands for Immediate Mode Graphical User Interface. Basically what that means is the entire element's functionality is taken care of with a simple statement (usually within an If for things like buttons) and all the logic for that element is done at that very instant. They do not "exist" from an OOP standpoint, and their lifespan does not even reach out to the current scope - only during their execution of the Widget's code itself do they exist. Each widget is assigned an ID which is automatically generated and assigned to differentiate between the two. However, since these widgets do not "exist" there is no such thing as communicating between them with lookup functions. Hell you could even have 10 different widgets use the same variable at the same time - of which they would ALL be updated synchronously across all widgets without ANY extra code. There are plenty of benefits and only a few drawbacks to implementing these once you understand how they work.

    Here is an IMGUI coded button and then a traditional RMGUI:
    Code:
    --IMGUI Style
    if button("label", 100, 100) then
        buttonAction()
    end
    
    --Windows Form/OOP Style
    local button = IMGUI.button("label",100,100) -- Adds this to some global list to be looped through
    button.setLabel("NewLabel")
    button.setVisible(true)
    button.onClick = buttonAction
    
    local textbox = IMGUI.textbox("Default Text", 250, 100)
    textbox.onTextChanged = textChange
    textbox.onEnterPressed = submit
    local currentText = textbox.getCurrentText()
    
    
    Pros:
    • Less memory needed to store states and information about the elements
    • Very minimal syncronization - You dont need to do things like "setLabel()" each update to change a value in real time
    • Quick - Less functions lookups/ops to do things means more performance (Mainly for other languages... no really Lua)
    • No complicated callback systems
    • Uses "widgets" rather than individual classes - which in turn lowers the code size
    Cons:
    • Needs to be drawn each frame - But the new Starbound Console behaves like this ANYWAY so its still a win
    • Requires a different way of thinking and not easy to grasp to those who are new to it - Slight learning curve
    • Since there is no storage in states for these buttons, it relies on many global variables... most of which will be coming from your Starbound Objects anyway in real time. Making this both a pro and a con.

    I really suck at explaining things, so if you are still confused, be sure to do some Googling on the topic. This type of GUI has been around for quite some time.
     
    Last edited: Oct 16, 2014
  2. Peelz

    Peelz Giant Laser Beams

    I am not sure if I fully understand the words that are coming out of your mouth, but if I do, would it be reasonable to suggest the following?:

    -A drop-down list
    -A numerical button that can be iterated through the values 0-9 by clicking on it (for things like combination locks)
     
  3. Those I shpuld be able to do. Only issue is there is no way to clip text as far as I can see so far. This means that the drop box options will just "pop" in instead of some fancy animations. The next issue that I ran into yesterday while making the Button was that currently there is no way to get arbitrary key and mouse states for whether so I cannot implement anything with a Drag-n-Drop style like sliders or scroll bars. I messaged @metadept to see if he or kyren could possibly add in a feature to get the mouse states as saving the keyboard states is already possible to implement myself. I just need whether the mouse buttons are either up or down at any given time

    Buttons are working too by the way...
    [​IMG]
    And here was the code to generate it:
    Code:
    function init()
    
    end
    
    function update(dt)
        IMGUI.context.maxid = 0 -- Reset our IDs for this frame
      
        if not IMGUI.mouseState[1] then -- Reset our Active/Hot if our mouse is not down.
            IMGUI.setActive(0)
            IMGUI.setHot(0)
        end
      
    
        --if IMGUI.isKeyDown(32) then
            if IMGUI.button({10,100}, 30, 50,false,"1!") then
                world.logInfo("Button 1 was pressed!")
            end
            if IMGUI.button({50,100}, 30, 50,false,"2!") then
                world.logInfo("Button 2 was pressed!")
            end
            if IMGUI.button({100,100}, 30, 50,false,"3!") then
                world.logInfo("Button 3 was pressed!")
            end
            if IMGUI.button({150,100}, 30, 50,false,"4!") then
                world.logInfo("Button 4 was pressed!")
            end
        --end
      
        IMGUI.mouseState[1] = false
    end
    
    function canvasClickEvent(position, button)
      --world.logInfo("Click detected at %s with button %s", position, button)
      IMGUI.mouseState[button] = true
    end
    
    function canvasKeyEvent(key, isKeyDown)
      --world.logInfo("Key %s was %s", key, isKeyDown and "pressed" or "released")
      IMGUI.keyState[key] = isKeyDown
    end
    Simple simple stuff. There are a couple of tid-bits of code needed that wont be in later, like manually setting the mouse and keystates since I have no way of checking any other way for now. Of course it looks completely rudimentary but right now I am just focusing on functionality. Later on we can make it look nice and pretty. Suggestions welcome on how we could format the buttons to follow some sort of "theme" for the entire system. I have plans on just adding in some sort of "Config" table as a parameter, but we shall see.
     
    Last edited: Oct 16, 2014
  4. Peelz

    Peelz Giant Laser Beams

    Thanks for that!
    I hope that they include more options for monitoring keyboard/mouse input. It doesn't seem like much of a stretch from what they have right now.
     
  5. The | Suit

    The | Suit Agent S. Forum Moderator

    I guess they are - long before the game was released in beta.
    One of the developers, stated they wanted to be able to create a fully functioning emulator inside starbound. I guess this is just the first step in that direction
     
  6. AstralGhost

    AstralGhost Pangalactic Porcupine

    Forgive me for what might be a dumb question but....

    How does this:
    Code:
    if IMGUI.button({10,100}, 30, 50,false,"1!") then
    world.logInfo("Button 1 was pressed!")
    end 
    ... Even work? This seems very.... non-intuitive. How does it know if it's being pressed, especially when it was just created at that same moment? I'm a little confused.
    Is the "IMGUI" actually just a polling state for input? I guess this would make sense as the buttons are supposed to be created every frame, so then they would just be graphics for the user, right?
    I guess it would help me understand it better if I could see what "IMGUI" actually is in this case.

    I've never been that great with GUIs. This is the first time I've actually ever even heard of an "IMGUI". I guess I'm just not familiar with this type of GUI and so I'm a little confused.

    Thanks for any clarification on this. :)

    Edit: I looked it up a bit with Google. So tell me if this is correct:
    Each GUI (thing) you create is both a render function and a polling function in one.
    Therefore when the user clicks on that button in a frame it goes on the stack as True and performs the code.

    So basically IMGUI is just a function (well in this case it looks like an object in your code) for creating procedural buttons/switches/thingamajigs/whozerwatsits, aka: polling and rendering.

    I guess that makes sense. Do I have that right?
     
    Last edited: Oct 17, 2014
  7. Thats it! And thats why these things are so great! There is no book keeping to keep track of or any states to manage. If you call the function it automatically performs the way it should with a simple function call. That is also why I mentioned there is a *slight* learning curve because it requires a different way of thinking... but once you get the hang of it it makes perfect sense.

    Just in case you are curious... Here is the "hidden" code behind that function:
    Code:
    IMGUI = {keyState={}, mouseState = {}, context = {scale = 1, maxid = 0, hot = 0, active = 0, focus = 0}}
    
    function IMGUI.button(pos,width,height,onlyPressed,text,color,sColor)
        local id = IMGUI.generateID()
        local x1,y1,x2,y2 = pos[1], pos[2], pos[1] + width, pos[2] - height
        local offset = 0
        text = text or ""
        color = color or {125,125,125,255}
        sColor = sColor or {255,255,255,255}
    
        if IMGUI.isInRect(x1,y1,x2,y2) then
            IMGUI.setHot(id) -- We are now Hot
            color, sColor = sColor, color -- We are hovering over the button, so swap colors
        
            if IMGUI.isMouseDown(1) then
                offset = 3
                if (not onlyPressed) or (not IMGUI.isActive(id)) then
                    IMGUI.setActive(id) -- Set us to active
                    IMGUI.setFocus(id)
                end
            end
        end
    
        console.canvasDrawRect({x1+offset,y1-offset,x2+offset,y2-offset}, color) -- Draw our stuff
        console.canvasDrawText(text, {position={pos[1] + (width/4), pos[2]}}, 12, sColor)
        return IMGUI.isActive(id)
    end
    Keep in mind its still a WIP so Im going to be changing that up alot in the future for more support for various things, but that is how things currently stand.

    On a side note. Yesterday I implemented the "Increment Box" for @Peelz but my dropbox did not sync so I was unable to capture a screenshot for you all when I arrived at home. I am currently trying to solve a problem when it comes to aligning the text. Since these things are "procedural" I need to know how big the text is going to be so that I can draw a box big enough to fit it... Sadly there is no support for it at the moment - so Im just guessing.[DOUBLEPOST=1413565515][/DOUBLEPOST][​IMG]
    I felt like re-coding it anyway... Here you go @Peelz
    Code:
    function update(dt)
        IMGUI.context.maxid = 0 -- Reset our IDs for this frame
       
        if not IMGUI.mouseState[1] then -- Reset our Active/Hot if our mouse is not down.
            IMGUI.setActive(0)
            IMGUI.setHot(0)
        end
       
        local color = {255,0,0} -- Red
        local guessed = checkPins()
        if guessed then
            color = {0,255,0} --Green
        end
       
        console.canvasDrawRect({0,400,400,0}, color)
       
        for i = 1, 5 do
            Pins[i] = IMGUI.incrementBox({100 + (i*20),75}, 25,25, Pins[i], 1) % 10
        end
    
        if guessed then
            console.canvasDrawText("You Win!", {position={120,125}}, 24, {255,255,255})
        end
       
        IMGUI.mouseState[1] = false
    end
    
    function checkPins()
        if Pins[1] == 4 and Pins[2] == 3 and Pins[3] == 1 and Pins[4] == 3 and Pins[5] == 1 then
            return true
        end
    end
     
    Last edited: Oct 17, 2014
    Peelz and AstralGhost like this.
  8. AstralGhost

    AstralGhost Pangalactic Porcupine

    Ha, I'm glad I figured that out. I've read this topic over like 10 times and couldn't make sense of it, though I love the idea.
    I'm definitely familiar with procedural coding, so all I have to do is think of it in terms of that and it makes complete sense.

    Thanks for the clarifications. :)
    Hope everything goes well!
    And it looks great so far!
     
  9. Supergeek

    Supergeek Scruffy Nerf-Herder

    Sev, you can estimate the size of the text if you render it as fixed-width, character by character. Wrap drawText inside a drawString or whatever.
     
    The | Suit and severedskullz like this.
  10. SpiderDave

    SpiderDave Scruffy Nerf-Herder

    I'm not a big fan of immediate mode guis, but this might be the way to go for this kind of interface.
     
  11. Peelz

    Peelz Giant Laser Beams

    Perfect @severedskullz ! That's exactly what I was thinking of! Now, if we just slap a padlock-looking skin on there and add in the functionality to affect the state of a nearby door, we have a functional door-lock!
     
    The | Suit likes this.
  12. The | Suit

    The | Suit Agent S. Forum Moderator

    Severed is on temperma hiatus. So he may or may not reply anytime soon.
     
  13. Peelz

    Peelz Giant Laser Beams

    That's sad to hear :(
     

Share This Page