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:
@@ -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}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user