OpenRCT2/src/openrct2-ui/windows/AssetPacks.cpp

349 lines
11 KiB
C++

/*****************************************************************************
* Copyright (c) 2014-2022 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 <openrct2-ui/interface/Widget.h>
#include <openrct2-ui/windows/Window.h>
#include <openrct2/AssetPack.h>
#include <openrct2/AssetPackManager.h>
#include <openrct2/Context.h>
#include <openrct2/drawing/Drawing.h>
#include <openrct2/drawing/Text.h>
#include <openrct2/localisation/Localisation.h>
#include <openrct2/object/ObjectManager.h>
#include <openrct2/sprites.h>
using namespace OpenRCT2;
static constexpr const StringId WINDOW_TITLE = STR_ASSET_PACKS;
static constexpr const int32_t WW = 400;
static constexpr const int32_t WH = 200;
// clang-format off
enum WindowAssetPacksWidgetIdx {
WIDX_BACKGROUND,
WIDX_TITLE,
WIDX_CLOSE,
WIDX_HIGH_LABEL,
WIDX_LIST,
WIDX_LOW_LABEL,
WIDX_MOVE_UP,
WIDX_MOVE_DOWN,
WIDX_APPLY,
};
static rct_widget WindowAssetPacksWidgets[] = {
WINDOW_SHIM(WINDOW_TITLE, WW, WH),
MakeWidget({ 0, 0 }, { 0, 0 }, WindowWidgetType::LabelCentred, WindowColour::Secondary, STR_HIGH_PRIORITY),
MakeWidget({ 0, 0 }, { 0, 0 }, WindowWidgetType::Scroll, WindowColour::Secondary, SCROLL_VERTICAL),
MakeWidget({ 0, 0 }, { 0, 0 }, WindowWidgetType::LabelCentred, WindowColour::Secondary, STR_LOW_PRIORITY),
MakeWidget({ 0, 0 }, { 0, 0 }, WindowWidgetType::FlatBtn, WindowColour::Secondary, SPR_G2_ARROW_UP, STR_DECREASE_PRIOTITY_TIP),
MakeWidget({ 0, 0 }, { 0, 0 }, WindowWidgetType::FlatBtn, WindowColour::Secondary, SPR_G2_ARROW_DOWN, STR_INCREASE_PRIOTITY_TIP),
MakeWidget({ 0, 0 }, { 0, 0 }, WindowWidgetType::FlatBtn, WindowColour::Secondary, SPR_G2_RELOAD, STR_RELOAD_ASSET_PACKS_TIP),
WIDGETS_END,
};
// clang-format on
class AssetPacksWindow final : public Window
{
private:
static constexpr int32_t ItemHeight = SCROLLABLE_ROW_HEIGHT + 1;
static constexpr int32_t ItemCheckBoxSize = ItemHeight - 3;
std::optional<size_t> _highlightedIndex;
std::optional<size_t> _selectedIndex;
public:
void OnOpen() override
{
widgets = WindowAssetPacksWidgets;
WindowInitScrollWidgets(*this);
}
void OnClose() override
{
Apply();
}
void OnMouseUp(WidgetIndex widgetIndex) override
{
switch (widgetIndex)
{
case WIDX_CLOSE:
Close();
break;
case WIDX_MOVE_UP:
ReorderSelectedAssetPack(-1);
break;
case WIDX_MOVE_DOWN:
ReorderSelectedAssetPack(1);
break;
case WIDX_APPLY:
Apply();
break;
}
}
ScreenSize OnScrollGetSize(int32_t scrollIndex) override
{
ScreenSize result;
auto assetPackManager = GetContext()->GetAssetPackManager();
if (assetPackManager != nullptr)
{
auto numAssetPacks = assetPackManager->GetCount() + 1; // +1 for the base assets item
result.height = static_cast<int32_t>(numAssetPacks * ItemHeight);
}
if (_highlightedIndex)
{
_highlightedIndex = {};
Invalidate();
}
return result;
}
void OnScrollMouseDown(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override
{
auto isCheckBox = false;
auto index = GetAssetPackIndexFromPosition(screenCoords, isCheckBox);
// Click on checkbox
if (index && isCheckBox)
{
auto assetPackManager = GetContext()->GetAssetPackManager();
if (assetPackManager != nullptr)
{
auto assetPack = assetPackManager->GetAssetPack(*index);
if (assetPack != nullptr)
{
assetPack->SetEnabled(!assetPack->IsEnabled());
Invalidate();
}
}
}
// Select item
if (_selectedIndex != index)
{
_selectedIndex = index;
Invalidate();
}
}
void OnScrollMouseOver(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override
{
auto isCheckBox = false;
auto index = GetAssetPackIndexFromPosition(screenCoords, isCheckBox);
if (_highlightedIndex != index)
{
_highlightedIndex = index;
Invalidate();
}
}
void OnPrepareDraw() override
{
widgets[WIDX_BACKGROUND].right = width - 1;
widgets[WIDX_BACKGROUND].bottom = height - 1;
widgets[WIDX_TITLE].right = width - 2;
widgets[WIDX_CLOSE].left = width - 13;
widgets[WIDX_CLOSE].right = width - 3;
auto& list = widgets[WIDX_LIST];
list.left = 6;
list.top = 20 + 11 + 3;
list.right = width - 2 - 24 - 1;
list.bottom = height - 6 - 11 - 3;
widgets[WIDX_HIGH_LABEL].bottom = list.top - 1;
widgets[WIDX_HIGH_LABEL].top = widgets[WIDX_HIGH_LABEL].bottom - 11 - 3;
widgets[WIDX_HIGH_LABEL].left = list.left;
widgets[WIDX_HIGH_LABEL].right = list.right;
widgets[WIDX_LOW_LABEL].top = list.bottom + 1 + 3;
widgets[WIDX_LOW_LABEL].bottom = widgets[WIDX_LOW_LABEL].top + 11 + 3;
widgets[WIDX_LOW_LABEL].left = list.left;
widgets[WIDX_LOW_LABEL].right = list.right;
auto toolstripY = 20;
auto toolstripRight = width - 2;
auto toolstripLeft = toolstripRight - 24;
for (WidgetIndex i = WIDX_MOVE_UP; i <= WIDX_APPLY; i++)
{
widgets[i].top = toolstripY;
widgets[i].bottom = toolstripY + 24;
widgets[i].left = toolstripLeft;
widgets[i].right = toolstripRight;
toolstripY += 24;
}
SetWidgetDisabled(WIDX_MOVE_UP, !_selectedIndex || _selectedIndex == 0U);
SetWidgetDisabled(WIDX_MOVE_DOWN, !_selectedIndex || _selectedIndex >= GetNumAssetPacks() - 1);
widgets[WIDX_APPLY].bottom = widgets[WIDX_LIST].bottom;
widgets[WIDX_APPLY].top = widgets[WIDX_APPLY].bottom - 24;
}
void OnDraw(rct_drawpixelinfo& dpi) override
{
DrawWidgets(dpi);
}
void OnScrollDraw(int32_t scrollIndex, rct_drawpixelinfo& dpi) override
{
auto dpiCoords = ScreenCoordsXY{ dpi.x, dpi.y };
gfx_fill_rect(
&dpi, { dpiCoords, dpiCoords + ScreenCoordsXY{ dpi.width - 1, dpi.height - 1 } }, ColourMapA[colours[1]].mid_light);
auto assetPackManager = GetContext()->GetAssetPackManager();
if (assetPackManager == nullptr)
return;
auto numAssetPacks = assetPackManager->GetCount();
auto y = 0;
for (size_t i = 0; i <= numAssetPacks; i++)
{
if (y > dpi.y + dpi.height)
break;
if (y + 11 < dpi.y)
continue;
auto isSelected = i == _selectedIndex;
auto isHighlighted = i == _highlightedIndex;
if (i == numAssetPacks)
{
auto ft = Formatter();
ft.Add<StringId>(STR_BASE_GRAPHICS_MUSIC_SOUND);
PaintItem(dpi, y, ft, true, isSelected, isHighlighted);
}
else
{
auto assetPack = assetPackManager->GetAssetPack(i);
if (assetPack != nullptr)
{
auto isChecked = assetPack->IsEnabled();
auto ft = Formatter();
ft.Add<StringId>(STR_STRING);
ft.Add<const char*>(assetPack->Name.c_str());
PaintItem(dpi, y, ft, isChecked, isSelected, isHighlighted);
}
}
y += ItemHeight;
}
}
private:
void PaintItem(rct_drawpixelinfo& dpi, int32_t y, Formatter& ft, bool isChecked, bool isSelected, bool isHighlighted)
{
auto listWidth = dpi.width - 1;
auto stringId = STR_BLACK_STRING;
auto fillRectangle = ScreenRect{ { 0, y }, { listWidth, y + ItemHeight - 1 } };
if (isSelected)
{
gfx_fill_rect(&dpi, fillRectangle, ColourMapA[colours[1]].mid_dark);
stringId = STR_WINDOW_COLOUR_2_STRINGID;
}
else if (isHighlighted)
{
gfx_fill_rect(&dpi, fillRectangle, ColourMapA[colours[1]].mid_dark);
}
DrawTextEllipsised(&dpi, { 16, y + 1 }, listWidth, stringId, ft);
auto checkboxSize = ItemHeight - 3;
PaintCheckbox(dpi, { { 2, y + 1 }, { 2 + checkboxSize + 1, y + 1 + checkboxSize } }, isChecked);
}
void PaintCheckbox(rct_drawpixelinfo& dpi, const ScreenRect& rect, bool checked)
{
gfx_fill_rect_inset(&dpi, rect, colours[1], INSET_RECT_F_E0);
if (checked)
{
auto checkmark = Formatter();
checkmark.Add<StringId>(STR_STRING);
checkmark.Add<char*>(CheckBoxMarkString);
DrawTextBasic(&dpi, ScreenCoordsXY{ rect.GetLeft() + 1, rect.GetTop() }, STR_WINDOW_COLOUR_2_STRINGID, checkmark);
}
}
std::optional<size_t> GetAssetPackIndexFromPosition(const ScreenCoordsXY& pos, bool& isCheckBox)
{
const auto index = pos.y / ItemHeight;
if (index < 0 || static_cast<size_t>(index) >= GetNumAssetPacks())
return std::nullopt;
isCheckBox = pos.x >= 2 && pos.x <= 2 + ItemCheckBoxSize + 1;
return static_cast<size_t>(index);
}
size_t GetNumAssetPacks() const
{
auto assetPackManager = GetContext()->GetAssetPackManager();
if (assetPackManager == nullptr)
return 0;
return assetPackManager->GetCount();
}
bool IsSelectedAssetPackEnabled() const
{
if (_selectedIndex)
{
auto assetPackManager = GetContext()->GetAssetPackManager();
if (assetPackManager != nullptr)
{
auto assetPack = assetPackManager->GetAssetPack(*_selectedIndex);
if (assetPack != nullptr)
{
return assetPack->IsEnabled();
}
}
}
return false;
}
void ReorderSelectedAssetPack(int32_t direction)
{
if (!_selectedIndex)
return;
auto assetPackManager = GetContext()->GetAssetPackManager();
if (assetPackManager == nullptr)
return;
if (direction < 0 && *_selectedIndex > 0)
{
assetPackManager->Swap(*_selectedIndex, *_selectedIndex - 1);
(*_selectedIndex)--;
Invalidate();
}
else if (direction > 0 && *_selectedIndex < assetPackManager->GetCount() - 1)
{
assetPackManager->Swap(*_selectedIndex, *_selectedIndex + 1);
(*_selectedIndex)++;
Invalidate();
}
}
void Apply()
{
auto& objectManager = GetContext()->GetObjectManager();
objectManager.ResetObjects();
auto assetPackManager = GetContext()->GetAssetPackManager();
if (assetPackManager != nullptr)
{
assetPackManager->SaveEnabledAssetPacks();
}
}
};
rct_window* WindowAssetPacksOpen()
{
auto flags = WF_AUTO_POSITION | WF_CENTRE_SCREEN;
return WindowFocusOrCreate<AssetPacksWindow>(WindowClass::AssetPacks, WW, WH, flags);
}