1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2025-12-11 01:52:32 +01:00

Remove ‘format’ and ‘args’ altogether

This commit is contained in:
Gymnasiast
2025-08-23 20:04:21 +02:00
parent 77012410a9
commit 7f2c4261b4
11 changed files with 181 additions and 161 deletions

View File

@@ -549,8 +549,8 @@ STR_1152 :Any load
STR_1153 :Height Marks on Ride Tracks
STR_1154 :Height Marks on Land
STR_1155 :Height Marks on Paths
STR_1156 :{MOVE_X}{10}{STRINGID}
STR_1157 :✓{MOVE_X}{10}{STRINGID}
STR_1156 :{MOVE_X}{10}{STRING}
STR_1157 :✓{MOVE_X}{10}{STRING}
STR_1158 :Cant remove this…
STR_1159 :Place scenery, gardens, and other accessories
STR_1160 :Create/adjust lakes & water

View File

@@ -15,16 +15,16 @@
#include <openrct2/core/StringTypes.h>
#include <openrct2/interface/Window.h>
#include <span>
#include <variant>
struct ImageId;
class Formatter;
namespace OpenRCT2::Dropdown
{
struct Item;
constexpr StringId kSeparatorString = 0;
constexpr StringId kFormatColourPicker = 0xFFFE;
constexpr StringId kFormatLandPicker = 0xFFFF;
constexpr int32_t kItemsMaxSize = 512;
struct DropdownState;
@@ -64,6 +64,8 @@ namespace OpenRCT2::Ui::Windows
uint32_t DropdownGetAppropriateImageDropdownItemsPerRow(uint32_t numItems);
} // namespace OpenRCT2::Ui::Windows
using StringVariant = std::variant<std::string, StringId>;
namespace OpenRCT2::Dropdown
{
enum Flag
@@ -79,21 +81,40 @@ namespace OpenRCT2::Dropdown
};
using ItemFlags = FlagHolder<uint8_t, ItemFlag>;
enum class ItemType
{
/**
* Regular label with padding at the left. Will get marked with » if selected.
*/
regular,
/**
* Same as regular, but will get marked with a tick if selected.
*/
toggle,
/**
* Label with no padding
*/
plain,
image,
colour,
separator,
};
struct Item
{
StringId format{};
union
{
int64_t generic;
const utf8* string;
ImageId image;
} args{};
ItemType type;
u8string text{};
/**
* Underlying value, e.g. the ID of an object, the backing value of an enum, etc.
*/
uint32_t value{};
ImageId image{};
ItemFlags flags{};
StringId tooltip{};
constexpr bool isSeparator() const
{
return format == kSeparatorString;
return type == ItemType::separator;
}
constexpr bool isDisabled() const
@@ -144,38 +165,29 @@ namespace OpenRCT2::Dropdown
/**
* Regular menu item, which shows a » symbol when selected
*/
constexpr Item MenuLabel(StringId stringId)
{
return Item{ STR_DROPDOWN_MENU_LABEL, stringId };
}
Item MenuLabel(StringId stringId);
Item MenuLabel(u8string_view string);
Item MenuLabel(StringId format, const Formatter& ft);
/**
* Leaves out the left padding where a checkmark can be drawn, use only for menu where no item can be checked.
*/
constexpr Item PlainMenuLabel(StringId stringId)
{
return Item{ stringId };
}
Item MenuLabel(const utf8* string);
Item PlainMenuLabel(const utf8* string);
Item PlainMenuLabel(StringId stringId);
Item PlainMenuLabel(u8string_view string);
/**
* Like MenuLabel, but shows a tick when selected.
*/
constexpr Item ToggleOption(StringId stringId)
{
return Item{ STR_TOGGLE_OPTION, stringId };
}
Item ToggleOption(StringId stringId);
constexpr Item Separator()
{
return Item{ kSeparatorString };
return Item{ .type = ItemType::separator };
}
constexpr Item ImageItem(ImageId image, StringId tooltip = kStringIdEmpty)
{
return Item{ .format = Dropdown::kFormatLandPicker, .args = { .image = image }, .tooltip = tooltip };
return Item{ .type = ItemType::image, .image = image, .tooltip = tooltip };
}
struct DropdownState
@@ -194,8 +206,10 @@ namespace OpenRCT2::Dropdown
for (int i = 0; i < N; ++i)
{
const ItemExt& item = items[i];
OpenRCT2::Ui::Windows::gDropdown.items[i].format = item.itemFormat;
OpenRCT2::Ui::Windows::gDropdown.items[i].args.generic = item.stringId;
if (item.itemFormat == kSeparatorString)
OpenRCT2::Ui::Windows::gDropdown.items[i] = Separator();
else
OpenRCT2::Ui::Windows::gDropdown.items[i] = ToggleOption(item.stringId);
}
}

