From 9bdc995f2098d11bbc3ef67e610ddaa499b3b35f Mon Sep 17 00:00:00 2001 From: myk002 Date: Wed, 9 Nov 2022 14:08:38 -0800 Subject: [PATCH] write docs and make implementation match the docs added ability to enable/disable all added showing current widget configuration added moving widget back to default coordinates --- docs/plugins/overlay.rst | 65 +++++++++++++++++++---- plugins/lua/overlay.lua | 108 +++++++++++++++++++++++++++++---------- 2 files changed, 138 insertions(+), 35 deletions(-) diff --git a/docs/plugins/overlay.rst b/docs/plugins/overlay.rst index 9416fba31..8a63c8f20 100644 --- a/docs/plugins/overlay.rst +++ b/docs/plugins/overlay.rst @@ -2,19 +2,66 @@ overlay ======= .. dfhack-tool:: - :summary: Provide an on-screen clickable DFHack launcher button. + :summary: Manage on-screen overlay widgets. :tags: dfhack interface -This tool places a small button in the lower left corner of the screen that you -can click to run DFHack commands with `gui/launcher`. - -If you would rather always run `gui/launcher` with the hotkeys, or just don't -want the DFHack button on-screen, just disable the plugin with -``disable overlay``. +The overlay framework manages the on-screen widgets that other tools (including +3rd party plugins and scripts) can register for display. If you are a developer +who wants to write an overlay widget, please see the `overlay-widget-guide`. Usage ----- -:: +``enable overlay`` + Display enabled widgets. +``overlay enable|disable all| [ ...]`` + Enable/disable all or specified widgets. Widgets can be specified by either + their name or their number, as returned by ``overlay list``. +``overlay list`` + Show a list of all the widgets that are registered with the overlay + framework. +``overlay position [default| ]`` + Display configuration information for the given widget or change the + position where it is rendered. See the `Widget position`_ section below for + details. +``overlay trigger `` + Intended to be used by keybindings for manually triggering a widget. For + example, you could use an ``overlay trigger`` keybinding to show a menu that + normally appears when you hover the mouse over a screen hotspot. + +Examples +-------- + +``overlay enable all`` + Enable all widgets. Note that they will only be displayed on the screens + that they are associated with. You can see which screens a widget will be + displayed on, along with whether the widget is a hotspot, by calling + ``overlay position``. +``overlay position hotkeys.menu`` + Show the current configuration of the `hotkeys` menu widget. +``overlay position dwarfmonitor.cursor -2 -3`` + Display the `dwarfmonitor` cursor position reporting widget in the lower + right corner of the screen, 2 tiles from the left and 3 tiles from the + bottom. +``overlay position dwarfmonitor.cursor default`` + Reset the `dwarfmonitor` cursor position to its default. +``overlay trigger hotkeys.menu`` + Trigger the `hotkeys` menu widget so that it shows its popup menu. This is + what is run when you hit :kbd:`Ctrl`:kbd:`Shift`:kbd:`C`. + +Widget position +--------------- + +Widgets can be positioned at any (``x``, ``y``) position on the screen, and can +be specified relative to any edge. Coordinates are 1-based, which means that +``1`` is the far left column (for ``x``) or the top row (for ``y``). Negative +numbers are measured from the right of the screen to the right edge of the +widget or from the bottom of the screen to the bottom of the widget, +respectively. + +For easy reference, the corners can be found at the following coordinates: - enable overlay +:(1, 1): top left corner +:(-1, 1): top right corner +:(1, -1): lower left corner +:(-1, -1): lower right corner diff --git a/plugins/lua/overlay.lua b/plugins/lua/overlay.lua index 69a32242b..5327d22f1 100644 --- a/plugins/lua/overlay.lua +++ b/plugins/lua/overlay.lua @@ -14,6 +14,7 @@ local DEFAULT_X_POS, DEFAULT_Y_POS = -2, -2 -- state and config -- -- ---------------- -- +local active_triggered_widget = nil 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 @@ -25,6 +26,7 @@ local function reset() if active_triggered_screen then active_triggered_screen:dismiss() end + active_triggered_widget = nil active_triggered_screen = nil widget_db = {} @@ -47,6 +49,7 @@ 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_widget = nil active_triggered_screen = nil return false end @@ -108,7 +111,12 @@ local function get_name(name_or_number) end local function do_by_names_or_numbers(args, fn) - for _,name_or_number in ipairs(normalize_list(args)) do + local arglist = normalize_list(args) + if #arglist == 0 then + dfhack.printerr('please specify a widget name or list number') + return + end + for _,name_or_number in ipairs(arglist) do local name = get_name(name_or_number) local db_entry = widget_db[name] if not db_entry then @@ -120,7 +128,7 @@ local function do_by_names_or_numbers(args, fn) end local function do_enable(args, quiet, skip_save) - do_by_names_or_numbers(args, function(name, db_entry) + local enable_fn = function(name, db_entry) overlay_config[name].enabled = true if db_entry.widget.hotspot then active_hotspot_widgets[name] = db_entry @@ -132,14 +140,23 @@ local function do_enable(args, quiet, skip_save) if not quiet then print(('enabled widget %s'):format(name)) end - end) + end + if args[1] == 'all' then + for name,db_entry in pairs(widget_db) do + if not overlay_config[name].enabled then + enable_fn(name, db_entry) + end + end + else + do_by_names_or_numbers(args, enable_fn) + end if not skip_save then save_config() end end local function do_disable(args) - do_by_names_or_numbers(args, function(name, db_entry) + local disable_fn = function(name, db_entry) overlay_config[name].enabled = false if db_entry.widget.hotspot then active_hotspot_widgets[name] = nil @@ -152,7 +169,16 @@ local function do_disable(args) end end print(('disabled widget %s'):format(name)) - end) + end + if args[1] == 'all' then + for name,db_entry in pairs(widget_db) do + if overlay_config[name].enabled then + disable_fn(name, db_entry) + end + end + else + do_by_names_or_numbers(args, disable_fn) + end save_config() end @@ -175,11 +201,10 @@ local function do_list(args) end 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_LIGHTGREEN or COLOR_YELLOW) dfhack.print(enabled and '[enabled] ' or '[disabled]') dfhack.color() - print((' %d) %s%s'):format(i, name, - db_entry.widget.overlay_trigger and ' (can trigger)' or '')) + print((' %d) %s'):format(i, name)) ::continue:: end if num_filtered > 0 then @@ -225,7 +250,7 @@ local function load_widgets(env_prefix, provider, env_fn) end end --- also called directly from cpp on init +-- called directly from cpp on init function reload() reset() @@ -256,18 +281,44 @@ function reload() reposition_widgets() end -local function do_reload() - reload() - print('reloaded overlay configuration') +local function dump_widget_config(name, widget) + local pos = overlay_config[name].pos + print(('widget %s is positioned at x=%d, y=%d'):format(name, pos.x, pos.y)) + if #widget.viewscreens > 0 then + print(' it will be attached to the following viewscreens:') + for _,vs in ipairs(widget.viewscreens) do + print((' %s'):format(vs)) + end + end + if widget.hotspot then + print(' on all screens it will act as a hotspot') + end end -local function do_reposition(args) +local function do_position(args) local name_or_number, x, y = table.unpack(args) local name = get_name(name_or_number) - -- TODO: check existence of widget, validate numbers, warn if offscreen - local pos = sanitize_pos{x=tonumber(x), y=tonumber(y)} - overlay_config[name].pos = pos + if not widget_db[name] then + if not name_or_number then + dfhack.printerr('please specify a widget name or list number') + else + dfhack.printerr(('widget not found: "%s"'):format(name)) + end + return + end local widget = widget_db[name].widget + local pos + if x == 'default' then + pos = sanitize_pos(widget.default_pos) + else + x, y = tonumber(x), tonumber(y) + if not x or not y then + dump_widget_config(name, widget) + return + end + pos = sanitize_pos{x=x, y=y} + end + overlay_config[name].pos = pos widget.frame = make_frame(pos, widget.frame) widget:updateLayout(get_screen_rect()) save_config() @@ -277,8 +328,8 @@ end -- note that the widget does not have to be enabled to be triggered local function do_trigger(args) if triggered_screen_has_lock() then - dfhack.printerr( - 'cannot trigger widget; another widget is already active') + dfhack.printerr(('cannot trigger widget; widget "%s" is already active') + :format(active_triggered_widget)) return end local target = args[1] @@ -286,6 +337,9 @@ local function do_trigger(args) local widget = db_entry.widget if widget.overlay_trigger then active_triggered_screen = widget:overlay_trigger() + if active_triggered_screen then + active_triggered_widget = name + end print(('triggered widget %s'):format(name)) end end) @@ -295,8 +349,7 @@ local command_fns = { enable=do_enable, disable=do_disable, list=do_list, - reload=do_reload, - reposition=do_reposition, + position=do_position, trigger=do_trigger, } @@ -325,7 +378,7 @@ end -- reduces the next call by a small random amount to introduce jitter into the -- widget processing timings -local function do_update(db_entry, now_ms, vs) +local function do_update(name, db_entry, now_ms, vs) if db_entry.next_update_ms > now_ms then return end local w = db_entry.widget local freq_ms = w.overlay_onupdate_max_freq_seconds * 1000 @@ -334,15 +387,18 @@ local function do_update(db_entry, now_ms, vs) if detect_frame_change(w, function() return w:overlay_onupdate(vs) end) then active_triggered_screen = w:overlay_trigger() - if active_triggered_screen then return true end + if active_triggered_screen then + active_triggered_widget = name + return true + end end end function update_hotspot_widgets() if triggered_screen_has_lock() then return end local now_ms = dfhack.getTickCount() - for _,db_entry in pairs(active_hotspot_widgets) do - if do_update(db_entry, now_ms) then return end + for name,db_entry in pairs(active_hotspot_widgets) do + if do_update(name, db_entry, now_ms) then return end end end @@ -350,8 +406,8 @@ function update_viewscreen_widgets(vs_name, vs) local vs_widgets = active_viewscreen_widgets[vs_name] if not vs_widgets then return end local now_ms = dfhack.getTickCount() - for _,db_entry in pairs(vs_widgets) do - if do_update(db_entry, now_ms, vs) then return end + for name,db_entry in pairs(vs_widgets) do + if do_update(name, db_entry, now_ms, vs) then return end end end