So I've started working on a pico8 U.I today. I'll be re-installing linux (switching to fedora) soon so I'm not only using this post as a backup, but also using this post as a sort of preview of things to come.
I wouldn't bother using this just yet, though. Its quite un-optimized so far and not yet finished, and its also not documented and the sample code used for the gif I generated was a bit sloppily thrown together, but if you want to take the code and tweak it to your own needs then by all means do so.
running code:
--s★ui --shooting★ function s★ui_init() s★ui={frame={},mse={0,0}} --configure here --[1]=bg, [2]=border, --[3]=content color --[4] = scrollbar back color --[5] = scrollbar btn color --[6] = scrollbar btn hover -- color --[7] = content btn bg color --[8] = content btn border -- color --[9] = content btn fg color --[10]= content btn bg hover --[11]= content btn border -- hover --[12]= content btn text -- hover s★ui_wincol={ 6, --1 5, --2 1, --3 1, --4 6, --5 9, --6 2, --7 1, --8 6, --9 1, --10 8, --11 6 --12 } --mouse icon s★ui_msei = 1 --content padding s★ui_padding=2 --enable mouse poke(0x5f2d, 1) end --ui update function function s★ui_upd() --ui mouse coords s★ui.mse[1] = stat(32) s★ui.mse[2] = stat(33) for i=1,#s★ui.frame do --draw the frame s★ui.frame[i].draw() end --draw mouse sprite spr(s★ui_msei, s★ui.mse[1], s★ui.mse[2]) end --create a new frame function s★ui_new(d) d = d or {} --frame can have custom cols, --bg,border and content d.bg=d.bg or s★ui_wincol[1] d.bdr=d.bdr or s★ui_wincol[2] d.cnt=d.cnt or s★ui_wincol[3] d.elm={} d.cs=0 d.highest_cnt=0 --close button, left as object --might add more btns later~ d.btns={ { title='x', x=117, y=2 } } --if disabled close if d.dc == true then d.btns[1].disabled = true end --else, create close action d.btns[1].action=function() d.active=false end d.btns[1].draw=function() local col=5 if s★ui.mse[1] > d.btns[1].x and s★ui.mse[1] < d.btns[1].x + 8 and s★ui.mse[2] > d.btns[1].y and s★ui.mse[2] < d.btns[1].y + 8 and d.dc != true then col=1 if stat(34) == 1 then d.btns[1].action() end end print(d.btns[1].title, d.btns[1].x+3, d.btns[1].y, col) end d.active=true d.draw=function() if d.active == false then return end cls(d.bg) for i=1, #d.elm do d.elm[i].draw() end rect(2, 8, 125, 125, d.bdr) rectfill(0, 0, 127, 7, d.bg) rectfill(0, 126, 127, 127, d.bg) for i=1, #d.btns do d.btns[i].draw() end print(d.title, 2, 2, d.cnt) rectfill(116, 111, 124, 124, s★ui_wincol[4]) local mainsc=s★ui_wincol[5] local hovcol={mainsc,mainsc} if s★ui.mse[1] > 116 and s★ui.mse[1]<124 and s★ui.mse[2]>112 and s★ui.mse[2]<118 then hovcol[1] = s★ui_wincol[6] if stat(34) == 1 then d.cs-=3 d.cs=max(d.cs, 0) end end if s★ui.mse[1] > 116 and s★ui.mse[1]<124 and s★ui.mse[2]>118 and s★ui.mse[2]<124 then hovcol[2] = s★ui_wincol[6] if stat(34) == 1 then d.cs+=3 end end print('⬆️',117, 112, hovcol[1]) print('⬇️',117, 119, hovcol[2]) rectfill(3, 118, 116, 124, 1) print(':'..d.cs/3, 4, 119, 6) end add(s★ui.frame, d) return s★ui.frame[#s★ui.frame] end --new element function s★ui_newelem(f, d) d.type = d.type or 'text' d.clicked=0 d.draw=function() local cy=d.y+8+s★ui_padding-f.cs local cx=d.x+2+s★ui_padding if d.type == 'text' then print(d.txt, cx, cy, d.col or f.cnt) end if d.type == 'hr' then line(4, cy, 123, cy, d.col or f.cnt) end if d.type == 'btn' then local cols={s★ui_wincol[7], s★ui_wincol[8], s★ui_wincol[9]} if s★ui.mse[1] >= cx and s★ui.mse[1] <= cx+(#d.txt*4)+2 and s★ui.mse[2] >= cy and s★ui.mse[2] <= cy+8 then cols={s★ui_wincol[10], s★ui_wincol[11], s★ui_wincol[12]} if stat(34) == 1 then d.clicked+=1 if d.clicked==1 then d.act() end else d.clicked=0 end end d.clicked=stat(34) rectfill(cx, cy, cx+(#d.txt*4)+2, cy+8, cols[1]) rect(cx, cy, cx+(#d.txt*4)+2, cy+8, cols[2]) print(d.txt, cx+2, cy+2, cols[3]) end if d.type == 'chk' then d.checked = d.checked or false print(d.txt, cx+8, cy+1, d.col or f.cnt) if s★ui.mse[1] >= cx and s★ui.mse[1] <= cx+(#d.txt*4)+6 and s★ui.mse[2] >= cy and s★ui.mse[2] <= cy+6 then cols={s★ui_wincol[10], s★ui_wincol[11], s★ui_wincol[12]} if stat(34) == 1 then d.clicked+=1 if d.clicked==1 then d.checked = not d.checked end else d.clicked=0 end end d.clicked=stat(34) rect(cx, cy, cx+6, cy+6, d.col or f.cnt) if d.checked==true then line(cx+1, cy+3, cx+2, cy+6, d.col or f.cnt) line(cx+2, cy+6, cx+5, cy, d.col or f.cnt) end end end add(f.elm, d) return f.elm[#f.elm] end |
sample code used in gif above:
function _init() s★ui_init() wnd_main = s★ui_new({ title='pico8-ui', dc=true }) --wnd_main content s★ui_newelem(wnd_main, { x=0, y=0, txt='welcome to the pico-8 ui\ndemo!\n\nlicense:\nthis doesn\'t actually have\none, but i thought it would\nlook kinda cool to have\nsome kind of massive text\nwall here for this demo,\nso here it is. please use the\nbuttons on the bottom right\nhand corner to scroll this\npage!'}) s★ui_newelem(wnd_main, { x=0, y=112, txt=[[oh, neat. you scrolled! now that you have scrolled, how about we get a little more into it? if you want to use this neat u.i, then be prepared because it takes 929 tokens. however, as you can tell its a fully functional u.i system with the ability to scroll content and create buttons, checkboxes and horizontal rules. here's a sample setup! function _init() s★ui_init() --setup main window wnd_main = s★ui_new({ title='pico8-ui', dc=true --disable close }) s★ui_newelem(wnd_main, { x=0, y=0, txt='this is some text' }) wnd_checkbox = s★ui_newelem( wnd_main, { type='chk', x=80, y=86, txt='checkbox' }) end ]]}) s★ui_newelem(wnd_main, { type='hr', x=0, y=13 }) --accept button s★ui_newelem(wnd_main, { type='btn', y=86, x=0, txt='i accept', act=function() sfx(0) if wnd_checkbox.checked == true then wnd_main.active=false end end }) --fix close button s★ui_newelem(wnd_main, { type='btn', y=96, x=0, txt='enable win close', act=function() sfx(1) wnd_main.dc = false end }) s★ui_newelem(wnd_main, { type='hr', x=0, y=106, col=13 }) wnd_checkbox = s★ui_newelem(wnd_main, { type='chk', x=80, y=86, txt='checkbox' }) --end end function _draw() cls() s★ui_upd() end |
This is an interesting project. Do you plan to do line wrapping at some point?
Probably, though I haven't had much time to work on it since I've been working a lot. But yeah, I may include line-wrapping. Maybe even text-fields or something, depends. I'm trying to use a little tokens as possible so its already had a complete re-write at one point, and may have another soon.
version 1.0
early release
765 tokens
Alright guys, so here it is. this is the first release~
Place this code somewhere:
--customization ssui = { --config --colors --[frame background]-- frm_bg=6, --[frame border]-- frm_bd=5, --[frame content]-- frm_cnt=1, --[normal icon clr]-- icn_norm=1, --[icon hover clr]-- icn_hovr=2, --[button bg clr]-- btn_bg=1, --[button border clr]-- btn_bd=12, --[button text clr]-- btn_cnt=6, --same, but hover --[button bg clr]-- btnh_bg=13, --[button border clr]-- btnh_bd=9, --[button text clr]-- btnh_cnt=6, --icons/sprites --mouse (1=pointer, --2=clicker) refers to --sprite index mouse={1, 2}, --frame-icon text symbols icn_scrollup='⬆️', icn_scrolldwn='⬇️', icn_closewin='❎', ----------------------------- --arrays frames={}, --other mousex=0, mousey=0, mousebtn=0, mousemode=1 } --the code function ssui_init() poke(0x5f2d, 1) end function ssui_frame(d) d = d or {} d.open = d.open or true d.active = d.active or true d.bg = d.bg or ssui.frm_bg d.bd = d.bd or ssui.frm_bd d.fg = d.fg or ssui.frm_cnt d.sy = 0 d.items={} d.draw = function() if d.open != true then return end cls(d.bg) for i=1,#d.items do local _bumpx=0 local _bumpy=0 local _x=3+d.items[i].x local _y=9+(d.items[i].y)-d.sy local _mx=ssui.mousex local _my=ssui.mousey local _col=d.items[i].tc or d.fg if d.items[i].type == 'hr' then line(_x, _y, 124, _y, _col) end if d.items[i].type == 'btn' then _bumpx = 2 _bumpy = 2 _col = ssui.btn_cnt local _w = (#d.items[i].txt*4)+1 local _bc1=ssui.btn_bg local _bc2=ssui.btn_bd if _mx>=_x and _mx<=_x+_w and _my>=_y and _my<=_y+8 then _col = ssui.btnh_col _bc1=ssui.btnh_bg _bc2=ssui.btnh_bd ssui.mousemode=2 if ssui.mousebtn==1 then d.items[i].clicker+=1 if d.items[i].clicker==1 then d.items[i].act() end else d.items[i].clicker = 0 end end rectfill(_x, _y, _x+_w,_y+8, _bc1) rect(_x, _y, _x+_w, _y+8, _bc2) end if d.items[i].type == 'chk' then _bumpx=7 rect(_x, _y, _x+4, _y+4, _col) if d.items[i].checked == true then print('x', _x+1, _y, _col) end if _mx>_x and _mx<_x+_bumpx+(#d.items[i].txt*4) and _my>_y and _my<_y+4 then if ssui.mousebtn == 1 then d.items[i].clicker+=1 if d.items[i].clicker==1 then d.items[i].checked = not d.items[i].checked end else d.items[i].clicker = 0 end end end print(d.items[i].txt or '', _bumpx+_x, _bumpy+_y, _col) end rect(1, 7, 126, 126, d.bd) rectfill(0, 0, 127, 6, d.bg) line(0, 127, 127, 127, d.bg) local _clscol=ssui.icn_norm local _mx=ssui.mousex local _my=ssui.mousey if _mx>117 and _mx<125 and _my>1 and _my<6 then if d.dc != true then _clscol=ssui.icn_hovr ssui.mousemode=2 if ssui.mousebtn==1 then d.open=false end end end print(d.title, 1, 1, d.fg) print(ssui.icn_closewin, 117, 1, _clscol) rectfill(3, 116, 124, 124, d.fg) print(':'..flr(d.sy), 4, 118, d.bg) _clscol = d.bg _clscol2= d.bg if _mx >=108 and _mx < 116 and _my >= 118 and _my < 124 then _clscol = ssui.icn_hovr if ssui.mousebtn==1 then d.sy-=1 end end if _mx >=116 and _mx < 124 and _my >= 118 and _my < 124 then _clscol2 = ssui.icn_hovr if ssui.mousebtn==1 then d.sy+=1 end end d.sy = max(d.sy, 0) print(ssui.icn_scrollup, 108, 118, _clscol) print(ssui.icn_scrolldwn, 116, 118, _clscol2) end d.add = function(d2) d2.clicker=0 add(d.items, d2) return d.items[#d.items] end add(ssui.frames, d) return ssui.frames[#ssui.frames] end function ssui_draw() ssui.mousemode=1 ssui.mousex=stat(32) ssui.mousey=stat(33) ssui.mousebtn=stat(34) for i=1, #ssui.frames do ssui.frames[i].draw() end spr(ssui.mouse[ssui.mousemode], ssui.mousex, ssui.mousey) end |
phew, thats a lot of compressed code. Anyway, now here's how you can use it:
For starters, inside of _init() we're going to create a sample frame. First, we initialize SSUI (Shooting Start U.I)
--init ssui ssui_init() |
Next, we create the frame!
--create frame and disable close frm_sample = ssui_frame({ title='picoui - ssui demo', dc=true }) |
by setting dc to true, we're disabling the ability to click the X at the top right corner. Now then, lets give this frame some content!
--simple text frm_sample.add({ x=0, y=0, txt='janky\n\nso this is what it means to\nhave a really neat u.i\nsystem that has scrolling\ncapabilities.' }) --checkbox frm_sample_chkbox = frm_sample.add({ x=0, y=48, txt='124 - lock y-scroll', type='chk' }) --button frm_sample_button = frm_sample.add({ x=0, y=60, txt='click me!', type='btn', act=function() sfx(0) frm_sample.dc = false end }) --hr frm_sample.add({ x=0, y=74, type='hr', }) |
All of these are commented so they should be readable. This code will create what you see in the gif. Clicking the button will play sfx 0 and also will give you the ability to close the frame.
To get everything drawing, lets put this inside _draw() (at the very bottom so it draws over everything else)
ssui_draw() |
Finally, to get the checkbox to actually function, lets add something in _update60() (or non 60, up to you tbh)
if frm_sample_chkbox.checked == true then frm_sample.sy=0 end |
Thats that. So far, this is all I'm capable of. Have fun creating multiple frames/content. Hope this helps some of you continue or start a project.
This is great! What do you think you'll use it for? I'm still brainstorming.
And do you have anywhere online I can follow more of your work? haha.
[Please log in to post a comment]