Quickie [was: Immediate Mode Gui]

Showcase your libraries, tools and other projects that help your fellow love users.
User avatar
vrld
Party member
Posts: 917
Joined: Sun Apr 04, 2010 9:14 pm
Location: Germany
Contact:

Re: Quickie [was: Immediate Mode Gui]

Post by vrld »

kikito wrote:I was not concerned about drawing the buttons; I was concerned about properly detecting that the mouse was over them - set the "state" to "hot" only if the mouse is really inside the circle, not in the rectangle of the button.
Oh, ok I seem to have misunderstood you then. There are currently two ways to achieve this:
  1. overwrite gui.core.mouse.inRect() or even gui.core.mouse.updateState() for specially shaped hit boxes, or
  2. create a new widget that does the correct check.
The first option will affect all widgets, so it would also solve the camera problem.

However, I am open to make the hit test more flexible. The only question is how to do so without making the interface too complicated. After all the biggest motivator for using IMGUI is that the code is straightforward and relatively easy to understand.
Gravy wrote:How hard would it be to implement double-clicking?
Relatively easy, but you have to carry the state yourself:

Code: Select all

if gui.core.Button("Double Click Me!", ...) then
    local now = love.timer.getTime()
    if now - lastclick < threshold then
        print("Double click!")
    end
    lastclick = now
end
trubblegum wrote:
I might want to (say) use a camera system, so the mouse/button coordinates are different. I might need to transform the mouse coordinates. I'm not sure quickie is able to support that right now.
It has worked fine with the HUMP camera for me :)
The problem is that Quicky extracts the mouse coordinates itself, without applying the camera transformation beforehand. For example, if the camera looks 10 pixels to the right, i.e. moves everything 10 pixels to the left, then the hit boxes will also be 10px off.

Anyway, apart from overwriting gui.core.mouse.inRect(), you can also apply the camera transformation to gui.core.mouse.x and gui.core.mouse.y before defining the widgets in love.update().
I have come here to chew bubblegum and kick ass... and I'm all out of bubblegum.

hump | HC | SUIT | moonshine
User avatar
trubblegum
Party member
Posts: 192
Joined: Wed Feb 22, 2012 10:40 pm

Re: Quickie [was: Immediate Mode Gui]

Post by trubblegum »

Or apply the camera transformation when you call the widget.
User avatar
vrld
Party member
Posts: 917
Joined: Sun Apr 04, 2010 9:14 pm
Location: Germany
Contact:

Re: Quickie [was: Immediate Mode Gui]

Post by vrld »

trubblegum wrote:Or apply the camera transformation when you call the widget.
You don't seem to understand the problem. It arises exactly because you apply the camera transformation before drawing the widget. This makes the recorded mouse coordinates inconsistent with where the widgets are drawn on the screen. In other words, the hitboxes are not where the widget is drawn.
Again, if the camera coordinate system's origin has a 100px offset to the right with regard to LÖVE's regular origin and you have a button that is 100px wide, you cannot active it by clicking on the button itself, but only to the right of the button.
As said, this can be prevented by applying the camera transformation to gui.core.mouse.x and gui.core.mouse.y. With hump.camera:

Code: Select all

function love.update(dt)
    gui.core.mouse.x, gui.core.mouse.y = cam:mousepos():unpack()
    -- carry on as ususal
end

function love.draw()
    cam:attach()
    draw_stuff()
    gui.core.draw()
    cam:detach()
end

Other than that: Changes ahead!
kikito wrote:I don't like that the "focus" it is "baked in" in the lib. I'd rather have it as an "addon" so it is easily customizable/reemplazable - a bit like how the style is done.

In my case I didn't want to have keyboard focus or interaction. I had to edit the lib and hunt for all the "focus" and "keyboard" lines. Other people might want to have a "gamepad focus" in addition of keyboard.
You can now disable keyboard focus using gui.core.disableKeyFocus(). You only need to call this once. If at some point you want to enable it again, use gui.core.enableKeyFocus(). You can also clear key focus by calling gui.core.clearKeyFocus(). clearKeyFocus will enable key focus if it was enabled (in fact, enableKeyFocus is an alias to clearKeyFocus).
kikito wrote:I was concerned about properly detecting that the mouse was over them - set the "state" to "hot" only if the mouse is really inside the circle, not in the rectangle of the button.
I've been crunching on this for a while - with success: You can now customize the hit-test of a widget. This is done by adding the function widgetHit to the style definition and to the widget functions (optional parameter). The widgetHit function will usually receive 6 parameter: the mouse coordinates (x,y) and the hitbox (x,y,w,h) supplied to widget function. Instead of the hitbox you can define your own parameters you want to send to the test function (see gui.core.mouse.updateState()).

You can customize the hit test in two and a half separate ways:
  1. Supply a specialized widgetHit function to the widget, i.e.:

    Code: Select all

    -- instead of
    if gui.Button("Click Me", 10,10, 100,20) then print('clicked') end
    -- do this
    if gui.Button("Click Me", 10,10, 100,20, mySpecialWidgetHit) then print('clicked') end
    Notice that the draw override got shifted to the right. The widget functions now have this general format:
    function gui.WIDGET(widget-parameters, x,y,w,h, [my-widget-hit], [my-widget-draw]).
  2. Define your own style and supply the according widgetHit function, or
  3. overwrite gui.core.style.widgetHit
I think that this is a good solution as it keeps the immediate mode spirit but still allows for a high degree of flexibility.
I have come here to chew bubblegum and kick ass... and I'm all out of bubblegum.

