Quickie [was: Immediate Mode Gui]

Showcase your libraries, tools and other projects that help your fellow love users.
User avatar
Jasoco
Inner party member
Posts: 3726
Joined: Mon Jun 22, 2009 9:35 am
Location: Pennsylvania, USA
Contact:

Re: Quickie [was: Immediate Mode Gui]

Post by Jasoco »

It's love.graphics.getWidth(). You forgot the (). That's what it means by "bad argument". There's actually a difference between something and something().
User avatar
trubblegum
Party member
Posts: 192
Joined: Wed Feb 22, 2012 10:40 pm

Re: Quickie [was: Immediate Mode Gui]

Post by trubblegum »

thanks for taking the time to respond, but i'm not actually using that method - "getWidth" was a typo in the post, not the code (fixed it for clarity).

the problem was no slider.values[0]
setting slider.min = 1 does the job.
User avatar
trubblegum
Party member
Posts: 192
Joined: Wed Feb 22, 2012 10:40 pm

Re: Quickie [was: Immediate Mode Gui]

Post by trubblegum »

Please pardon my asking so many questions, but how do I get, set, and unset focus for widgets without knowing their id?

My problem is that when I type in chat (an input widget), my spaceship runs off to join the circus :shock:
I have looked over the code and tried to figure it out, but I've only been coding for fifteen years or so, so I don't mind if you think I'm being dumb :death:

PS : here's a scroll bar made from the slider, for scrolling through a set of options.

Code: Select all

-------------------------------
-- in quickie/scroller.lua : --
-------------------------------
local core = require((...):match("(.-)[^%.]+$") .. 'core')

return function(info, x,y,w,h, draw)
	assert(type(info) == 'table' and info.value, "Incomplete scroller value info")
	info.min = info.min or 0
	info.max = info.max or math.max(info.value, 1)
	info.step = info.step or (info.max - info.min) / 50
	local fraction = (info.value - info.min) / (info.max - info.min)

	local id = core.generateID()
	core.mouse.updateState(id, x,y,w,h)
	core.makeCyclable(id)
	core.registerDraw(id,draw or core.style.Scroller, fraction, x,y,w,h, info.vertical)

	-- mouse update
	if core.isActive(id) then
		core.setKeyFocus(id)
		if info.vertical then
			fraction = math.min(1, math.max(0, (core.mouse.y - y) / h))
		else
			fraction = math.min(1, math.max(0, (core.mouse.x - x) / w))
		end
		local v = fraction * (info.max - info.min) + info.min
		if v ~= info.value then
			info.value = v
			return true
		end
	end

	-- keyboard update
	local changed = false
	if core.hasKeyFocus(id) then
		local keys = info.vertical and {'up', 'down'} or {'right', 'left'}
		if core.keyboard.key == keys[1] then
			info.value = math.min(info.max, info.value + info.step)
			changed = true
		elseif core.keyboard.key == keys[2] then
			info.value = math.max(info.min, info.value - info.step)
			changed = true
		end
	end

	return changed
end

----------------------------
-- in style-default.lua : --
----------------------------
local function Scroller(state, fraction, x,y,w,h, vertical)
	local c = color[state]
	love.graphics.setColor(c.bg)
	love.graphics.rectangle('fill', x,y,w,h)

	love.graphics.setColor(c.fg)
	local hw,hh = w,h
	if vertical then
		yy = y + (h * fraction) - 8
		if yy < y then
			yy = y
		end
		if yy > (y + h) - 16 then
			yy = (y + h) - 16
		end
		y = yy
		hh = w
	else
		hw = w * fraction
	end
	love.graphics.rectangle('fill', x,y,hw,hh)
end
I haven't gotten around to developing it into a full on scroll box, so here's my hackified implementation :

Code: Select all

gui = require 'quickie'
scrollables = {}

