|
|
@ -5,27 +5,56 @@ local json = require('json')
|
|
|
|
local utils = require('utils')
|
|
|
|
local utils = require('utils')
|
|
|
|
local widgets = require('gui.widgets')
|
|
|
|
local widgets = require('gui.widgets')
|
|
|
|
|
|
|
|
|
|
|
|
local WIDGETS_ENABLED_FILE = 'dfhack-config/overlay/widgets.json'
|
|
|
|
local OVERLAY_CONFIG_FILE = 'dfhack-config/overlay.json'
|
|
|
|
local WIDGETS_STATE_DIR = 'dfhack-config/overlay/widgets/'
|
|
|
|
local OVERLAY_WIDGETS_VAR = 'OVERLAY_WIDGETS'
|
|
|
|
|
|
|
|
|
|
|
|
local widget_db = {} -- map of widget name to state
|
|
|
|
local DEFAULT_X_POS, DEFAULT_Y_POS = -2, -2
|
|
|
|
local widget_index = {} -- list of widget names
|
|
|
|
|
|
|
|
|
|
|
|
-- ---------------- --
|
|
|
|
|
|
|
|
-- state and config --
|
|
|
|
|
|
|
|
-- ---------------- --
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
local active_triggered_screen = nil -- if non-nil, hotspots will not get updates
|
|
|
|
|
|
|
|
local widget_db = {} -- map of widget name to ephermeral state
|
|
|
|
|
|
|
|
local widget_index = {} -- ordered list of widget names
|
|
|
|
|
|
|
|
local overlay_config = {} -- map of widget name to persisted state
|
|
|
|
local active_hotspot_widgets = {} -- map of widget names to the db entry
|
|
|
|
local active_hotspot_widgets = {} -- map of widget names to the db entry
|
|
|
|
local active_viewscreen_widgets = {} -- map of vs_name to map of w.names -> db
|
|
|
|
local active_viewscreen_widgets = {} -- map of vs_name to map of w.names -> db
|
|
|
|
local active_triggered_screen = nil
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
local function load_config(path)
|
|
|
|
local function reset()
|
|
|
|
local ok, config = safecall(json.decode_file, path)
|
|
|
|
if active_triggered_screen then
|
|
|
|
return ok and config or {}
|
|
|
|
active_triggered_screen:dismiss()
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
active_triggered_screen = nil
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
widget_db = {}
|
|
|
|
|
|
|
|
widget_index = {}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
local ok, config = pcall(json.decode_file, OVERLAY_CONFIG_FILE)
|
|
|
|
|
|
|
|
overlay_config = ok and config or {}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
active_hotspot_widgets = {}
|
|
|
|
|
|
|
|
active_viewscreen_widgets = {}
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
local function save_config(data, path)
|
|
|
|
local function save_config()
|
|
|
|
if not safecall(json.encode_file, data, path) then
|
|
|
|
if not safecall(json.encode_file, overlay_config, OVERLAY_CONFIG_FILE) then
|
|
|
|
dfhack.printerr(('failed to save overlay config file: "%s"')
|
|
|
|
dfhack.printerr(('failed to save overlay config file: "%s"')
|
|
|
|
:format(path))
|
|
|
|
:format(path))
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
local function triggered_screen_has_lock()
|
|
|
|
|
|
|
|
if not active_triggered_screen then return false end
|
|
|
|
|
|
|
|
if active_triggered_screen:isActive() then return true end
|
|
|
|
|
|
|
|
active_triggered_screen = nil
|
|
|
|
|
|
|
|
return false
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-- ----------- --
|
|
|
|
|
|
|
|
-- utility fns --
|
|
|
|
|
|
|
|
-- ----------- --
|
|
|
|
|
|
|
|
|
|
|
|
local function normalize_list(element_or_list)
|
|
|
|
local function normalize_list(element_or_list)
|
|
|
|
if type(element_or_list) == 'table' then return element_or_list end
|
|
|
|
if type(element_or_list) == 'table' then return element_or_list end
|
|
|
|
return {element_or_list}
|
|
|
|
return {element_or_list}
|
|
|
@ -44,6 +73,32 @@ local function is_empty(tbl)
|
|
|
|
return true
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
local function sanitize_pos(pos)
|
|
|
|
|
|
|
|
local x = math.floor(tonumber(pos.x) or DEFAULT_X_POS)
|
|
|
|
|
|
|
|
local y = math.floor(tonumber(pos.y) or DEFAULT_Y_POS)
|
|
|
|
|
|
|
|
-- if someone accidentally uses 1-based instead of 0-based indexing, fix it
|
|
|
|
|
|
|
|
if x == 0 then x = 1 end
|
|
|
|
|
|
|
|
if y == 0 then y = 1 end
|
|
|
|
|
|
|
|
return {x=x, y=y}
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
local function make_frame(pos, old_frame)
|
|
|
|
|
|
|
|
old_frame = old_frame or {}
|
|
|
|
|
|
|
|
local frame = {w=old_frame.w, h=old_frame.h}
|
|
|
|
|
|
|
|
if pos.x < 0 then frame.r = math.abs(pos.x) - 1 else frame.l = pos.x - 1 end
|
|
|
|
|
|
|
|
if pos.y < 0 then frame.b = math.abs(pos.y) - 1 else frame.t = pos.y - 1 end
|
|
|
|
|
|
|
|
return frame
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
local function get_screen_rect()
|
|
|
|
|
|
|
|
local w, h = dfhack.screen.getWindowSize()
|
|
|
|
|
|
|
|
return gui.ViewRect{rect=gui.mkdims_wh(0, 0, w, h)}
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-- ------------- --
|
|
|
|
|
|
|
|
-- CLI functions --
|
|
|
|
|
|
|
|
-- ------------- --
|
|
|
|
|
|
|
|
|
|
|
|
local function get_name(name_or_number)
|
|
|
|
local function get_name(name_or_number)
|
|
|
|
local num = tonumber(name_or_number)
|
|
|
|
local num = tonumber(name_or_number)
|
|
|
|
if num and widget_index[num] then
|
|
|
|
if num and widget_index[num] then
|
|
|
@ -64,47 +119,46 @@ local function do_by_names_or_numbers(args, fn)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
local function save_enabled()
|
|
|
|
local function do_enable(args, quiet, skip_save)
|
|
|
|
local enabled_map = {}
|
|
|
|
|
|
|
|
for name,db_entry in pairs(widget_db) do
|
|
|
|
|
|
|
|
enabled_map[name] = db_entry.enabled
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
save_config(enabled_map, WIDGETS_ENABLED_FILE)
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
local function do_enable(args)
|
|
|
|
|
|
|
|
do_by_names_or_numbers(args, function(name, db_entry)
|
|
|
|
do_by_names_or_numbers(args, function(name, db_entry)
|
|
|
|
db_entry.enabled = true
|
|
|
|
overlay_config[name].enabled = true
|
|
|
|
if db_entry.config.hotspot then
|
|
|
|
if db_entry.widget.hotspot then
|
|
|
|
active_hotspot_widgets[name] = db_entry
|
|
|
|
active_hotspot_widgets[name] = db_entry
|
|
|
|
end
|
|
|
|
end
|
|
|
|
for _,vs_name in ipairs(normalize_list(db_entry.config.viewscreens)) do
|
|
|
|
for _,vs_name in ipairs(normalize_list(db_entry.widget.viewscreens)) do
|
|
|
|
vs_name = normalize_viewscreen_name(vs_name)
|
|
|
|
vs_name = normalize_viewscreen_name(vs_name)
|
|
|
|
ensure_key(active_viewscreen_widgets, vs_name)[name] = db_entry
|
|
|
|
ensure_key(active_viewscreen_widgets, vs_name)[name] = db_entry
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if not quiet then
|
|
|
|
|
|
|
|
print(('enabled widget %s'):format(name))
|
|
|
|
|
|
|
|
end
|
|
|
|
end)
|
|
|
|
end)
|
|
|
|
save_enabled()
|
|
|
|
if not skip_save then
|
|
|
|
|
|
|
|
save_config()
|
|
|
|
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
local function do_disable(args)
|
|
|
|
local function do_disable(args)
|
|
|
|
do_by_names_or_numbers(args, function(name, db_entry)
|
|
|
|
do_by_names_or_numbers(args, function(name, db_entry)
|
|
|
|
db_entry.enabled = false
|
|
|
|
overlay_config[name].enabled = false
|
|
|
|
if db_entry.config.hotspot then
|
|
|
|
if db_entry.widget.hotspot then
|
|
|
|
active_hotspot_widgets[name] = nil
|
|
|
|
active_hotspot_widgets[name] = nil
|
|
|
|
end
|
|
|
|
end
|
|
|
|
for _,vs_name in ipairs(normalize_list(db_entry.config.viewscreens)) do
|
|
|
|
for _,vs_name in ipairs(normalize_list(db_entry.widget.viewscreens)) do
|
|
|
|
vs_name = normalize_viewscreen_name(vs_name)
|
|
|
|
vs_name = normalize_viewscreen_name(vs_name)
|
|
|
|
ensure_key(active_viewscreen_widgets, vs_name)[name] = nil
|
|
|
|
ensure_key(active_viewscreen_widgets, vs_name)[name] = nil
|
|
|
|
if is_empty(active_viewscreen_widgets[vs_name]) then
|
|
|
|
if is_empty(active_viewscreen_widgets[vs_name]) then
|
|
|
|
active_viewscreen_widgets[vs_name] = nil
|
|
|
|
active_viewscreen_widgets[vs_name] = nil
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
print(('disabled widget %s'):format(name))
|
|
|
|
end)
|
|
|
|
end)
|
|
|
|
save_enabled()
|
|
|
|
save_config()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
local function do_list(args)
|
|
|
|
local function do_list(args)
|
|
|
|
local filter = args and #args > 0
|
|
|
|
local filter = args and #args > 0
|
|
|
|
|
|
|
|
local num_filtered = 0
|
|
|
|
for i,name in ipairs(widget_index) do
|
|
|
|
for i,name in ipairs(widget_index) do
|
|
|
|
if filter then
|
|
|
|
if filter then
|
|
|
|
local passes = false
|
|
|
|
local passes = false
|
|
|
@ -114,51 +168,125 @@ local function do_list(args)
|
|
|
|
break
|
|
|
|
break
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
if not passes then goto continue end
|
|
|
|
if not passes then
|
|
|
|
|
|
|
|
num_filtered = num_filtered + 1
|
|
|
|
|
|
|
|
goto continue
|
|
|
|
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
local enabled = widget_db[name].enabled
|
|
|
|
local db_entry = widget_db[name]
|
|
|
|
|
|
|
|
local enabled = overlay_config[name].enabled
|
|
|
|
dfhack.color(enabled and COLOR_YELLOW or COLOR_LIGHTGREEN)
|
|
|
|
dfhack.color(enabled and COLOR_YELLOW or COLOR_LIGHTGREEN)
|
|
|
|
dfhack.print(enabled and '[enabled] ' or '[disabled]')
|
|
|
|
dfhack.print(enabled and '[enabled] ' or '[disabled]')
|
|
|
|
dfhack.color()
|
|
|
|
dfhack.color()
|
|
|
|
print((' %d) %s%s'):format(i, name,
|
|
|
|
print((' %d) %s%s'):format(i, name,
|
|
|
|
widget_db[name].widget.overlay_trigger and ' (can trigger)' or ''))
|
|
|
|
db_entry.widget.overlay_trigger and ' (can trigger)' or ''))
|
|
|
|
::continue::
|
|
|
|
::continue::
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if num_filtered > 0 then
|
|
|
|
|
|
|
|
print(('(%d widgets filtered out)'):format(num_filtered))
|
|
|
|
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
local function make_frame(config, old_frame)
|
|
|
|
local function load_widget(name, widget_class)
|
|
|
|
local old_frame, frame = old_frame or {}, {}
|
|
|
|
local widget = widget_class{name=name}
|
|
|
|
frame.w, frame.h = old_frame.w, old_frame.h
|
|
|
|
widget_db[name] = {
|
|
|
|
local pos = utils.assign({x=-1, y=20}, config.pos or {})
|
|
|
|
widget=widget,
|
|
|
|
-- if someone accidentally uses 1-based instead of 0-based indexing, fix it
|
|
|
|
next_update_ms=widget.overlay_onupdate and 0 or math.huge,
|
|
|
|
if pos.x == 0 then pos.x = 1 end
|
|
|
|
}
|
|
|
|
if pos.y == 0 then pos.y = 1 end
|
|
|
|
if not overlay_config[name] then overlay_config[name] = {} end
|
|
|
|
if pos.x < 0 then frame.r = math.abs(pos.x) - 1 else frame.l = pos.x - 1 end
|
|
|
|
local config = overlay_config[name]
|
|
|
|
if pos.y < 0 then frame.b = math.abs(pos.y) - 1 else frame.t = pos.y - 1 end
|
|
|
|
if not config.pos then
|
|
|
|
return frame
|
|
|
|
config.pos = sanitize_pos(widget.default_pos)
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
widget.frame = make_frame(config.pos, widget.frame)
|
|
|
|
|
|
|
|
if config.enabled then
|
|
|
|
|
|
|
|
do_enable(name, true, true)
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
config.enabled = false
|
|
|
|
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
local function get_screen_rect()
|
|
|
|
local function load_widgets(env_prefix, provider, env_fn)
|
|
|
|
local w, h = dfhack.screen.getWindowSize()
|
|
|
|
local env_name = env_prefix .. provider
|
|
|
|
return gui.ViewRect{rect=gui.mkdims_wh(0, 0, w, h)}
|
|
|
|
local ok, provider_env = pcall(env_fn, env_name)
|
|
|
|
|
|
|
|
if not ok or not provider_env[OVERLAY_WIDGETS_VAR] then return end
|
|
|
|
|
|
|
|
local overlay_widgets = provider_env[OVERLAY_WIDGETS_VAR]
|
|
|
|
|
|
|
|
if type(overlay_widgets) ~= 'table' then
|
|
|
|
|
|
|
|
dfhack.printerr(
|
|
|
|
|
|
|
|
('error loading overlay widgets from "%s": %s map is malformed')
|
|
|
|
|
|
|
|
:format(env_name, OVERLAY_WIDGETS_VAR))
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
for widget_name,widget_class in pairs(overlay_widgets) do
|
|
|
|
|
|
|
|
local name = provider .. '.' .. widget_name
|
|
|
|
|
|
|
|
if not safecall(load_widget, name, widget_class) then
|
|
|
|
|
|
|
|
dfhack.printerr(('error loading overlay widget "%s"'):format(name))
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-- also called directly from cpp on init
|
|
|
|
|
|
|
|
function reload()
|
|
|
|
|
|
|
|
reset()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for _,plugin in ipairs(dfhack.internal.listPlugins()) do
|
|
|
|
|
|
|
|
load_widgets('plugins.', plugin, require)
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
for _,script_path in ipairs(dfhack.internal.getScriptPaths()) do
|
|
|
|
|
|
|
|
local files = dfhack.filesystem.listdir_recursive(
|
|
|
|
|
|
|
|
script_path, nil, false)
|
|
|
|
|
|
|
|
if not files then goto skip_path end
|
|
|
|
|
|
|
|
for _,f in ipairs(files) do
|
|
|
|
|
|
|
|
if not f.isdir and
|
|
|
|
|
|
|
|
f.path:endswith('.lua') and
|
|
|
|
|
|
|
|
not f.path:startswith('test/') and
|
|
|
|
|
|
|
|
not f.path:startswith('internal/') then
|
|
|
|
|
|
|
|
local script_name = f.path:sub(1, #f.path - 4) -- remove '.lua'
|
|
|
|
|
|
|
|
load_widgets('', script_name, reqscript)
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
::skip_path::
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for name in pairs(widget_db) do
|
|
|
|
|
|
|
|
table.insert(widget_index, name)
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
table.sort(widget_index)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
reposition_widgets()
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
local function do_reload()
|
|
|
|
|
|
|
|
reload()
|
|
|
|
|
|
|
|
print('reloaded overlay configuration')
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
local function do_reposition(args)
|
|
|
|
local function do_reposition(args)
|
|
|
|
local name_or_number, x, y = table.unpack(args)
|
|
|
|
local name_or_number, x, y = table.unpack(args)
|
|
|
|
local name = get_name(name_or_number)
|
|
|
|
local name = get_name(name_or_number)
|
|
|
|
local db_entry = widget_db[name]
|
|
|
|
-- TODO: check existence of widget, validate numbers, warn if offscreen
|
|
|
|
local config = db_entry.config
|
|
|
|
local pos = sanitize_pos{x=tonumber(x), y=tonumber(y)}
|
|
|
|
config.pos.x, config.pos.y = tonumber(x), tonumber(y)
|
|
|
|
overlay_config[name].pos = pos
|
|
|
|
db_entry.widget.frame = make_frame(config, db_entry.widget.frame)
|
|
|
|
local widget = widget_db[name].widget
|
|
|
|
db_entry.widget:updateLayout(get_screen_rect())
|
|
|
|
widget.frame = make_frame(pos, widget.frame)
|
|
|
|
save_config(config, WIDGETS_STATE_DIR .. name .. '.json')
|
|
|
|
widget:updateLayout(get_screen_rect())
|
|
|
|
|
|
|
|
save_config()
|
|
|
|
|
|
|
|
print(('repositioned widget %s to x=%d, y=%d'):format(name, pos.x, pos.y))
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-- note that the widget does not have to be enabled to be triggered
|
|
|
|
local function do_trigger(args)
|
|
|
|
local function do_trigger(args)
|
|
|
|
|
|
|
|
if triggered_screen_has_lock() then
|
|
|
|
|
|
|
|
dfhack.printerr(
|
|
|
|
|
|
|
|
'cannot trigger widget; another widget is already active')
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
end
|
|
|
|
local target = args[1]
|
|
|
|
local target = args[1]
|
|
|
|
do_by_names_or_numbers(target, function(name, db_entry)
|
|
|
|
do_by_names_or_numbers(target, function(name, db_entry)
|
|
|
|
if db_entry.widget.overlay_trigger then
|
|
|
|
local widget = db_entry.widget
|
|
|
|
db_entry.widget:overlay_trigger()
|
|
|
|
if widget.overlay_trigger then
|
|
|
|
|
|
|
|
widget:overlay_trigger()
|
|
|
|
|
|
|
|
print(('triggered widget %s'):format(name))
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end)
|
|
|
|
end)
|
|
|
|
end
|
|
|
|
end
|
|
|
@ -167,7 +295,7 @@ local command_fns = {
|
|
|
|
enable=do_enable,
|
|
|
|
enable=do_enable,
|
|
|
|
disable=do_disable,
|
|
|
|
disable=do_disable,
|
|
|
|
list=do_list,
|
|
|
|
list=do_list,
|
|
|
|
reload=function() reload() end,
|
|
|
|
reload=do_reload,
|
|
|
|
reposition=do_reposition,
|
|
|
|
reposition=do_reposition,
|
|
|
|
trigger=do_trigger,
|
|
|
|
trigger=do_trigger,
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -177,82 +305,13 @@ local HELP_ARGS = utils.invert{'help', '--help', '-h'}
|
|
|
|
function overlay_command(args)
|
|
|
|
function overlay_command(args)
|
|
|
|
local command = table.remove(args, 1) or 'help'
|
|
|
|
local command = table.remove(args, 1) or 'help'
|
|
|
|
if HELP_ARGS[command] or not command_fns[command] then return false end
|
|
|
|
if HELP_ARGS[command] or not command_fns[command] then return false end
|
|
|
|
|
|
|
|
|
|
|
|
command_fns[command](args)
|
|
|
|
command_fns[command](args)
|
|
|
|
return true
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
local function instantiate_widget(name, config)
|
|
|
|
-- ---------------- --
|
|
|
|
local provider = config.provider
|
|
|
|
-- event management --
|
|
|
|
local ok, provider_env = pcall(require, provider)
|
|
|
|
-- ---------------- --
|
|
|
|
if not ok then
|
|
|
|
|
|
|
|
ok, provider_env = pcall(require, 'plugins.'..provider)
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
if not ok then
|
|
|
|
|
|
|
|
ok, provider_env = pcall(reqscript, provider)
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
if not ok then
|
|
|
|
|
|
|
|
dfhack.printerr(
|
|
|
|
|
|
|
|
('error loading overlay widget "%s": could not find provider' ..
|
|
|
|
|
|
|
|
' environment "%s"')
|
|
|
|
|
|
|
|
:format(name, provider))
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
local classname = config.class
|
|
|
|
|
|
|
|
if not provider_env[classname] then
|
|
|
|
|
|
|
|
dfhack.printerr(
|
|
|
|
|
|
|
|
('error loading overlay widget "%s": could not find class "%s"')
|
|
|
|
|
|
|
|
:format(name, classname))
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return provider_env[classname]{frame=make_frame(config)}
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
local function load_widget(name, config, enabled)
|
|
|
|
|
|
|
|
local widget = instantiate_widget(name, config)
|
|
|
|
|
|
|
|
if not widget then return end
|
|
|
|
|
|
|
|
local db_entry = {
|
|
|
|
|
|
|
|
enabled=enabled,
|
|
|
|
|
|
|
|
config=config,
|
|
|
|
|
|
|
|
widget=widget,
|
|
|
|
|
|
|
|
next_update_ms=widget.overlay_onupdate and 0 or math.huge,
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
widget_db[name] = db_entry
|
|
|
|
|
|
|
|
if enabled then do_enable(name) end
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function reload()
|
|
|
|
|
|
|
|
widget_db = {}
|
|
|
|
|
|
|
|
widget_index = {}
|
|
|
|
|
|
|
|
active_hotspot_widgets = {}
|
|
|
|
|
|
|
|
active_viewscreen_widgets = {}
|
|
|
|
|
|
|
|
active_triggered_screen = nil
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
local enabled_map = load_config(WIDGETS_ENABLED_FILE)
|
|
|
|
|
|
|
|
for _,fname in ipairs(dfhack.filesystem.listdir(WIDGETS_STATE_DIR)) do
|
|
|
|
|
|
|
|
local _,_,name = fname:find('^(.*)%.json$')
|
|
|
|
|
|
|
|
if not name then goto continue end
|
|
|
|
|
|
|
|
local widget_config = load_config(WIDGETS_STATE_DIR..fname)
|
|
|
|
|
|
|
|
if not widget_config.provider or not widget_config.class then
|
|
|
|
|
|
|
|
dfhack.printerr(
|
|
|
|
|
|
|
|
('error loading overlay widget "%s": "provider" and' ..
|
|
|
|
|
|
|
|
' "class" must be specified in %s%s')
|
|
|
|
|
|
|
|
:format(name, WIDGETS_STATE_DIR, fname))
|
|
|
|
|
|
|
|
goto continue
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
load_widget(name, widget_config, not not enabled_map[name])
|
|
|
|
|
|
|
|
::continue::
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for name in pairs(widget_db) do
|
|
|
|
|
|
|
|
table.insert(widget_index, name)
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
table.sort(widget_index)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
reposition_widgets()
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
local function detect_frame_change(widget, fn)
|
|
|
|
local function detect_frame_change(widget, fn)
|
|
|
|
local frame = widget.frame
|
|
|
|
local frame = widget.frame
|
|
|
@ -280,10 +339,7 @@ local function do_update(db_entry, now_ms, vs)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
function update_hotspot_widgets()
|
|
|
|
function update_hotspot_widgets()
|
|
|
|
if active_triggered_screen then
|
|
|
|
if triggered_screen_has_lock() then return end
|
|
|
|
if active_triggered_screen:isActive() then return end
|
|
|
|
|
|
|
|
active_triggered_screen = nil
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
local now_ms = dfhack.getTickCount()
|
|
|
|
local now_ms = dfhack.getTickCount()
|
|
|
|
for _,db_entry in pairs(active_hotspot_widgets) do
|
|
|
|
for _,db_entry in pairs(active_hotspot_widgets) do
|
|
|
|
if do_update(db_entry, now_ms) then return end
|
|
|
|
if do_update(db_entry, now_ms) then return end
|
|
|
@ -330,15 +386,25 @@ function reposition_widgets()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-- ------------------------------------------------- --
|
|
|
|
|
|
|
|
-- OverlayWidget (base class of all overlay widgets) --
|
|
|
|
|
|
|
|
-- ------------------------------------------------- --
|
|
|
|
|
|
|
|
|
|
|
|
OverlayWidget = defclass(OverlayWidget, widgets.Widget)
|
|
|
|
OverlayWidget = defclass(OverlayWidget, widgets.Widget)
|
|
|
|
OverlayWidget.ATTRS{
|
|
|
|
OverlayWidget.ATTRS{
|
|
|
|
overlay_onupdate_max_freq_seconds=5,
|
|
|
|
name=DEFAULT_NIL, -- this is set by the framework to the widget name
|
|
|
|
|
|
|
|
default_pos={x=DEFAULT_X_POS, y=DEFAULT_Y_POS}, -- initial widget screen pos, 1-based
|
|
|
|
|
|
|
|
hotspot=false, -- whether to call overlay_onupdate for all screens
|
|
|
|
|
|
|
|
viewscreens={}, -- override with list of viewscrens to interpose
|
|
|
|
|
|
|
|
overlay_onupdate_max_freq_seconds=5, -- throttle calls to overlay_onupdate
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function OverlayWidget:preinit(info)
|
|
|
|
-- set defaults for frame. the widget is expected to keep these up to date as
|
|
|
|
info.frame = info.frame or {}
|
|
|
|
-- display contents change.
|
|
|
|
info.frame.w = info.frame.w or 5
|
|
|
|
function OverlayWidget:init()
|
|
|
|
info.frame.h = info.frame.h or 1
|
|
|
|
self.frame = self.frame or {}
|
|
|
|
|
|
|
|
self.frame.w = self.frame.w or 5
|
|
|
|
|
|
|
|
self.frame.h = self.frame.h or 1
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
return _ENV
|
|
|
|
return _ENV
|
|
|
|