You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
456 lines
9.6 KiB
456 lines
9.6 KiB
#pragma once |
|
|
|
#include <cstddef> |
|
#include <cstdint> |
|
#include <string> |
|
#include <vector> |
|
|
|
#include "DiabloUI/ui_flags.hpp" |
|
#include "engine/clx_sprite.hpp" |
|
#include "engine/render/text_render.hpp" |
|
#include "utils/enum_traits.h" |
|
#include "utils/stubs.h" |
|
|
|
namespace devilution { |
|
|
|
enum class UiType : uint8_t { |
|
Text, |
|
ArtText, |
|
ArtTextButton, |
|
ImageClx, |
|
ImageAnimatedClx, |
|
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 UiImageClx : public UiItemBase { |
|
public: |
|
UiImageClx(ClxSprite sprite, SDL_Rect rect, UiFlags flags = UiFlags::None) |
|
: UiItemBase(UiType::ImageClx, rect, flags) |
|
, sprite_(sprite) |
|
{ |
|
} |
|
|
|
[[nodiscard]] bool isCentered() const |
|
{ |
|
return HasAnyOf(GetFlags(), UiFlags::AlignCenter); |
|
} |
|
|
|
[[nodiscard]] ClxSprite sprite() const |
|
{ |
|
return sprite_; |
|
} |
|
|
|
void setSprite(ClxSprite sprite) |
|
{ |
|
sprite_ = sprite; |
|
} |
|
|
|
private: |
|
ClxSprite sprite_; |
|
}; |
|
|
|
//============================================================================= |
|
class UiImageAnimatedClx : public UiItemBase { |
|
public: |
|
UiImageAnimatedClx(ClxSpriteList list, SDL_Rect rect, UiFlags flags = UiFlags::None) |
|
: UiItemBase(UiType::ImageAnimatedClx, rect, flags) |
|
, list_(list) |
|
{ |
|
} |
|
|
|
[[nodiscard]] bool isCentered() const |
|
{ |
|
return HasAnyOf(GetFlags(), UiFlags::AlignCenter); |
|
} |
|
|
|
[[nodiscard]] ClxSprite sprite(uint16_t frame) const |
|
{ |
|
return list_[frame]; |
|
} |
|
|
|
[[nodiscard]] uint16_t numFrames() const |
|
{ |
|
return list_.numSprites(); |
|
} |
|
|
|
private: |
|
ClxSpriteList list_; |
|
}; |
|
|
|
//============================================================================= |
|
|
|
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(ClxSprite bg, ClxSprite thumb, ClxSpriteList arrow, SDL_Rect rect, UiFlags flags = UiFlags::None) |
|
: UiItemBase(UiType::Scrollbar, rect, flags) |
|
, m_bg(bg) |
|
, m_thumb(thumb) |
|
, m_arrow(arrow) |
|
{ |
|
} |
|
|
|
// private: |
|
ClxSprite m_bg; |
|
ClxSprite m_thumb; |
|
ClxSpriteList 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
|
|
|