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]




