1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2025-12-10 09:32:29 +01:00

Rework ride list sort options into table headers (#24893)

This commit is contained in:
Aaron van Geffen
2025-08-05 16:47:20 +02:00
committed by GitHub
parent 47d202f08a
commit b9c3306c3d
4 changed files with 106 additions and 47 deletions

View File

@@ -3839,3 +3839,5 @@ STR_6797 :No preview
STR_6798 :Screenshot
STR_6799 :Mini map
STR_7000 :or
STR_7001 :Ride name
STR_7002 :{STRINGID} {STRINGID}

View File

@@ -1,5 +1,6 @@
0.4.26 (in development)
------------------------------------------------------------------------
- Improved: [#24893] The ride list now has headers, and can be sorted in both directions.
- Fix: [#16988] AppImage version does not show changelog.
0.4.25 (2025-08-03)

View File

@@ -1771,6 +1771,8 @@ namespace OpenRCT2
STR_RELIABILITY = 1832,
STR_RELIABILITY_LABEL = 1839,
STR_RIDES = 974,
STR_RIDE_LIST_HEADER_FORMAT = 7002,
STR_RIDE_LIST_NAME = 7001,
STR_RIDE_LIST_AGE = 5772,
STR_RIDE_LIST_BUILT_LAST_YEAR_LABEL = 5777,
STR_RIDE_LIST_BUILT_THIS_YEAR_LABEL = 5776,

View File

@@ -7,10 +7,9 @@
* OpenRCT2 is licensed under the GNU General Public License version 3.
*****************************************************************************/
#include "../interface/Theme.h"
#include <iterator>
#include <openrct2-ui/interface/Dropdown.h>
#include <openrct2-ui/interface/Theme.h>
#include <openrct2-ui/interface/Widget.h>
#include <openrct2-ui/windows/Windows.h>
#include <openrct2/Context.h>
@@ -50,12 +49,12 @@ namespace OpenRCT2::Ui::Windows
WIDX_CLOSE,
WIDX_PAGE_BACKGROUND,
WIDX_OPEN_CLOSE_ALL,
WIDX_CURRENT_INFORMATION_TYPE,
WIDX_INFORMATION_TYPE_DROPDOWN,
WIDX_SORT,
WIDX_TAB_1,
WIDX_TAB_2,
WIDX_TAB_3,
WIDX_HEADER_NAME,
WIDX_HEADER_OTHER,
WIDX_HEADER_CUSTOMISE,
WIDX_LIST,
WIDX_CLOSE_LIGHT,
WIDX_OPEN_LIGHT,
@@ -67,12 +66,12 @@ namespace OpenRCT2::Ui::Windows
makeWindowShim(kWindowTitle, kWindowSize),
makeWidget({ 0, 43}, {340, 197}, WidgetType::resize, WindowColour::secondary ), // tab page background
makeWidget({315, 60}, { 24, 24}, WidgetType::flatBtn, WindowColour::secondary, ImageId(SPR_TOGGLE_OPEN_CLOSE), STR_OPEN_OR_CLOSE_ALL_RIDES ), // open / close all toggle
makeWidget({150, 46}, {124, 12}, WidgetType::dropdownMenu, WindowColour::secondary ), // current information type
makeWidget({262, 47}, { 11, 10}, WidgetType::button, WindowColour::secondary, STR_DROPDOWN_GLYPH, STR_RIDE_LIST_INFORMATION_TYPE_TIP), // information type dropdown button
makeWidget({280, 46}, { 54, 12}, WidgetType::button, WindowColour::secondary, STR_SORT, STR_RIDE_LIST_SORT_TIP ), // sort button
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) ),
@@ -80,7 +79,7 @@ namespace OpenRCT2::Ui::Windows
);
// clang-format on
enum
enum InformationType
{
INFORMATION_TYPE_STATUS,
INFORMATION_TYPE_POPULARITY,
@@ -164,7 +163,8 @@ namespace OpenRCT2::Ui::Windows
{
private:
bool _quickDemolishMode = false;
int32_t _windowRideListInformationType = INFORMATION_TYPE_STATUS;
InformationType _windowRideListInformationType = INFORMATION_TYPE_STATUS;
bool _windowListSortDescending = false;
struct RideListEntry
{
@@ -178,16 +178,14 @@ namespace OpenRCT2::Ui::Windows
{
SetWidgets(_rideListWidgets);
WindowInitScrollWidgets(*this);
WindowSetResize(*this, { 340, 240 }, { 400, 700 });
WindowSetResize(*this, kWindowSize, kWindowSize * 2);
page = PAGE_RIDES;
selected_list_item = -1;
frame_no = 0;
list_information_type = INFORMATION_TYPE_STATUS;
RefreshList();
list_information_type = 0;
_windowRideListInformationType = INFORMATION_TYPE_STATUS;
_quickDemolishMode = false;
}
@@ -209,12 +207,6 @@ namespace OpenRCT2::Ui::Windows
height = min_height;
}
widgets[WIDX_SORT].left = width - 60;
widgets[WIDX_SORT].right = width - 60 + 54;
auto dropdownStart = widgets[WIDX_CURRENT_INFORMATION_TYPE].top;
ResizeDropdown(WIDX_CURRENT_INFORMATION_TYPE, { 100, dropdownStart }, { width - 166, kDropdownHeight });
// Refreshing the list can be a very intensive operation
// owing to its use of ride_has_any_track_elements().
// This makes sure it's only refreshed every 64 ticks.
@@ -235,9 +227,28 @@ namespace OpenRCT2::Ui::Windows
case WIDX_CLOSE:
Close();
break;
case WIDX_SORT:
list_information_type = _windowRideListInformationType;
selected_list_item = -1;
case WIDX_HEADER_NAME:
if (list_information_type != INFORMATION_TYPE_STATUS)
{
list_information_type = INFORMATION_TYPE_STATUS;
_windowListSortDescending = false;
}
else
{
_windowListSortDescending ^= true;
}
RefreshList();
break;
case WIDX_HEADER_OTHER:
if (list_information_type != _windowRideListInformationType)
{
list_information_type = _windowRideListInformationType;
_windowListSortDescending = true;
}
else
{
_windowListSortDescending ^= true;
}
RefreshList();
break;
case WIDX_TAB_1:
@@ -247,7 +258,6 @@ namespace OpenRCT2::Ui::Windows
{
page = widgetIndex - WIDX_TAB_1;
frame_no = 0;
selected_list_item = -1;
if (page != PAGE_RIDES && _windowRideListInformationType > INFORMATION_TYPE_RUNNING_COST)
{
_windowRideListInformationType = INFORMATION_TYPE_STATUS;
@@ -289,10 +299,8 @@ namespace OpenRCT2::Ui::Windows
WindowDropdownShowText(
{ windowPos.x + widget.left, windowPos.y + widget.top }, widget.height(), colours[1], 0, 2);
}
else if (widgetIndex == WIDX_INFORMATION_TYPE_DROPDOWN)
else if (widgetIndex == WIDX_HEADER_CUSTOMISE)
{
const auto& widget = widgets[widgetIndex - 1];
int32_t lastType;
if (page == PAGE_RIDES)
lastType = INFORMATION_TYPE_NAUSEA;
@@ -321,9 +329,14 @@ namespace OpenRCT2::Ui::Windows
numItems++;
}
const auto& headerWidget = widgets[WIDX_HEADER_OTHER];
const auto& customWidget = widgets[WIDX_HEADER_CUSTOMISE];
auto totalWidth = headerWidget.width() + customWidget.width();
WindowDropdownShowTextCustomWidth(
{ windowPos.x + widget.left, windowPos.y + widget.top }, widget.height(), colours[1], 0,
Dropdown::Flag::StayOpen, numItems, widget.width() - 3);
{ windowPos.x + headerWidget.left, windowPos.y + headerWidget.top }, headerWidget.height(), colours[1], 0,
Dropdown::Flag::StayOpen, numItems, totalWidth);
if (selectedIndex != -1)
{
Dropdown::SetChecked(selectedIndex, true);
@@ -350,7 +363,7 @@ namespace OpenRCT2::Ui::Windows
Invalidate();
}
else if (widgetIndex == WIDX_INFORMATION_TYPE_DROPDOWN)
else if (widgetIndex == WIDX_HEADER_CUSTOMISE)
{
if (dropdownIndex == -1)
return;
@@ -365,8 +378,15 @@ namespace OpenRCT2::Ui::Windows
}
}
_windowRideListInformationType = informationType;
_windowRideListInformationType = InformationType(informationType);
Invalidate();
// Automatically change sort if we're sorting by the custom/info column
if (list_information_type != INFORMATION_TYPE_STATUS)
{
list_information_type = _windowRideListInformationType;
RefreshList();
}
}
}
@@ -389,15 +409,7 @@ namespace OpenRCT2::Ui::Windows
ScreenSize OnScrollGetSize(int32_t scrollIndex) override
{
const auto newHeight = static_cast<int32_t>(_rideList.size() * kScrollableRowHeight);
if (selected_list_item != -1)
{
selected_list_item = -1;
Invalidate();
}
auto top = newHeight - widgets[WIDX_LIST].bottom + widgets[WIDX_LIST].top + 21;
if (top < 0)
top = 0;
auto top = std::max(0, newHeight - widgets[WIDX_LIST].bottom + widgets[WIDX_LIST].top + 21);
if (top < scrolls[0].contentOffsetY)
{
scrolls[0].contentOffsetY = top;
@@ -453,7 +465,8 @@ namespace OpenRCT2::Ui::Windows
*/
void OnPrepareDraw() override
{
widgets[WIDX_CURRENT_INFORMATION_TYPE].text = ride_info_type_string_mapping[_windowRideListInformationType];
auto ft = Formatter();
ft.Add<StringId>(STR_UP);
// Set correct active tab
for (int32_t i = 0; i < 3; i++)
@@ -469,6 +482,17 @@ namespace OpenRCT2::Ui::Windows
widgets[WIDX_LIST].right = width - 26;
widgets[WIDX_LIST].bottom = height - 15;
widgets[WIDX_HEADER_CUSTOMISE].right = widgets[WIDX_LIST].right - 1;
widgets[WIDX_HEADER_CUSTOMISE].left = widgets[WIDX_HEADER_CUSTOMISE].right - 14;
auto columnWidth = (widgets[WIDX_LIST].width() - widgets[WIDX_HEADER_CUSTOMISE].width()) / 2;
widgets[WIDX_HEADER_OTHER].right = widgets[WIDX_HEADER_CUSTOMISE].left - 1;
widgets[WIDX_HEADER_OTHER].left = widgets[WIDX_HEADER_OTHER].right - columnWidth + 1;
widgets[WIDX_HEADER_NAME].right = widgets[WIDX_HEADER_NAME].left + columnWidth - 1;
widgets[WIDX_OPEN_CLOSE_ALL].right = width - 2;
widgets[WIDX_OPEN_CLOSE_ALL].left = width - 25;
widgets[WIDX_CLOSE_LIGHT].right = width - 7;
@@ -527,6 +551,26 @@ namespace OpenRCT2::Ui::Windows
WindowDrawWidgets(*this, rt);
DrawTabImages(rt);
const auto drawButtonCaption = [rt, this](Widget& widget, StringId strId, InformationType sortType) {
StringId indicatorId = kStringIdNone;
if (list_information_type == sortType && !(strId == STR_STATUS && sortType == INFORMATION_TYPE_STATUS))
indicatorId = _windowListSortDescending ? STR_DOWN : STR_UP;
auto ft = Formatter();
ft.Add<StringId>(strId);
ft.Add<StringId>(indicatorId);
auto cdpi = const_cast<const RenderTarget&>(rt);
DrawTextEllipsised(
cdpi, windowPos + ScreenCoordsXY{ widget.left + 1, widget.top + 1 }, widget.width(),
STR_RIDE_LIST_HEADER_FORMAT, ft, { colours[1] });
};
drawButtonCaption(widgets[WIDX_HEADER_NAME], STR_RIDE_LIST_NAME, INFORMATION_TYPE_STATUS);
auto otherStringId = ride_info_type_string_mapping[_windowRideListInformationType];
drawButtonCaption(widgets[WIDX_HEADER_OTHER], otherStringId, _windowRideListInformationType);
// Draw number of attractions on bottom
auto ft = Formatter();
ft.Add<uint16_t>(static_cast<uint16_t>(_rideList.size()));
@@ -569,7 +613,9 @@ namespace OpenRCT2::Ui::Windows
// Ride name
auto ft = Formatter();
ridePtr->formatNameTo(ft);
DrawTextEllipsised(rt, { 0, y - 1 }, 159, format, ft);
auto& nameHeader = widgets[WIDX_HEADER_NAME];
DrawTextEllipsised(rt, { 0, y - 1 }, nameHeader.width() - 2, format, ft);
// Ride information
ft = Formatter();
@@ -758,6 +804,8 @@ namespace OpenRCT2::Ui::Windows
ft.Add<uint16_t>(ridePtr->ratings.nausea);
}
break;
default:
break;
}
if (formatSecondaryEnabled)
@@ -765,7 +813,9 @@ namespace OpenRCT2::Ui::Windows
ft.Rewind();
ft.Add<StringId>(formatSecondary);
}
DrawTextEllipsised(rt, { 160, y - 1 }, 157, format, ft);
auto infoHeader = widgets[WIDX_HEADER_OTHER];
DrawTextEllipsised(rt, { infoHeader.left - 4, y - 1 }, infoHeader.width() - 2, format, ft);
y += kScrollableRowHeight;
}
}
@@ -813,21 +863,25 @@ namespace OpenRCT2::Ui::Windows
template<typename TSortPred>
void SortListByPredicate(const TSortPred& pred)
{
std::sort(_rideList.begin(), _rideList.end(), [&pred](const auto& lhs, const auto& rhs) {
bool desc = _windowListSortDescending;
std::stable_sort(_rideList.begin(), _rideList.end(), [desc, pred](const auto& lhs, const auto& rhs) {
const Ride* rideLhs = GetRide(lhs.Id);
const Ride* rideRhs = GetRide(rhs.Id);
if (rideLhs == nullptr || rideRhs == nullptr)
{
return rideLhs != nullptr;
}
return !pred(*rideLhs, *rideRhs);
bool result = pred(*rideLhs, *rideRhs);
return desc ? !result : result;
});
}
void SortListByName()
{
std::sort(_rideList.begin(), _rideList.end(), [](const auto& lhs, const auto& rhs) {
return !(0 <= String::logicalCmp(lhs.Name.c_str(), rhs.Name.c_str()));
bool desc = _windowListSortDescending;
std::stable_sort(_rideList.begin(), _rideList.end(), [desc](const auto& lhs, const auto& rhs) {
bool result = (0 > String::logicalCmp(lhs.Name.c_str(), rhs.Name.c_str()));
return desc ? !result : result;
});
}