Skip to content

feat(ui): Remake of Advanced Search#30

Merged
glaszczkoldre merged 6 commits into
mainfrom
New_search
Jun 4, 2026
Merged

feat(ui): Remake of Advanced Search#30
glaszczkoldre merged 6 commits into
mainfrom
New_search

Conversation

@glaszczkoldre

@glaszczkoldre glaszczkoldre commented Jun 4, 2026

Copy link
Copy Markdown
Collaborator

Summary by CodeRabbit

  • Refactor

    • Consolidated search into the Advanced Search dialog; quick-search popup and related service removed.
    • Advanced Search now shows results as a preview grid of cards with selection and double-click-to-search.
    • Ctrl+F opens Advanced Search; Ctrl+Shift+F toggles the Search Results widget.
  • UI

    • Menu entry "Find Items..." uses Ctrl+F and updated tooltip.
    • Search Results shows a “Searching...” state and improved empty-state messages.

Redesigned the Advanced Search dialog from a 4-column table into a more efficient 2-column layout with a dedicated filter panel and results view. Cleaned up rendering code, improved layout responsiveness, and streamlined the filter organization.
- `ImguiMapEditor/UI/Dialogs/AdvancedSearchDialog.cpp` — refactored UI into a 2-column layout with separate render functions for filters, updated bottom bar, and cleaned up rendering logic
- `ImguiMapEditor/UI/Dialogs/AdvancedSearchDialog.h` — updated class structure, removed old column renderers, added new section-based render methods
Refactored the advanced search dialog UI, including improved result rendering with clipper support, updated layout, and modernized styling while removing unused dependencies.
- `ImguiMapEditor/Controllers/SearchController.cpp` — removed unused `setItemPickerService` call from `onMapLoaded`
- `ImguiMapEditor/UI/Dialogs/AdvancedSearchDialog.cpp` — implemented `ImGuiListClipper` for search results, modernized dialog layout with `SeparatorText` and bordered footer, added `std::format` for label generation, and fixed tooltip/button handling
- `ImguiMapEditor/UI/Dialogs/AdvancedSearchDialog.h` — removed unused `AppLogic::ItemPickerService` dependency and associated methods/state variables
Added state-based 'Searching...' UI feedback to AdvancedSearchDialog and SearchResultsWidget, and refactored the search results preview into a grid layout with card-based rendering.
- `ImguiMapEditor/UI/Dialogs/AdvancedSearchDialog.cpp` — added deferred search execution, state-based empty messages, grid layout for results, and search sorting logic
- `ImguiMapEditor/UI/Dialogs/AdvancedSearchDialog.h` — added search state tracking flags and search execution method
- `ImguiMapEditor/UI/Widgets/SearchResultsWidget.cpp` — added searching state feedback and setter methods for search buffer and searching flag
- `ImguiMapEditor/UI/Widgets/SearchResultsWidget.h` — added search state flag and setter interfaces
Removed the redundant QuickSearchPopup component and its associated ItemPickerService to consolidate search functionality into the Advanced Search dialog.
- `ImguiMapEditor/Application.cpp` — removed `quick_search` component wiring
- `ImguiMapEditor/Application/CallbackMediator.cpp` — redirected Ctrl+F to open Advanced Search, removed quick search logic
- `ImguiMapEditor/Application/CallbackMediator.h` — removed `ItemPickerService` and `QuickSearchPopup` forward declarations
- `ImguiMapEditor/Application/Frame/RenderOrchestrator.cpp` — removed `quick_search_popup` rendering
- `ImguiMapEditor/Application/Frame/RenderOrchestrator.h` — removed `quick_search_popup` from context
- `ImguiMapEditor/CMakeLists.txt` — removed `ItemPickerService.cpp` and `QuickSearchPopup.cpp` from build
- `ImguiMapEditor/Controllers/SearchController.cpp` — removed `QuickSearchPopup` and `ItemPickerService` initialization and usage
- `ImguiMapEditor/Controllers/SearchController.h` — removed `ItemPickerService` header and `QuickSearchPopup` UI component
- `ImguiMapEditor/Presentation/MenuBar.cpp` — removed Quick Find menu item and updated Find Items shortcut
- `ImguiMapEditor/Presentation/MenuBar.h` — removed `on_quick_find_` callback
- `ImguiMapEditor/Services/ItemPickerService.cpp` — [deleted] removed service
- `ImguiMapEditor/Services/ItemPickerService.h` — [deleted] removed service header
- `ImguiMapEditor/UI/Widgets/QuickSearchPopup.cpp` — [deleted] removed UI widget
- `ImguiMapEditor/UI/Widgets/QuickSearchPopup.h` — [deleted] removed UI widget header
@coderabbitai

coderabbitai Bot commented Jun 4, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: ac9a395c-8f11-4f34-b3f6-0c4f35a8ca01

