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