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 »

Sorry it took so long to answer your posts :roll:

The bug in input.lua should now be fixed, but I couldn't reproduce the slider2d.lua. I also added basic UTF-8 editing support (this file could also be interesting for other projects) to support umlauts and the like. However, due to the way 0.8 handles text input, this will only work properly in LÖVE 0.9. For this keyboard.pressed(key, code) was split into keyboard.pressed(key) and keyboard.textinput(str). Porting for these changes is straigtforward:

LÖVE 0.8

Code: Select all

function love.keypressed(key, code)
    gui.keyboard.pressed(key)
    if code >= 32 and code < 127 then -- old behavior
        gui.keyboard.textinput(string.char(code))
    end
end
LÖVE 0.9

Code: Select all

function love.keypressed(key, code)
    gui.keyboard.pressed(key)
end

function love.textinput(str)
    gui.keyboard.textinput(str) -- yay, utf8!
end
That multiline textbox is nice by the way. I'll see how to integrate it into Quickie!
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 »

Hi!

I did some utf8 manipulation a while back and it resulted in this utf-8 parsing thing:

https://github.com/kikito/utf8_validato ... or.lua#L35

Apparently, using string.find is faster than parsing the bytes one by one.

Full disclaimer: the original implementation was from Michael Kulchenko, I just found a bug and adapted to my needs.
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 »

kikito wrote:Apparently, using string.find is faster than parsing the bytes one by one.
Apparently not for iterating though (or am I missing something?):

Code: Select all

local function iter(s, i)
        if i >= #s then return end
        local b, nbytes = s:byte(i+1,i+1), 1

        -- determine width of the codepoint by counting the number of set bits in the first byte
        -- warning: there is no validation of the following bytes!
        if     b >= 0xc0 and b <= 0xdf then nbytes = 2 -- 1100 0000 to 1101 1111
        elseif b >= 0xe0 and b <= 0xef then nbytes = 3 -- 1110 0000 to 1110 1111
        elseif b >= 0xf0 and b <= 0xf7 then nbytes = 4 -- 1111 0000 to 1111 0111
        elseif b >= 0xf8 and b <= 0xfb then nbytes = 5 -- 1111 1000 to 1111 1011
        elseif b >= 0xfc and b <= 0xfd then nbytes = 6 -- 1111 1100 to 1111 1101
        elseif b <  0x00 or  b >  0x7f then error(("Invalid codepoint: 0x%02x"):format(b))
        end
        return i+nbytes, s:sub(i+1,i+nbytes), nbytes
end

function iter2(s, i)
    if i >= #s then return end
    i = i + 1
    local nbytes = 0
    if     i == s:find("[%z\1-\127]", i) then nbytes = 1
    elseif i == s:find("[\194-\223][\123-\191]", i) then nbytes = 2
    elseif i == s:find(       "\224[\160-\191][\128-\191]", i)
        or i == s:find("[\225-\236][\128-\191][\128-\191]", i)
        or i == s:find(       "\237[\128-\159][\128-\191]", i)
        or i == s:find("[\238-\239][\128-\191][\128-\191]", i) then nbytes = 3
    elseif i == s:find(       "\240[\144-\191][\128-\191][\128-\191]", i)
        or i == s:find("[\241-\243][\128-\191][\128-\191][\128-\191]", i)
        or i == s:find(       "\244[\128-\143][\128-\191][\128-\191]", i) then nbytes = 4
    end
    assert(nbytes > 0, "Invalid UTF8")
    return i+nbytes-1, s:sub(i, i+nbytes-1), nbytes
end

Code: Select all

function timeit(f, n)
	local start = os.clock()
	for i = 1,n do f() end
	return os.clock() - start
end

print(timeit(function() for _ in iter, s, 0 do end end, 100000))
--> 0.99

print(timeit(function() for _ in iter2, s, 0 do end end, 100000))
--> 2.34
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 »

Oh - hey, good to know :crazy: I will have to update *my* utf8 function then.

EDIT: well, maybe not. I see that you are skipping bytes, which I can't do when validating. Maybe when all the bytes are parsed it is worth it to use match. I didn't personally run any tests myself.
When I write def I mean function.
User avatar
murks
Party member
Posts: 185
Joined: Tue Jun 03, 2014 4:18 pm

Re: Quickie [was: Immediate Mode Gui]

Post by murks »

Now that I've found the actual Quickie thread instead of the ridiculous naming thing, here's the minor bug I want to report:

The example code in README.md doesn't execute:

