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
1 change: 1 addition & 0 deletions docs/about/Authors.rst
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ Robert Heinrich rh73
Robert Janetzko robertjanetzko
Rocco Moretti roccomoretti
RocheLimit
Rodrigo Cardoso Buske robuske
rofl0r rofl0r
root
Rose RosaryMala
Expand Down
4 changes: 4 additions & 0 deletions docs/changelog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ Template for new versions:

## New Features

- `orders`: add an inline overlay for viewing and changing manager-order positions

## Fixes

- Fix broken weather lookup in ``World::ReadCurrentWeather``
Expand All @@ -78,6 +80,8 @@ Template for new versions:

## Lua

- `orders`: make work-order list geometry available to Lua overlays

## Removed

# 53.16-r1.1
Expand Down
10 changes: 10 additions & 0 deletions docs/plugins/orders.rst
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,16 @@ useful for when the conditions were true when the order started, but they have
become false and now you're just getting repeated cancellation spam as the
order cannot be fulfilled.

orders.position
~~~~~~~~~~~~~~~

Displays one-based positions beside fort-wide work orders. Use the positions
toggle to show or hide the position fields. Click a position, enter a one-based
destination, and press Enter to move the order.
Modified shortcuts cancel the edit and pass through to DFHack or Dwarf
Fortress. The inline operation is silent when successful and shows invalid
input in an error dialog.

orders.skillrestrictions and orders.laborrestrictions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Expand Down
73 changes: 9 additions & 64 deletions plugins/lua/orders.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ local _ENV = mkmodule('plugins.orders')
local dialogs = require('gui.dialogs')
local gui = require('gui')
local overlay = require('plugins.overlay')
local work_order_list = require('plugins.orders.work_order_list')
local position_overlay = require('plugins.orders.position_overlay')
local textures = require('gui.textures')
local utils = require('utils')
local widgets = require('gui.widgets')
Expand Down Expand Up @@ -714,11 +716,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}
Expand Down Expand Up @@ -756,63 +753,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.',
Expand All @@ -837,6 +777,7 @@ function OrdersSearchOverlay:init()
frame={t=0, l=0},
key='CUSTOM_ALT_S',
on_change=self:callback('update_filter'),
on_focus=position_overlay.clear_active_edit,
on_submit=self:callback('on_submit'),
on_submit2=self:callback('on_submit2'),
},
Expand Down Expand Up @@ -888,6 +829,8 @@ function OrdersSearchOverlay:init()
main_panel,
minimized_panel,
}
position_overlay.bind_orders_search_field(
self.subviews.filter)

self.minimized = false
self.matched_indices = {}
Expand Down Expand Up @@ -952,7 +895,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 =
work_order_list.get_visible_order_indices()
if order_idx < viewport_start or order_idx > viewport_end then
mi.info.work_orders.scroll_position_work_orders = order_idx
end
Expand Down Expand Up @@ -1030,7 +974,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 = work_order_list.get_order_y(match_order_idx)

if match_y then
local pen = (match_order_idx == selected_order_idx) and SELECTED_PEN or MATCH_PEN
Expand All @@ -1047,6 +991,7 @@ end
OVERLAY_WIDGETS = {
recheck=RecheckOverlay,
importexport=OrdersOverlay,
position=position_overlay.PositionOverlay,
search=OrdersSearchOverlay,
skillrestrictions=SkillRestrictionOverlay,
laborrestrictions=LaborRestrictionsOverlay,
Expand Down
75 changes: 75 additions & 0 deletions plugins/lua/orders/position.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
local _ENV = mkmodule('plugins.orders.position')

---@param value string|number
---@param label string
---@return integer|nil position
---@return string|nil error_message
local function parse_position(value, label)
local position
if type(value) == 'number' then
position = value
elseif type(value) == 'string' and value:match('^%d+$') then
position = tonumber(value)
end

position = position and math.tointeger(position)
if not position or position < 1 then
return nil, ('%s must be a positive integer; got %q.'):
format(label, tostring(value))
end

return position
end

--- Validates and moves an existing order pointer within a zero-based vector.
---@param orders df.manager_order[]
---@param current_position string|number One-based current position.
---@param new_value string|number One-based destination position.
---@return df.manager_order|nil order
---@return string|nil error_message
local function move_in_vector(orders, current_position, new_value)
local parsed_current_position, current_error =
parse_position(current_position, 'Current position')
if not parsed_current_position then return nil, current_error end

local new_position, new_error = parse_position(new_value, 'New position')
if not new_position then return nil, new_error end

local order_count = #orders
if parsed_current_position > order_count then
return nil,
('Current position %d is outside the valid range 1..%d.'):
format(parsed_current_position, order_count)
end
if new_position > order_count then
return nil, ('New position %d is outside the valid range 1..%d.'):
format(new_position, order_count)
end

local current_index = parsed_current_position - 1
local order = orders[current_index]
if not order then return nil, 'The selected manager order no longer exists.' end
if parsed_current_position == new_position then return order end

-- DF containers use zero-based indices. Erasing a pointer-vector cell does
-- not delete its pointee, so retain and reinsert the existing order.
orders:erase(current_index)
orders:insert(new_position - 1, order)
return order
end

--- Moves an existing manager-order pointer to a one-based vector position.
---@param current_position string|number
---@param new_value string|number
---@return df.manager_order|nil order
---@return string|nil error_message
function move(current_position, new_value)
return move_in_vector(
df.global.world.manager_orders.all, current_position, new_value)
end

unit_test_hooks = {
move_in_vector = move_in_vector,
}

return _ENV
Loading