function love.load()
	-- make up some stuff
	i = 1
	while i <= 30 do
		table.insert(scrollables, i, {label = i, value = i})
		i = i + 1
	end
	itemheight = 16
	-- scroller.range sets the maximum number of options to show at a time
	scroller = {value = 1, min = 1, range = 8, vertical = true}
	scroller.max = math.max((#scrollables - scroller.range) + 1, 1)
end

function love.update(dt)
	local i = math.floor(scroller.value)
	local y = 0
	while y < scroller.range do
		if scrollables[i] then
			if gui.Button(scrollables[i].label, 16, 16 + (itemheight * y), love.graphics.getWidth() - 64, itemheight) then
				print('pressed '..scrollables[i].value)
			end
		else
			break
		end
		i = i + 1
		y = y + 1
	end
	gui.Scroller(scroller, love.graphics.getWidth() - 32, 16, 16, scroller.range * itemheight, false)
end

function love.draw()
	gui.core.draw()
end
User avatar
trubblegum
Party member
Posts: 192
Joined: Wed Feb 22, 2012 10:40 pm

Re: Quickie [was: Immediate Mode Gui]

Post by trubblegum »

Had another poke around, and as far as I can make out, the following should do the job.

You have to know the id of the widget you're trying to work with, so I've made sure my chat input box is the first out the door, but this will cause problems later on, when I need more input widgets.

To get focus : focus = gui.core.context.focus

To set focus : gui.core.keyboard.setKeyFocus(id), same as gui.core.context.focus = id

To unset focus : gui.core.context.focus = false or nil

I may have to make input() return its own id as a second value in order to get this working.
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 »

There are two things causing your issue:
  • You cannot get the widget object with keyfocus, because there is no such thing as a widget object.
  • There is always a "something" that has keyboard focus.
Both are actually wanted and a result of the immediate mode api.

A solution to your problem is to steal the keyboard focus depending on whether you want to enable the chat or not:

Code: Select all

if not enable_chat then
    gui.core.setKeyFocus('no one') -- any value but a number > 0 or nil is fine
end
-- widgets as usual
And where you handle the ship input:

Code: Select all

if not enable_chat then
    -- handle input
end
Note that you also need to clear key focus when you enter the chat mode.
I have come here to chew bubblegum and kick ass... and I'm all out of bubblegum.

hump | HC | SUIT | moonshine
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 »

I have been using this library to make the interface of my A-Star demo and now I feel a bit confident to give some feedback about it.

I like the initial idea very much - immediate mode gui feels straightforward and innovative.

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.

So, here is my request: move the focus-related bits to a focus.lua file, loaded by default if you want, but easily overridable.

I would consider making the same thing with mouseover (I'm thinking about round buttons) - move the functions related with that to a separate mouseover.lua file so they can be customized. But that might be a bit too extreme.

Regards!
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 »

When I think about it, there actually is an easier way to disable keyboard input: Omit calling gui.core.keyboard.pressed(key,code). However, since input really is a core part of a GUI system, I wont split core.lua into core.lua, keyboard.lua and mouse.lua. Gamepad focus can be done by utilizing gui.core.keyboard.pressed() at the right places.

The drawing style can be overwritten at two points:
  1. via the gui.core.style table, which contains default drawing styles for all widgets and
  2. via the last argument to the widget functions, which can be used to define individual styles for widgets.
Rounded buttons on mouse-over can be done like this (requires love 0.8):

Code: Select all

gui.core.style.Button = function(state, title, x,y,w,h)
    local c = color[state]
    love.graphics.setColor(c.bg)
    if state == 'active' or state == 'hot' then
        love.graphics.arc('fill', x,y+h/2, h/2, math.pi, 2*math.pi)
        love.graphics.rectangle('fill', x,y,w,h)
        love.graphics.arc('fill', x+w,y+h/2, h/2, 0, math.pi)
    else -- state == 'normal'
        love.graphics.rectangle('fill', x,y,w,h)
    end
    love.graphics.setColor(c.fg)
    local f = love.graphics.getFont()
    love.graphics.print(title, x + (w-f:getWidth(title))/2, y + (h-f:getHeight(title))/2)
end
I have come here to chew bubblegum and kick ass... and I'm all out of bubblegum.

hump | HC | SUIT | moonshine
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 »

vrld wrote:Omit calling gui.core.keyboard.pressed(key,code).
I will investigate that. However, that still leaves the first-"created" window with a "focus" state, right? I don't want any focus for my app.
vrld wrote:Rounded buttons on mouse-over can be done like this (requires love 0.8)
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. But it's not only that; 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.
When I write def I mean function.
Gravy
Citizen
Posts: 80
Joined: Sun Jan 22, 2012 10:15 pm
Location: CA, USA

Re: Quickie [was: Immediate Mode Gui]

Post by Gravy »

This lib looks great and I'm planning on using it.

How hard would it be to implement double-clicking? I guess that this could be done by adding a "wasClicked" boolean for every button, and set wasClicked to false after 0.X seconds, but this could get cumbersome if there are a lot of buttons. Is there a better way? Or is it already doable somehow?
User avatar
trubblegum
Party member
Posts: 192
Joined: Wed Feb 22, 2012 10:40 pm

Re: Quickie [was: Immediate Mode Gui]

Post by trubblegum »

Heh .. I'm aware there's no object, but widgets can be manipulated to some degree.
I'm totally with kikito on this .. focus and tabbing needs to be optional and controllable, so that not all elements go into the tab order.
Optionality seems like a lot to ask, but if you don't get more control, I, for one (can't speak for anyone else, of course), would prefer not to have it at all. I can live without a chat shortcut if it's breaking the engine.

In the end I coded up my own GUI lib over the course of today. It's more stately, and gives me the flexibility I want, but for general ease of use, prototyping, or quick projects, quickie is the bee's knees - great job :awesome:
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 :)
Post Reply

Who is online

Users browsing this forum: No registered users and 0 guests