📥 Commits

Reviewing files that changed from the base of the PR and between 5d0a512 and 3baa705.

📒 Files selected for processing (2)
  • ImguiMapEditor/UI/Dialogs/AdvancedSearchDialog.cpp
  • ImguiMapEditor/UI/Dialogs/AdvancedSearchDialog.h

Walkthrough

This PR removes the QuickSearchPopup widget and ItemPickerService, consolidating search functionality into the AdvancedSearchDialog. Ctrl+F hotkey is remapped to open the advanced search dialog instead of the quick popup. The AdvancedSearchDialog is refactored with a grid-based preview UI replacing the previous list layout, implements deferred search execution via a new executeMapSearch() method, and reorganizes filter controls into dedicated render sections. SearchResultsWidget gains is_searching_ state tracking to display search progress. All callback wiring in Application, CallbackMediator, RenderOrchestrator, and MenuBar is updated to remove quick-search references and ensure consistent hotkey/menu integration.

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 8.57% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(ui): Remake of Advanced Search' accurately reflects the main changes: removing the QuickSearchPopup component and significantly refactoring the AdvancedSearchDialog with a new 2-column grid layout for search results.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

@sourcery-ai

sourcery-ai Bot commented Jun 4, 2026

Copy link
Copy Markdown

Reviewer's Guide

Refactors the Advanced Search feature into a two-panel layout with improved state handling and result rendering, wires it to Ctrl+F and the Search menu as the primary catalog search, removes the legacy QuickSearchPopup and ItemPickerService, and enhances SearchResultsWidget with explicit searching state and messaging.

File-Level Changes

Change Details Files
Redesigned Advanced Search dialog UI and UX, including layout, filters, results rendering, and footer actions.
  • Replaced 4-column ImGui table layout with two bordered child panels for filters (left) and results (right), plus a bordered footer with centered buttons.
  • Split filter rendering into dedicated sections (Search, OR types, AND properties, Hints) using ImGui::SeparatorText and two-column checkbox tables to better group type and property filters.
  • Reimplemented result rendering from a vertical list with text and copy buttons into a responsive icon grid with 32x32 clickable cards, rounded backgrounds, hover tooltips with larger previews, and state-based empty messaging (idle, no filters, no matches, searching).
  • Added deferred map search execution via is_searching_ and pending_map_search_ flags so that the UI can show a "Searching..." state in both AdvancedSearchDialog and SearchResultsWidget before running the map search.
  • Changed footer layout to show result count on the left and compact right-aligned buttons (Search Map, disabled Select Item placeholder, Cancel) without fixed widths.
ImguiMapEditor/UI/Dialogs/AdvancedSearchDialog.cpp
ImguiMapEditor/UI/Dialogs/AdvancedSearchDialog.h
Adjusted search behavior and result ordering in Advanced Search and SearchResultsWidget to better communicate searching state and result semantics.
  • Updated updatePreviewResults to early-return only when both query and all filters are empty, and to sort preview results by item server_id ascending with creatures last.
  • Introduced explicit is_searching_ flag in AdvancedSearchDialog to drive empty-state messaging for results and to coordinate with SearchResultsWidget when a map search is initiated.
  • Extended SearchResultsWidget with is_searching_ state, a setter for it, and a setter for the search_buffer so that it can display appropriate messages (Searching..., No results found, Type to search...) and be pre-populated from AdvancedSearchDialog.
  • Ensured SearchResultsWidget clears is_searching_ in setResults and clear, and sets it to true in doSearch before invoking the async search callback.
ImguiMapEditor/UI/Dialogs/AdvancedSearchDialog.cpp
ImguiMapEditor/UI/Dialogs/AdvancedSearchDialog.h
ImguiMapEditor/UI/Widgets/SearchResultsWidget.cpp
ImguiMapEditor/UI/Widgets/SearchResultsWidget.h
Rewired keyboard shortcuts and menu actions so Advanced Search replaces the old Quick Search as the primary catalog search entry point.
  • Changed CallbackMediator wiring to open AdvancedSearchDialog from the Search menu "Find Items..." action and from the hotkey quick search callback (Ctrl+F), while keeping Ctrl+Shift+F mapped to show the SearchResultsWidget.
  • Updated MenuBar search menu to remove "Quick Find" and repurpose "Find Items..." to use the QUICK_SEARCH shortcut label and a tooltip describing filtered catalog search.
  • Adjusted RenderOrchestrator to render only the AdvancedSearchDialog (as Ctrl+F) and removed references to QuickSearchPopup in the dialog rendering pipeline.
  • Updated Application and related context structs to pass around only AdvancedSearchDialog and SearchResultsWidget pointers for search UI components.
