Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/changelog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ Template for new versions:

## New Features
- `autodump`: new ``undestroy`` option reverts pending item destruction while the game is still paused
- `stocks`: the overlay's ``collapse all`` hotkey now toggles, expanding all categories again when everything is collapsed

## Fixes
- Fix broken weather lookup in ``World::ReadCurrentWeather``
Expand All @@ -69,6 +70,7 @@ Template for new versions:
- `aquifer`: make ``--skip-top`` and top-relative ``--levels`` take effect for the ``drain``, ``convert``, and ``add`` actions instead of being silently ignored
- `autodump`: ``destroy`` no longer leaves the contents of destroyed containers in limbo or crashes when destroying unit-held items
- `timestream`: deal properly with units who have breathing difficulties
- `stocks`: overlay now resets scroll position when collapsing categories so the item list is no longer left blank and unscrollable

## Misc Improvements
- Added ``Coord2d`` and ``Coord3d`` C++ templates, providing a standard set operations for 2-tuples and 3-tuples of any numeric type
Expand Down
33 changes: 30 additions & 3 deletions plugins/lua/stocks.lua
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,45 @@ local widgets = require('gui.widgets')

local stocks = df.global.game.main_interface.stocks

-- these are only safe to call while the stocks page is open; guard against
-- stray activations as the page closes
local function collapse_all()
if not stocks.open then return end
local num_sections = #stocks.current_type_a_expanded
for idx=0,num_sections-1 do
stocks.current_type_a_expanded[idx] = false
end
stocks.i_height = num_sections * 3
-- the collapsed list is much shorter; reset the scroll position so the
-- view isn't left pointing past the end of the list
stocks.scroll_position_item = 0
end

local function expand_all()
if not stocks.open then return end
local num_sections = #stocks.current_type_a_expanded
for idx=0,num_sections-1 do
stocks.current_type_a_expanded[idx] = true
end
num_items = #stocks.current_type_i_list
local num_items = #stocks.current_type_i_list
stocks.i_height = (num_items + num_sections) * 3
end

local function all_collapsed()
if not stocks.open then return true end
for idx=0,#stocks.current_type_a_expanded-1 do
if stocks.current_type_a_expanded[idx] then return false end
end
return true
end

local function toggle_all()
if not stocks.open then return end
if all_collapsed() then expand_all() else collapse_all() end
end

local function remove_empty()
if not stocks.open then return end
local empties = {}
for itype,v in ipairs(stocks.storeamount) do
if v == 0 and stocks.badamount[itype] == 0 then
Expand All @@ -36,6 +57,10 @@ local function remove_empty()
for idx=#stocks.filtered_type_list-1,0,-1 do
if empties[stocks.filtered_type_list[idx]] then stocks.filtered_type_list:erase(idx) end
end
-- removing types shortens the type list; keep the scroll position in bounds
if stocks.scroll_position_type >= #stocks.filtered_type_list then
stocks.scroll_position_type = math.max(0, #stocks.filtered_type_list - 1)
end
end

-- -------------------
Expand All @@ -57,9 +82,11 @@ function StocksOverlay:init()
self:addviews{
widgets.HotkeyLabel{
frame={t=0, l=0},
label='collapse all',
label=function()
return all_collapsed() and 'expand all' or 'collapse all'
end,
key='CUSTOM_CTRL_X',
on_activate=collapse_all,
on_activate=toggle_all,
},
widgets.HotkeyLabel{
frame={t=1, l=0},
Expand Down
87 changes: 87 additions & 0 deletions test/plugins/stocks.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
config.mode = 'fortress'
config.target = 'overlay'

local gui = require('gui')
local overlay = require('plugins.overlay')

local stocks = df.global.game.main_interface.stocks

-- feed a key to the stocks overlay widget's input handler. This is the same
-- code path the viewscreen's interposed feed() takes; we invoke it directly
-- because the focus string cache only refreshes on game frames, which tests
-- cannot wait for.
local function send_key(key)
overlay.get_state().db['stocks.overlay'].widget:onInput({[key]=true})
end

local function set_stocks_open(open)
if stocks.open ~= open then
gui.simulateInput(dfhack.gui.getDFViewscreen(true), 'D_STOCKS')
end
end

local function expanded_count()
local n = 0
for i=0,#stocks.current_type_a_expanded-1 do
if stocks.current_type_a_expanded[i] then n = n + 1 end
end
return n
end

local saved_open

config.wrapper = function(test_fn)
saved_open = stocks.open
-- the widget db can be empty if a rescan is still in progress
overlay.rescan()
return dfhack.with_finalize(function()
-- close and reopen so the game repopulates any lists the test mutated
set_stocks_open(false)
set_stocks_open(saved_open)
end, test_fn)
end

function test.toggle_collapses_then_expands()
set_stocks_open(true)
local num_sections = #stocks.current_type_a_expanded
expect.true_(num_sections > 0)
for i=0,num_sections-1 do
stocks.current_type_a_expanded[i] = true
end
stocks.scroll_position_item = 5
send_key('CUSTOM_CTRL_X')
expect.eq(0, expanded_count())
-- collapsing resets the item scroll position so it stays in bounds
expect.eq(0, stocks.scroll_position_item)
-- with everything collapsed, the same hotkey expands
send_key('CUSTOM_CTRL_X')
expect.eq(num_sections, expanded_count())
end

function test.expand_hotkey_expands_all()
set_stocks_open(true)
for i=0,#stocks.current_type_a_expanded-1 do
stocks.current_type_a_expanded[i] = false
end
send_key('CUSTOM_CTRL_Z')
expect.eq(#stocks.current_type_a_expanded, expanded_count())
end

function test.remove_empty_keeps_type_scroll_in_bounds()
set_stocks_open(true)
stocks.scroll_position_type = #stocks.filtered_type_list + 10
send_key('CUSTOM_CTRL_E')
expect.true_(stocks.scroll_position_type >= 0)
expect.true_(stocks.scroll_position_type < #stocks.filtered_type_list)
end

function test.closed_stocks_ignores_overlay_keys()
set_stocks_open(false)
-- the stocks.open guard must make every hotkey a no-op while the page is
-- closed, even if an activation slips through as the page closes
local n = expanded_count()
send_key('CUSTOM_CTRL_X')
send_key('CUSTOM_CTRL_Z')
send_key('CUSTOM_CTRL_E')
expect.eq(n, expanded_count())
end
Loading