From ae16b65c890e41297aeea4c7223a5239a4a53193 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 7 Oct 2023 12:10:13 -0700 Subject: [PATCH 1/8] process keys before cursor dragging so a held mouse button doesn't inhibit text input --- library/lua/gui/widgets.lua | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index 05d237f35..34b154bf8 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -739,12 +739,6 @@ function EditField:onInput(keys) end end return not not self.key - elseif keys._MOUSE_L_DOWN then - local mouse_x = self:getMousePos() - if mouse_x then - self:setCursor(self.start_pos + mouse_x - (self.text_offset or 0)) - return true - end elseif keys._STRING then local old = self.text if keys._STRING == 0 then @@ -795,6 +789,12 @@ function EditField:onInput(keys) elseif keys.CUSTOM_CTRL_V then self:insert(dfhack.internal.getClipboardTextCp437()) return true + elseif keys._MOUSE_L_DOWN then + local mouse_x = self:getMousePos() + if mouse_x then + self:setCursor(self.start_pos + mouse_x - (self.text_offset or 0)) + return true + end end -- if we're modal, then unconditionally eat all the input From 37ad0c3e6f29b4a5a1d16ab7e0a8d39f03852c28 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 7 Oct 2023 12:10:52 -0700 Subject: [PATCH 2/8] add more focus string details for info panels --- library/modules/Gui.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/library/modules/Gui.cpp b/library/modules/Gui.cpp index 58763262f..a53876d8a 100644 --- a/library/modules/Gui.cpp +++ b/library/modules/Gui.cpp @@ -240,7 +240,14 @@ DEFINE_GET_FOCUS_STRING_HANDLER(dwarfmode) switch(game->main_interface.info.current_mode) { case df::enums::info_interface_mode_type::CREATURES: - newFocusString += '/' + enum_item_key(game->main_interface.info.creatures.current_mode); + if (game->main_interface.info.creatures.showing_overall_training) + newFocusString += "/OverallTraining"; + else if (game->main_interface.info.creatures.showing_activity_details) + newFocusString += "/ActivityDetails"; + else if (game->main_interface.info.creatures.adding_trainer) + newFocusString += "/AddingTrainer"; + else + newFocusString += '/' + enum_item_key(game->main_interface.info.creatures.current_mode); break; case df::enums::info_interface_mode_type::BUILDINGS: newFocusString += '/' + enum_item_key(game->main_interface.info.buildings.mode); From ae1d6f98f64210751b9e420fe1e4b0be65bd02f6 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 7 Oct 2023 12:11:24 -0700 Subject: [PATCH 3/8] add basic framework for info search widget --- plugins/lua/sort.lua | 14 ++-- plugins/lua/sort/creatures.lua | 139 +++++++++++++++++++++++++++++++++ 2 files changed, 147 insertions(+), 6 deletions(-) create mode 100644 plugins/lua/sort/creatures.lua diff --git a/plugins/lua/sort.lua b/plugins/lua/sort.lua index 6d8c8a298..1159ce56e 100644 --- a/plugins/lua/sort.lua +++ b/plugins/lua/sort.lua @@ -1,5 +1,6 @@ local _ENV = mkmodule('plugins.sort') +local creatures = require('plugins.sort.creatures') local gui = require('gui') local overlay = require('plugins.overlay') local setbelief = reqscript('modtools/set-belief') @@ -275,29 +276,29 @@ local function get_ranged_skill_effectiveness_rating(unit) return get_rating(ranged_skill_effectiveness(unit), 0, 800000, 72, 52, 31, 11) end -local function make_sort_by_ranged_skill_effectiveness_desc(list) +local function make_sort_by_ranged_skill_effectiveness_desc() return function(unit_id_1, unit_id_2) if unit_id_1 == unit_id_2 then return 0 end local unit1 = df.unit.find(unit_id_1) local unit2 = df.unit.find(unit_id_2) if not unit1 then return -1 end if not unit2 then return 1 end - local rating1 = ranged_skill_effectiveness(unit1, list) - local rating2 = ranged_skill_effectiveness(unit2, list) + local rating1 = ranged_skill_effectiveness(unit1) + local rating2 = ranged_skill_effectiveness(unit2) if rating1 == rating2 then return sort_by_name_desc(unit_id_1, unit_id_2) end return utils.compare(rating2, rating1) end end -local function make_sort_by_ranged_skill_effectiveness_asc(list) +local function make_sort_by_ranged_skill_effectiveness_asc() return function(unit_id_1, unit_id_2) if unit_id_1 == unit_id_2 then return 0 end local unit1 = df.unit.find(unit_id_1) local unit2 = df.unit.find(unit_id_2) if not unit1 then return -1 end if not unit2 then return 1 end - local rating1 = ranged_skill_effectiveness(unit1, list) - local rating2 = ranged_skill_effectiveness(unit2, list) + local rating1 = ranged_skill_effectiveness(unit1) + local rating2 = ranged_skill_effectiveness(unit2) if rating1 == rating2 then return sort_by_name_desc(unit_id_1, unit_id_2) end return utils.compare(rating1, rating2) end @@ -1261,6 +1262,7 @@ end OVERLAY_WIDGETS = { squad_assignment=SquadAssignmentOverlay, squad_annotation=SquadAnnotationOverlay, + creatures=creatures.InfoOverlay, } dfhack.onStateChange[GLOBAL_KEY] = function(sc) diff --git a/plugins/lua/sort/creatures.lua b/plugins/lua/sort/creatures.lua new file mode 100644 index 000000000..adf6b3fe9 --- /dev/null +++ b/plugins/lua/sort/creatures.lua @@ -0,0 +1,139 @@ +local _ENV = mkmodule('plugins.sort.creatures') + +local overlay = require('plugins.overlay') +local widgets = require('gui.widgets') + +local creatures = df.global.game.main_interface.info.creatures + +-- ---------------------- +-- InfoOverlay +-- + +InfoOverlay = defclass(InfoOverlay, overlay.OverlayWidget) +InfoOverlay.ATTRS{ + default_pos={x=64, y=9}, + default_enabled=true, + viewscreens={ + 'dwarfmode/Info/CREATURES/CITIZEN', + 'dwarfmode/Info/CREATURES/PET', + 'dwarfmode/Info/CREATURES/OverallTraining', + 'dwarfmode/Info/CREATURES/AddingTrainer', + 'dwarfmode/Info/CREATURES/OTHER', + 'dwarfmode/Info/CREATURES/DECEASED', + }, + hotspot=true, + overlay_onupdate_max_freq_seconds=0, + frame={w=40, h=3}, +} + +function InfoOverlay:init() + self.state = {} + + self:addviews{ + widgets.BannerPanel{ + view_id='panel', + frame={l=0, t=0, r=0, h=1}, + subviews={ + widgets.EditField{ + view_id='search', + frame={l=1, t=0, r=1}, + label_text="Search: ", + key='CUSTOM_ALT_S', + on_change=self:callback('text_input'), + }, + }, + }, + } +end + +function InfoOverlay:overlay_onupdate(scr) + if next(self.state) and not dfhack.gui.matchFocusString('dwarfmode/Info', scr) then + -- TODO: add dynamically allocated elements that were not visible at the time of + -- closure back to the list so they can be properly disposed of + self.state = {} + self.subviews.search:setText('') + self.subviews.search:setFocus(false) + self.overlay_onupdate_max_freq_seconds = 60 + end +end + +local function are_tabs_in_two_rows() + local pen = dfhack.screen.readTile(64, 6, false) -- tile is occupied iff tabs are in one row + return pen.ch == 0 +end + +local function resize_overlay(self) + local sw = dfhack.screen.getWindowSize() + local overlay_width = math.min(40, sw-(self.frame_rect.x1 + 30)) + if overlay_width ~= self.frame.w then + self.frame.w = overlay_width + return true + end +end + +function InfoOverlay:updateFrames() + local ret = resize_overlay(self) + local two_rows = are_tabs_in_two_rows() + if (self.two_rows == two_rows) then return ret end + self.two_rows = two_rows + self.subviews.panel.frame.t = two_rows and 2 or 0 + return true +end + +local function get_key() + if creatures.current_mode == df.unit_list_mode_type.PET then + if creatures.showing_overall_training then + return 'PET_OT' + elseif creatures.adding_trainer then + return 'PET_AT' + end + end + return df.unit_list_mode_type[creatures.current_mode] +end + +local function check_context(self) + local key = get_key() + if self.state.prev_key ~= key then + self.state.prev_key = key + local prev_text = ensure_key(self.state, key).prev_text + self.subviews.search:setText(prev_text or '') + end +end + +function InfoOverlay:onRenderBody(dc) + if next(self.state) then + check_context(self) + end + if self:updateFrames() then + self:updateLayout() + end + self.overlay_onupdate_max_freq_seconds = 0 + InfoOverlay.super.onRenderBody(self, dc) +end + +function InfoOverlay:text_input(text) + if not next(self.state) and text == '' then return end + -- the EditField state is guaranteed to be consistent with the current + -- context since when clicking to switch tabs, onRenderBody is always called + -- before this text_input callback, even if a key is pressed before the next + -- graphical frame would otherwise be printed. if this ever becomes untrue, + -- then we can add an on_char handler to the EditField that also calls + -- check_context. + local key = get_key() + local prev_text = ensure_key(self.state, key).prev_text + if text == prev_text then return end + if prev_text and text:startswith(prev_text) then + -- TODO: search + print('performing incremental search; text:', text, 'key:', key) + else + -- TODO: save list if not already saved + -- TODO: else restore list from saved list + -- TODO: if text ~= '' then search + -- TODO: sort according to vanilla sort widget state + print('performing full search; text:', text, 'key:', key) + end + -- TODO: save visible list + self.state[key].prev_text = text +end + +return _ENV From 673287d0a4bbf15940e29dcd3d53a4102e6013a1 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 7 Oct 2023 18:40:47 -0700 Subject: [PATCH 4/8] implement search logic --- plugins/lua/sort/creatures.lua | 199 ++++++++++++++++++++++++++++++--- 1 file changed, 183 insertions(+), 16 deletions(-) diff --git a/plugins/lua/sort/creatures.lua b/plugins/lua/sort/creatures.lua index adf6b3fe9..563bd3cca 100644 --- a/plugins/lua/sort/creatures.lua +++ b/plugins/lua/sort/creatures.lua @@ -2,9 +2,168 @@ local _ENV = mkmodule('plugins.sort.creatures') local overlay = require('plugins.overlay') local widgets = require('gui.widgets') +local utils = require('utils') local creatures = df.global.game.main_interface.info.creatures +-- these sort functions attempt to match the vanilla sort behavior, which is not +-- quite the same as the rest of DFHack. For example, in other DFHack sorts, +-- we'd always sort by name descending as a secondary sort. To match vanilla sorting, +-- if the primary sort is ascending, the secondary name sort will also be ascending. +-- +-- also note that vanilla sorts are not stable, so there might still be some jitter +-- if the player clicks one of the vanilla sort widgets after searching +local function sort_by_name_desc(a, b) + return a.sort_name < b.sort_name +end + +local function sort_by_name_asc(a, b) + return a.sort_name > b.sort_name +end + +local function sort_by_prof_desc(a, b) + if a.profession_list_order1 == b.profession_list_order1 then + return sort_by_name_desc(a, b) + end + return a.profession_list_order1 < b.profession_list_order1 +end + +local function sort_by_prof_asc(a, b) + if a.profession_list_order1 == b.profession_list_order1 then + return sort_by_name_asc(a, b) + end + return a.profession_list_order1 > b.profession_list_order1 +end + +local function sort_by_job_name_desc(a, b) + if a.job_sort_name == b.job_sort_name then + return sort_by_name_desc(a, b) + end + return a.job_sort_name < b.job_sort_name +end + +local function sort_by_job_name_asc(a, b) + if a.job_sort_name == b.job_sort_name then + -- use descending tertiary sort for visual stability + return sort_by_name_desc(a, b) + end + return a.job_sort_name > b.job_sort_name +end + +local function sort_by_job_desc(a, b) + if not not a.jb == not not b.jb then + return sort_by_job_name_desc(a, b) + end + return not not a.jb +end + +local function sort_by_job_asc(a, b) + if not not a.jb == not not b.jb then + return sort_by_job_name_asc(a, b) + end + return not not b.jb +end + +local function sort_by_stress_desc(a, b) + if a.stress == b.stress then + return sort_by_name_desc(a, b) + end + return a.stress > b.stress +end + +local function sort_by_stress_asc(a, b) + if a.stress == b.stress then + return sort_by_name_asc(a, b) + end + return a.stress < b.stress +end + +local function get_sort() + if creatures.sorting_cit_job then + return creatures.sorting_cit_job_is_ascending and sort_by_job_asc or sort_by_job_desc + elseif creatures.sorting_cit_stress then + return creatures.sorting_cit_stress_is_ascending and sort_by_stress_asc or sort_by_stress_desc + elseif creatures.sorting_cit_nameprof_doing_prof then + return creatures.sorting_cit_nameprof_is_ascending and sort_by_prof_asc or sort_by_prof_desc + else + return creatures.sorting_cit_nameprof_is_ascending and sort_by_name_asc or sort_by_name_desc + end +end + +local function copy_to_lua_table(vec) + local tab = {} + for k,v in ipairs(vec) do + tab[k+1] = v + end + return tab +end + +local function general_search(vec, get_search_key_fn, get_sort_fn, data, filter, incremental) + if not data.saved_original then + data.saved_original = copy_to_lua_table(vec) + elseif not incremental then + vec:assign(data.saved_original) + end + if filter ~= '' then + local search_tokens = filter:split() + for idx = #vec-1,0,-1 do + local search_key = get_search_key_fn(vec[idx]) + if search_key and not utils.search_text(search_key, search_tokens) then + vec:erase(idx) + end + end + end + data.saved_visible = copy_to_lua_table(vec) + if get_sort_fn then + table.sort(data.saved_visible, get_sort_fn()) + vec:assign(data.saved_visible) + end +end + +-- add dynamically allocated elements that were not visible at the time of +-- closure back to the vector so they can be cleaned up when it is next initialized +local function cri_unitst_cleanup(vec, data) + if not data.saved_visible or not data.saved_original then return end + for _,elem in ipairs(data.saved_original) do + if not utils.linear_index(data.saved_visible, elem) then + vec:insert('#', elem) + end + end +end + +local function make_cri_unitst_handlers(vec) + return { + search_fn=curry(general_search, vec, + function(elem) return elem.sort_name end, + get_sort), + cleanup_fn=curry(cri_unitst_cleanup, vec), + } +end + +local function overall_training_search(data, filter, incremental) + general_search(creatures.atk_index, function(elem) + local raw = df.creature_raw.find(elem) + if not raw then return '' end + return raw.name[1] + end, nil, data, filter, incremental) +end + +local function assign_trainer_search(data, filter, incremental) + general_search(creatures.trainer, function(elem) + if not elem then return nil end + return ('%s %s'):format(dfhack.TranslateName(elem.name), dfhack.units.getProfessionName(elem)) + end, nil, data, filter, incremental) +end + +local HANDLERS = { + CITIZEN=make_cri_unitst_handlers(creatures.cri_unit.CITIZEN), + PET=make_cri_unitst_handlers(creatures.cri_unit.PET), + OTHER=make_cri_unitst_handlers(creatures.cri_unit.OTHER), + DECEASED=make_cri_unitst_handlers(creatures.cri_unit.DECEASED), + PET_OT={search_fn=overall_training_search}, + PET_AT={search_fn=assign_trainer_search}, +} + -- ---------------------- -- InfoOverlay -- @@ -46,10 +205,18 @@ function InfoOverlay:init() } end -function InfoOverlay:overlay_onupdate(scr) - if next(self.state) and not dfhack.gui.matchFocusString('dwarfmode/Info', scr) then - -- TODO: add dynamically allocated elements that were not visible at the time of - -- closure back to the list so they can be properly disposed of +local function cleanup(state) + for k,v in pairs(state) do + local cleanup_fn = safe_index(HANDLERS, k, 'cleanup_fn') + if cleanup_fn then cleanup_fn(v) end + end +end + +function InfoOverlay:overlay_onupdate() + if next(self.state) and + not dfhack.gui.matchFocusString('dwarfmode/Info', dfhack.gui.getDFViewscreen(true)) + then + cleanup(self.state) self.state = {} self.subviews.search:setText('') self.subviews.search:setFocus(false) @@ -121,19 +288,19 @@ function InfoOverlay:text_input(text) -- check_context. local key = get_key() local prev_text = ensure_key(self.state, key).prev_text - if text == prev_text then return end - if prev_text and text:startswith(prev_text) then - -- TODO: search - print('performing incremental search; text:', text, 'key:', key) - else - -- TODO: save list if not already saved - -- TODO: else restore list from saved list - -- TODO: if text ~= '' then search - -- TODO: sort according to vanilla sort widget state - print('performing full search; text:', text, 'key:', key) - end - -- TODO: save visible list + -- some screens reset their contents between context switches; regardless + -- a switch back to the context should results in an incremental search + local incremental = prev_text and text:startswith(prev_text) + HANDLERS[key].search_fn(self.state[key], text, incremental) self.state[key].prev_text = text end +function InfoOverlay:onInput(keys) + if keys._MOUSE_R and self.subviews.search.focus then + self.subviews.search:setFocus(false) + return true + end + return InfoOverlay.super.onInput(self, keys) +end + return _ENV From 504948333035af6be03d98bb532c04fe44e5930b Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 7 Oct 2023 18:55:39 -0700 Subject: [PATCH 5/8] refactor text search routine out into utils fn --- docs/changelog.txt | 2 ++ docs/dev/Lua API.rst | 21 +++++++++++++--- library/lua/gui/widgets.lua | 29 +--------------------- library/lua/utils.lua | 26 +++++++++++++++++++ plugins/lua/buildingplan/itemselection.lua | 1 - 5 files changed, 46 insertions(+), 33 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 1a97e637d..57b78c087 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -67,8 +67,10 @@ Template for new versions: ## API ## Lua +- ``utils.search_text``: text search routine (generalized from ``widgets.FilteredList``) ## Removed +- ``FILTER_FULL_TEXT``: moved from ``gui.widgets`` to ``utils``; if your full text search preference is lost, please reset it in `gui/control-panel` # 50.11-r1 diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 9f9a3e374..f5314e4fb 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -3337,6 +3337,20 @@ utils Exactly like ``erase_sorted_key``, but if field is specified, takes the key from ``item[field]``. +* ``utils.search_text(text,search_tokens)`` + + Returns true if all the search tokens are found within ``text``. The text and + search tokens are normalized to lower case and special characters (e.g. ``A`` + with a circle on it) are converted to their "basic" forms (e.g. ``a``). + ``search_tokens`` can be a string or a table of strings. If it is a string, + it is split into space-separated tokens before matching. The search tokens + are treated literally, so any special regular expression characters do not + need to be escaped. If ``utils.FILTER_FULL_TEXT`` is ``true``, then the + search tokens can match any part of ``text``. If it is ``false``, then the + matches must happen at the beginning of words within ``text``. You can change + the value of ``utils.FILTER_FULL_TEXT`` in `gui/control-panel` on the + "Preferences" tab. + * ``utils.call_with_string(obj,methodname,...)`` Allocates a temporary string object, calls ``obj:method(tmp,...)``, and @@ -5291,12 +5305,11 @@ FilteredList class ------------------ This widget combines List, EditField and Label into a combo-box like -construction that allows filtering the list by subwords of its items. +construction that allows filtering the list. In addition to passing through all attributes supported by List, it supports: -:case_sensitive: If ``true``, matching is case sensitive. Defaults to ``false``. :edit_pen: If specified, used instead of ``cursor_pen`` for the edit field. :edit_below: If true, the edit field is placed below the list instead of above. :edit_key: If specified, the edit field is disabled until this key is pressed. @@ -5345,9 +5358,9 @@ Filter behavior: By default, the filter matches substrings that start at the beginning of a word (or after any punctuation). You can instead configure filters to match any -substring with a command like:: +substring across the full text with a command like:: - :lua require('gui.widgets').FILTER_FULL_TEXT=true + :lua require('utils').FILTER_FULL_TEXT=true TabBar class ------------ diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index 05d237f35..61da7af65 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -2017,12 +2017,9 @@ end -- Filtered List -- ------------------- -FILTER_FULL_TEXT = false - FilteredList = defclass(FilteredList, Widget) FilteredList.ATTRS { - case_sensitive = false, edit_below = false, edit_key = DEFAULT_NIL, edit_ignore_keys = DEFAULT_NIL, @@ -2172,7 +2169,6 @@ function FilteredList:setFilter(filter, pos) pos = nil for i,v in ipairs(self.choices) do - local ok = true local search_key = v.search_key if not search_key then if type(v.text) ~= 'table' then @@ -2187,30 +2183,7 @@ function FilteredList:setFilter(filter, pos) search_key = table.concat(texts, ' ') end end - for _,key in ipairs(tokens) do - key = key:escape_pattern() - if key ~= '' then - search_key = dfhack.toSearchNormalized(search_key) - key = dfhack.toSearchNormalized(key) - if not self.case_sensitive then - search_key = string.lower(search_key) - key = string.lower(key) - end - - -- the separate checks for non-space or non-punctuation allows - -- punctuation itself to be matched if that is useful (e.g. - -- filenames or parameter names) - if not FILTER_FULL_TEXT and not search_key:match('%f[^%p\x00]'..key) - and not search_key:match('%f[^%s\x00]'..key) then - ok = false - break - elseif FILTER_FULL_TEXT and not search_key:find(key) then - ok = false - break - end - end - end - if ok then + if utils.search_text(search_key, tokens) then table.insert(choices, v) cidx[#choices] = i if ipos == i then diff --git a/library/lua/utils.lua b/library/lua/utils.lua index 3883439f1..fb41835da 100644 --- a/library/lua/utils.lua +++ b/library/lua/utils.lua @@ -460,6 +460,32 @@ function erase_sorted(vector,item,field,cmp) return erase_sorted_key(vector,key,field,cmp) end +FILTER_FULL_TEXT = false + +function search_text(text, search_tokens) + text = dfhack.toSearchNormalized(text) + if type(search_tokens) ~= 'table' then + search_tokens = search_tokens:split() + end + + for _,search_token in ipairs(search_tokens) do + if search_token == '' then goto continue end + search_token = dfhack.toSearchNormalized(search_token:escape_pattern()) + + -- the separate checks for non-space or non-punctuation allows + -- punctuation itself to be matched if that is useful (e.g. + -- filenames or parameter names) + if not FILTER_FULL_TEXT and not text:match('%f[^%p\x00]'..search_token) + and not text:match('%f[^%s\x00]'..search_token) then + return false + elseif FILTER_FULL_TEXT and not text:find(search_token) then + return false + end + ::continue:: + end + return true +end + -- Calls a method with a string temporary function call_with_string(obj,methodname,...) return dfhack.with_temp_object( diff --git a/plugins/lua/buildingplan/itemselection.lua b/plugins/lua/buildingplan/itemselection.lua index 9dfd0cc69..4b8ee73d8 100644 --- a/plugins/lua/buildingplan/itemselection.lua +++ b/plugins/lua/buildingplan/itemselection.lua @@ -151,7 +151,6 @@ function ItemSelection:init() widgets.FilteredList{ view_id='flist', frame={t=0, b=0}, - case_sensitive=false, choices=choices, icon_width=2, on_submit=self:callback('toggle_group'), From 8184a093d9b0f76a33dbef94c5ec68c00c79f5a4 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 7 Oct 2023 19:00:26 -0700 Subject: [PATCH 6/8] support dynamic onupdate frequency adjustments set to 0 for an immediate burst of high frequency calls --- docs/changelog.txt | 1 + docs/dev/overlay-dev-guide.rst | 5 ++++- plugins/lua/overlay.lua | 6 +++++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 1a97e637d..ba05e6e87 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -61,6 +61,7 @@ Template for new versions: ## Fixes ## Misc Improvements +- `overlay`: allow ``overlay_onupdate_max_freq_seconds`` to be dynamically set to 0 for a burst of high-frequency updates ## Documentation diff --git a/docs/dev/overlay-dev-guide.rst b/docs/dev/overlay-dev-guide.rst index 54e200700..b5b6cf0e3 100644 --- a/docs/dev/overlay-dev-guide.rst +++ b/docs/dev/overlay-dev-guide.rst @@ -135,7 +135,10 @@ The ``overlay.OverlayWidget`` superclass defines the following class attributes: seconds) that your widget can take to react to changes in information and not annoy the player. Set to 0 to be called at the maximum rate. Be aware that running more often than you really need to will impact game FPS, - especially if your widget can run while the game is unpaused. + especially if your widget can run while the game is unpaused. If you change + the value of this attribute dynamically, it may not be noticed until the + previous timeout expires. However, if you need a burst of high-frequency + updates, set it to ``0`` and it will be noticed immediately. Registering a widget with the overlay framework *********************************************** diff --git a/plugins/lua/overlay.lua b/plugins/lua/overlay.lua index 9751561a8..d8b3c4f81 100644 --- a/plugins/lua/overlay.lua +++ b/plugins/lua/overlay.lua @@ -433,8 +433,12 @@ end -- reduces the next call by a small random amount to introduce jitter into the -- widget processing timings 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 + if w.overlay_onupdate_max_freq_seconds ~= 0 and + db_entry.next_update_ms > now_ms + then + return + end db_entry.next_update_ms = get_next_onupdate_timestamp(now_ms, w) if detect_frame_change(w, function() return w:overlay_onupdate(vs) end) then if register_trigger_lock_screen(w:overlay_trigger(), name) then From d0ffd78479e5e6e29d2f08751df7113e0e999e92 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sat, 7 Oct 2023 19:04:19 -0700 Subject: [PATCH 7/8] more focus string details for location selector --- library/modules/Gui.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/library/modules/Gui.cpp b/library/modules/Gui.cpp index 58763262f..50a878070 100644 --- a/library/modules/Gui.cpp +++ b/library/modules/Gui.cpp @@ -556,7 +556,13 @@ DEFINE_GET_FOCUS_STRING_HANDLER(dwarfmode) } if (game->main_interface.location_selector.open) { newFocusString = baseFocus; - newFocusString += "/LocationSelector"; + newFocusString += "/LocationSelector/"; + if (game->main_interface.location_selector.choosing_temple_religious_practice) + newFocusString += "Temple"; + else if (game->main_interface.location_selector.choosing_craft_guild) + newFocusString += "Guildhall"; + else + newFocusString += "Default"; focusStrings.push_back(newFocusString); } if (game->main_interface.location_details.open) { From 53c29a05c96513de26168882af209c6f2ada4aa4 Mon Sep 17 00:00:00 2001 From: DFHack-Urist via GitHub Actions <63161697+DFHack-Urist@users.noreply.github.com> Date: Sun, 8 Oct 2023 02:19:08 +0000 Subject: [PATCH 8/8] Auto-update submodules scripts: master --- scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts b/scripts index 728d90271..0759b6b7d 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 728d902712655592ec4385e88fd36077641ccfb1 +Subproject commit 0759b6b7dde184c3bf36669f92138748a0e2382b