View File

@@ -21,6 +21,7 @@
#include <openrct2/drawing/Drawing.h>
#include <openrct2/localisation/Formatter.h>
#include <openrct2/localisation/Formatting.h>
#include <openrct2/localisation/Language.h>
#include <openrct2/ui/WindowManager.h>
namespace OpenRCT2::Ui::Windows
@@ -48,14 +49,6 @@ namespace OpenRCT2::Ui::Windows
Dropdown::DropdownState gDropdown{};
static void ResetDropdownFlags()
{
for (size_t i = 0; i < std::size(gDropdown.items); i++)
{
gDropdown.items[i].flags.clearAll();
}
}
class DropdownWindow final : public Window
{
int32_t NumColumns;
@@ -71,7 +64,6 @@ namespace OpenRCT2::Ui::Windows
// Input state
gDropdown.highlightedIndex = -1;
ResetDropdownFlags();
gDropdown.hasTooltips = false;
gDropdown.defaultIndex = -1;
InputSetState(InputState::DropdownActive);
@@ -87,6 +79,23 @@ namespace OpenRCT2::Ui::Windows
return Config::Get().interface.EnlargedUi ? 6 : 0;
}
static void drawTextItem(
RenderTarget& rt, ScreenCoordsXY screenCoords, int32_t width, const Dropdown::Item& item, bool highlighted,
StringId format, colour_t background)
{
ColourWithFlags colour = { background };
if (highlighted)
colour.colour = COLOUR_WHITE;
if (item.isDisabled())
colour = { background, EnumToFlag(ColourFlag::inset) };
auto yOffset = GetAdditionalRowPadding();
Formatter ft;
ft.Add<const utf8*>(item.text.c_str());
DrawTextEllipsised(rt, { screenCoords.x + 2, screenCoords.y + yOffset }, width - 7, format, ft, { colour });
}
void OnDraw(RenderTarget& rt) override
{
DrawWidgets(rt);
@@ -124,40 +133,55 @@ namespace OpenRCT2::Ui::Windows
}
else
{
if (i == highlightedIndex)
auto highlighted = (i == highlightedIndex);
if (highlighted)
{
// Darken the cell's background slightly when highlighted
const ScreenCoordsXY rightBottom = screenCoords + ScreenCoordsXY{ ItemWidth - 1, ItemHeight - 1 };
GfxFilterRect(rt, { screenCoords, rightBottom }, FilterPaletteID::PaletteDarken3);
}
StringId item = gDropdown.items[i].format;
if (item == Dropdown::kFormatLandPicker || item == Dropdown::kFormatColourPicker)
const auto& item = gDropdown.items[i];
switch (item.type)
{
// Image item
auto image = gDropdown.items[i].args.image;
if (item == Dropdown::kFormatColourPicker && highlightedIndex == i)
image = image.WithIndexOffset(1);
GfxDrawSprite(rt, image, screenCoords);
}
else
{
// Text item
if (i < Dropdown::kItemsMaxSize && gDropdown.items[i].isChecked())
item++;
case Dropdown::ItemType::regular:
{
auto formatString = STR_OPTIONS_DROPDOWN_ITEM;
if (i < Dropdown::kItemsMaxSize && gDropdown.items[i].isChecked())
formatString = STR_OPTIONS_DROPDOWN_ITEM_SELECTED;
// Calculate colour
ColourWithFlags colour = { colours[0].colour };
if (i == highlightedIndex)
colour.colour = COLOUR_WHITE;
if (i < Dropdown::kItemsMaxSize && gDropdown.items[i].isDisabled())
colour = { colours[0].colour, EnumToFlag(ColourFlag::inset) };
drawTextItem(rt, screenCoords, width, item, highlighted, formatString, colours[0].colour);
break;
}
case Dropdown::ItemType::toggle:
{
auto formatString = STR_TOGGLE_OPTION;
if (i < Dropdown::kItemsMaxSize && gDropdown.items[i].isChecked())
formatString = STR_TOGGLE_OPTION_CHECKED;
// Draw item string
auto yOffset = GetAdditionalRowPadding();
Formatter ft(reinterpret_cast<uint8_t*>(&gDropdown.items[i].args.generic));
DrawTextEllipsised(
rt, { screenCoords.x + 2, screenCoords.y + yOffset }, width - 7, item, ft, { colour });
drawTextItem(rt, screenCoords, width, item, highlighted, formatString, colours[0].colour);
break;
}
case Dropdown::ItemType::plain:
{
drawTextItem(rt, screenCoords, width, item, highlighted, STR_STRING, colours[0].colour);
break;
}
case Dropdown::ItemType::image:
{
GfxDrawSprite(rt, item.image, screenCoords);
break;
}
case Dropdown::ItemType::colour:
{
auto image = item.image;
if (highlightedIndex == i)
image = image.WithIndexOffset(1);
GfxDrawSprite(rt, image, screenCoords);
break;
}
case Dropdown::ItemType::separator:
break;
}
}
}
@@ -319,14 +343,11 @@ namespace OpenRCT2::Ui::Windows
const ScreenCoordsXY& screenPos, int32_t extray, ColourWithFlags colour, uint8_t flags, size_t num_items,
size_t prefRowsPerColumn)
{
char buffer[256];
// Calculate the longest string width
int32_t max_string_width = 0;
for (size_t i = 0; i < num_items; i++)
{
FormatStringLegacy(buffer, 256, gDropdown.items[i].format, static_cast<void*>(&gDropdown.items[i].args.generic));
int32_t string_width = GfxGetStringWidth(buffer, FontStyle::Medium);
int32_t string_width = GfxGetStringWidth(gDropdown.items[i].text.c_str(), FontStyle::Medium);
max_string_width = std::max(string_width, max_string_width);
}
@@ -595,9 +616,7 @@ namespace OpenRCT2::Ui::Windows
auto imageId = (orderedColour == COLOUR_INVISIBLE) ? ImageId(SPR_G2_ICON_PALETTE_INVISIBLE, COLOUR_WHITE)
: ImageId(SPR_PALETTE_BTN, orderedColour);
gDropdown.items[i].format = Dropdown::kFormatColourPicker;
gDropdown.items[i].args.image = imageId;
gDropdown.items[i].tooltip = kColourTooltips[i];
gDropdown.items[i] = { .type = Dropdown::ItemType::colour, .image = imageId, .tooltip = kColourTooltips[i] };
}
// Show dropdown
@@ -625,13 +644,34 @@ using namespace OpenRCT2;
namespace OpenRCT2::Dropdown
{
Item MenuLabel(const utf8* string)
Item MenuLabel(StringId stringId)
{
return Item{ STR_OPTIONS_DROPDOWN_ITEM, { .string = string } };
return Item{ ItemType::regular, LanguageGetString(stringId) };
}
Item PlainMenuLabel(const utf8* string)
Item MenuLabel(u8string_view string)
{
return Item{ STR_STRING, { .string = string } };
return Item{ .type = ItemType::regular, .text = u8string(string) };
}
Item MenuLabel(StringId format, const Formatter& ft)
{
auto string = FormatStringIDLegacy(format, ft.Data());
return Item{ .type = ItemType::regular, .text = string };
}
Item PlainMenuLabel(u8string_view string)
{
return Item{ .type = ItemType::plain, .text = u8string(string) };
}
Item PlainMenuLabel(StringId stringId)
{
return Item{ ItemType::plain, LanguageGetString(stringId) };
}
Item ToggleOption(StringId stringId)
{
return Item{ ItemType::toggle, LanguageGetString(stringId) };
}
} // namespace OpenRCT2::Dropdown

