From b3d3814ce985daa8f77568837bd2c4b240817cd6 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Wed, 11 Mar 2026 23:17:38 -0700 Subject: [PATCH] Add search bar and Active/Ready filter to quest log Adds a name search input and All/Active/Ready radio buttons above the quest list. Clears the filter automatically when openAndSelectQuest() is called so the target quest is always visible. --- include/ui/quest_log_screen.hpp | 4 ++++ src/ui/quest_log_screen.cpp | 40 +++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/include/ui/quest_log_screen.hpp b/include/ui/quest_log_screen.hpp index eef78289..0bb791e0 100644 --- a/include/ui/quest_log_screen.hpp +++ b/include/ui/quest_log_screen.hpp @@ -31,6 +31,10 @@ private: uint32_t lastDetailRequestQuestId_ = 0; double lastDetailRequestAt_ = 0.0; std::unordered_set questDetailQueryNoResponse_; + // Search / filter + char questSearchFilter_[64] = {}; + // 0=all, 1=active only, 2=complete only + int questFilterMode_ = 0; }; }} // namespace wowee::ui diff --git a/src/ui/quest_log_screen.cpp b/src/ui/quest_log_screen.cpp index 09eca800..e52a2085 100644 --- a/src/ui/quest_log_screen.cpp +++ b/src/ui/quest_log_screen.cpp @@ -248,6 +248,17 @@ void QuestLogScreen::render(game::GameHandler& gameHandler, InventoryScreen& inv else activeCount++; } + // Search bar + filter buttons on one row + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x - 210.0f); + ImGui::InputTextWithHint("##qsearch", "Search quests...", questSearchFilter_, sizeof(questSearchFilter_)); + ImGui::SameLine(); + if (ImGui::RadioButton("All", questFilterMode_ == 0)) questFilterMode_ = 0; + ImGui::SameLine(); + if (ImGui::RadioButton("Active", questFilterMode_ == 1)) questFilterMode_ = 1; + ImGui::SameLine(); + if (ImGui::RadioButton("Ready", questFilterMode_ == 2)) questFilterMode_ = 2; + + // Summary counts ImGui::TextColored(ImVec4(0.95f, 0.85f, 0.35f, 1.0f), "Active: %d", activeCount); ImGui::SameLine(); ImGui::TextColored(ImVec4(0.45f, 0.95f, 0.45f, 1.0f), "Ready: %d", completeCount); @@ -270,14 +281,36 @@ void QuestLogScreen::render(game::GameHandler& gameHandler, InventoryScreen& inv for (size_t i = 0; i < quests.size(); i++) { if (quests[i].questId == pendingSelectQuestId_) { selectedIndex = static_cast(i); + // Clear filter so the target quest is visible + questSearchFilter_[0] = '\0'; + questFilterMode_ = 0; break; } } pendingSelectQuestId_ = 0; } + // Build a case-insensitive lowercase copy of the search filter once + char filterLower[64] = {}; + for (size_t fi = 0; fi < sizeof(questSearchFilter_) && questSearchFilter_[fi]; ++fi) + filterLower[fi] = static_cast(std::tolower(static_cast(questSearchFilter_[fi]))); + + int visibleQuestCount = 0; for (size_t i = 0; i < quests.size(); i++) { const auto& q = quests[i]; + + // Apply mode filter + if (questFilterMode_ == 1 && q.complete) continue; + if (questFilterMode_ == 2 && !q.complete) continue; + + // Apply name search filter + if (filterLower[0]) { + std::string titleLower = cleanQuestTitleForUi(q.title, q.questId); + for (char& c : titleLower) c = static_cast(std::tolower(static_cast(c))); + if (titleLower.find(filterLower) == std::string::npos) continue; + } + + visibleQuestCount++; ImGui::PushID(static_cast(i)); bool selected = (selectedIndex == static_cast(i)); @@ -321,6 +354,13 @@ void QuestLogScreen::render(game::GameHandler& gameHandler, InventoryScreen& inv } ImGui::PopID(); } + if (visibleQuestCount == 0) { + ImGui::Spacing(); + if (filterLower[0] || questFilterMode_ != 0) + ImGui::TextDisabled("No quests match the filter."); + else + ImGui::TextDisabled("No active quests."); + } ImGui::EndChild(); ImGui::SameLine();