Files
itgmania212121/Themes/default/Scripts/04 item_scroller.lua
T
ListenerJubatus f257f19dad Add new 5.1 default theme Lambda
I'd like to thank everyone who made this possible;, including but not
limited to Riakodoadm, MrThatKid, kyzentun, Luizsan, JoseVarelaP,
everyone else who provided feedback and bug reports.
2017-08-08 01:07:34 -06:00

217 lines
6.5 KiB
Lua

-- item_scroller is a replacement for ActorScroller. Kyzentun made it
-- because writing code is easier than reading documentation and code and
-- carefully verifying exactly what some unfamiliar actor class does.
-- Original versions found in Consensual were named sick_wheel.
-- item_scroller is based on the idea that you have some table of data to
-- display, and an actor that can display one element of data.
-- See Docs/Themerdocs/item_scroller.md for documentation.
function table.rotate_right(t, r)
local new_t= {}
for n= 1, #t do
local index= ((n - r - 1) % #t) + 1
new_t[n]= t[index]
end
return new_t
end
function table.rotate_left(t, r)
local new_t= {}
for n= 1, #t do
local index= ((n + r - 1) % #t) + 1
new_t[n]= t[index]
end
return new_t
end
function wrapped_index(start, offset, set_size)
return ((start - 1 + offset) % set_size) + 1
end
function force_to_range(min, number, max)
return math.min(max, math.max(min, number))
end
-- These local functions are meant for internal use only. Things that use
-- item_scroller should not need to call them directly.
local function check_metatable(item_metatable)
assert(item_metatable.__index.create_actors, "The metatable must have a create_actors function. This should return a Def.ActorFrame containing whatever actors will be needed for display.")
assert(item_metatable.__index.transform, "The metatable must have a transform function. The transform function must take 3 arguments: position in list (1 to num_items), number of items in list, boolean indicating whether item has focus.")
assert(item_metatable.__index.set, "The metatable must have a set function. The set function must take an instance of info, which it should use to set its actors to display the info.")
end
local function calc_start(self, info, pos)
if self.disable_repeating and #info < #self.items then
return pos
end
pos= math.floor(pos) - self.focus_pos
if self.disable_wrapping then
pos= force_to_range(1, pos, #info - #self.items + 1)
if pos < 1 then pos= 1 end
end
return pos
end
local function call_item_set(self, item_index, info_index)
self.info_map[item_index]= info_index
self.items[item_index]:set(self.info_set[info_index])
end
local function no_repeat_internal_scroll(self, focus_pos)
for i, item in ipairs(self.items) do
item:transform(i, #self.items, i == focus_pos, focus_pos)
end
end
local function internal_scroll(self, start_pos)
if self.disable_repeating and #self.info_set < #self.items then
no_repeat_internal_scroll(self, start_pos)
return
end
local shift_amount= start_pos - self.info_pos
if math.abs(shift_amount) < #self.items then
self.items= table.rotate_left(self.items, shift_amount)
self.info_map= table.rotate_left(self.info_map, shift_amount)
self.info_pos= start_pos
if shift_amount < 0 then
local absa= math.abs(shift_amount)
for n= 1, absa+1 do
if self.items[n] then
local info_index= self:maybe_wrap_index(self.info_pos, n, self.info_set)
call_item_set(self, n, info_index)
end
end
elseif shift_amount > 0 then
for n= #self.items - shift_amount, #self.items do
if self.items[n] then
local info_index= self:maybe_wrap_index(self.info_pos, n, self.info_set)
call_item_set(self, n, info_index)
end
end
end
else
self.info_pos= start_pos
for i= 1, #self.items do
local info_index= self:maybe_wrap_index(self.info_pos, i, self.info_set)
call_item_set(self, i, info_index)
end
end
if self.disable_repeating then
for i, item in ipairs(self.items) do
item:transform(i, #self.items, i == self.focus_pos, self.focus_pos)
end
else
for i, item in ipairs(self.items) do
item:transform(i, #self.items, i == self.focus_pos, self.focus_pos)
end
end
end
local function no_repeat_set_info_set(self, pos)
self.info_pos= 1
for n= 1, #self.info_set do
call_item_set(self, n, n)
end
for n= #self.info_set+1, #self.items do
call_item_set(self, n, n)
end
internal_scroll(self, pos)
end
item_scroller_mt= {
__index= {
create_actors= function(self, name, num_items, item_metatable, mx, my, item_params)
item_params= item_params or {}
self.name= name
self.info_pos= 1
self.info_map= {}
self.num_items= num_items
assert(item_metatable, "A metatable for items to be put in the item_scroller must be provided.")
check_metatable(item_metatable)
self.focus_pos= math.floor(num_items / 2)
mx= mx or 0
my= my or 0
self.items= {}
local args= {
Name= self.name,
InitCommand= function(subself)
subself:xy(mx, my)
self.container= subself
end
}
for n= 1, num_items do
local item= setmetatable({}, item_metatable)
local sub_params= DeepCopy(item_params)
sub_params.name= "item" .. n
local actor_frame= item:create_actors(sub_params)
self.items[#self.items+1]= item
args[#args+1]= actor_frame
end
return Def.ActorFrame(args)
end,
maybe_wrap_index= function(self, ipos, n, info)
if self.disable_wrapping then
return ipos - 1 + n
else
return wrapped_index(ipos, n, #info)
end
end,
set_info_set= function(self, info, pos)
self.info_set= info
if self.disable_repeating and #self.info_set < #self.items then
no_repeat_set_info_set(self, pos)
return
end
local start_pos= calc_start(self, info, pos)
self.info_pos= start_pos
for n= 1, #self.items do
local index= self:maybe_wrap_index(start_pos, n, info)
call_item_set(self, n, index)
end
internal_scroll(self, start_pos)
end,
set_element_info= function(self, element, info)
self.info_set[element]= info
for i= 1, #self.items do
if self.info_map[i] == element then
call_item_set(self, i, element)
end
end
end,
scroll_to_pos= function(self, pos)
local start_pos= calc_start(self, self.info_set, pos)
internal_scroll(self, start_pos)
end,
scroll_by_amount= function(self, a)
internal_scroll(self, self.info_pos + a)
end,
get_info_at_focus_pos= function(self)
local index= self:maybe_wrap_index(self.info_pos, self.focus_pos,
self.info_set)
return self.info_set[index]
end,
get_actor_item_at_focus_pos= function(self)
return self.items[self.focus_pos]
end,
get_items_by_info_index= function(self, index)
local ret= {}
for i= 1, #self.items do
if self.info_map[i] == index then
ret[#ret+1]= self.items[i]
end
end
return ret
end,
find_item_by_info= function(self, info)
local ret= {}
for i, item in ipairs(self.items) do
if item.info == info then
ret[#ret+1]= item
end
end
return ret
end,
}}