mirror of
https://github.com/OpenRCT2/OpenRCT2.git
synced 2026-05-06 15:59:36 -04:00
45ff80d890
* Allow flag/bitmask coexistence in readers * Add factory helpers and bulk setters for per-widget flags * Switch coexistence from OR-both to per-window opt-in * Migrate seven small windows to per-widget flags * Migrate seven more windows to per-widget flags * Migrate Cheats, MapGen and Footpath to per-widget flags * Fix #26421: scenery tab highlight wraps when there are 64+ groups * Migrate Ride window to per-widget flags * Migrate ride and maze construction windows to per-widget flags * Migrate six more windows to per-widget flags * Migrate Banner, EditorScenarioOptions and RideList to per-widget flags * Migrate SceneryScatter and LoadSave to per-widget flags * Migrate EditorObjectSelection, GameBottomToolbar, Guest, Map, NewRide and Park to per-widget flags * Migrate TopToolbar, TrackList, Research, Player and Options to per-widget flags * Opt in remaining windows to per-widget flags * Narrow getters and setters to per-widget flags * Remove bitmask fields and useWidgetFlags switch * Restore switch statements for tab/density pressed state * Add makeHoldableSpinnerWidgets to drop per-page loops * Update tests for per-widget flag storage * Add changelog entry for #26421 * Reword narrative-tense comments from the migration * Fix clang-format violations * Remove dead holdable-widget tables from Park and Finances
141 lines
5.2 KiB
C++
141 lines
5.2 KiB
C++
/*****************************************************************************
|
|
* Copyright (c) 2014-2026 OpenRCT2 developers
|
|
*
|
|
* For a complete list of all authors, please refer to contributors.md
|
|
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
|
*
|
|
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
|
*****************************************************************************/
|
|
|
|
#include <gtest/gtest.h>
|
|
#include <openrct2-ui/interface/Widget.h>
|
|
#include <openrct2/core/FlagHolder.hpp>
|
|
#include <openrct2/interface/Widget.h>
|
|
#include <openrct2/interface/WindowBase.h>
|
|
#include <span>
|
|
|
|
using namespace OpenRCT2;
|
|
|
|
TEST(WidgetStateTest, WidgetDefaultConstructedHasNoFlags)
|
|
{
|
|
Widget widget{};
|
|
ASSERT_TRUE(widget.flags.isEmpty());
|
|
ASSERT_FALSE(widget.flags.has(WidgetFlag::isPressed));
|
|
ASSERT_FALSE(widget.flags.has(WidgetFlag::isDisabled));
|
|
ASSERT_FALSE(widget.flags.has(WidgetFlag::isHoldable));
|
|
}
|
|
|
|
TEST(WidgetStateTest, FlagHolderSetConditional)
|
|
{
|
|
WidgetFlags flags;
|
|
flags.set(WidgetFlag::isPressed, true);
|
|
ASSERT_TRUE(flags.has(WidgetFlag::isPressed));
|
|
flags.set(WidgetFlag::isPressed, false);
|
|
ASSERT_FALSE(flags.has(WidgetFlag::isPressed));
|
|
}
|
|
|
|
TEST(WidgetStateTest, FlagsAreIndependent)
|
|
{
|
|
WidgetFlags flags;
|
|
flags.set(WidgetFlag::isPressed);
|
|
flags.set(WidgetFlag::isHoldable);
|
|
ASSERT_TRUE(flags.has(WidgetFlag::isPressed));
|
|
ASSERT_TRUE(flags.has(WidgetFlag::isHoldable));
|
|
ASSERT_FALSE(flags.has(WidgetFlag::isDisabled));
|
|
flags.unset(WidgetFlag::isPressed);
|
|
ASSERT_FALSE(flags.has(WidgetFlag::isPressed));
|
|
ASSERT_TRUE(flags.has(WidgetFlag::isHoldable));
|
|
}
|
|
|
|
// Regression guard for #26421: widgets at index >= 64 must round-trip cleanly.
|
|
// The scenery window's 65th tab has widget index 79. A uint64 bitmask indexed
|
|
// by widget index would wrap (1uLL << 79 == 1uLL << 15) and cause the first
|
|
// tab to falsely report as pressed. Per-widget flags have no such cap.
|
|
TEST(WidgetStateTest, HighIndexRoundTripsWithoutShiftWrap)
|
|
{
|
|
WindowBase w;
|
|
w.widgets.resize(300);
|
|
w.widgets[79].flags.set(WidgetFlag::isPressed);
|
|
|
|
ASSERT_TRUE(w.widgets[79].flags.has(WidgetFlag::isPressed));
|
|
// Must not false-positive at any prior shift-wrap alias.
|
|
ASSERT_FALSE(w.widgets[15].flags.has(WidgetFlag::isPressed)); // 79 % 64
|
|
ASSERT_FALSE(w.widgets[143].flags.has(WidgetFlag::isPressed)); // 143 % 64 == 15
|
|
ASSERT_FALSE(w.widgets[271].flags.has(WidgetFlag::isPressed));
|
|
}
|
|
|
|
TEST(WidgetStateTest, SetWidgetsPreservesFlags)
|
|
{
|
|
Widget mutableSource[3] = { Widget{}, Widget{}, Widget{} };
|
|
mutableSource[1].flags.set(WidgetFlag::isPressed);
|
|
mutableSource[1].flags.set(WidgetFlag::isHoldable);
|
|
|
|
WindowBase w;
|
|
w.setWidgets(std::span<const Widget>(mutableSource, 3));
|
|
|
|
ASSERT_EQ(w.widgets.size(), 3u);
|
|
ASSERT_TRUE(w.widgets[1].flags.has(WidgetFlag::isPressed));
|
|
ASSERT_TRUE(w.widgets[1].flags.has(WidgetFlag::isHoldable));
|
|
ASSERT_FALSE(w.widgets[0].flags.has(WidgetFlag::isPressed));
|
|
ASSERT_FALSE(w.widgets[2].flags.has(WidgetFlag::isPressed));
|
|
}
|
|
|
|
TEST(WidgetStateTest, WithFlagSetsFlagWithoutDisturbingOthers)
|
|
{
|
|
using Ui::withFlag;
|
|
|
|
Widget base{};
|
|
base.flags.set(WidgetFlag::isPressed);
|
|
|
|
auto holdable = withFlag(base, WidgetFlag::isHoldable);
|
|
ASSERT_TRUE(holdable.flags.has(WidgetFlag::isPressed));
|
|
ASSERT_TRUE(holdable.flags.has(WidgetFlag::isHoldable));
|
|
ASSERT_FALSE(holdable.flags.has(WidgetFlag::isDisabled));
|
|
}
|
|
|
|
TEST(WidgetStateTest, MakeHoldableWidgetHasHoldableFlag)
|
|
{
|
|
using Ui::makeHoldableWidget;
|
|
using Ui::WindowColour;
|
|
|
|
constexpr auto w = makeHoldableWidget({ 0, 0 }, { 10, 10 }, WidgetType::button, WindowColour::primary);
|
|
static_assert(w.flags.has(WidgetFlag::isHoldable));
|
|
static_assert(!w.flags.has(WidgetFlag::isPressed));
|
|
static_assert(!w.flags.has(WidgetFlag::isDisabled));
|
|
ASSERT_TRUE(w.flags.has(WidgetFlag::isHoldable));
|
|
}
|
|
|
|
TEST(WidgetStateTest, MakeWidgetDefaultHasNoFlags)
|
|
{
|
|
using Ui::makeWidget;
|
|
using Ui::WindowColour;
|
|
|
|
constexpr auto w = makeWidget({ 0, 0 }, { 10, 10 }, WidgetType::button, WindowColour::primary);
|
|
static_assert(w.flags.isEmpty());
|
|
ASSERT_TRUE(w.flags.isEmpty());
|
|
}
|
|
|
|
TEST(WidgetStateTest, MakeSpinnerWidgetsHasNoHoldableFlag)
|
|
{
|
|
using Ui::makeSpinnerWidgets;
|
|
using Ui::WindowColour;
|
|
|
|
constexpr auto widgets = makeSpinnerWidgets({ 0, 0 }, { 100, 14 }, WidgetType::spinner, WindowColour::primary);
|
|
static_assert(!widgets[0].flags.has(WidgetFlag::isHoldable));
|
|
static_assert(!widgets[1].flags.has(WidgetFlag::isHoldable));
|
|
static_assert(!widgets[2].flags.has(WidgetFlag::isHoldable));
|
|
}
|
|
|
|
TEST(WidgetStateTest, MakeHoldableSpinnerWidgetsHasHoldableOnIncrementButtons)
|
|
{
|
|
using Ui::makeHoldableSpinnerWidgets;
|
|
using Ui::WindowColour;
|
|
|
|
constexpr auto widgets = makeHoldableSpinnerWidgets({ 0, 0 }, { 100, 14 }, WidgetType::spinner, WindowColour::primary);
|
|
// Element [0] is the spinner input field; increment/decrement buttons are
|
|
// [1] and [2] and must be holdable for press-and-hold repeat.
|
|
static_assert(!widgets[0].flags.has(WidgetFlag::isHoldable));
|
|
static_assert(widgets[1].flags.has(WidgetFlag::isHoldable));
|
|
static_assert(widgets[2].flags.has(WidgetFlag::isHoldable));
|
|
}
|