ImguiMapEditor/Application/CallbackMediator.cpp
ImguiMapEditor/Application/CallbackMediator.h
ImguiMapEditor/Presentation/MenuBar.cpp
ImguiMapEditor/Presentation/MenuBar.h
ImguiMapEditor/Application/Frame/RenderOrchestrator.cpp
ImguiMapEditor/Application/Frame/RenderOrchestrator.h
ImguiMapEditor/Application.cpp
Removed legacy QuickSearchPopup and ItemPickerService infrastructure and simplified SearchController wiring.
  • Deleted ItemPickerService and QuickSearchPopup source and header files, and removed them from CMakeLists.txt build configuration.
  • Simplified SearchController by removing QuickSearchPopup management and ItemPickerService wiring, leaving only AdvancedSearchDialog and SearchResultsWidget as UI components and MapSearchService as the backing service.
  • Cleaned up CallbackMediator, RenderOrchestrator, and related context structs to drop QuickSearchPopup and ItemPickerService references and fields.
ImguiMapEditor/Services/ItemPickerService.cpp
ImguiMapEditor/Services/ItemPickerService.h
ImguiMapEditor/UI/Widgets/QuickSearchPopup.cpp
ImguiMapEditor/UI/Widgets/QuickSearchPopup.h
ImguiMapEditor/Controllers/SearchController.cpp
ImguiMapEditor/Controllers/SearchController.h
ImguiMapEditor/Application/CallbackMediator.cpp
ImguiMapEditor/Application/CallbackMediator.h
ImguiMapEditor/Application/Frame/RenderOrchestrator.cpp
ImguiMapEditor/Application/Frame/RenderOrchestrator.h
ImguiMapEditor/Application.cpp
ImguiMapEditor/CMakeLists.txt

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@github-actions github-actions Bot added Type: Enhancement New feature or request. Area: UI Changes to the wxWidgets GUI (palettes, dialogs, menus, toolbar). labels Jun 4, 2026

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey - I've found 1 issue, and left some high level feedback:

  • In AdvancedSearchDialog::updatePreviewResults, the condition if (search_items && search_service_ && !property_filter_.hasAnySelected() || !type_filter_.creature) relies on &&/|| precedence and is hard to read; consider adding parentheses to make the intended logic explicit (e.g., if ((search_items && search_service_ && !property_filter_.hasAnySelected()) || !type_filter_.creature) or similar).
  • In the sorting lambda for preview_results_, the server ID is narrowed to uint16_t (uint16_t a_id = ...); if server IDs can exceed 65535, consider using the original wider type to avoid silent truncation affecting sort order.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `AdvancedSearchDialog::updatePreviewResults`, the condition `if (search_items && search_service_ && !property_filter_.hasAnySelected() || !type_filter_.creature)` relies on `&&`/`||` precedence and is hard to read; consider adding parentheses to make the intended logic explicit (e.g., `if ((search_items && search_service_ && !property_filter_.hasAnySelected()) || !type_filter_.creature)` or similar).
- In the sorting lambda for `preview_results_`, the server ID is narrowed to `uint16_t` (`uint16_t a_id = ...`); if server IDs can exceed 65535, consider using the original wider type to avoid silent truncation affecting sort order.

## Individual Comments

