support focus paths for overlay widgets

and migrate existing widgets to use them
develop
Myk Taylor 2023-02-06 02:37:32 -08:00
parent 032b62dcbe
commit bf91ffb1fe
No known key found for this signature in database
5 changed files with 56 additions and 38 deletions

@ -46,6 +46,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences:
## API
## Lua
- `overlay`: overlay widgets can now specify focus paths for the viewscreens they attach to so they only appear in specific contexts. see `overlay-dev-guide` for details.
## Removed

@ -109,7 +109,11 @@ The ``overlay.OverlayWidget`` superclass defines the following class attributes:
``dwarfmode`` and the adventure mode map viewscreen would be
``dungeonmode``. If there is only one viewscreen that this widget is
associated with, it can be specified as a string instead of a list of
strings with a single element.
strings with a single element. If you only want your widget to appear in
certain contexts, you can specify a focus path, in the same syntax as the
`keybinding` command. For example, ``dwarfmode/Info/CREATURES/CITIZEN`` will
ensure the overlay widget is only displayed when the "Citizens" subtab under
the "Units" panel is active.
- ``hotspot`` (default: ``false``)
If set to ``true``, your widget's ``overlay_onupdate`` function will be
called whenever the `overlay` plugin's ``plugin_onupdate()`` function is

@ -4,16 +4,11 @@ local gui = require('gui')
local overlay = require('plugins.overlay')
local widgets = require('gui.widgets')
local function is_labor_panel_visible()
local info = df.global.game.main_interface.info
return info.open and info.current_mode == df.info_interface_mode_type.LABOR
end
AutolaborOverlay = defclass(AutolaborOverlay, overlay.OverlayWidget)
AutolaborOverlay.ATTRS{
default_pos={x=7,y=-13},
default_enabled=true,
viewscreens='dwarfmode',
viewscreens='dwarfmode/Info/LABOR',
frame={w=29, h=5},
frame_style=gui.MEDIUM_FRAME,
frame_background=gui.CLEAR_PEN,
@ -34,9 +29,7 @@ function AutolaborOverlay:init()
end
function AutolaborOverlay:render(dc)
if not is_labor_panel_visible() or not isEnabled() then
return false
end
if not isEnabled() then return false end
AutolaborOverlay.super.render(self, dc)
end

@ -9,11 +9,6 @@ local widgets = require('gui.widgets')
-- OrdersOverlay
--
local function is_orders_panel_visible()
local info = df.global.game.main_interface.info
return info.open and info.current_mode == df.info_interface_mode_type.WORK_ORDERS
end
local function do_sort()
dfhack.run_command('orders', 'sort')
end
@ -49,7 +44,7 @@ OrdersOverlay = defclass(OrdersOverlay, overlay.OverlayWidget)
OrdersOverlay.ATTRS{
default_pos={x=53,y=-6},
default_enabled=true,
viewscreens='dwarfmode',
viewscreens='dwarfmode/Info/WORK_ORDERS',
frame={w=30, h=4},
frame_style=gui.MEDIUM_FRAME,
frame_background=gui.CLEAR_PEN,
@ -84,16 +79,6 @@ function OrdersOverlay:init()
}
end
function OrdersOverlay:render(dc)
if not is_orders_panel_visible() then return false end
OrdersOverlay.super.render(self, dc)
end
function OrdersOverlay:onInput(keys)
if not is_orders_panel_visible() then return false end
OrdersOverlay.super.onInput(self, keys)
end
OVERLAY_WIDGETS = {
overlay=OrdersOverlay,
}

@ -81,19 +81,18 @@ function normalize_list(element_or_list)
return {element_or_list}
end
-- normalize "short form" viewscreen names to "long form"
-- normalize "short form" viewscreen names to "long form" and remove any focus
local function normalize_viewscreen_name(vs_name)
if vs_name == 'all' or vs_name:match('viewscreen_.*st') then
return vs_name
if vs_name == 'all' or vs_name:match('^viewscreen_.*st') then
return vs_name:match('^[^/]+')
end
return 'viewscreen_' .. vs_name .. 'st'
return 'viewscreen_' .. vs_name:match('^[^/]+') .. 'st'
end
-- reduce "long form" viewscreen names to "short form"
-- reduce "long form" viewscreen names to "short form"; keep focus
function simplify_viewscreen_name(vs_name)
_,_,short_name = vs_name:find('^viewscreen_(.*)st$')
if short_name then return short_name end
return vs_name
local short_name = vs_name:match('^viewscreen_([^/]+)st')
return short_name or vs_name
end
local function is_empty(tbl)
@ -241,10 +240,23 @@ local function do_list(args)
end
end
local function get_focus_strings(viewscreens)
local focus_strings = nil
for _,vs in ipairs(viewscreens) do
if vs:match('/') then
focus_strings = focus_strings or {}
vs = simplify_viewscreen_name(vs)
table.insert(focus_strings, vs)
end
end
return focus_strings
end
local function load_widget(name, widget_class)
local widget = widget_class{name=name}
widget_db[name] = {
widget=widget,
focus_strings=get_focus_strings(normalize_list(widget.viewscreens)),
next_update_ms=widget.overlay_onupdate and 0 or math.huge,
}
if not overlay_config[name] then overlay_config[name] = {} end
@ -426,12 +438,30 @@ function update_hotspot_widgets()
end
end
local function matches_focus_strings(db_entry, vs_name)
if not db_entry.focus_strings then return true end
local matched = true
local simple_vs_name = simplify_viewscreen_name(vs_name)
for _,fs in ipairs(db_entry.focus_strings) do
if fs:startswith(simple_vs_name) then
matched = false
if dfhack.gui.matchFocusString(fs, vs) then
return true
end
end
end
return matched
end
local function _update_viewscreen_widgets(vs_name, vs, now_ms)
local vs_widgets = active_viewscreen_widgets[vs_name]
if not vs_widgets then return end
now_ms = now_ms or dfhack.getTickCount()
for name,db_entry in pairs(vs_widgets) do
if do_update(name, db_entry, now_ms, vs) then return end
if matches_focus_strings(db_entry, vs_name) and
do_update(name, db_entry, now_ms, vs) then
return
end
end
return now_ms
end
@ -439,15 +469,18 @@ end
function update_viewscreen_widgets(vs_name, vs)
if triggered_screen_has_lock() then return end
local now_ms = _update_viewscreen_widgets(vs_name, vs, nil)
if now_ms then
_update_viewscreen_widgets('all', vs, now_ms)
end
end
local function _feed_viewscreen_widgets(vs_name, keys)
local vs_widgets = active_viewscreen_widgets[vs_name]
if not vs_widgets then return false end
for _,db_entry in pairs(vs_widgets) do
local w = db_entry.widget
if detect_frame_change(w, function() return w:onInput(keys) end) then
if matches_focus_strings(db_entry, vs_name) and
detect_frame_change(w, function() return w:onInput(keys) end) then
return true
end
end
@ -465,9 +498,11 @@ local function _render_viewscreen_widgets(vs_name, dc)
dc = dc or gui.Painter.new()
for _,db_entry in pairs(vs_widgets) do
local w = db_entry.widget
if matches_focus_strings(db_entry, vs_name) then
detect_frame_change(w, function() w:render(dc) end)
end
end
end
function render_viewscreen_widgets(vs_name)
local dc = _render_viewscreen_widgets(vs_name, nil)