1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2025-12-23 15:52:55 +01:00

Fix #24888: Add search for ride list (#24890)

* Fix #24888: Add search for ride list

* Cache ride name

* Hide unwanted ride list entries rather than rebuilding the list

* Minor touchups

* Display count of filtered rides/stalls in ride list

---------

Co-authored-by: Aaron van Geffen <aaron@aaronweb.net>
This commit is contained in:
Michał Janiszewski
2025-08-15 18:51:14 +02:00
committed by GitHub
parent 1436d8ef2a
commit b32d29330b
2 changed files with 95 additions and 16 deletions

View File

@@ -2,6 +2,7 @@
------------------------------------------------------------------------
- Feature: [#24949] Add an option to the command line screenshot function to draw debug bounding boxes.
- Improved: [#24734] Save files now use Zstd compression for faster saving and smaller files.
- Improved: [#24890] The ride list is now searchable.
- Improved: [#24893] The ride list now has headers, and can be sorted in both directions.
- Improved: [#24921] The command line sprite build command now prints out the images table entry for the compiled sprite file.
- Improved: [#24953] Opening the Scenario Editor, Track Designer or Track Designs Manager now display the progress bar.

View File

@@ -52,6 +52,8 @@ namespace OpenRCT2::Ui::Windows
WIDX_TAB_1,
WIDX_TAB_2,
WIDX_TAB_3,
WIDX_SEARCH_TEXT_BOX,
WIDX_SEARCH_CLEAR_BUTTON,
WIDX_HEADER_NAME,
WIDX_HEADER_OTHER,
WIDX_HEADER_CUSTOMISE,
@@ -69,13 +71,15 @@ namespace OpenRCT2::Ui::Windows
makeTab ({ 3, 17}, STR_LIST_RIDES_TIP ), // tab 1
makeTab ({ 34, 17}, STR_LIST_SHOPS_AND_STALLS_TIP ), // tab 2
makeTab ({ 65, 17}, STR_LIST_KIOSKS_AND_FACILITIES_TIP ), // tab 3
makeWidget({ 3, 47}, {150, 14}, WidgetType::tableHeader, WindowColour::secondary ), // name header
makeWidget({153, 47}, {147, 14}, WidgetType::tableHeader, WindowColour::secondary ), // other header
makeWidget({300, 47}, { 14, 14}, WidgetType::button, WindowColour::secondary, STR_DROPDOWN_GLYPH ), // customise button
makeWidget({ 3, 60}, {334, 177}, WidgetType::scroll, WindowColour::secondary, SCROLL_VERTICAL ), // list
makeWidget({320, 62}, { 14, 14}, WidgetType::imgBtn, WindowColour::secondary, ImageId(SPR_G2_RCT1_CLOSE_BUTTON_0) ),
makeWidget({320, 76}, { 14, 14}, WidgetType::imgBtn, WindowColour::secondary, ImageId(SPR_G2_RCT1_OPEN_BUTTON_0) ),
makeWidget({315, 90}, { 24, 24}, WidgetType::flatBtn, WindowColour::secondary, ImageId(SPR_DEMOLISH), STR_QUICK_DEMOLISH_RIDE )
makeWidget({ 3, 47}, {284, 14}, WidgetType::textBox, WindowColour::secondary ), // search text box
makeWidget({284, 47}, { 50, 14}, WidgetType::button, WindowColour::secondary, STR_OBJECT_SEARCH_CLEAR, STR_CLEAR_SCENERY_TIP ), // search clear button
makeWidget({ 3, 62}, {150, 14}, WidgetType::tableHeader, WindowColour::secondary ), // name header
makeWidget({153, 62}, {147, 14}, WidgetType::tableHeader, WindowColour::secondary ), // other header
makeWidget({300, 62}, { 14, 14}, WidgetType::button, WindowColour::secondary, STR_DROPDOWN_GLYPH ), // customise button
makeWidget({ 3, 75}, {334, 177}, WidgetType::scroll, WindowColour::secondary, SCROLL_VERTICAL ), // list
makeWidget({320, 77}, { 14, 14}, WidgetType::imgBtn, WindowColour::secondary, ImageId(SPR_G2_RCT1_CLOSE_BUTTON_0) ),
makeWidget({320, 91}, { 14, 14}, WidgetType::imgBtn, WindowColour::secondary, ImageId(SPR_G2_RCT1_OPEN_BUTTON_0) ),
makeWidget({315,105}, { 24, 24}, WidgetType::flatBtn, WindowColour::secondary, ImageId(SPR_DEMOLISH), STR_QUICK_DEMOLISH_RIDE )
);
// clang-format on
@@ -165,11 +169,13 @@ namespace OpenRCT2::Ui::Windows
bool _quickDemolishMode = false;
InformationType _windowRideListInformationType = INFORMATION_TYPE_STATUS;
bool _windowListSortDescending = false;
u8string _searchFilter;
struct RideListEntry
{
RideId Id;
u8string Name;
bool Visible;
};
std::vector<RideListEntry> _rideList;
@@ -188,6 +194,7 @@ namespace OpenRCT2::Ui::Windows
_windowRideListInformationType = INFORMATION_TYPE_STATUS;
_quickDemolishMode = false;
_searchFilter.clear();
}
/**
@@ -257,6 +264,14 @@ namespace OpenRCT2::Ui::Windows
RefreshList();
}
break;
case WIDX_SEARCH_TEXT_BOX:
WindowStartTextbox(*this, widgetIndex, _searchFilter, kTextInputSize);
break;
case WIDX_SEARCH_CLEAR_BUTTON:
_searchFilter.clear();
ApplySearchQuery();
InvalidateWidget(WIDX_SEARCH_TEXT_BOX);
break;
case WIDX_CLOSE_LIGHT:
CloseAllRides();
break;
@@ -399,13 +414,25 @@ namespace OpenRCT2::Ui::Windows
SortList();
}
void OnTextInput(WidgetIndex widgetIndex, std::string_view text) override
{
if (widgetIndex != WIDX_SEARCH_TEXT_BOX)
return;
if (text == _searchFilter)
return;
_searchFilter.assign(text);
ApplySearchQuery();
}
/**
*
* rct2: 0x006B35A1
*/
ScreenSize OnScrollGetSize(int32_t scrollIndex) override
{
const auto newHeight = static_cast<int32_t>(_rideList.size() * kScrollableRowHeight);
const auto newHeight = static_cast<int32_t>(CountVisibleItems() * kScrollableRowHeight);
auto top = std::max(0, newHeight - widgets[WIDX_LIST].bottom + widgets[WIDX_LIST].top + 21);
if (top < scrolls[0].contentOffsetY)
{
@@ -416,14 +443,30 @@ namespace OpenRCT2::Ui::Windows
return { 0, newHeight };
}
int32_t GetNthVisibleItemIndex(int32_t target) const
{
auto j = 0;
for (auto i = 0u; i < _rideList.size(); i++)
{
if (!_rideList[i].Visible)
continue;
if (j == target)
return i;
j++;
}
return -1;
}
/**
*
* rct2: 0x006B361F
*/
void OnScrollMouseDown(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override
{
const auto index = screenCoords.y / kScrollableRowHeight;
if (index < 0 || static_cast<size_t>(index) >= _rideList.size())
const auto index = GetNthVisibleItemIndex(screenCoords.y / kScrollableRowHeight);
if (index < 0)
return;
// Open ride window
@@ -448,8 +491,8 @@ namespace OpenRCT2::Ui::Windows
*/
void OnScrollMouseOver(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override
{
const auto index = screenCoords.y / kScrollableRowHeight;
if (index < 0 || static_cast<size_t>(index) >= _rideList.size())
const auto index = GetNthVisibleItemIndex(screenCoords.y / kScrollableRowHeight);
if (index < 0)
return;
selected_list_item = index;
@@ -480,6 +523,11 @@ namespace OpenRCT2::Ui::Windows
widgets[WIDX_LIST].right = width - 26;
widgets[WIDX_LIST].bottom = height - 15;
widgets[WIDX_SEARCH_CLEAR_BUTTON].right = widgets[WIDX_LIST].right - 1;
widgets[WIDX_SEARCH_CLEAR_BUTTON].left = widgets[WIDX_SEARCH_CLEAR_BUTTON].right - 40;
widgets[WIDX_SEARCH_TEXT_BOX].right = widgets[WIDX_SEARCH_CLEAR_BUTTON].left - 2;
widgets[WIDX_SEARCH_TEXT_BOX].string = _searchFilter.data();
widgets[WIDX_HEADER_CUSTOMISE].right = widgets[WIDX_LIST].right - 1;
widgets[WIDX_HEADER_CUSTOMISE].left = widgets[WIDX_HEADER_CUSTOMISE].right - 14;
@@ -570,7 +618,7 @@ namespace OpenRCT2::Ui::Windows
// Draw number of attractions on bottom
auto ft = Formatter();
ft.Add<uint16_t>(static_cast<uint16_t>(_rideList.size()));
ft.Add<uint16_t>(static_cast<uint16_t>(CountVisibleItems()));
DrawTextBasic(
rt, windowPos + ScreenCoordsXY{ 4, widgets[WIDX_LIST].bottom + 2 }, ride_list_statusbar_count_strings[page],
ft);
@@ -589,6 +637,9 @@ namespace OpenRCT2::Ui::Windows
auto y = 0;
for (size_t i = 0; i < _rideList.size(); i++)
{
if (!_rideList[i].Visible)
continue;
StringId format = STR_BLACK_STRING;
if (_quickDemolishMode)
format = STR_RED_STRINGID;
@@ -882,6 +933,28 @@ namespace OpenRCT2::Ui::Windows
});
}
bool IsFiltered(const u8string& rideName)
{
if (_searchFilter.empty())
return true;
// Check if the filter matches the ride name
return String::contains(u8string_view(rideName), _searchFilter, true);
}
void ApplySearchQuery()
{
for (auto& entry : _rideList)
{
entry.Visible = IsFiltered(entry.Name);
}
}
size_t CountVisibleItems() const
{
return std::count_if(_rideList.begin(), _rideList.end(), [](const RideListEntry& entry) { return entry.Visible; });
}
/**
*
* rct2: 0x006B39A8
@@ -897,14 +970,19 @@ namespace OpenRCT2::Ui::Windows
continue;
}
// Get the ride name once and use it for both filtering and storage
auto rideName = rideRef.getName();
if (rideRef.windowInvalidateFlags & RIDE_INVALIDATE_RIDE_LIST)
{
rideRef.windowInvalidateFlags &= ~RIDE_INVALIDATE_RIDE_LIST;
}
RideListEntry entry{};
entry.Id = rideRef.id;
entry.Name = rideRef.getName();
RideListEntry entry{
.Id = rideRef.id,
.Name = std::move(rideName),
.Visible = IsFiltered(rideName),
};
_rideList.push_back(std::move(entry));
}