Code: Select all

	
        function love.keypressed(key, code)
		gui.keyboard.pressed(key)
		-- LÖVE 0.8: see if this code can be converted in a character
		if pcall(string.char, code) code > 0 then
			 gui.keyboard.textinput(string.char(code))
		 end
	 end
I think it should be:

Code: Select all

        function love.keypressed(key, code)
		gui.keyboard.pressed(key)
		-- LÖVE 0.8: see if this code can be converted in a character
		if pcall(string.char, code) and code > 0 then
			 gui.keyboard.textinput(string.char(code))
		 end
	 end
I would also love some documentation, for example because I couldn't explain why cycling (using tab) didn't work in my code. It took me hours until I realised that I needed this particular function. The löve 0.8 compatibility thing obfuscated its function rather well, it suggests that this is about text input. Proper documentation would have helped.
I would even write some documentation but there's plenty I don't understand, for example push/pop, widgetHit, draw and so on.
User avatar
murks
Party member
Posts: 185
Joined: Tue Jun 03, 2014 4:18 pm

Re: Quickie [was: Immediate Mode Gui]

Post by murks »

I have another question regarding cycling.
Basically I switch between game states in love.update().
So far there's a main menu and an introduction, you can switch back and forth between them. This works fine if I click the button. However, if I use tab to highlight the introduction button and hit enter, there's no way to highlight the 'back' button using tab. Only if I click introduction the back button can be highlighted using tab. It seems as if the cycling thingy is left in a bad state or rather is stuck in the main menu.
Cycling remembers the position in the main menu, and if it's not on the first item in the main menu, it is impossible to cycle through the second menu. Is there a way to 'reset' the cycling thingy when switching between menus?

I may provide some code once I got the nerve to create a minimal working example.

EDIT: Attached is not a minimal example but all the code I have so far. I hope it's not too bad. Ways to reproduce:

A.
1. You are in the main menu, 'tab' to INTRODUCTION or EXIT.
2. Click on INTRODUCTION.
3. You are at the INTRODUCTION screen and there's no way to select BACK using 'tab'.

B.
1. You are in the main menu, 'tab' to EXIT.
2. Click on START.
3. Hit ESC.
4. You are at the retry screen now and there's no way to select buttons by 'tab'.
Attachments
gui.love
(117.09 KiB) Downloaded 226 times
User avatar
murks
Party member
Posts: 185
Joined: Tue Jun 03, 2014 4:18 pm

Re: Quickie [was: Immediate Mode Gui]

Post by murks »

I fixed my issue by calling

Code: Select all

gui.keyboard.clearFocus()
at the appropriate places. This resets the keyboard focus so that the first element in each menu is focused by default.
User avatar
murks
Party member
Posts: 185
Joined: Tue Jun 03, 2014 4:18 pm

Re: Quickie [was: Immediate Mode Gui]

Post by murks »

vrld wrote:Update!

Thanks to MarekkPie, you can now customize the keys that cycle widgets. The default binding is

Code: Select all

gui.core.keyboard.cycle.next = {key = 'tab'}
gui.core.keyboard.cycle.prev = {key = 'tab', 'lshift', 'rshift'}
... which means that 'tab' selects the next widget, while 'tab' + 'lshift' or 'tab' + 'rshift' select the previous widgets. This scheme allows for any two-(but not three [nor four])-key combination to cycle widgets. For example,

Code: Select all

gui.core.keyboard.cycle = {
    prev = {key = ' ', 'a'},
    next = {key = ' ', 'd'},
}
cycles on `space + a' and `space + d'.

Cycle keys take precedence over remaining key actions, so if you set them to 'left' and 'right', you won't be able to move in a textbox anymore.

Updated code is at github: https://github.com/vrld/Quickie
Is it somehow possible to cycle on more than one key? I'm looking something like left or up for previous and right or down for next.
User avatar
murks
Party member
Posts: 185
Joined: Tue Jun 03, 2014 4:18 pm

Re: Quickie [was: Immediate Mode Gui]

Post by murks »

Another question:
Is there a nice way to play a sound on:
a) mouse moves over a button? I found some isHot property in the Quickie code, but I don't understand how to use it and I'd need to only play the sound if the mouse was not already on this button.
b) same for keyboard, play the sound only when cycling through the buttons.
User avatar
murks
Party member
Posts: 185
Joined: Tue Jun 03, 2014 4:18 pm

Re: Quickie [was: Immediate Mode Gui]

Post by murks »

I was quite surprised to see that by default there's no way to display images. I wrote a very simple image widget in case anyone is interested.
Post Reply

Who is online

Users browsing this forum: No registered users and 4 guests