Skip to content

Conversation

@pajawojciech
Copy link

@pajawojciech pajawojciech commented Dec 2, 2025

Add search to manager orders

Adds search box to find orders by job name + arrow indicators to show which one you're looking at.

What it does

  • Search box appears on orders screen (Alt+S to focus)
  • Type keywords to filter (e.g. "iron picks" finds "forge iron picks")
  • Multi-word search works in any order ("picks iron" and "pi ir" also matches)
  • Alt+P/N to jump between matches. Also Enter and Shift+Enter works when focused
  • Shows "3 of 12" style counter
  • Arrow point at current match
  • Arrows hide when you: scroll view, edit search or add / remove from orders list

Implementation

What didn't work

  • Tried to clear search input when exiting orders window - couldn't find reliable way to detect window exit

Related issues

order-search.mp4

Adds search overlay to find and navigate manager orders with arrow indicators showing current search result. Search uses Alt+S to focus, Alt+P/N for prev/next navigation. Overlays are disabled by default.
Copy link
Contributor

@ChrisJohnsen ChrisJohnsen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a nice workaround for the inability to filter the view like in the other info panel search widgets.

I like the labeled magic numbers in OrderHighlightOverlay. DF's layout behavior is not fun thing to have to replicate, but I think this would be fairly clear to someone that hasn't looked at this stuff as much (at least as long as they have DF at hand to try out different window sizes).

Re: not being able to detect exiting the window: yeah, I have also wanted something
like a callback for "this overlay is no longer active (not going to be rendered)".


  • Would utils.search_text be useful here? The main SortOverlay uses it for searching.
  • Could the first match automatically be highlighted on Enter?
    I can see how you might not want to highlight (thus likely move the scroll position) for each change in search input, but Enter seems like a nice place to jump to the first match.
  • The highlight should probably be cleared when the search text changes (especially when the highlighted order does not match the new search input).
  • It might just be my color vision being flaky, but I completely did not notice the highlight arrow at first. Now that I know what to look for, it isn't hard to spot. But my first cycling through the matches was confusing because it wasn't obvious which order was being indicated.

@pajawojciech pajawojciech marked this pull request as draft December 6, 2025 13:06
@pajawojciech
Copy link
Author

* Would utils.search_text be useful here? The main SortOverlay uses it for searching.

I switched to using utils.search_text instead of the custom search logic

* Could the first match automatically be highlighted on Enter?

Added Enter/Shift+Enter to cycle through matches using the default submit/submit2 methods.

* The highlight should probably be cleared when the search text changes (especially when the highlighted order does not match the new search input).

Fixed, now the highlight gets cleared whenever the search text changes.

* It might just be my color vision being flaky, but I completely did not notice the highlight arrow at first. Now that I know what to look for, it isn't hard to spot. But my first cycling through the matches was confusing because it wasn't obvious which order was being indicated.

I reshaped the arrow, moved it to the right side of icon and used more contrasting colors (black on white) to make it more visible.
obraz


@ChrisJohnsen Thanks for all the detailed feedback! I made each change in a separate commit to make the review easier. Let me know if there's anything else that needs adjusting.

@pajawojciech pajawojciech marked this pull request as ready for review December 7, 2025 11:18
Copy link
Contributor

@ChrisJohnsen ChrisJohnsen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good spotting of the need to hide when job_details.open. The other functionality changes all seem good (with a new note about order deletion handling).

I've put some more thoughts on the specifics of the code in this review.

Comment on lines +11 to +14
-- Shared state for search cursor visibility
local search_cursor_visible = false
local search_last_scroll_position = -1
local order_count_at_highlight = 0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The first two of these are shadowed later by the locals declared at the top of the OrdersSearchOverlay section. Probably all these search/highlight locals should just be consolidated in that section.

for i = 0, #orders - 1 do
local order = orders[i]
local search_key = get_order_search_key(order)
if search_key and utils.search_text(search_key, text) then
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As it currently is, get_order_search_key has the following returns:

  • some non-falsy value from the reaction map
  • an empty string
    (it can also throw an error if the reaction map couldn't be loaded)

All of the return values are currently "truthy", so they won't affect the if condition here.

Searching an empty search_key will only "succeed" when it is searched for an empty string, which won't happen here because of the guard before the loop. So it all works, but it indicates a misalignment with the definition of get_order_search_key.


Probably it makes the most sense to keep this check and change get_order_search_key to return nil if the reaction map isn't available (guarding against a potential "attempt to index a nil value" error there) or if the map didn't have an entry for the order.

Comment on lines +1019 to +1022
self.cached_list_start_y = nil
self.cached_viewport_size = nil
self.cached_screen_width = nil
self.cached_screen_height = nil
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

None of these appear to be used anywhere?

Comment on lines +1010 to +1015
self.ORDER_HEIGHT = 3
self.TABS_WIDTH_THRESHOLD = 155
self.LIST_START_Y_ONE_TABS_ROW = 8
self.LIST_START_Y_TWO_TABS_ROWS = 10
self.BOTTOM_MARGIN = 9
self.ARROW_X = 10
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These constants don't need to be fields of the overlay. Probably just a collection of module-locals at the top of the OrderHighlightOverlay section would be fine.

Comment on lines +1016 to +1017
self.ARROW_FG = COLOR_BLACK
self.ARROW_BG = COLOR_WHITE
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These values can probably just be inlined into the dfhack.pen.parse call. The table key names there will establish the fg/bg intentions.

function OrderHighlightOverlay:render(dc)
if mi.job_details.open then return end

if search_cursor_visible then
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The indentation isn't getting too bad here, but this might be better as a early-returning guard instead of having the rest of the body of the function tucked into the if block. It makes it clear that nothing else is going to happen for the guarded condition.

E.g.

if not search_cursor_visible then return end

return selected_y
end

function OrderHighlightOverlay:render(dc)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is a good idea bring back an unconditional call to the super render method here. It isn't technically necessary since there is nothing else that needs to be drawn in this overlay (e.g., a frame, sub-widgets, or onRenderFrame/onRenderBody methods), but having it can prevent hard-to-guess-at "why isn't this stuff isn't being drawn?" problems if any of those are added in the future.

encrust_str)
end

local function build_reaction_map()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

build_reaction_map could be inlined into get_cached_reaction_map (the only caller). I don't think there is any need to have the non-caching version available. An early return (if the map is already cached) can be used to limit the nesting in get_cached_reaction_map.

@pajawojciech pajawojciech marked this pull request as draft December 8, 2025 19:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants