add table library; fix lineends and tabs; some fixes in widget(table.remove instead of table[n] = nil); a test "every-frame rect render" for gui
This commit is contained in:
parent
79fe7d2ab6
commit
b26085efb4
@ -1,28 +1,25 @@
|
||||
|
||||
Basic goals:
|
||||
1. Layout compiler
|
||||
Three stages of support:
|
||||
Absolute positioning and alignment
|
||||
Relative positioning and alignment
|
||||
Packing + reflow
|
||||
Packing is considerably more complex than the first two but has huge benefits.
|
||||
Compilation on existing widgets should be allowed, and parameters should be adjustable via funcpointers
|
||||
(so that tweening is possible)
|
||||
2. Event model
|
||||
Click (down+up)
|
||||
Slide (down-hold-up)
|
||||
Raw down, raw up
|
||||
Rectangle collision detection
|
||||
3. Widgets
|
||||
Button
|
||||
Modal button
|
||||
Label text
|
||||
Paragraph text
|
||||
Markdown(or similar) text
|
||||
Slider
|
||||
Checkbox
|
||||
Frame + clipping
|
||||
Color picker
|
||||
|
||||
**************************************Make sure this is a unix-format textfile**************************
|
||||
|
||||
1. Layout compiler
|
||||
Three stages of support:
|
||||
Absolute positioning and alignment
|
||||
Relative positioning and alignment
|
||||
Packing + reflow
|
||||
Packing is considerably more complex than the first two but has huge benefits.
|
||||
Compilation on existing widgets should be allowed, and parameters should be adjustable via funcpointers
|
||||
(so that tweening is possible)
|
||||
2. Event model
|
||||
Click (down+up)
|
||||
Slide (down-hold-up)
|
||||
Raw down, raw up
|
||||
Rectangle collision detection
|
||||
3. Widgets
|
||||
Button
|
||||
Modal button
|
||||
Label text
|
||||
Paragraph text
|
||||
Markdown(or similar) text
|
||||
Slider
|
||||
Checkbox
|
||||
Frame + clipping
|
||||
Color picker
|
4
lua.c
4
lua.c
@ -163,11 +163,13 @@ void icelua_loadbasefuncs(lua_State *L)
|
||||
lua_pushcfunction(L, luaopen_base);
|
||||
lua_call(L, 0, 0);
|
||||
|
||||
// here's the other two
|
||||
// here's the other three
|
||||
lua_pushcfunction(L, luaopen_string);
|
||||
lua_call(L, 0, 0);
|
||||
lua_pushcfunction(L, luaopen_math);
|
||||
lua_call(L, 0, 0);
|
||||
lua_pushcfunction(L, luaopen_table);
|
||||
lua_call(L, 0, 0);
|
||||
|
||||
// overwrite dofile/loadfile.
|
||||
lua_pushcfunction(L, icelua_fn_base_loadfile);
|
||||
|
@ -601,6 +601,7 @@ function client.hook_render()
|
||||
if players and players[players.current] then
|
||||
players[players.current].show_hud()
|
||||
end
|
||||
gui_rect_frame_test()
|
||||
end
|
||||
|
||||
client.hook_tick = h_tick_init
|
||||
|
@ -28,29 +28,29 @@ DIR_PKG_MAP = DIR_PKG_MAP or "pkg/maps"
|
||||
MAP_DEFAULT = MAP_DEFAULT or DIR_PKG_MAP.."/mesa.vxl"
|
||||
|
||||
LIB_LIST = LIB_LIST or {
|
||||
DIR_PKG_LIB.."/icegui/widgets.lua",
|
||||
DIR_PKG_LIB.."/icegui/widgets.lua",
|
||||
|
||||
DIR_PKG_LIB.."/lib_bits.lua",
|
||||
DIR_PKG_LIB.."/lib_collect.lua",
|
||||
DIR_PKG_LIB.."/lib_gui.lua",
|
||||
DIR_PKG_LIB.."/lib_map.lua",
|
||||
DIR_PKG_LIB.."/lib_namegen.lua",
|
||||
DIR_PKG_LIB.."/lib_pmf.lua",
|
||||
DIR_PKG_LIB.."/lib_sdlkey.lua",
|
||||
DIR_PKG_LIB.."/lib_util.lua",
|
||||
DIR_PKG_LIB.."/lib_vector.lua",
|
||||
|
||||
DIR_PKG_LIB.."/obj_player.lua",
|
||||
DIR_PKG_LIB.."/obj_intent.lua",
|
||||
DIR_PKG_LIB.."/lib_bits.lua",
|
||||
DIR_PKG_LIB.."/lib_collect.lua",
|
||||
DIR_PKG_LIB.."/lib_gui.lua",
|
||||
DIR_PKG_LIB.."/lib_map.lua",
|
||||
DIR_PKG_LIB.."/lib_namegen.lua",
|
||||
DIR_PKG_LIB.."/lib_pmf.lua",
|
||||
DIR_PKG_LIB.."/lib_sdlkey.lua",
|
||||
DIR_PKG_LIB.."/lib_util.lua",
|
||||
DIR_PKG_LIB.."/lib_vector.lua",
|
||||
|
||||
DIR_PKG_LIB.."/obj_player.lua",
|
||||
DIR_PKG_LIB.."/obj_intent.lua",
|
||||
}
|
||||
|
||||
-- load libs
|
||||
local i
|
||||
for i=1,#LIB_LIST do
|
||||
local asdf_qwerty = i
|
||||
i = nil
|
||||
dofile(LIB_LIST[asdf_qwerty])
|
||||
i = asdf_qwerty
|
||||
local asdf_qwerty = i
|
||||
i = nil
|
||||
dofile(LIB_LIST[asdf_qwerty])
|
||||
i = asdf_qwerty
|
||||
end
|
||||
i = nil
|
||||
|
||||
@ -93,184 +93,184 @@ WPN_RIFLE = 1
|
||||
weapon_models = {}
|
||||
|
||||
weapons = {
|
||||
[WPN_RIFLE] = function (plr)
|
||||
local this = {} this.this = this
|
||||
|
||||
this.cfg = {
|
||||
dmg = {
|
||||
head = 100,
|
||||
body = 49,
|
||||
legs = 33,
|
||||
},
|
||||
|
||||
ammo_clip = 10,
|
||||
ammo_reserve = 50,
|
||||
time_fire = 1/2,
|
||||
time_reload = 2.5,
|
||||
|
||||
recoil_x = 0.0001,
|
||||
recoil_y = -0.05,
|
||||
|
||||
name = "Rifle"
|
||||
}
|
||||
|
||||
function this.restock()
|
||||
this.ammo_clip = this.cfg.ammo_clip
|
||||
this.ammo_reserve = this.cfg.ammo_reserve
|
||||
end
|
||||
|
||||
function this.reset()
|
||||
this.t_fire = nil
|
||||
this.t_reload = nil
|
||||
this.reloading = false
|
||||
this.restock()
|
||||
end
|
||||
|
||||
this.reset()
|
||||
|
||||
local function prv_fire(sec_current)
|
||||
local sya = math.sin(plr.angy)
|
||||
local cya = math.cos(plr.angy)
|
||||
local sxa = math.sin(plr.angx)
|
||||
local cxa = math.cos(plr.angx)
|
||||
local fwx,fwy,fwz
|
||||
fwx,fwy,fwz = sya*cxa, sxa, cya*cxa
|
||||
|
||||
-- perform a trace
|
||||
local d,cx1,cy1,cz1,cx2,cy2,cz2
|
||||
d,cx1,cy1,cz1,cx2,cy2,cz2
|
||||
= trace_map_ray_dist(plr.x+sya*0.4,plr.y,plr.z+cya*0.4, fwx,fwy,fwz, 127.5)
|
||||
d = d or 127.5
|
||||
|
||||
-- see if there's anyone we can kill
|
||||
local hurt_idx = nil
|
||||
local hurt_part = nil
|
||||
local hurt_dist = d*d
|
||||
local i,j
|
||||
|
||||
for i=1,players.max do
|
||||
local p = players[i]
|
||||
if p and p ~= plr and p.alive then
|
||||
local dx = p.x-plr.x
|
||||
local dy = p.y-plr.y+0.1
|
||||
local dz = p.z-plr.z
|
||||
|
||||
for j=1,3 do
|
||||
local dd = dx*dx+dy*dy+dz*dz
|
||||
|
||||
local dotk = dx*fwx+dy*fwy+dz*fwz
|
||||
local dot = math.sqrt(dd-dotk*dotk)
|
||||
if dot < 0.55 and dd < hurt_dist then
|
||||
hurt_idx = i
|
||||
hurt_dist = dd
|
||||
hurt_part = ({"head","body","legs"})[j]
|
||||
|
||||
break
|
||||
end
|
||||
dy = dy + 1.0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if hurt_idx then
|
||||
-- TODO: ship this off to the server!
|
||||
players[hurt_idx].gun_damage(
|
||||
hurt_part, this.cfg.dmg[hurt_part], plr)
|
||||
elseif cx2 then
|
||||
-- TODO: block health rather than instant block removal
|
||||
map_block_break(cx2,cy2,cz2)
|
||||
end
|
||||
|
||||
-- TODO: fire a tracer
|
||||
|
||||
-- apply recoil
|
||||
-- attempting to emulate classic behaviour provided i have it right
|
||||
plr.recoil(sec_current, this.cfg.recoil_y, this.cfg.recoil_x)
|
||||
end
|
||||
|
||||
function this.reload()
|
||||
if this.ammo_clip ~= this.cfg.ammo_clip then
|
||||
if this.ammo_reserve ~= 0 then
|
||||
if not this.reloading then
|
||||
this.reloading = true
|
||||
plr.zooming = false
|
||||
this.t_reload = nil
|
||||
end end end
|
||||
end
|
||||
|
||||
function this.click(button, state)
|
||||
if button == 1 then
|
||||
-- LMB
|
||||
if this.ammo_clip > 0 then
|
||||
this.firing = state
|
||||
else
|
||||
this.firing = false
|
||||
-- TODO: play sound
|
||||
end
|
||||
elseif button == 3 then
|
||||
-- RMB
|
||||
if state and not this.reloading then
|
||||
plr.zooming = not plr.zooming
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function this.get_model()
|
||||
return weapon_models[WPN_RIFLE]
|
||||
end
|
||||
|
||||
function this.draw(px, py, pz, ya, xa, ya2)
|
||||
client.model_render_bone_global(this.get_model(), 0,
|
||||
px, py, pz, ya, xa, ya2, 3)
|
||||
end
|
||||
|
||||
function this.tick(sec_current, sec_delta)
|
||||
if this.reloading then
|
||||
if not this.t_reload then
|
||||
this.t_reload = sec_current + this.cfg.time_reload
|
||||
end
|
||||
|
||||
if sec_current >= this.t_reload then
|
||||
local adelta = this.cfg.ammo_clip - this.ammo_clip
|
||||
if adelta > this.ammo_reserve then
|
||||
adelta = this.ammo_reserve
|
||||
end
|
||||
this.ammo_reserve = this.ammo_reserve - adelta
|
||||
this.ammo_clip = this.ammo_clip + adelta
|
||||
this.t_reload = nil
|
||||
this.reloading = false
|
||||
plr.arm_rest_right = 0
|
||||
else
|
||||
local tremain = this.t_reload - sec_current
|
||||
local telapsed = this.cfg.time_reload - tremain
|
||||
local roffs = math.min(tremain,telapsed)
|
||||
roffs = math.min(roffs,0.3)/0.3
|
||||
|
||||
plr.arm_rest_right = roffs
|
||||
end
|
||||
elseif this.firing and this.ammo_clip == 0 then
|
||||
this.firing = false
|
||||
elseif this.firing and ((not this.t_fire) or sec_current >= this.t_fire) then
|
||||
prv_fire(sec_current)
|
||||
|
||||
this.t_fire = this.t_fire or sec_current
|
||||
this.t_fire = this.t_fire + this.cfg.time_fire
|
||||
if this.t_fire < sec_current then
|
||||
this.t_fire = sec_current
|
||||
end
|
||||
|
||||
this.ammo_clip = this.ammo_clip - 1
|
||||
|
||||
-- TODO: poll: do we want to require a new click per shot?
|
||||
end
|
||||
|
||||
if this.t_fire and this.t_fire < sec_current then
|
||||
this.t_fire = nil
|
||||
end
|
||||
end
|
||||
|
||||
return this
|
||||
end,
|
||||
[WPN_RIFLE] = function (plr)
|
||||
local this = {} this.this = this
|
||||
|
||||
this.cfg = {
|
||||
dmg = {
|
||||
head = 100,
|
||||
body = 49,
|
||||
legs = 33,
|
||||
},
|
||||
|
||||
ammo_clip = 10,
|
||||
ammo_reserve = 50,
|
||||
time_fire = 1/2,
|
||||
time_reload = 2.5,
|
||||
|
||||
recoil_x = 0.0001,
|
||||
recoil_y = -0.05,
|
||||
|
||||
name = "Rifle"
|
||||
}
|
||||
|
||||
function this.restock()
|
||||
this.ammo_clip = this.cfg.ammo_clip
|
||||
this.ammo_reserve = this.cfg.ammo_reserve
|
||||
end
|
||||
|
||||
function this.reset()
|
||||
this.t_fire = nil
|
||||
this.t_reload = nil
|
||||
this.reloading = false
|
||||
this.restock()
|
||||
end
|
||||
|
||||
this.reset()
|
||||
|
||||
local function prv_fire(sec_current)
|
||||
local sya = math.sin(plr.angy)
|
||||
local cya = math.cos(plr.angy)
|
||||
local sxa = math.sin(plr.angx)
|
||||
local cxa = math.cos(plr.angx)
|
||||
local fwx,fwy,fwz
|
||||
fwx,fwy,fwz = sya*cxa, sxa, cya*cxa
|
||||
|
||||
-- perform a trace
|
||||
local d,cx1,cy1,cz1,cx2,cy2,cz2
|
||||
d,cx1,cy1,cz1,cx2,cy2,cz2
|
||||
= trace_map_ray_dist(plr.x+sya*0.4,plr.y,plr.z+cya*0.4, fwx,fwy,fwz, 127.5)
|
||||
d = d or 127.5
|
||||
|
||||
-- see if there's anyone we can kill
|
||||
local hurt_idx = nil
|
||||
local hurt_part = nil
|
||||
local hurt_dist = d*d
|
||||
local i,j
|
||||
|
||||
for i=1,players.max do
|
||||
local p = players[i]
|
||||
if p and p ~= plr and p.alive then
|
||||
local dx = p.x-plr.x
|
||||
local dy = p.y-plr.y+0.1
|
||||
local dz = p.z-plr.z
|
||||
|
||||
for j=1,3 do
|
||||
local dd = dx*dx+dy*dy+dz*dz
|
||||
|
||||
local dotk = dx*fwx+dy*fwy+dz*fwz
|
||||
local dot = math.sqrt(dd-dotk*dotk)
|
||||
if dot < 0.55 and dd < hurt_dist then
|
||||
hurt_idx = i
|
||||
hurt_dist = dd
|
||||
hurt_part = ({"head","body","legs"})[j]
|
||||
|
||||
break
|
||||
end
|
||||
dy = dy + 1.0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if hurt_idx then
|
||||
-- TODO: ship this off to the server!
|
||||
players[hurt_idx].gun_damage(
|
||||
hurt_part, this.cfg.dmg[hurt_part], plr)
|
||||
elseif cx2 then
|
||||
-- TODO: block health rather than instant block removal
|
||||
map_block_break(cx2,cy2,cz2)
|
||||
end
|
||||
|
||||
-- TODO: fire a tracer
|
||||
|
||||
-- apply recoil
|
||||
-- attempting to emulate classic behaviour provided i have it right
|
||||
plr.recoil(sec_current, this.cfg.recoil_y, this.cfg.recoil_x)
|
||||
end
|
||||
|
||||
function this.reload()
|
||||
if this.ammo_clip ~= this.cfg.ammo_clip then
|
||||
if this.ammo_reserve ~= 0 then
|
||||
if not this.reloading then
|
||||
this.reloading = true
|
||||
plr.zooming = false
|
||||
this.t_reload = nil
|
||||
end end end
|
||||
end
|
||||
|
||||
function this.click(button, state)
|
||||
if button == 1 then
|
||||
-- LMB
|
||||
if this.ammo_clip > 0 then
|
||||
this.firing = state
|
||||
else
|
||||
this.firing = false
|
||||
-- TODO: play sound
|
||||
end
|
||||
elseif button == 3 then
|
||||
-- RMB
|
||||
if state and not this.reloading then
|
||||
plr.zooming = not plr.zooming
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function this.get_model()
|
||||
return weapon_models[WPN_RIFLE]
|
||||
end
|
||||
|
||||
function this.draw(px, py, pz, ya, xa, ya2)
|
||||
client.model_render_bone_global(this.get_model(), 0,
|
||||
px, py, pz, ya, xa, ya2, 3)
|
||||
end
|
||||
|
||||
function this.tick(sec_current, sec_delta)
|
||||
if this.reloading then
|
||||
if not this.t_reload then
|
||||
this.t_reload = sec_current + this.cfg.time_reload
|
||||
end
|
||||
|
||||
if sec_current >= this.t_reload then
|
||||
local adelta = this.cfg.ammo_clip - this.ammo_clip
|
||||
if adelta > this.ammo_reserve then
|
||||
adelta = this.ammo_reserve
|
||||
end
|
||||
this.ammo_reserve = this.ammo_reserve - adelta
|
||||
this.ammo_clip = this.ammo_clip + adelta
|
||||
this.t_reload = nil
|
||||
this.reloading = false
|
||||
plr.arm_rest_right = 0
|
||||
else
|
||||
local tremain = this.t_reload - sec_current
|
||||
local telapsed = this.cfg.time_reload - tremain
|
||||
local roffs = math.min(tremain,telapsed)
|
||||
roffs = math.min(roffs,0.3)/0.3
|
||||
|
||||
plr.arm_rest_right = roffs
|
||||
end
|
||||
elseif this.firing and this.ammo_clip == 0 then
|
||||
this.firing = false
|
||||
elseif this.firing and ((not this.t_fire) or sec_current >= this.t_fire) then
|
||||
prv_fire(sec_current)
|
||||
|
||||
this.t_fire = this.t_fire or sec_current
|
||||
this.t_fire = this.t_fire + this.cfg.time_fire
|
||||
if this.t_fire < sec_current then
|
||||
this.t_fire = sec_current
|
||||
end
|
||||
|
||||
this.ammo_clip = this.ammo_clip - 1
|
||||
|
||||
-- TODO: poll: do we want to require a new click per shot?
|
||||
end
|
||||
|
||||
if this.t_fire and this.t_fire < sec_current then
|
||||
this.t_fire = nil
|
||||
end
|
||||
end
|
||||
|
||||
return this
|
||||
end,
|
||||
}
|
||||
|
||||
weapons_enabled = {}
|
||||
@ -278,50 +278,50 @@ weapons_enabled[WPN_RIFLE] = true
|
||||
|
||||
-- teams
|
||||
teams = {
|
||||
[0] = {
|
||||
name = "Blue Master Race",
|
||||
color_mdl = {16,32,128},
|
||||
color_chat = {0,0,255},
|
||||
},
|
||||
[1] = {
|
||||
name = "Green Master Race",
|
||||
color_mdl = {16,128,32},
|
||||
color_chat = {0,192,0},
|
||||
},
|
||||
[0] = {
|
||||
name = "Blue Master Race",
|
||||
color_mdl = {16,32,128},
|
||||
color_chat = {0,0,255},
|
||||
},
|
||||
[1] = {
|
||||
name = "Green Master Race",
|
||||
color_mdl = {16,128,32},
|
||||
color_chat = {0,192,0},
|
||||
},
|
||||
}
|
||||
|
||||
cpalette_base = {
|
||||
0x7F,0x7F,0x7F,
|
||||
0xFF,0x00,0x00,
|
||||
0xFF,0x7F,0x00,
|
||||
0xFF,0xFF,0x00,
|
||||
0x00,0xFF,0x00,
|
||||
0x00,0xFF,0xFF,
|
||||
0x00,0x00,0xFF,
|
||||
0xFF,0x00,0xFF,
|
||||
0x7F,0x7F,0x7F,
|
||||
0xFF,0x00,0x00,
|
||||
0xFF,0x7F,0x00,
|
||||
0xFF,0xFF,0x00,
|
||||
0x00,0xFF,0x00,
|
||||
0x00,0xFF,0xFF,
|
||||
0x00,0x00,0xFF,
|
||||
0xFF,0x00,0xFF,
|
||||
}
|
||||
|
||||
cpalette = {}
|
||||
do
|
||||
local i,j
|
||||
for i=0,7 do
|
||||
local r,g,b
|
||||
r = cpalette_base[i*3+1]
|
||||
g = cpalette_base[i*3+2]
|
||||
b = cpalette_base[i*3+3]
|
||||
for j=0,3 do
|
||||
local cr = math.floor((r*j)/3)
|
||||
local cg = math.floor((g*j)/3)
|
||||
local cb = math.floor((b*j)/3)
|
||||
cpalette[#cpalette+1] = {cr,cg,cb}
|
||||
end
|
||||
for j=1,4 do
|
||||
local cr = r + math.floor(((255-r)*j)/4)
|
||||
local cg = g + math.floor(((255-g)*j)/4)
|
||||
local cb = b + math.floor(((255-b)*j)/4)
|
||||
cpalette[#cpalette+1] = {cr,cg,cb}
|
||||
end
|
||||
end
|
||||
local i,j
|
||||
for i=0,7 do
|
||||
local r,g,b
|
||||
r = cpalette_base[i*3+1]
|
||||
g = cpalette_base[i*3+2]
|
||||
b = cpalette_base[i*3+3]
|
||||
for j=0,3 do
|
||||
local cr = math.floor((r*j)/3)
|
||||
local cg = math.floor((g*j)/3)
|
||||
local cb = math.floor((b*j)/3)
|
||||
cpalette[#cpalette+1] = {cr,cg,cb}
|
||||
end
|
||||
for j=1,4 do
|
||||
local cr = r + math.floor(((255-r)*j)/4)
|
||||
local cg = g + math.floor(((255-g)*j)/4)
|
||||
local cb = b + math.floor(((255-b)*j)/4)
|
||||
cpalette[#cpalette+1] = {cr,cg,cb}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
damage_blk = {}
|
||||
|
@ -1,4 +1,9 @@
|
||||
-- look into actual drawing functionality!~
|
||||
-- the client drawing code requires manual memory management:
|
||||
-- we have to allocate a buffer and draw pixels to it
|
||||
-- this code can't deal with that problem...
|
||||
-- lib_gui will have to provide a layer that takes the abstract APIs here and adds drawing functions
|
||||
-- on top. the abstract API can assist by adding a "dirty" flag so that cache management is straightforward.
|
||||
|
||||
-- sketch listener and collision system:
|
||||
-- rect and layers detection (derive layers from hierarchy)
|
||||
@ -11,6 +16,7 @@
|
||||
-- 3. iterate through the children, moving them to the positions and sizes desired as specified by the packing mode.
|
||||
|
||||
-- also something to note - when we draw we have to pass a clip rectangle upwards so that scrolling is possible.
|
||||
-- this isn't strictly necessary, but if the possibility is there, use it!
|
||||
|
||||
local P = {}
|
||||
|
||||
@ -18,106 +24,107 @@ local widget_mt = {}
|
||||
function widget_mt.__add(a, b) a.add_child(b) return a end
|
||||
function widget_mt.__sub(a, b) a.remove_child(b) return a end
|
||||
function widget_mt.__tostring(a)
|
||||
return a.x.."x "..a.y.."y "..a.relx().."rx "..a.rely().."ry"
|
||||
return a.x.."x "..a.y.."y "..a.relx().."rx "..a.rely().."ry"
|
||||
end
|
||||
|
||||
function P.widget(options)
|
||||
|
||||
local this = {x = options.x or 0, y = options.y or 0,
|
||||
parent = options.parent or nil,
|
||||
children = options.children or {},
|
||||
align_x = options.align_x or 0.5, align_y = options.align_y or 0.5}
|
||||
|
||||
local width = options.width or 0
|
||||
local height = options.height or 0
|
||||
local margin_left = options.margin_left or 0
|
||||
local margin_right = options.margin_right or 0
|
||||
local margin_top = options.margin_top or 0
|
||||
local margin_bottom = options.margin_bottom or 0
|
||||
|
||||
-- align 0 = top-left
|
||||
-- align 1 = bottom-right
|
||||
-- align 0.5 = center
|
||||
|
||||
function this.width() return width end
|
||||
function this.height() return height end
|
||||
|
||||
function this.margin_left() return margin_left end
|
||||
function this.margin_top() return margin_top end
|
||||
function this.margin_right() return margin_right end
|
||||
function this.margin_bottom() return margin_bottom end
|
||||
|
||||
function this.min_width() return width end
|
||||
function this.min_height() return height end
|
||||
|
||||
function this.relx()
|
||||
local pos = this.x - (this.width() * this.align_x)
|
||||
if this.parent == nil then return pos
|
||||
else return pos + this.parent.relx() end
|
||||
end
|
||||
|
||||
function this.rely()
|
||||
local pos = this.y - (this.height() * this.align_y)
|
||||
if this.parent == nil then return pos
|
||||
else return pos + this.parent.rely() end
|
||||
end
|
||||
|
||||
function this.l() return this.relx() end
|
||||
function this.t() return this.rely() end
|
||||
function this.r() return this.relx() + this.width() end
|
||||
function this.b() return this.rely() + this.bottom() end
|
||||
function this.cx() return this.relx() + this.width() * 0.5 end
|
||||
function this.cy() return this.rely() + this.height() * 0.5 end
|
||||
|
||||
function this.inner()
|
||||
local l = this.l() + this.margin_left()
|
||||
local t = this.t() + this.margin_top()
|
||||
local r = this.r() - this.margin_right()
|
||||
local b = this.b() - this.margin_bottom()
|
||||
return {x=l, y=t, left=l, top=t, right=r, bottom=b,
|
||||
width=r-l, height=b-t, cx=l+(r-l)*0.5, cy=t+(b-t)*0.5}
|
||||
end
|
||||
|
||||
function this.detach() if this.parent then this.parent.children[this] = nil; this.parent = nil end end
|
||||
function this.add_child(child) child.detach(); child.parent = this; this.children[child] = child end
|
||||
function this.remove_child(child) child.detach() end
|
||||
function this.remove_all_children() for k,child in pairs(this.children) do this.remove_child(child) end end
|
||||
function this.set_parent(parent) this.detach(); this.parent = parent; parent.children[this] = this end
|
||||
function this.despawn()
|
||||
for k,child in pairs(this.children) do child.despawn() end this.remove_all_children()
|
||||
end
|
||||
|
||||
setmetatable(this, widget_mt)
|
||||
local this = {x = options.x or 0, y = options.y or 0,
|
||||
parent = options.parent or nil,
|
||||
children = options.children or {},
|
||||
align_x = options.align_x or 0.5, align_y = options.align_y or 0.5}
|
||||
|
||||
local width = options.width or 0
|
||||
local height = options.height or 0
|
||||
local margin_left = options.margin_left or 0
|
||||
local margin_right = options.margin_right or 0
|
||||
local margin_top = options.margin_top or 0
|
||||
local margin_bottom = options.margin_bottom or 0
|
||||
|
||||
-- align 0 = top-left
|
||||
-- align 1 = bottom-right
|
||||
-- align 0.5 = center
|
||||
|
||||
function this.width() return width end
|
||||
function this.height() return height end
|
||||
|
||||
function this.margin_left() return margin_left end
|
||||
function this.margin_top() return margin_top end
|
||||
function this.margin_right() return margin_right end
|
||||
function this.margin_bottom() return margin_bottom end
|
||||
|
||||
function this.min_width() return width end
|
||||
function this.min_height() return height end
|
||||
|
||||
function this.relx()
|
||||
local pos = this.x - (this.width() * this.align_x)
|
||||
if this.parent == nil then return pos
|
||||
else return pos + this.parent.relx() end
|
||||
end
|
||||
|
||||
function this.rely()
|
||||
local pos = this.y - (this.height() * this.align_y)
|
||||
if this.parent == nil then return pos
|
||||
else return pos + this.parent.rely() end
|
||||
end
|
||||
|
||||
function this.l() return this.relx() end
|
||||
function this.t() return this.rely() end
|
||||
function this.r() return this.relx() + this.width() end
|
||||
function this.b() return this.rely() + this.height() end
|
||||
function this.cx() return this.relx() + this.width() * 0.5 end
|
||||
function this.cy() return this.rely() + this.height() * 0.5 end
|
||||
|
||||
function this.inner()
|
||||
local l = this.l() + this.margin_left()
|
||||
local t = this.t() + this.margin_top()
|
||||
local r = this.r() - this.margin_right()
|
||||
local b = this.b() - this.margin_bottom()
|
||||
return {x=l, y=t, left=l, top=t, right=r, bottom=b,
|
||||
width=r-l, height=b-t, cx=l+(r-l)*0.5, cy=t+(b-t)*0.5}
|
||||
end
|
||||
|
||||
function this.aabb(x, y, w, h)
|
||||
return not (this.l()>x or this.r()<x+w or this.t()>y or this.b()<y+h)
|
||||
end
|
||||
|
||||
function this.collide(x, y, w, h)
|
||||
-- very simple aabb collision for mousing. returns the "first and deepest child".
|
||||
w = w or 1
|
||||
h = h or 1
|
||||
local hit = this.aabb(x, y, w, h)
|
||||
local result = this
|
||||
for k, v in pairs(this.children) do
|
||||
result = v.collide(x, y, w, h) or this
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
function this.detach() if this.parent then table.remove(this.parent.children, this) this.parent = nil end end
|
||||
function this.add_child(child) child.detach(); child.parent = this; this.children[child] = child end
|
||||
function this.remove_child(child) child.detach() end
|
||||
function this.remove_all_children() for k,child in pairs(this.children) do this.remove_child(child) end end
|
||||
function this.set_parent(parent) this.detach(); this.parent = parent; parent.children[this] = this end
|
||||
function this.despawn()
|
||||
for k,child in pairs(this.children) do child.despawn() end this.remove_all_children()
|
||||
end
|
||||
|
||||
setmetatable(this, widget_mt)
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
function P.widget_frame(x, y, width, height)
|
||||
local w = widget{x=x, y=y, width=width, height=height}
|
||||
|
||||
-- presumably this is a drawing widget.
|
||||
-- the key distinction between this API and the Flash monstrosity I made is that I can readily mix
|
||||
-- drawing widgets and non-drawing widgets.
|
||||
|
||||
-- so then, if a packer is just another child, that means that it can inherit the size of the parent's inner.
|
||||
-- but what the children "see" reported is what the packer wants to make available.
|
||||
-- bottom-up traversal. Now I'm remembering this part.
|
||||
-- most custom widgets use their reported width and height, and that's that, but packers don't.
|
||||
|
||||
return w
|
||||
return this
|
||||
end
|
||||
|
||||
if _REQUIREDNAME == nil then
|
||||
widgets = P
|
||||
widgets = P
|
||||
else
|
||||
_G[_REQUIREDNAME] = P
|
||||
end
|
||||
|
||||
local test = P.widget{x=100, y=100}
|
||||
local test = P.widget{x=100, y=100, width=100, height=100}
|
||||
print(test)
|
||||
local test2 = P.widget{x=100, y=100}
|
||||
print(test2)
|
||||
local test2 = P.widget{x=100, y=100, width=100, height=100}
|
||||
test2.set_parent(test)
|
||||
print(test2)
|
||||
print(test.collide(150,150))
|
||||
|
||||
return P
|
@ -105,4 +105,17 @@ function gui_string_edit(str, maxlen, key, modif)
|
||||
|
||||
return str
|
||||
end
|
||||
|
||||
function gui_rect_frame_test()
|
||||
-- someday this will grow up to be a real rectangle renderer
|
||||
local img = common.img_new(32, 32)
|
||||
for x = 0, 31, 1 do
|
||||
for y = 0, 31, 1 do
|
||||
common.img_pixel_set(img, x, y, 0xFFFF0000)
|
||||
end
|
||||
end
|
||||
client.img_blit(img, 0, 0)
|
||||
common.img_free(img)
|
||||
end
|
||||
|
||||
end
|
||||
|
Loading…
x
Reference in New Issue
Block a user