diff --git a/docs/about/Authors.rst b/docs/about/Authors.rst index eefd440758..077880c439 100644 --- a/docs/about/Authors.rst +++ b/docs/about/Authors.rst @@ -202,6 +202,7 @@ Robert Heinrich rh73 Robert Janetzko robertjanetzko Rocco Moretti roccomoretti RocheLimit +Rodrigo Cardoso Buske robuske rofl0r rofl0r root Rose RosaryMala diff --git a/docs/changelog.txt b/docs/changelog.txt index 78e8a56cc3..5a18e4be5e 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -87,6 +87,8 @@ Template for new versions: ## Lua +- `orders`: make work-order list geometry available to Lua overlays + ## Removed # 53.16-r1.1 diff --git a/plugins/lua/orders.lua b/plugins/lua/orders.lua index a470e3e2ce..1e1dc5e272 100644 --- a/plugins/lua/orders.lua +++ b/plugins/lua/orders.lua @@ -3,6 +3,7 @@ local _ENV = mkmodule('plugins.orders') local dialogs = require('gui.dialogs') local gui = require('gui') local overlay = require('plugins.overlay') +local workOrderList = require('plugins.orders.work_order_list') local textures = require('gui.textures') local utils = require('utils') local widgets = require('gui.widgets') @@ -714,11 +715,6 @@ end -- OrdersSearchOverlay -- -local ORDER_HEIGHT = 3 -local TABS_WIDTH_THRESHOLD = 155 -local LIST_START_Y_ONE_TABS_ROW = 8 -local LIST_START_Y_TWO_TABS_ROWS = 10 -local BOTTOM_MARGIN = 9 local ARROW_X = 10 local SELECTED_PEN = dfhack.pen.parse{fg=COLOR_BLACK, bg=COLOR_WHITE, bold=true} @@ -756,63 +752,6 @@ local function concat_order_names() return table.concat(names, "|") end -local function getListStartY() - local rect = gui.get_interface_rect() - - if rect.width >= TABS_WIDTH_THRESHOLD then - return LIST_START_Y_ONE_TABS_ROW - else - return LIST_START_Y_TWO_TABS_ROWS - end -end - -local function getViewportSize() - local rect = gui.get_interface_rect() - local list_start_y = getListStartY() - - local available_height = rect.height - list_start_y - BOTTOM_MARGIN - return math.floor(available_height / ORDER_HEIGHT) -end - -local function getVisibleOrderIndices() - local orders = df.global.world.manager_orders.all - local scroll_pos = mi.info.work_orders.scroll_position_work_orders - - if #orders == 0 then return 0, -1 end - - local viewport_size = getViewportSize() - local viewport_start = scroll_pos - local viewport_end = scroll_pos + viewport_size - 1 - - -- Handle end-of-list case - if viewport_end >= #orders then - viewport_end = #orders - 1 - viewport_start = math.max(0, viewport_end - viewport_size + 1) - end - - return viewport_start, viewport_end -end - -local function calculateOrderY(order_idx) - local orders = df.global.world.manager_orders.all - - if #orders == 0 or order_idx < 0 or order_idx >= #orders then - return nil - end - - local viewport_start, viewport_end = getVisibleOrderIndices() - - -- Check if order is in viewport - if order_idx < viewport_start or order_idx > viewport_end then - return nil - end - - local list_start_y = getListStartY() - local pos_in_viewport = order_idx - viewport_start - - return list_start_y + (pos_in_viewport * ORDER_HEIGHT) -end - OrdersSearchOverlay = defclass(OrdersSearchOverlay, overlay.OverlayWidget) OrdersSearchOverlay.ATTRS{ desc='Adds a search box to find and navigate to matching manager orders.', @@ -952,7 +891,8 @@ function OrdersSearchOverlay:cycle_match(direction) -- Scroll to the selected match only if not already visible local order_idx = self.matched_indices[self.current_match_idx] - local viewport_start, viewport_end = getVisibleOrderIndices() + local viewport_start, viewport_end = + workOrderList.getVisibleOrderIndices() if order_idx < viewport_start or order_idx > viewport_end then mi.info.work_orders.scroll_position_work_orders = order_idx end @@ -1030,7 +970,7 @@ function OrdersSearchOverlay:render_highlights(dc) self.matched_indices[self.current_match_idx] or nil for _, match_order_idx in ipairs(self.matched_indices) do - local match_y = calculateOrderY(match_order_idx) + local match_y = workOrderList.getOrderY(match_order_idx) if match_y then local pen = (match_order_idx == selected_order_idx) and SELECTED_PEN or MATCH_PEN diff --git a/plugins/lua/orders/work_order_list.lua b/plugins/lua/orders/work_order_list.lua new file mode 100644 index 0000000000..f86bbfc029 --- /dev/null +++ b/plugins/lua/orders/work_order_list.lua @@ -0,0 +1,90 @@ +local _ENV = mkmodule('plugins.orders.work_order_list') + +local gui = require('gui') + +ORDER_HEIGHT = 3 + +local TABS_WIDTH_THRESHOLD = 155 +local LIST_START_Y_ONE_TABS_ROW = 8 +local LIST_START_Y_TWO_TABS_ROWS = 10 +local BOTTOM_MARGIN = 9 + +---@param interface_width integer +---@return integer +local function calculateListStartY(interface_width) + if interface_width >= TABS_WIDTH_THRESHOLD then + return LIST_START_Y_ONE_TABS_ROW + end + return LIST_START_Y_TWO_TABS_ROWS +end + +---@param interface_height integer +---@param list_start_y integer +---@return integer +local function calculateViewportSize(interface_height, list_start_y) + local available_height = interface_height - list_start_y - BOTTOM_MARGIN + return math.floor(available_height / ORDER_HEIGHT) +end + +---@param order_count integer +---@param viewport_size integer +---@param requested_start integer +---@return integer viewport_start +---@return integer viewport_end +local function calculateVisibleOrderIndices( + order_count, viewport_size, requested_start) + if order_count == 0 then return 0, -1 end + + local viewport_start = requested_start + local viewport_end = requested_start + viewport_size - 1 + + -- Handle end-of-list case + if viewport_end >= order_count then + viewport_end = order_count - 1 + viewport_start = math.max(0, viewport_end - viewport_size + 1) + end + return viewport_start, viewport_end +end + +---@return integer +function getListStartY() + return calculateListStartY(gui.get_interface_rect().width) +end + +---@return integer +function getViewportSize() + return calculateViewportSize( + gui.get_interface_rect().height, getListStartY()) +end + +---@return integer viewport_start +---@return integer viewport_end +function getVisibleOrderIndices() + local order_count = #df.global.world.manager_orders.all + local requested_start = df.global.game.main_interface.info.work_orders + .scroll_position_work_orders + return calculateVisibleOrderIndices( + order_count, getViewportSize(), requested_start) +end + +---@param order_idx integer +---@return integer|nil y +function getOrderY(order_idx) + local orders = df.global.world.manager_orders.all + if order_idx < 0 or order_idx >= #orders then return nil end + + local viewport_start, viewport_end = getVisibleOrderIndices() + + -- Check if order is in viewport + if order_idx < viewport_start or order_idx > viewport_end then return nil end + + return getListStartY() + (order_idx - viewport_start) * ORDER_HEIGHT +end + +unitTestHooks = { + calculateListStartY = calculateListStartY, + calculateViewportSize = calculateViewportSize, + calculateVisibleOrderIndices = calculateVisibleOrderIndices, +} + +return _ENV diff --git a/test/plugins/orders.lua b/test/plugins/orders.lua index dab396ba96..74d3720159 100644 --- a/test/plugins/orders.lua +++ b/test/plugins/orders.lua @@ -1,6 +1,8 @@ config.mode = 'fortress' config.target = 'orders' +local workOrderList = require('plugins.orders.work_order_list') + local FILE_PATH_PATTERN = dfhack.getConfigPath() .. '/orders/%s.json' local BACKUP_FILE_NAME = 'tmp-backup' @@ -266,3 +268,31 @@ function test.list() expect.eq(CR_OK, status) expect.str_find(BACKUP_FILE_NAME:gsub('%-', '%%-'), output) end + +function test.work_order_list_geometry() + local hooks = workOrderList.unitTestHooks + + expect.eq(10, hooks.calculateListStartY(154)) + expect.eq(8, hooks.calculateListStartY(155)) + expect.eq(4, hooks.calculateViewportSize(30, 8)) + + local viewport_start, viewport_end = + hooks.calculateVisibleOrderIndices(0, 4, 0) + expect.eq(0, viewport_start) + expect.eq(-1, viewport_end) + + viewport_start, viewport_end = + hooks.calculateVisibleOrderIndices(10, 4, 2) + expect.eq(2, viewport_start) + expect.eq(5, viewport_end) + + viewport_start, viewport_end = + hooks.calculateVisibleOrderIndices(10, 4, 8) + expect.eq(6, viewport_start) + expect.eq(9, viewport_end) + + viewport_start, viewport_end = + hooks.calculateVisibleOrderIndices(10, 20, 8) + expect.eq(0, viewport_start) + expect.eq(9, viewport_end) +end