View File

@@ -732,6 +732,7 @@ namespace OpenRCT2::Ui::Windows
{
const auto& gameState = getGameState();
const auto& scenarioOptions = gameState.scenarioOptions;
auto objectiveType = EnumValue(scenarioOptions.objective.Type);
int32_t numItems = 0;
for (auto i = 0; i < EnumValue(Scenario::ObjectiveType::count); i++)
@@ -750,6 +751,11 @@ namespace OpenRCT2::Ui::Windows
if (objectiveAllowedByMoneyUsage && objectiveAllowedByPaymentSettings)
{
gDropdown.items[numItems] = Dropdown::MenuLabel(ObjectiveDropdownOptionNames[i]);
gDropdown.items[numItems].value = i;
if (i == objectiveType)
{
gDropdown.items[numItems].setChecked(true);
}
numItems++;
}
}
@@ -758,16 +764,6 @@ namespace OpenRCT2::Ui::Windows
WindowDropdownShowTextCustomWidth(
{ windowPos.x + dropdownWidget->left, windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1,
colours[1], 0, Dropdown::Flag::StayOpen, numItems, dropdownWidget->width() - 3);
auto objectiveType = EnumValue(scenarioOptions.objective.Type);
for (int32_t j = 0; j < numItems; j++)
{
if (gDropdown.items[j].args.generic - STR_OBJECTIVE_DROPDOWN_NONE == objectiveType)
{
gDropdown.items[j].setChecked(true);
break;
}
}
}
void ShowCategoryDropdown()
@@ -1017,9 +1013,7 @@ namespace OpenRCT2::Ui::Windows
switch (widgetIndex)
{
case WIDX_OBJECTIVE_DROPDOWN:
// TODO: Don't rely on string ID order
auto newObjectiveType = static_cast<Scenario::ObjectiveType>(
gDropdown.items[dropdownIndex].args.generic - STR_OBJECTIVE_DROPDOWN_NONE);
auto newObjectiveType = static_cast<Scenario::ObjectiveType>(gDropdown.items[dropdownIndex].value);
if (gameState.scenarioOptions.objective.Type != newObjectiveType)
SetObjective(newObjectiveType);
break;

View File

@@ -717,9 +717,9 @@ namespace OpenRCT2::Ui::Windows
void ShowLocateDropdown(Widget& widget)
{
constexpr std::array<Dropdown::Item, 2> dropdownItems = {
Dropdown::Item{ STR_LOCATE_SUBJECT_TIP },
Dropdown::Item{ STR_FOLLOW_SUBJECT_TIP },
std::array<Dropdown::Item, 2> dropdownItems = {
Dropdown::PlainMenuLabel(STR_LOCATE_SUBJECT_TIP),
Dropdown::PlainMenuLabel(STR_FOLLOW_SUBJECT_TIP),
};
WindowDropdownShowText(

View File

@@ -338,10 +338,9 @@ namespace OpenRCT2::Ui::Windows
for (size_t i = 0; i < _numPages; i++)
{
gDropdown.items[i].format = STR_DROPDOWN_MENU_LABEL;
uint16_t* args = reinterpret_cast<uint16_t*>(&gDropdown.items[i].args.generic);
args[0] = STR_PAGE_X;
args[1] = static_cast<uint16_t>(i + 1);
Formatter ft;
ft.Add<uint16_t>(i + 1);
gDropdown.items[i] = Dropdown::MenuLabel(STR_PAGE_X, ft);
}
gDropdown.items[static_cast<int32_t>(_selectedPage)].setChecked(true);
break;

View File

@@ -229,18 +229,9 @@ namespace OpenRCT2::Ui::Windows
auto curRide = GetRide(rideIndex);
if (curRide != nullptr)
{
// HACK until dropdown items have longer argument buffers
gDropdown.items[numItems].format = STR_DROPDOWN_MENU_LABEL;
Formatter ft(reinterpret_cast<uint8_t*>(&gDropdown.items[numItems].args.generic));
if (curRide->customName.empty())
{
curRide->formatNameTo(ft);
}
else
{
gDropdown.items[numItems].format = STR_OPTIONS_DROPDOWN_ITEM;
ft.Add<const char*>(curRide->customName.c_str());
}
auto name = curRide->getName();
gDropdown.items[numItems] = Dropdown::MenuLabel(name);
numItems++;
}
}

View File

@@ -822,12 +822,10 @@ namespace OpenRCT2::Ui::Windows
{
const Resolution& resolution = resolutions[i];
gDropdown.items[i].format = STR_DROPDOWN_MENU_LABEL;
uint16_t* args = reinterpret_cast<uint16_t*>(&gDropdown.items[i].args.generic);
args[0] = STR_RESOLUTION_X_BY_Y;
args[1] = resolution.Width;
args[2] = resolution.Height;
Formatter ft;
ft.Add<uint16_t>(resolution.Width);
ft.Add<uint16_t>(resolution.Height);
gDropdown.items[i] = Dropdown::MenuLabel(STR_RESOLUTION_X_BY_Y, ft);
if (resolution.Width == Config::Get().general.FullscreenWidth
&& resolution.Height == Config::Get().general.FullscreenHeight)

View File

@@ -1768,8 +1768,11 @@ namespace OpenRCT2::Ui::Windows
int32_t name = GetRideComponentName(rtd.NameConvention.vehicle).number;
for (int32_t i = 0; i < ride->numTrains; i++)
{
gDropdown.items[currentItem].format = STR_DROPDOWN_MENU_LABEL;
gDropdown.items[currentItem].args.generic = name | (currentItem << 16);
Formatter ft;
ft.Add<uint16_t>(i + 1);
gDropdown.items[currentItem] = Dropdown::MenuLabel(name, ft);
gDropdown.items[currentItem].value = (1 << 16) | i;
if (TrainMustBeHidden(*ride, i))
{
gDropdown.items[currentItem].setDisabled(true);
@@ -1779,10 +1782,12 @@ namespace OpenRCT2::Ui::Windows
// Stations
name = GetRideComponentName(rtd.NameConvention.station).number;
for (int32_t i = 1; i <= ride->numStations; i++)
for (int32_t i = 0; i < ride->numStations; i++)
{
gDropdown.items[currentItem].format = STR_DROPDOWN_MENU_LABEL;
gDropdown.items[currentItem].args.generic = name | (i << 16);
Formatter ft;
ft.Add<uint16_t>(i + 1);
gDropdown.items[currentItem] = Dropdown::MenuLabel(name, ft);
gDropdown.items[currentItem].value = (1 << 16) | i;
currentItem++;
}
@@ -1832,6 +1837,7 @@ namespace OpenRCT2::Ui::Windows
{
auto index = info.NumItems;
gDropdown.items[index] = Dropdown::MenuLabel(text);
gDropdown.items[index].value = EnumValue(status);
if (info.CurrentStatus == status)
{
info.CheckedIndex = index;
@@ -2132,7 +2138,7 @@ namespace OpenRCT2::Ui::Windows
{
gDropdown.items[i] = Dropdown::MenuLabel(_entranceDropdownData[i].LabelId);
if (_entranceDropdownData[i].EntranceTypeId == ride->entranceStyle)
gDropdown.items[i].format = STR_DROPDOWN_MENU_LABEL_SELECTED;
gDropdown.items[i].setChecked(true);
}
WindowDropdownShowTextCustomWidth(
@@ -2199,21 +2205,7 @@ namespace OpenRCT2::Ui::Windows
}
if (dropdownIndex < static_cast<int32_t>(std::size(gDropdown.items)))
{
switch (gDropdown.items[dropdownIndex].args.generic)
{
case STR_CLOSE_RIDE:
status = RideStatus::closed;
break;
case STR_SIMULATE_RIDE:
status = RideStatus::simulating;
break;
case STR_TEST_RIDE:
status = RideStatus::testing;
break;
case STR_OPEN_RIDE:
status = RideStatus::open;
break;
}
status = static_cast<RideStatus>(gDropdown.items[dropdownIndex].value);
}
auto gameAction = GameActions::RideSetStatusAction(ride->id, status);
GameActions::Execute(&gameAction);
@@ -4369,8 +4361,9 @@ namespace OpenRCT2::Ui::Windows
auto numDropdownItems = 2;
gDropdown.items[0] = Dropdown::MenuLabel(STR_ALL_VEHICLES_IN_SAME_COLOURS);
gDropdown.items[1].format = STR_DROPDOWN_MENU_LABEL;
gDropdown.items[1].args.generic = (vehicleTypeName << 16) | STR_DIFFERENT_COLOURS_PER;
Formatter ft;
ft.Add<StringId>(vehicleTypeName);
gDropdown.items[1] = Dropdown::MenuLabel(STR_DIFFERENT_COLOURS_PER, ft);
if (getNumVisibleCars() > 1)
{
@@ -4407,11 +4400,13 @@ namespace OpenRCT2::Ui::Windows
}
}
int64_t vehicleIndex = dropdownIndex + 1;
gDropdown.items[dropdownIndex].format = STR_DROPDOWN_MENU_LABEL;
gDropdown.items[dropdownIndex].args.generic = (vehicleIndex << 32)
| ((GetRideComponentName(ride->getRideTypeDescriptor().NameConvention.vehicle).capitalised) << 16)
| stringId;
uint16_t vehicleIndex = dropdownIndex + 1;
Formatter ft;
ft.Add<StringId>(
GetRideComponentName(ride->getRideTypeDescriptor().NameConvention.vehicle).capitalised);
ft.Add<uint16_t>(vehicleIndex);
gDropdown.items[dropdownIndex] = Dropdown::MenuLabel(stringId, ft);
dropdownIndex++;
}

View File

@@ -315,7 +315,6 @@ namespace OpenRCT2::Ui::Windows
lastType = INFORMATION_TYPE_RUNNING_COST;
int32_t numItems = 0;
int32_t selectedIndex = -1;
for (int32_t type = INFORMATION_TYPE_STATUS; type <= lastType; type++)
{
if ((getGameState().park.flags & PARK_FLAGS_NO_MONEY))
@@ -326,12 +325,12 @@ namespace OpenRCT2::Ui::Windows
}
}
gDropdown.items[numItems] = Dropdown::MenuLabel(ride_info_type_string_mapping[type]);
gDropdown.items[numItems].value = type;
if (type == _windowRideListInformationType)
{
selectedIndex = numItems;
gDropdown.items[numItems].setChecked(true);
}
gDropdown.items[numItems] = Dropdown::MenuLabel(ride_info_type_string_mapping[type]);
numItems++;
}
@@ -342,11 +341,6 @@ namespace OpenRCT2::Ui::Windows
WindowDropdownShowTextCustomWidth(
{ windowPos.x + headerWidget.left, windowPos.y + headerWidget.top }, headerWidget.height(), colours[1], 0,
Dropdown::Flag::StayOpen, numItems, totalWidth);
if (selectedIndex != -1)
{
gDropdown.items[selectedIndex].setChecked(true);
}
}
}
@@ -375,14 +369,9 @@ namespace OpenRCT2::Ui::Windows
return;
int32_t informationType = INFORMATION_TYPE_STATUS;
uint32_t arg = static_cast<uint32_t>(gDropdown.items[dropdownIndex].args.generic);
for (size_t i = 0; i < std::size(ride_info_type_string_mapping); i++)
{
if (arg == ride_info_type_string_mapping[i])
{
informationType = static_cast<int32_t>(i);
}
}
auto selectedValue = gDropdown.items[dropdownIndex].value;
if (selectedValue < std::size(ride_info_type_string_mapping))
informationType = selectedValue;
_windowRideListInformationType = InformationType(informationType);
Invalidate();

View File

@@ -221,8 +221,8 @@ namespace OpenRCT2::Ui::Windows
const auto& listWidget = widgets[WIDX_LIST];
std::array<Dropdown::Item, 2> dropdownItems = {
Dropdown::Item{ STR_JOIN_GAME },
Dropdown::Item{ server.Favourite ? STR_REMOVE_FROM_FAVOURITES : STR_ADD_TO_FAVOURITES },
Dropdown::PlainMenuLabel(STR_JOIN_GAME),
Dropdown::PlainMenuLabel(server.Favourite ? STR_REMOVE_FROM_FAVOURITES : STR_ADD_TO_FAVOURITES),
};
auto dropdownPos = ScreenCoordsXY{