### Comment 1
<location path="ImguiMapEditor/UI/Dialogs/AdvancedSearchDialog.cpp" line_range="401-407" />
<code_context>
-    // Search items (only if not creature-only filter)
+
     bool search_items = !type_filter_.creature || type_filter_.hasAnySelected();
     if (search_items && search_service_ && !property_filter_.hasAnySelected() || !type_filter_.creature) {
-        // If only creature is selected, skip item search
-        if (!(type_filter_.creature && !type_filter_.depot && !type_filter_.mailbox && 
+        if (!(type_filter_.creature && !type_filter_.depot && !type_filter_.mailbox &&
               !type_filter_.trash_holder && !type_filter_.container && !type_filter_.door &&
               !type_filter_.magic_field && !type_filter_.teleport && !type_filter_.bed &&
               !type_filter_.key && !type_filter_.podium)) {
-            
+
             auto item_results = search_service_->searchItemDatabase(
-                search_buffer_,
-                type_filter_,
</code_context>
<issue_to_address>
**issue (bug_risk):** Clarify boolean logic in item search condition to avoid unintended behavior.

The condition `if (search_items && search_service_ && !property_filter_.hasAnySelected() || !type_filter_.creature)` relies on `&&`/`||` precedence and is effectively `if ((search_items && search_service_ && !property_filter_.hasAnySelected()) || !type_filter_.creature)`. This allows the body to run whenever `!type_filter_.creature` is true, even if `search_items` is false or `search_service_` is null (which would then be dereferenced). Please add explicit grouping (e.g. `if (search_service_ && search_items && !property_filter_.hasAnySelected())`) and handle the `!type_filter_.creature` case separately if it’s intended as a special override.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread ImguiMapEditor/UI/Dialogs/AdvancedSearchDialog.cpp Outdated
@codacy-production

codacy-production Bot commented Jun 4, 2026

Copy link
Copy Markdown

Not up to standards ⛔

🔴 Issues 5 medium

Alerts:
⚠ 5 issues (≤ 0 issues of at least minor severity)

Results:
5 new issues

Category Results
BestPractice 1 medium
ErrorProne 1 medium
Complexity 3 medium

View in Codacy

🟢 Metrics 17 complexity · 0 duplication

Metric Results
Complexity 17
Duplication 0

View in Codacy

AI Reviewer: first review requested successfully. AI can make mistakes. Always validate suggestions.

Run reviewer

TIP This summary will be updated as you push new changes.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📋 Review Summary

This PR successfully remakes the Advanced Search dialog, replacing the older Quick Search popup with a more robust 2-column layout and grid-based results with sprite previews. It also improves the Search Results Widget by adding a "Searching..." state and better empty state handling.

🔍 General Feedback

  • Architecture: The removal of ItemPickerService and the consolidation of search logic into MapSearchService and AdvancedSearchDialog is a good simplification.
  • UI/UX: The new grid-based results with tooltips and double-click to search map is a significant improvement over the old list-based quick search.
  • Correctness: Found one logical bug in updatePreviewResults that could prevent item results from showing when both creature and property filters are active.
  • Performance: The grid uses ImGuiListClipper, which is good for performance with large result sets.

I've left a few inline comments regarding the logic bug and some minor UI polish opportunities.


// Load sprite texture
uint32_t tex_id = 0;
bool sprite_rendered = false;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔴 Bug in logic: This condition incorrectly skips item search if a creature filter is selected alongside any property filter, even if item types (like Depot) are also selected.

For example, if the user selects Type: Depot and Type: Creature and Property: Unpassable, they expect to see Unpassable Depots. However, because !type_filter_.creature is false and !property_filter_.hasAnySelected() is false, the entire condition evaluates to false, and no items are searched.

The item search should be performed if at least one item type is selected (or no types are selected) regardless of whether the creature filter is active, as searchItemDatabase correctly handles the property filtering.

Suggested change
bool sprite_rendered = false;
bool has_item_type_selected = type_filter_.hasAnySelected();
// ... search_service_->searchItemDatabase already handles filtering items by type and property.
if (search_service_) {

(Wait, I should check if hasAnySelected includes creature. If it does, then the suggested check is type_filter_.hasAnySelected()).
Actually, the inner check already filters out the "only creature" case. So you can probably just simplify this to if (search_service_) {.

ImGui::SetClipboardText(std::to_string(result.item->server_id).c_str());
Presentation::showSuccess("Item ID copied");
// Grid flow
if (col < columns - 1 && idx + 1 < static_cast<int>(preview_results_.size())) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 The is_searching_ flag in the widget is set to true here, but AdvancedSearchDialog::executeMapSearch (called next frame) will immediately call results_widget_->setResults(), which sets is_searching_ back to false before the widget even renders its next frame. Similar to the dialog's own is_searching_ flag, this deferred logic doesn't actually result in a visible "Searching..." state for synchronous searches.

executeMapSearch();
}

ImGui::SetNextWindowSize(ImVec2(850, 600), ImGuiCond_Once);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 The "Searching..." state will never be rendered because is_searching_ is set to false immediately before calling executeMapSearch() and then rendering the rest of the frame.

To fix this, executeMapSearch() should be deferred by one more frame, or is_searching_ should only be set to false AFTER the search completes (e.g. at the end of executeMapSearch).

However, since the search is synchronous and blocks the main thread, the user still won't see the spinner until the next frame anyway. If the search is fast, this doesn't matter much.


// Tooltip on hover
if (hovered) {
ImGui::BeginTooltip();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Sorting by uint16_t might be fragile if server IDs ever exceed 65535 (though unlikely in current OT protocol). Using int or uint32_t for a_id/b_id would be safer. Also, using a larger constant than UINT16_MAX for creatures (like 0xFFFFFFFF) would ensure they always stay at the end.

@codacy-production codacy-production Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull Request Overview

The remake of the Advanced Search dialog is currently not up to standards. A critical logic error in the filtering condition will cause a application crash when searching for items because operator precedence allows the code to bypass a null check on the search service. Furthermore, the 'Searching...' state implementation is functionally broken as it executes a blocking search on the main thread before the UI can render the updated state, resulting in a frame hitch rather than a progress indicator.

The PR also introduces a feature regression by removing the 'Copy' utility for IDs/Names and includes incomplete 'Select Item' functionality. Security-wise, the use of strncpy in the search widget should be replaced with safer alternatives to comply with secure coding standards.

About this PR

  • The PR description is empty. Please provide context regarding the design requirements and confirm if the removal of the 'Copy ID/Name' feature was an intentional design decision.
  • The 'Select Item' button is currently a disabled placeholder. If this feature is out of scope for this PR, consider removing the UI element until the backend logic is ready to avoid user confusion.

Test suggestions

  • Search with name or ID correctly filters the preview results list
  • Applying multiple type filters (OR) broadens the result set
  • Applying property filters (AND) narrows the result set
  • Double-clicking a result item triggers a map search through the SearchService
  • Deferred search logic correctly triggers 'is_searching' state before executing the search
  • Sorting correctly handles creatures by moving them to the end of the results
Prompt proposal for missing tests
Consider implementing these tests if applicable:
1. Search with name or ID correctly filters the preview results list
2. Applying multiple type filters (OR) broadens the result set
3. Applying property filters (AND) narrows the result set
4. Double-clicking a result item triggers a map search through the SearchService
5. Deferred search logic correctly triggers 'is_searching' state before executing the search
6. Sorting correctly handles creatures by moving them to the end of the results

TIP Improve review quality by adding custom instructions
TIP How was this review? Give us feedback

// Search items (only if not creature-only filter)

bool search_items = !type_filter_.creature || type_filter_.hasAnySelected();
if (search_items && search_service_ && !property_filter_.hasAnySelected() || !type_filter_.creature) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔴 HIGH RISK

Critical: Potential null pointer dereference due to operator precedence. If type_filter_.creature is false, the search_service_ null check is bypassed. Wrap the search service and property filter checks in parentheses or add an explicit guard: (search_items && search_service_ && !property_filter_.hasAnySelected()) || !type_filter_.creature still risks the crash if the right side is true; the logic should likely be search_service_ && ((search_items && !property_filter_.hasAnySelected()) || !type_filter_.creature).


void setResults(const std::vector<Domain::Search::MapSearchResult>& results);
void setSearching(bool searching) { is_searching_ = searching; }
void setSearchBuffer(const char* query) { strncpy(search_buffer_, query, sizeof(search_buffer_) - 1); search_buffer_[sizeof(search_buffer_) - 1] = '\0'; }

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔴 HIGH RISK

Suggestion: The use of strncpy is discouraged as it is error-prone regarding null-termination. Use snprintf or a safer string-copy alternative to prevent potential buffer overflows.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@ImguiMapEditor/Application/CallbackMediator.cpp`:
- Around line 542-545: The hotkey handler registered via
ctx.hotkey->setAdvancedSearchCallback currently forces vs->show_search_results =
true so it never closes; change the lambda in that registration (the closure
capturing vs from ctx.view_settings) to toggle the flag instead (set
vs->show_search_results to its negation) so Ctrl+Shift+F both opens and closes
the Search Results widget.

In `@ImguiMapEditor/UI/Dialogs/AdvancedSearchDialog.cpp`:
- Around line 467-475: AdvancedSearchDialog::render currently calls
executeMapSearch which invokes MapSearchService::search() inline (blocking the
UI); replace those synchronous calls so the creature/item branches use the
service's asynchronous search path (the same async API used elsewhere) instead
of MapSearchService::search(). Keep setting
results_widget_->setSearchBuffer(...) as before, invoke the async search on
search_service_ with the same search string and mode (for creature name and item
server_id), and wire the async completion callback to populate the local results
variable and update the results_widget_ (and cancel any prior async search if
the service supports cancellation) so the map scan runs off the render thread
rather than synchronously in executeMapSearch/AdvancedSearchDialog::render.
- Around line 400-408: The if-condition that gates calling
search_service_->searchItemDatabase can be true when search_service_ is null
because of operator precedence and the trailing "!type_filter_.creature" term;
update the condition so search_service_ is always required before dereferencing
(e.g. group the left-side checks with parentheses) and also extend the
creature-only exclusion list to include type_filter_.weapon, type_filter_.armor,
type_filter_.ammo, and type_filter_.rune so creature-only cases still suppress
item previews; specifically modify the boolean expression that uses
search_items, search_service_, property_filter_.hasAnySelected(), and all
type_filter_ flags guarding the call to search_service_->searchItemDatabase to
ensure (search_items && search_service_ && !property_filter_.hasAnySelected())
is evaluated together and the creature-only check includes
weapon/armor/ammo/rune.

In `@ImguiMapEditor/UI/Widgets/SearchResultsWidget.cpp`:
- Around line 434-437: Currently the widget sets is_searching_ = true before
checking on_search_async_, which leaves the widget stuck in searching state if
the callback isn't wired (SearchController::onMapLoaded). Change the logic so
you only set is_searching_ to true when on_search_async_ is valid: check
on_search_async_ first and then set is_searching_ and call
on_search_async_(search_buffer_, search_items_, search_creatures_); otherwise
ensure is_searching_ is explicitly set to false (or early-return) so the flag is
never left true when no backend callback exists.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: c190ec71-08fd-48e9-8ef2-9a690ea687ea

📥 Commits

Reviewing files that changed from the base of the PR and between e98957a and f34a8a1.

📒 Files selected for processing (18)
  • ImguiMapEditor/Application.cpp
  • ImguiMapEditor/Application/CallbackMediator.cpp
  • ImguiMapEditor/Application/CallbackMediator.h
  • ImguiMapEditor/Application/Frame/RenderOrchestrator.cpp
  • ImguiMapEditor/Application/Frame/RenderOrchestrator.h
  • ImguiMapEditor/CMakeLists.txt
  • ImguiMapEditor/Controllers/SearchController.cpp
  • ImguiMapEditor/Controllers/SearchController.h
  • ImguiMapEditor/Presentation/MenuBar.cpp
  • ImguiMapEditor/Presentation/MenuBar.h
  • ImguiMapEditor/Services/ItemPickerService.cpp
  • ImguiMapEditor/Services/ItemPickerService.h
  • ImguiMapEditor/UI/Dialogs/AdvancedSearchDialog.cpp
  • ImguiMapEditor/UI/Dialogs/AdvancedSearchDialog.h
  • ImguiMapEditor/UI/Widgets/QuickSearchPopup.cpp
  • ImguiMapEditor/UI/Widgets/QuickSearchPopup.h
  • ImguiMapEditor/UI/Widgets/SearchResultsWidget.cpp
  • ImguiMapEditor/UI/Widgets/SearchResultsWidget.h
💤 Files with no reviewable changes (10)
  • ImguiMapEditor/Services/ItemPickerService.cpp
  • ImguiMapEditor/Controllers/SearchController.h
  • ImguiMapEditor/UI/Widgets/QuickSearchPopup.h
  • ImguiMapEditor/CMakeLists.txt
  • ImguiMapEditor/Application.cpp
  • ImguiMapEditor/Services/ItemPickerService.h
  • ImguiMapEditor/Application/Frame/RenderOrchestrator.h
  • ImguiMapEditor/UI/Widgets/QuickSearchPopup.cpp
  • ImguiMapEditor/Application/CallbackMediator.h
  • ImguiMapEditor/Presentation/MenuBar.h

Comment thread ImguiMapEditor/Application/CallbackMediator.cpp Outdated
Comment thread ImguiMapEditor/UI/Dialogs/AdvancedSearchDialog.cpp Outdated
Comment on lines 467 to 475
if (selected.is_creature && selected.creature) {
// Search map for this creature by name
results = search_service_->search(
selected.creature->name,
Services::MapSearchMode::ByName,
false, // search_items
true, // search_creatures
1000
);
// Set widget search buffer so empty state shows "No results found"
if (results_widget_) results_widget_->setSearchBuffer(selected.creature->name.c_str());
results = search_service_->search(selected.creature->name, Services::MapSearchMode::ByName, false, true, 1000);
} else if (selected.item) {
// Search map for this item by server ID
results = search_service_->search(
std::to_string(selected.item->server_id),
Services::MapSearchMode::ByServerId,
true, // search_items
false, // search_creatures
1000
);
}

// Output to SearchResultsWidget
if (results_widget_) {
results_widget_->setResults(results);
}

// Auto-show the search results widget
if (view_settings_) {
*view_settings_ = true;
auto id_str = std::to_string(selected.item->server_id);
if (results_widget_) results_widget_->setSearchBuffer(id_str.c_str());
results = search_service_->search(id_str, Services::MapSearchMode::ByServerId, true, false, 1000);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Don't scan the map synchronously from the render loop.

executeMapSearch() is called from AdvancedSearchDialog::render(), but it performs MapSearchService::search() inline. That search walks the map, so on large maps this still blocks the UI thread right after the one-frame "Searching..." state. Please route this through the existing async search path instead of calling search() directly here.

🧰 Tools
🪛 Cppcheck (2.20.0)

[style] 467-467: The function 'forEachTileOnFloor' is never used.

(unusedFunction)


[style] 474-474: The function 'forEachTileOnFloorMutable' is never used.

(unusedFunction)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ImguiMapEditor/UI/Dialogs/AdvancedSearchDialog.cpp` around lines 467 - 475,
AdvancedSearchDialog::render currently calls executeMapSearch which invokes
MapSearchService::search() inline (blocking the UI); replace those synchronous
calls so the creature/item branches use the service's asynchronous search path
(the same async API used elsewhere) instead of MapSearchService::search(). Keep
setting results_widget_->setSearchBuffer(...) as before, invoke the async search
on search_service_ with the same search string and mode (for creature name and
item server_id), and wire the async completion callback to populate the local
results variable and update the results_widget_ (and cancel any prior async
search if the service supports cancellation) so the map scan runs off the render
thread rather than synchronously in
executeMapSearch/AdvancedSearchDialog::render.

Comment thread ImguiMapEditor/UI/Widgets/SearchResultsWidget.cpp Outdated
Refactored search toggling behavior, improved safety in async search execution, and refined item database filtering to properly exclude creature-only searches. Replaced deprecated string copying in search buffer management.
- `ImguiMapEditor/Application/CallbackMediator.cpp` — changed search results toggle from forced show to toggle behavior
- `ImguiMapEditor/UI/Dialogs/AdvancedSearchDialog.cpp` — expanded item exclusion logic to correctly skip item search when only creature or combat types are selected
- `ImguiMapEditor/UI/Widgets/SearchResultsWidget.cpp` — added null check for `on_search_async_` before execution
- `ImguiMapEditor/UI/Widgets/SearchResultsWidget.h` — replaced `strncpy` with `snprintf` for safer buffer writing

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📋 Review Summary

The PR successfully refactors the search functionality by consolidating the "Quick Search" and "Advanced Search" into a single, unified experience. The redesign of the AdvancedSearchDialog into a 2-column layout with a results grid is a significant UX improvement, and the use of ImGuiListClipper ensures it remains performant even with large result sets.

However, there are some concerns regarding the performance of the map search, which is currently executed synchronously on the main thread, potentially leading to UI freezes. Additionally, the handling of the "Searching..." state has some minor logical flaws that might prevent the spinner from being visible.

🔍 General Feedback

  • Performance: offload executeMapSearch to a background thread to prevent blocking the UI during map-wide searches.
  • UX: The grid layout is excellent, but a slightly larger cell size might help in identifying creatures and complex items.
  • Consistency: Consider unifying the async search patterns across SearchController, AdvancedSearchDialog, and SearchResultsWidget.

Great job on the UI overhaul! 🚀

if (ImGui::Checkbox("Readable", &property_filter_.readable)) filters_changed_ = true;
if (ImGui::Checkbox("Pickupable", &property_filter_.pickupable)) filters_changed_ = true;
if (ImGui::Checkbox("Animation", &property_filter_.animation)) filters_changed_ = true;
if (ImGui::Checkbox("Ignore Look", &property_filter_.ignore_look)) filters_changed_ = true;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 The 32x32 CELL_SIZE might be too cramped for some creature previews or larger items. While Tibia tiles are 32x32, multi-tile creatures might not be very recognizable at this scale. Increasing this slightly (e.g., to 48x48) might improve usability while still keeping a good density.

}

void AdvancedSearchDialog::renderSearchSection() {
ImGui::SeparatorText(ICON_FA_MAGNIFYING_GLASS " Search");

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 filters_changed_ is set to true on every keystroke in the search input. While searchItemDatabase is generally fast, iterating over ~30k item types and performing fuzzy matching on every character might cause micro-stutters. Consider adding a small debounce or only updating when the query length is >= 2-3 characters (unless it's numeric).


void AdvancedSearchDialog::renderHintsSection() {
ImGui::SeparatorText(ICON_FA_CIRCLE_INFO " Hints");
ImGui::TextDisabled("Double-click result to search map.");

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟢 Using ImGuiListClipper for the results grid is a good performance optimization for handling large search result sets.

if (!result.item->name.empty()) {
ImGui::Text("Name: %s", result.item->name.c_str());

// Tooltip on hover

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 setSearching is called here, but executeMapSearch in the next frame sets is_searching_ = false before actually finishing the work. This defeats the purpose of the spinner.

}

void SearchResultsWidget::setResults(const std::vector<Domain::Search::MapSearchResult>& results) {
is_searching_ = false;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 setResults clears is_searching_, which is correct if the search is async. However, if the caller is synchronous (like AdvancedSearchDialog), the UI will be frozen during the search anyway.

const float spacing = ImGui::GetStyle().ItemSpacing.x;
const float footer_h = ImGui::GetFrameHeightWithSpacing() + ImGui::GetStyle().ItemSpacing.y * 2.0f + 20.0f;
const float content_h = ImGui::GetContentRegionAvail().y - footer_h;
const float filters_w = ImGui::GetContentRegionAvail().x * 0.35f;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 The footer height calculation uses a magic number 20.0f. Consider using style variables (e.g., ImGuiStyleVar_ItemSpacing) or a more descriptive constant for better maintainability and scaling with different DPI/themes.

ImGui::BeginTooltip();
if (sprite_rendered) {
ImGui::Image(TexId(tex_id), ImVec2(PREVIEW_SIZE, PREVIEW_SIZE));
ImGui::SameLine();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔴 executeMapSearch is called synchronously on the UI thread. Since it iterates over all map tiles (via search_service_->search), this will cause a significant UI freeze on large maps. It should be offloaded to a background thread, possibly by utilizing the SearchController::launchAsync mechanism already present in the codebase.


ctx.menu_bar->setFindItemsCallback(
[ctx]() { ctx.view_settings->show_search_results = true; });
[ctx]() { if (ctx.advanced_search) ctx.advanced_search->open(); });

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟢 Consolidating quick search and advanced search into a single unified dialog simplifies the UI and keyboard shortcuts for the user.

executeMapSearch();
}

ImGui::SetNextWindowSize(ImVec2(850, 600), ImGuiCond_Once);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 is_searching_ is set to false immediately before calling executeMapSearch. Combined with the fact that executeMapSearch is synchronous, the "Searching..." state and spinner in SearchResultsWidget will likely never be visible to the user, or only for a single frame.

    if (pending_map_search_) {
        pending_map_search_ = false;
        is_searching_ = false; // Should remain true until search completes
        executeMapSearch();
    }

std::vector<size_t> filtered_indices_;
size_t filtered_count_ = 0;
bool filter_dirty_ = true;
bool is_searching_ = false;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 The SearchResultsWidget now has its own is_searching_ state, but it seems to be duplicated with the logic in AdvancedSearchDialog. It might be cleaner to have SearchResultsWidget manage its own "searching" state or have a unified way to report progress if searches are moved to SearchController.

@gemini-code-assist

Copy link
Copy Markdown

Warning

Gemini encountered an error creating the summary. You can try again by commenting /gemini summary.

Removed the `is_searching_` state flag and associated UI logic, as deferred search execution was already handling the requirement effectively. Adjusted footer padding calculation for better UI spacing consistency.
- `ImguiMapEditor/UI/Dialogs/AdvancedSearchDialog.cpp` — removed `is_searching_` management, updated search triggering logic, and refined footer height calculation
- `ImguiMapEditor/UI/Dialogs/AdvancedSearchDialog.h` — removed `is_searching_` member variable
@glaszczkoldre glaszczkoldre merged commit 6870c60 into main Jun 4, 2026
5 of 7 checks passed

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📋 Review Summary

This PR remakes the Advanced Search dialog and consolidates it with the Quick Search functionality. The UI has been improved with a grid preview of results and better state management (e.g., "Searching..." state).

🔍 General Feedback

Overall, the refactoring is a good step towards a more unified search experience. The use of deferred search logic and ImGuiListClipper shows attention to performance and UI responsiveness.

I've noted a few minor issues regarding:

  • Fragile logic in filter checking.
  • Potential edge cases in sorting.
  • Minor UX improvements for the loading state.
  • Defensive programming for layout calculations.

Most of these are non-blocking but would improve the robustness of the search feature.

constexpr float IMG_PADDING = 2.0f;
const float pad = ImGui::GetStyle().WindowPadding.x;
const float avail_w = ImGui::GetContentRegionAvail().x;
const int columns = std::max(1, static_cast<int>((avail_w - pad) / CELL_SIZE));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 The creature_only boolean calculation is fragile as it manually excludes every other flag in TypeFilter. If TypeFilter is updated with new types in the future, this logic will likely break or become incorrect.

Consider adding a method like TypeFilter::hasAnyItemTypeSelected() to encapsulate this check.


if (ImGui::Begin(ICON_FA_MAGNIFYING_GLASS_PLUS " Advanced Search###AdvancedSearch", &is_open_,
ImGuiWindowFlags_NoCollapse)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 If footer_h happens to be larger than the available vertical space, content_h will be negative, which can lead to undefined behavior or assertions in ImGui::BeginChild.

Suggested change
const float content_h = std::max(0.0f, ImGui::GetContentRegionAvail().y - footer_h);

std::string text = std::format("{} {}", search_buffer_[0] ? ICON_FA_CIRCLE_EXCLAMATION : ICON_FA_KEYBOARD, msg);
const char* icon;
const char* msg;
if (is_searching_) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟢 The ICON_FA_SPINNER icon is static. For a better "searching" UX, you might want to rotate it or use a proper ImGui spinner if available.

auto preview = Utils::GetCreaturePreview(*client_data_, *sprite_manager_, result.creature->outfit);
if (preview && preview.texture) {
tex_id = preview.texture->id();
sprite_rendered = true;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Using UINT16_MAX to sort creatures at the end might cause unexpected ordering if an item in the database actually has a server_id of 65535. While rare in many OT versions, it's a magic number dependency.

Consider using a more robust comparison that explicitly checks is_creature first.

@karolak6612 karolak6612 deleted the New_search branch June 6, 2026 10:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area: UI Changes to the wxWidgets GUI (palettes, dialogs, menus, toolbar). Type: Enhancement New feature or request.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants