diff --git a/docs/changelog.txt b/docs/changelog.txt index 626191f9f..96a57e16d 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -48,6 +48,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 diff --git a/docs/dev/overlay-dev-guide.rst b/docs/dev/overlay-dev-guide.rst index c7e094299..d31ce246d 100644 --- a/docs/dev/overlay-dev-guide.rst +++ b/docs/dev/overlay-dev-guide.rst @@ -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 diff --git a/plugins/lua/autolabor.lua b/plugins/lua/autolabor.lua index b91227650..6ef4d7279 100644 --- a/plugins/lua/autolabor.lua +++ b/plugins/lua/autolabor.lua @@ -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 diff --git a/plugins/lua/orders.lua b/plugins/lua/orders.lua index 972edcfea..72bb6185e 100644 --- a/plugins/lua/orders.lua +++ b/plugins/lua/orders.lua @@ -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, } diff --git a/plugins/lua/overlay.lua b/plugins/lua/overlay.lua index bf944d7e9..c3522c204 100644 --- a/plugins/lua/overlay.lua +++ b/plugins/lua/overlay.lua @@ -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,7 +469,9 @@ 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) - _update_viewscreen_widgets('all', vs, now_ms) + if now_ms then + _update_viewscreen_widgets('all', vs, now_ms) + end end local function _feed_viewscreen_widgets(vs_name, keys) @@ -447,7 +479,8 @@ local function _feed_viewscreen_widgets(vs_name, keys) 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,7 +498,9 @@ 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 - detect_frame_change(w, function() w:render(dc) end) + if matches_focus_strings(db_entry, vs_name) then + detect_frame_change(w, function() w:render(dc) end) + end end end