|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
|
|
#include <cstddef>
|
|
|
|
|
#include <cstdint>
|
|
|
|
|
#include <string>
|
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
|
|
#include "DiabloUI/art.h"
|
|
|
|
|
#include "DiabloUI/ui_flags.hpp"
|
|
|
|
|
#include "engine/cel_sprite.hpp"
|
|
|
|
|
#include "engine/pcx_sprite.hpp"
|
|
|
|
|
#include "engine/render/text_render.hpp"
|
|
|
|
|
#include "utils/enum_traits.h"
|
|
|
|
|
#include "utils/stubs.h"
|
|
|
|
|
|
|
|
|
|
namespace devilution {
|
|
|
|
|
|
|
|
|
|
enum class UiType {
|
|
|
|
|
Text,
|
|
|
|
|
ArtText,
|
|
|
|
|
ArtTextButton,
|
|
|
|
|
Image,
|
|
|
|
|
ImageCel,
|
|
|
|
|
ImagePcx,
|
|
|
|
|
ImageAnimatedPcx,
|
|
|
|
|
Button,
|
|
|
|
|
List,
|
|
|
|
|
Scrollbar,
|
|
|
|
|
Edit,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class UiItemBase {
|
|
|
|
|
public:
|
|
|
|
|
virtual ~UiItemBase() = default;
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] UiType GetType() const
|
|
|
|
|
{
|
|
|
|
|
return type_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] bool IsType(UiType testType) const
|
|
|
|
|
{
|
|
|
|
|
return type_ == testType;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] UiFlags GetFlags() const
|
|
|
|
|
{
|
|
|
|
|
return uiFlags_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] bool IsHidden() const
|
|
|
|
|
{
|
|
|
|
|
return HasAnyOf(uiFlags_, UiFlags::ElementHidden);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] bool IsNotInteractive() const
|
|
|
|
|
{
|
|
|
|
|
return HasAnyOf(uiFlags_, UiFlags::ElementHidden | UiFlags::ElementDisabled);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Hide()
|
|
|
|
|
{
|
|
|
|
|
uiFlags_ |= UiFlags::ElementHidden;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Show()
|
|
|
|
|
{
|
|
|
|
|
uiFlags_ &= ~UiFlags::ElementHidden;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
UiItemBase(UiType type, SDL_Rect rect, UiFlags flags)
|
|
|
|
|
: type_(type)
|
|
|
|
|
, m_rect(rect)
|
|
|
|
|
, uiFlags_(flags)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SetFlags(UiFlags flags)
|
|
|
|
|
{
|
|
|
|
|
uiFlags_ = flags;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
UiType type_;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
SDL_Rect m_rect;
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
UiFlags uiFlags_;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//=============================================================================
|
|
|
|
|
|
|
|
|
|
class UiImage : public UiItemBase {
|
|
|
|
|
public:
|
|
|
|
|
UiImage(Art *art, SDL_Rect rect, UiFlags flags = UiFlags::None, bool animated = false, int frame = 0)
|
|
|
|
|
: UiItemBase(UiType::Image, rect, flags)
|
|
|
|
|
, art_(art)
|
|
|
|
|
, animated_(animated)
|
|
|
|
|
, frame_(frame)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] bool IsCentered() const
|
|
|
|
|
{
|
|
|
|
|
return HasAnyOf(GetFlags(), UiFlags::AlignCenter);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] Art *GetArt() const
|
|
|
|
|
{
|
|
|
|
|
return art_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] bool IsAnimated() const
|
|
|
|
|
{
|
|
|
|
|
return animated_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] int GetFrame() const
|
|
|
|
|
{
|
|
|
|
|
return frame_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SetFrame(int frame)
|
|
|
|
|
{
|
|
|
|
|
frame_ = frame;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
Art *art_;
|
|
|
|
|
bool animated_;
|
|
|
|
|
int frame_;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//=============================================================================
|
|
|
|
|
class UiImageCel : public UiItemBase {
|
|
|
|
|
public:
|
|
|
|
|
UiImageCel(CelSpriteWithFrameHeight sprite, SDL_Rect rect, UiFlags flags = UiFlags::None, bool animated = false, int frame = 0)
|
|
|
|
|
: UiItemBase(UiType::ImageCel, rect, flags)
|
|
|
|
|
, sprite_(sprite)
|
|
|
|
|
, animated_(animated)
|
|
|
|
|
, frame_(frame)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] bool IsCentered() const
|
|
|
|
|
{
|
|
|
|
|
return HasAnyOf(GetFlags(), UiFlags::AlignCenter);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] CelSpriteWithFrameHeight GetSprite() const
|
|
|
|
|
{
|
|
|
|
|
return sprite_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] bool IsAnimated() const
|
|
|
|
|
{
|
|
|
|
|
return animated_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] int GetFrame() const
|
|
|
|
|
{
|
|
|
|
|
return frame_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SetFrame(int frame)
|
|
|
|
|
{
|
|
|
|
|
frame_ = frame;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
CelSpriteWithFrameHeight sprite_;
|
|
|
|
|
bool animated_;
|
|
|
|
|
int frame_;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//=============================================================================
|
|
|
|
|
class UiImagePcx : public UiItemBase {
|
|
|
|
|
public:
|
|
|
|
|
UiImagePcx(PcxSprite sprite, SDL_Rect rect, UiFlags flags = UiFlags::None)
|
|
|
|
|
: UiItemBase(UiType::ImagePcx, rect, flags)
|
|
|
|
|
, sprite_(sprite)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] bool IsCentered() const
|
|
|
|
|
{
|
|
|
|
|
return HasAnyOf(GetFlags(), UiFlags::AlignCenter);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] PcxSprite GetSprite() const
|
|
|
|
|
{
|
|
|
|
|
return sprite_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void setSprite(PcxSprite sprite)
|
|
|
|
|
{
|
|
|
|
|
sprite_ = sprite;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
PcxSprite sprite_;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//=============================================================================
|
|
|
|
|
class UiImageAnimatedPcx : public UiItemBase {
|
|
|
|
|
public:
|
|
|
|
|
UiImageAnimatedPcx(PcxSpriteSheet sheet, SDL_Rect rect, UiFlags flags = UiFlags::None)
|
|
|
|
|
: UiItemBase(UiType::ImageAnimatedPcx, rect, flags)
|
|
|
|
|
, sheet_(sheet)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] bool IsCentered() const
|
|
|
|
|
{
|
|
|
|
|
return HasAnyOf(GetFlags(), UiFlags::AlignCenter);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] PcxSprite GetSprite(uint16_t frame) const
|
|
|
|
|
{
|
|
|
|
|
return sheet_.sprite(frame);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] uint16_t NumFrames() const
|
|
|
|
|
{
|
|
|
|
|
return sheet_.numFrames();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
PcxSpriteSheet sheet_;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//=============================================================================
|
|
|
|
|
|
|
|
|
|
class UiArtText : public UiItemBase {
|
|
|
|
|
public:
|
|
|
|
|
/**
|
|
|
|
|
* @brief Constructs a UI element containing a (presumed to be) static line of text
|
|
|
|
|
* @param text Pointer to the first character of a c-string
|
|
|
|
|
* @param rect screen region defining the area to draw the text
|
|
|
|
|
* @param flags UiFlags controlling color/alignment/size
|
|
|
|
|
* @param spacing Spacing between characters
|
|
|
|
|
* @param lineHeight Vertical distance between text lines
|
|
|
|
|
*/
|
|
|
|
|
UiArtText(const char *text, SDL_Rect rect, UiFlags flags = UiFlags::None, int spacing = 1, int lineHeight = -1)
|
|
|
|
|
: UiItemBase(UiType::ArtText, rect, flags)
|
|
|
|
|
, text_(text)
|
|
|
|
|
, spacing_(spacing)
|
|
|
|
|
, lineHeight_(lineHeight)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Constructs a UI element containing a line of text that may change between frames
|
|
|
|
|
* @param ptext Pointer to a c-string (pointer to a pointer to the first character)
|
|
|
|
|
* @param rect screen region defining the area to draw the text
|
|
|
|
|
* @param flags UiFlags controlling color/alignment/size
|
|
|
|
|
* @param spacing Spacing between characters
|
|
|
|
|
* @param lineHeight Vertical distance between text lines
|
|
|
|
|
*/
|
|
|
|
|
UiArtText(const char **ptext, SDL_Rect rect, UiFlags flags = UiFlags::None, int spacing = 1, int lineHeight = -1)
|
|
|
|
|
: UiItemBase(UiType::ArtText, rect, flags)
|
|
|
|
|
, textPointer_(ptext)
|
|
|
|
|
, spacing_(spacing)
|
|
|
|
|
, lineHeight_(lineHeight)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] string_view GetText() const
|
|
|
|
|
{
|
|
|
|
|
if (text_ != nullptr)
|
|
|
|
|
return text_;
|
|
|
|
|
return *textPointer_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] int GetSpacing() const
|
|
|
|
|
{
|
|
|
|
|
return spacing_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] int GetLineHeight() const
|
|
|
|
|
{
|
|
|
|
|
return lineHeight_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
const char *text_ = nullptr;
|
|
|
|
|
const char **textPointer_ = nullptr;
|
|
|
|
|
int spacing_;
|
|
|
|
|
int lineHeight_;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//=============================================================================
|
|
|
|
|
|
|
|
|
|
class UiScrollbar : public UiItemBase {
|
|
|
|
|
public:
|
|
|
|
|
UiScrollbar(PcxSprite bg, PcxSprite thumb, PcxSpriteSheet arrow, SDL_Rect rect, UiFlags flags = UiFlags::None)
|
|
|
|
|
: UiItemBase(UiType::Scrollbar, rect, flags)
|
|
|
|
|
, m_bg(bg)
|
|
|
|
|
, m_thumb(thumb)
|
|
|
|
|
, m_arrow(arrow)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// private:
|
|
|
|
|
PcxSprite m_bg;
|
|
|
|
|
PcxSprite m_thumb;
|
|
|
|
|
PcxSpriteSheet m_arrow;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//=============================================================================
|
|
|
|
|
|
|
|
|
|
class UiArtTextButton : public UiItemBase {
|
|
|
|
|
public:
|
|
|
|
|
using Callback = void (*)();
|
|
|
|
|
|
|
|
|
|
UiArtTextButton(string_view text, Callback action, SDL_Rect rect, UiFlags flags = UiFlags::None)
|
|
|
|
|
: UiItemBase(UiType::ArtTextButton, rect, flags)
|
|
|
|
|
, text_(text)
|
|
|
|
|
, action_(action)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SetFlags(UiFlags flags)
|
|
|
|
|
{
|
|
|
|
|
UiItemBase::SetFlags(flags);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] string_view GetText() const
|
|
|
|
|
{
|
|
|
|
|
return text_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Activate() const
|
|
|
|
|
{
|
|
|
|
|
action_();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
string_view text_;
|
|
|
|
|
Callback action_;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//=============================================================================
|
|
|
|
|
|
|
|
|
|
class UiEdit : public UiItemBase {
|
|
|
|
|
public:
|
|
|
|
|
UiEdit(string_view hint, char *value, std::size_t maxLength, bool allowEmpty, SDL_Rect rect, UiFlags flags = UiFlags::None)
|
|
|
|
|
: UiItemBase(UiType::Edit, rect, flags)
|
|
|
|
|
, m_hint(hint)
|
|
|
|
|
, m_value(value)
|
|
|
|
|
, m_max_length(maxLength)
|
|
|
|
|
, m_allowEmpty(allowEmpty)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// private:
|
|
|
|
|
string_view m_hint;
|
|
|
|
|
char *m_value;
|
|
|
|
|
std::size_t m_max_length;
|
|
|
|
|
bool m_allowEmpty;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//=============================================================================
|
|
|
|
|
|
|
|
|
|
// Plain text
|
|
|
|
|
|
|
|
|
|
class UiText : public UiItemBase {
|
|
|
|
|
public:
|
|
|
|
|
UiText(string_view text, SDL_Rect rect, UiFlags flags = UiFlags::ColorDialogWhite)
|
|
|
|
|
: UiItemBase(UiType::Text, rect, flags)
|
|
|
|
|
, text_(text)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] string_view GetText() const
|
|
|
|
|
{
|
|
|
|
|
return text_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
string_view text_;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//=============================================================================
|
|
|
|
|
|
|
|
|
|
// A button (uses Diablo sprites)
|
|
|
|
|
|
|
|
|
|
class UiButton : public UiItemBase {
|
|
|
|
|
public:
|
|
|
|
|
using Callback = void (*)();
|
|
|
|
|
|
|
|
|
|
UiButton(string_view text, Callback action, SDL_Rect rect, UiFlags flags = UiFlags::None)
|
|
|
|
|
: UiItemBase(UiType::Button, rect, flags)
|
|
|
|
|
, text_(text)
|
|
|
|
|
, action_(action)
|
|
|
|
|
, pressed_(false)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] string_view GetText() const
|
|
|
|
|
{
|
|
|
|
|
return text_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Activate() const
|
|
|
|
|
{
|
|
|
|
|
action_();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] bool IsPressed() const
|
|
|
|
|
{
|
|
|
|
|
return pressed_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Press()
|
|
|
|
|
{
|
|
|
|
|
pressed_ = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Release()
|
|
|
|
|
{
|
|
|
|
|
pressed_ = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
string_view text_;
|
|
|
|
|
Callback action_;
|
|
|
|
|
|
|
|
|
|
// State
|
|
|
|
|
bool pressed_;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//=============================================================================
|
|
|
|
|
|
|
|
|
|
class UiListItem {
|
|
|
|
|
public:
|
|
|
|
|
UiListItem(string_view text = "", int value = 0, UiFlags uiFlags = UiFlags::None)
|
|
|
|
|
: m_text(text)
|
|
|
|
|
, m_value(value)
|
|
|
|
|
, uiFlags(uiFlags)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
UiListItem(string_view text, std::vector<DrawStringFormatArg> &args, int value = 0, UiFlags uiFlags = UiFlags::None)
|
|
|
|
|
: m_text(text)
|
|
|
|
|
, args(args)
|
|
|
|
|
, m_value(value)
|
|
|
|
|
, uiFlags(uiFlags)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// private:
|
|
|
|
|
string_view m_text;
|
|
|
|
|
std::vector<DrawStringFormatArg> args;
|
|
|
|
|
int m_value;
|
|
|
|
|
UiFlags uiFlags;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class UiList : public UiItemBase {
|
|
|
|
|
public:
|
|
|
|
|
using vUiListItem = std::vector<std::unique_ptr<UiListItem>>;
|
|
|
|
|
|
|
|
|
|
UiList(const vUiListItem &vItems, size_t viewportMaxSize, Sint16 x, Sint16 y, Uint16 item_width, Uint16 item_height, UiFlags flags = UiFlags::None, int spacing = 1)
|
|
|
|
|
: UiList(PrivateConstructor {}, vItems, std::min<size_t>(viewportMaxSize, vItems.size()), x, y, item_width, item_height, flags, spacing)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] SDL_Rect itemRect(int i) const
|
|
|
|
|
{
|
|
|
|
|
SDL_Rect tmp;
|
|
|
|
|
tmp.x = m_x;
|
|
|
|
|
tmp.y = m_y + m_height * i;
|
|
|
|
|
tmp.w = m_width;
|
|
|
|
|
tmp.h = m_height;
|
|
|
|
|
|
|
|
|
|
return tmp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] size_t indexAt(Sint16 y) const
|
|
|
|
|
{
|
|
|
|
|
ASSERT(y >= m_rect.y);
|
|
|
|
|
const size_t index = (y - m_rect.y) / m_height;
|
|
|
|
|
ASSERT(index < m_vecItems.size());
|
|
|
|
|
return index;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] UiListItem *GetItem(std::size_t i) const
|
|
|
|
|
{
|
|
|
|
|
return m_vecItems[i];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] int GetSpacing() const
|
|
|
|
|
{
|
|
|
|
|
return spacing_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] bool IsPressed(size_t index) const
|
|
|
|
|
{
|
|
|
|
|
return pressed_item_index_ == index;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Press(size_t index)
|
|
|
|
|
{
|
|
|
|
|
pressed_item_index_ = index;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Release()
|
|
|
|
|
{
|
|
|
|
|
pressed_item_index_ = -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// private:
|
|
|
|
|
size_t viewportSize;
|
|
|
|
|
Sint16 m_x, m_y;
|
|
|
|
|
Uint16 m_width, m_height;
|
|
|
|
|
std::vector<UiListItem *> m_vecItems;
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
struct PrivateConstructor final {
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
UiList(PrivateConstructor tag, const vUiListItem &vItems, size_t viewportSize, Sint16 x, Sint16 y, Uint16 item_width, Uint16 item_height, UiFlags flags, int spacing)
|
|
|
|
|
: UiItemBase(UiType::List, { x, y, item_width, static_cast<Uint16>(item_height * viewportSize) }, flags)
|
|
|
|
|
, viewportSize(viewportSize)
|
|
|
|
|
, m_x(x)
|
|
|
|
|
, m_y(y)
|
|
|
|
|
, m_width(item_width)
|
|
|
|
|
, m_height(item_height)
|
|
|
|
|
, spacing_(spacing)
|
|
|
|
|
{
|
|
|
|
|
for (const auto &item : vItems)
|
|
|
|
|
m_vecItems.push_back(item.get());
|
|
|
|
|
|
|
|
|
|
pressed_item_index_ = -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int spacing_;
|
|
|
|
|
|
|
|
|
|
// State
|
|
|
|
|
size_t pressed_item_index_;
|
|
|
|
|
};
|
|
|
|
|
} // namespace devilution
|