hump | HC | SUIT | moonshine
User avatar
vrld
Party member
Posts: 917
Joined: Sun Apr 04, 2010 9:14 pm
Location: Germany
Contact:

Re: Quickie [was: Immediate Mode Gui]

Post by vrld »

This seems to be the month of GUI libraries, so I'll join in: Quickie has undergone some big changes. In no particular order:

API Changes
Widgets no use named arguments. That means that the old

Code: Select all

if gui.Button("OK", x,y,120,25) then print('foo') end
now becomes

Code: Select all

if gui.Button{text = "OK", pos = {x,y}, size = {120,25}} then print('foo') end
This is an part attributed to the next point.

Also, gui.core.mouse and gui.core.keyboard have moved to their own files and gui.mouse and gui.keyboard respectively.


Automatic Layout
Quickie now has auto layout support called groups. Groups place widgets next to each other and can grow either down, right, up or left:

Code: Select all

gui.group.push{grow = "right", size = {100, 25}
gui.Label{text = "Name:", align = "right"}
gui.Input{info = name_input}
gui.group.pop{}
Notice the omission of the pos and size argument in the widgets. Magic. You can still override the size and (relative) position of the widget.

As you might have guessed from push/pop, groups are stackable. This allows for easy nesting, e.g.

Code: Select all

gui.group.push{grow = "down", size = {250, 25}, padding = 2}
    gui.group.push{grow = "right"} -- size is inherited by parent group
        gui.Label{text = "IP:", size = {70}} -- size partially overwritten: height is 25
        gui.Input{info = ip_input}
    gui.group.pop{}
    gui.group.push{grow = "right"}
        gui.Label{text = "Port:", size = {70}}
        gui.Input{info = port_input}
    gui.group.pop{}
    if gui.Button{text = "Connect", pos = {72}} then -- relative horizontal positioning
        connect(ip_input.text, port_input.text)
    end
gui.group.pop{}

Automatic Sizing
Checkbox, Button and Label can compute the minimally needed width/height to display the widget if you provide 'tight' as either size parameter. This is mostly relevant for checkboxes (which can have labels now):

Code: Select all

local music = {checked = true, label = "Play music"}
local sound = {checked = true, label = "Play sounds"}
function love.update(dt)
    gui.group.push{grow = 'right'}
        gui.Checkbox{info = music, size = {'tight'}}
        gui.Checkbox{info = sound, size = {'tight'}}
    gui.group.pop{}
end

Default Style
The old default style wasn't exactly pleasing to the eye. The new default style is more aesthetic, while still being easily customizable. Obligatory screen shot:
quickie.jpg
quickie.jpg (18.71 KiB) Viewed 5065 times
For the file that produced the screenshot, see the attached .love.

The documentation will follow soon-ish, but I hope that most of it is clear from the demo source code. Happy coding! :D
Attachments
quickie-demo.love
(19.92 KiB) Downloaded 332 times
I have come here to chew bubblegum and kick ass... and I'm all out of bubblegum.

hump | HC | SUIT | moonshine
User avatar
Roland_Yonaba
Inner party member
Posts: 1563
Joined: Tue Jun 21, 2011 6:08 pm
Location: Ouagadougou (Burkina Faso)
Contact:

Re: Quickie [was: Immediate Mode Gui]

Post by Roland_Yonaba »

Subarashi....
Nice work!
User avatar
Robin
The Omniscient
Posts: 6506
Joined: Fri Feb 20, 2009 4:29 pm
Location: The Netherlands
Contact:

Re: Quickie [was: Immediate Mode Gui]

Post by Robin »

Neat!
Help us help you: attach a .love.
User avatar
forestwolf42
Prole
Posts: 24
Joined: Tue Dec 20, 2011 5:18 pm

Re: Quickie [was: Immediate Mode Gui]

Post by forestwolf42 »

Awesome, I would definitely like some documentation for this, I think I get the gist of the group functions, but when I use them in my own code I keep getting error messages.
User avatar
kikito
Inner party member
Posts: 3153
Joined: Sat Oct 03, 2009 5:22 pm
Location: Madrid, Spain
Contact:

Re: Quickie [was: Immediate Mode Gui]

Post by kikito »

Te new push/pop business and the repetition scream "DSL" to me. other than that, contracts on the changes!
When I write def I mean function.
User avatar
vrld
Party member
Posts: 917
Joined: Sun Apr 04, 2010 9:14 pm
Location: Germany
Contact:

Re: Quickie [was: Immediate Mode Gui]

Post by vrld »

forestwolf42 wrote:Awesome, I would definitely like some documentation for this
I could say that I'm working on it, but that would be a lie. :roll:
The documentation will come eventually, but until then you have to rely on the demo and the announcement post.
forestwolf42 wrote:I think I get the gist of the group functions, but when I use them in my own code I keep getting error messages.
What error messages do you get? Are you sure you balanced the push/pop functions?
kikito wrote:Te new push/pop business and the repetition scream "DSL" to me
Not quite sure what you mean: What repetitions and how does that relate to DSLs?
I have come here to chew bubblegum and kick ass... and I'm all out of bubblegum.

hump | HC | SUIT | moonshine
User avatar
forestwolf42
Prole
Posts: 24
Joined: Tue Dec 20, 2011 5:18 pm

Re: Quickie [was: Immediate Mode Gui]

Post by forestwolf42 »

main.lua:36: attempt to call field 'push' (a table value)
stack traceback:

When trying to call the gui.group.push{grow = "right", size = {100, 25}
Post Reply

Who is online

Users browsing this forum: Google [Bot] and 77 guests