Browse Source

DiabloUI: Implement some dialogs

Partially addresses #303
pull/314/head
Gleb Mazovetskiy 7 years ago committed by Anders Jenbo
parent
commit
458fbcef88
  1. 1
      CMakeLists.txt
  2. 53
      SourceX/DiabloUI/button.cpp
  3. 52
      SourceX/DiabloUI/button.h
  4. 100
      SourceX/DiabloUI/diabloui.cpp
  5. 7
      SourceX/DiabloUI/diabloui.h
  6. 145
      SourceX/DiabloUI/dialogs.cpp
  7. 12
      SourceX/DiabloUI/dialogs.h
  8. 6
      SourceX/DiabloUI/mainmenu.cpp
  9. 8
      SourceX/DiabloUI/progress.cpp
  10. 18
      SourceX/DiabloUI/selconn.cpp
  11. 50
      SourceX/DiabloUI/selgame.cpp
  12. 46
      SourceX/DiabloUI/selhero.cpp
  13. 10
      SourceX/DiabloUI/selyesno.cpp
  14. 6
      SourceX/DiabloUI/text.cpp
  15. 10
      SourceX/DiabloUI/text.h
  16. 62
      SourceX/DiabloUI/text_draw.cpp
  17. 15
      SourceX/DiabloUI/text_draw.h
  18. 2
      SourceX/DiabloUI/title.cpp
  19. 124
      SourceX/DiabloUI/ui_item.h

1
CMakeLists.txt

@ -200,6 +200,7 @@ set(devilutionx_SRCS
SourceX/dvlnet/frame_queue.cpp
SourceX/DiabloUI/art_draw.cpp
SourceX/DiabloUI/art.cpp
SourceX/DiabloUI/button.cpp
SourceX/DiabloUI/credits.cpp
SourceX/DiabloUI/diabloui.cpp
SourceX/DiabloUI/dialogs.cpp

53
SourceX/DiabloUI/button.cpp

@ -0,0 +1,53 @@
#include "DiabloUI/button.h"
#include "DiabloUI/art_draw.h"
#include "DiabloUI/text_draw.h"
namespace dvl {
Art SmlButton;
void LoadSmlButtonArt()
{
LoadArt("ui_art\\but_sml.pcx", &SmlButton, 15);
}
void RenderButton(UiButton *button)
{
int frame;
if (button->has_flag(UIS_DISABLED)) {
frame = button->frame_map[UiButton::DISABLED];
} else if (button->pressed) {
frame = button->frame_map[UiButton::PRESSED];
} else {
frame = button->frame_map[UiButton::DEFAULT];
}
DrawArt(button->rect.x, button->rect.y, button->art, frame, button->rect.w, button->rect.h);
SDL_Rect text_rect = button->rect;
if (!button->pressed) --text_rect.y;
DrawTTF(button->text, text_rect, UIS_CENTER,
SDL_Color{ 243, 243, 243, 0 }, SDL_Color{ 0, 0, 0, 0 }, &button->render_cache);
}
bool HandleMouseEventButton(const SDL_Event &event, UiButton *button)
{
if (event.button.button != SDL_BUTTON_LEFT)
return false;
switch (event.type) {
case SDL_MOUSEBUTTONUP:
button->action();
return true;
case SDL_MOUSEBUTTONDOWN:
button->pressed = true;
return true;
default:
return false;
}
}
void HandleGlobalMouseUpButton(UiButton *button)
{
button->pressed = false;
}
} // namespace dvl

52
SourceX/DiabloUI/button.h

@ -0,0 +1,52 @@
#pragma once
#include "DiabloUI/art.h"
#include "DiabloUI/ui_item.h"
namespace dvl {
extern Art SmlButton;
void LoadSmlButtonArt();
inline void UnloadSmlButtonArt()
{
SmlButton.Unload();
}
constexpr decltype(SDL_Rect().w) SML_BUTTON_WIDTH = 110;
constexpr decltype(SDL_Rect().h) SML_BUTTON_HEIGHT = 27;
enum class ButtonFrame {
BG_GOLD = 0,
BG_GOLD_PRESSED,
BG_YELLOW,
BG_YELLOW_PRESSED,
DISABLED,
};
constexpr UiButton::FrameMap ButtonBgYellowFrameMap = {
static_cast<int>(ButtonFrame::BG_YELLOW),
static_cast<int>(ButtonFrame::BG_YELLOW_PRESSED),
static_cast<int>(ButtonFrame::DISABLED),
};
constexpr UiButton::FrameMap ButtonBgGoldFrameMap = {
static_cast<int>(ButtonFrame::BG_GOLD),
static_cast<int>(ButtonFrame::BG_GOLD_PRESSED),
static_cast<int>(ButtonFrame::DISABLED),
};
constexpr UiButton MakeSmlButton(
const char *text, void (*action)(), decltype(SDL_Rect().x) x, decltype(SDL_Rect().y) y, int flags = 0)
{
return UiButton(
&SmlButton,
(flags & UIS_GOLD) ? ButtonBgGoldFrameMap : ButtonBgYellowFrameMap,
text,
action,
SDL_Rect{ x, y, SML_BUTTON_WIDTH, SML_BUTTON_HEIGHT },
flags);
}
void RenderButton(UiButton *button);
bool HandleMouseEventButton(const SDL_Event &event, UiButton *button);
void HandleGlobalMouseUpButton(UiButton *button);
} // namespace dvl

100
SourceX/DiabloUI/diabloui.cpp

@ -11,6 +11,7 @@
#include "DiabloUI/art_draw.h"
#include "DiabloUI/text_draw.h"
#include "DiabloUI/fonts.h"
#include "DiabloUI/button.h"
namespace dvl {
@ -379,10 +380,7 @@ bool UiFocusNavigation(SDL_Event *event)
}
}
const bool mouse_handled = gUiItems && gUiItemCnt && UiItemMouseEvents(event, gUiItems, gUiItemCnt);
if (event->type == SDL_MOUSEBUTTONUP && event->button.button == SDL_BUTTON_LEFT)
scrollBarState.downArrowPressed = scrollBarState.upArrowPressed = false;
if (mouse_handled)
if (UiItemMouseEvents(event, gUiItems, gUiItemCnt))
return true;
if (gfnListEsc && event->type == SDL_KEYDOWN && event->key.keysym.sym == SDLK_ESCAPE) {
@ -634,23 +632,37 @@ void DrawSelector(const SDL_Rect &rect)
DrawArt(rect.x + rect.w - art->w(), y, art, frame);
}
void UiRender()
{
void UiPollAndRender() {
SDL_Event event;
while (SDL_PollEvent(&event)) {
UiFocusNavigation(&event);
}
UiRenderItems(gUiItems, gUiItemCnt);
UiRender(gUiItems, gUiItemCnt);
UiFadeIn();
}
void UiRender(UiItem *items, std::size_t size)
{
UiRenderItems(items, size);
DrawLogo();
DrawMouse();
UiFadeIn();
}
namespace {
void Render(const UiText &ui_text)
void Render(UiText *ui_text)
{
DrawArtStr(ui_text.text, ui_text.rect, ui_text.flags);
DrawTTF(ui_text->text,
ui_text->rect,
ui_text->flags,
ui_text->color,
ui_text->shadow_color,
&ui_text->render_cache);
}
void Render(const UiArtText &ui_art_text)
{
DrawArtStr(ui_art_text.text, ui_art_text.rect, ui_art_text.flags);
}
void Render(const UiImage &ui_image)
@ -716,28 +728,34 @@ void Render(const UiEdit &ui_edit)
DrawArtStr(ui_edit.value, rect, ui_edit.flags, /*drawTextCursor=*/true);
}
void RenderItem(const UiItem &item)
void RenderItem(UiItem *item)
{
if (item.has_flag(UIS_HIDDEN))
if (item->has_flag(UIS_HIDDEN))
return;
switch (item.type) {
switch (item->type) {
case UI_TEXT:
Render(item.text);
Render(&item->text);
break;
case UI_ART_TEXT:
Render(item->art_text);
break;
case UI_IMAGE:
Render(item.image);
Render(item->image);
break;
case UI_ART_TEXT_BUTTON:
Render(item.art_text_button);
Render(item->art_text_button);
break;
case UI_BUTTON:
RenderButton(&item->button);
break;
case UI_LIST:
Render(item.list);
Render(item->list);
break;
case UI_SCROLLBAR:
Render(item.scrollbar);
Render(item->scrollbar);
break;
case UI_EDIT:
Render(item.edit);
Render(item->edit);
break;
}
}
@ -801,17 +819,19 @@ bool HandleMouseEventScrollBar(const SDL_Event &event, const UiScrollBar &ui_sb)
return false;
}
bool HandleMouseEvent(const SDL_Event &event, const UiItem &item)
bool HandleMouseEvent(const SDL_Event &event, UiItem *item)
{
if (item.has_flag(UIS_HIDDEN) || !IsInsideRect(event, item.rect()))
if (item->has_any_flag(UIS_HIDDEN | UIS_DISABLED) || !IsInsideRect(event, item->rect()))
return false;
switch (item.type) {
switch (item->type) {
case UI_ART_TEXT_BUTTON:
return HandleMouseEventArtTextButton(event, item.art_text_button);
return HandleMouseEventArtTextButton(event, item->art_text_button);
case UI_BUTTON:
return HandleMouseEventButton(event, &item->button);
case UI_LIST:
return HandleMouseEventList(event, item.list);
return HandleMouseEventList(event, item->list);
case UI_SCROLLBAR:
return HandleMouseEventScrollBar(event, item.scrollbar);
return HandleMouseEventScrollBar(event, item->scrollbar);
default:
return false;
}
@ -819,20 +839,34 @@ bool HandleMouseEvent(const SDL_Event &event, const UiItem &item)
} // namespace
void UiRenderItems(UiItem *items, int size)
void UiRenderItems(UiItem *items, std::size_t size)
{
for (int i = 0; i < size; i++)
RenderItem(items[i]);
for (std::size_t i = 0; i < size; i++)
RenderItem(&items[i]);
}
bool UiItemMouseEvents(SDL_Event *event, UiItem *items, int size)
bool UiItemMouseEvents(SDL_Event *event, UiItem *items, std::size_t size)
{
for (int i = 0; i < size; i++) {
if (HandleMouseEvent(*event, items[i]))
return true;
if (!items || size == 0) return false;
bool handled = false;
for (std::size_t i = 0; i < size; i++) {
if (HandleMouseEvent(*event, &items[i])) {
handled = true;
break;
}
}
return false;
if (event->type == SDL_MOUSEBUTTONUP && event->button.button == SDL_BUTTON_LEFT) {
scrollBarState.downArrowPressed = scrollBarState.upArrowPressed = false;
for (std::size_t i = 0; i < size; ++i) {
UiItem &item = items[i];
if (item.type == UI_BUTTON)
HandleGlobalMouseUpButton(&item.button);
}
}
return handled;
}
void DrawLogo(int t, int size)

7
SourceX/DiabloUI/diabloui.h

@ -38,7 +38,7 @@ extern void (*gfnSoundFunction)(char *file);
bool IsInsideRect(const SDL_Event &event, const SDL_Rect &rect);
void UiFadeIn(int steps = 16);
bool UiFocusNavigation(SDL_Event *event);
bool UiItemMouseEvents(SDL_Event *event, UiItem *items, int size);
bool UiItemMouseEvents(SDL_Event *event, UiItem *items, std::size_t size);
int GetCenterOffset(int w, int bw = 0);
void DrawLogo(int t = 0, int size = LOGO_MED);
void DrawMouse();
@ -49,8 +49,9 @@ void UiFocusNavigationEsc();
void UiFocusNavigationYesNo();
void UiInitList(int min, int max, void (*fnFocus)(int value), void (*fnSelect)(int value), void (*fnEsc)(), UiItem *items, int size, bool wraps = false, bool (*fnYesNo)() = NULL);
void UiInitScrollBar(UiScrollBar *ui_sb, std::size_t viewport_size, const std::size_t *current_offset);
void UiRender();
void UiRenderItems(UiItem *items, int size);
void UiPollAndRender();
void UiRender(UiItem *items, std::size_t size);
void UiRenderItems(UiItem *items, std::size_t size);
void DvlIntSetting(const char *valuename, int *value);
void DvlStringSetting(const char *valuename, char *string, int len);

145
SourceX/DiabloUI/dialogs.cpp

@ -1,46 +1,79 @@
#include "DiabloUI/dialogs.h"
#include "devilution.h"
#include "DiabloUI/diabloui.h"
#include "DiabloUI/button.h"
#include "DiabloUI/fonts.h"
namespace dvl {
Art dialogArt; // "ui_art\\spopup.pcx"
char dialogMessage[256];
extern SDL_Surface *pal_surface;
namespace {
Art progressArt; // "ui_art\\prog_bg.pcx"
Art dialogArt;
Art progressArt;
char dialogText[256];
char dialogCaption[256];
bool fontWasLoaded;
bool textInputWasActive;
UiItem *dialogItems;
std::size_t dialogItemsSize;
enum class State {
DEFAULT = 0,
OK,
CANCEL,
};
State state;
void DialogActionOK()
{
state = State::OK;
}
void DialogActionCancel()
{
state = State::CANCEL;
}
constexpr auto DIALOG_ART_S = UiImage(&dialogArt, { 180, 168, 280, 144 });
constexpr auto DIALOG_ART_L = UiImage(&dialogArt, { 128, 168, 385, 280 });
UiItem OKCANCEL_DIALOG[] = {
UiImage(&dialogArt, { 180, 168, 280, 144 }),
UiText(dialogMessage, { 200, 180, 240, 80 }, UIS_CENTER),
UiArtTextButton("OK", &DialogActionOK, { 200, 265, 110, 28 }, UIS_SML1),
UiArtTextButton("Cancel", &DialogActionCancel, { 330, 265, 110, 28 }, UIS_SML2),
DIALOG_ART_S,
UiText(dialogText, { 200, 200, 240, 80 }, UIS_CENTER),
MakeSmlButton("OK", &DialogActionOK, 200, 265),
MakeSmlButton("Cancel", &DialogActionCancel, 330, 265),
};
UiItem OK_DIALOG[] = {
UiImage(&dialogArt, { 180, 168, 280, 144 }),
UiText(dialogMessage, { 200, 180, 240, 80 }, UIS_CENTER),
UiArtTextButton("OK", &DialogActionOK, { 200, 265, 110, 28 }, UIS_SML1),
DIALOG_ART_S,
UiText(dialogText, { 200, 200, 240, 80 }, UIS_CENTER),
MakeSmlButton("OK", &DialogActionOK, 266, 265),
};
UiItem OK_DIALOG_WITH_CAPTION[] = {
DIALOG_ART_L,
UiText(dialogText, { 200, 200, 240, 80 }, UIS_CENTER),
UiText(dialogCaption, { 200, 280, 240, 80 }, UIS_CENTER),
MakeSmlButton("OK", &DialogActionOK, 266, 401),
};
UiItem PROGRESS_DIALOG[] = {
UiImage(&dialogArt, { 180, 168, 280, 144 }),
UiText(dialogMessage, { 180, 177, 280, 43 }, UIS_CENTER),
DIALOG_ART_S,
UiText(dialogText, { 180, 177, 280, 43 }, UIS_CENTER),
UiImage(&progressArt, { 205, 220, 228, 38 }),
UiArtTextButton("Cancel", &DialogActionCancel, { 265, 267, 110, 28 }, UIS_SML1),
MakeSmlButton("Cancel", &DialogActionCancel, 330, 265),
};
UiListItem SELOK_DIALOG_ITEMS[] = {
{ "OK", 0 }
};
UiItem SELOK_DIALOG[] = {
UiText(dialogMessage, { 140, 210, 400, 168 }, UIS_CENTER),
UiText(dialogText, { 140, 210, 400, 168 }, UIS_CENTER),
UiList(SELOK_DIALOG_ITEMS, 230, 390, 180, 35, UIS_CENTER),
};
@ -49,4 +82,88 @@ UiItem SPAWNERR_DIALOG[] = {
UiArtTextButton("OK", &DialogActionOK, { 230, 407, 180, 43 }),
};
void Init(const char *text, const char *caption, bool error)
{
strcpy(dialogText, text);
if (caption == nullptr) {
LoadMaskedArt(error ? "ui_art\\srpopup.pcx" : "ui_art\\spopup.pcx", &dialogArt);
dialogItems = OK_DIALOG;
dialogItemsSize = size(OK_DIALOG);
} else {
LoadMaskedArt(error ? "ui_art\\lrpopup.pcx" : "ui_art\\lpopup.pcx", &dialogArt);
strcpy(dialogCaption, caption);
dialogItems = OK_DIALOG_WITH_CAPTION;
dialogItemsSize = size(OK_DIALOG_WITH_CAPTION);
}
LoadSmlButtonArt();
fontWasLoaded = font != nullptr;
if (!fontWasLoaded)
LoadTtfFont();
textInputWasActive = SDL_IsTextInputActive();
SDL_StopTextInput();
}
void Deinit()
{
dialogArt.Unload();
UnloadSmlButtonArt();
if (!fontWasLoaded)
UnloadTtfFont();
if (textInputWasActive)
SDL_StartTextInput();
for (std::size_t i = 0; i < dialogItemsSize; ++i) {
dialogItems[i].FreeCache();
}
}
void DialogLoop(UiItem *items, std::size_t num_items, UiItem *render_behind, std::size_t render_behind_size)
{
SDL_Event event;
state = State::DEFAULT;
if (render_behind_size == 0)
SDL_FillRect(pal_surface, nullptr, 0);
do {
while (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_KEYDOWN:
switch (event.key.keysym.sym) {
case SDLK_ESCAPE:
case SDLK_RETURN:
case SDLK_KP_ENTER:
case SDLK_SPACE:
state = State::OK;
break;
default:
break;
}
break;
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
UiItemMouseEvents(&event, items, num_items);
break;
case SDL_QUIT:
exit(0);
}
}
UiRenderItems(render_behind, render_behind_size);
UiRender(items, num_items);
UiFadeIn();
} while (state == State::DEFAULT);
}
} // namespace
void UiErrorOkDialog(const char *text, const char *caption, UiItem *render_behind, std::size_t render_behind_size)
{
Init(text, caption, /*error=*/true);
DialogLoop(dialogItems, dialogItemsSize, render_behind, render_behind_size);
Deinit();
}
void UiErrorOkDialog(const char *text, UiItem *render_behind, std::size_t render_behind_size)
{
UiErrorOkDialog(text, nullptr, render_behind, render_behind_size);
}
} // namespace dvl

12
SourceX/DiabloUI/dialogs.h

@ -0,0 +1,12 @@
#pragma once
#include <cstddef>
#include "DiabloUI/ui_item.h"
namespace dvl {
void UiErrorOkDialog(const char *text, UiItem *renderBehind, std::size_t renderBehindSize);
void UiErrorOkDialog(const char *text, const char *caption, UiItem *render_behind, std::size_t render_behind_size);
} // namespace dvl

6
SourceX/DiabloUI/mainmenu.cpp

@ -17,7 +17,7 @@ UiListItem MAINMENU_DIALOG_ITEMS[] = {
UiItem MAINMENU_DIALOG[] = {
UiImage(&ArtBackground, { 0, 0, 640, 480 }),
UiList(MAINMENU_DIALOG_ITEMS, 64, 192, 510, 43, UIS_HUGE | UIS_GOLD | UIS_CENTER),
UiText(nullptr, { 17, 444, 605, 21 }, UIS_SMALL)
UiArtText(nullptr, { 17, 444, 605, 21 }, UIS_SMALL)
};
void UiMainMenuSelect(int value)
@ -38,7 +38,7 @@ void mainmenu_restart_repintro()
void mainmenu_Load(char *name, void (*fnSound)(char *file))
{
gfnSoundFunction = fnSound;
MAINMENU_DIALOG[size(MAINMENU_DIALOG) - 1].text.text = name;
MAINMENU_DIALOG[size(MAINMENU_DIALOG) - 1].art_text.text = name;
MainMenuResult = 0;
@ -63,7 +63,7 @@ BOOL UiMainMenuDialog(char *name, int *pdwResult, void (*fnSound)(char *file), i
mainmenu_restart_repintro(); // for automatic starts
while (MainMenuResult == 0) {
UiRender();
UiPollAndRender();
if (GetTickCount() >= dwAttractTicks) {
MainMenuResult = MAINMENU_ATTRACT_MODE;
}

8
SourceX/DiabloUI/progress.cpp

@ -1,6 +1,7 @@
#include "devilution.h"
#include "miniwin/ddraw.h"
#include "DiabloUI/button.h"
#include "DiabloUI/diabloui.h"
#include "DiabloUI/art_draw.h"
#include "DiabloUI/fonts.h"
@ -10,7 +11,6 @@ namespace dvl {
Art ArtPopupSm;
Art ArtProgBG;
Art ProgFil;
Art ButImage;
SDL_Surface *msgSurface;
SDL_Surface *cancleSurface;
int textWidth;
@ -21,7 +21,7 @@ void progress_Load(char *msg)
LoadArt("ui_art\\spopup.pcx", &ArtPopupSm);
LoadArt("ui_art\\prog_bg.pcx", &ArtProgBG);
LoadArt("ui_art\\prog_fil.pcx", &ProgFil);
LoadArt("ui_art\\but_sml.pcx", &ButImage, 15);
LoadSmlButtonArt();
LoadTtfFont();
if (font != NULL) {
@ -39,7 +39,7 @@ void progress_Free()
ArtPopupSm.Unload();
ArtProgBG.Unload();
ProgFil.Unload();
ButImage.Unload();
UnloadSmlButtonArt();
SDL_FreeSurface(msgSurface);
msgSurface = NULL;
SDL_FreeSurface(cancleSurface);
@ -59,7 +59,7 @@ void progress_Render(BYTE progress)
if (progress) {
DrawArt(GetCenterOffset(227), y + 52, &ProgFil, 0, 227 * progress / 100);
}
DrawArt(GetCenterOffset(110), y + 99, &ButImage, 2, 110);
DrawArt(GetCenterOffset(110), y + 99, &SmlButton, 2, 110);
if (msgSurface) {
SDL_Rect dsc_rect = {

18
SourceX/DiabloUI/selconn.cpp

@ -18,7 +18,7 @@ _SNETVERSIONDATA *selconn_FileInfo;
DWORD provider;
UiText SELCONNECT_DIALOG_DESCRIPTION(selconn_Description, { 35, 275, 205, 66 });
UiArtText SELCONNECT_DIALOG_DESCRIPTION(selconn_Description, { 35, 275, 205, 66 });
UiListItem SELCONN_DIALOG_ITEMS[] = {
#ifndef NONET
{ "Client-Server (TCP)", 0 },
@ -30,13 +30,13 @@ UiListItem SELCONN_DIALOG_ITEMS[] = {
};
UiItem SELCONNECT_DIALOG[] = {
UiImage(&ArtBackground, { 0, 0, 640, 480 }),
UiText("Multi Player Game", { 24, 161, 590, 35 }, UIS_CENTER | UIS_BIG),
UiText(selconn_MaxPlayers, { 35, 218, 205, 21 }),
UiText("Requirements:", { 35, 256, 205, 21 }),
UiArtText("Multi Player Game", { 24, 161, 590, 35 }, UIS_CENTER | UIS_BIG),
UiArtText(selconn_MaxPlayers, { 35, 218, 205, 21 }),
UiArtText("Requirements:", { 35, 256, 205, 21 }),
SELCONNECT_DIALOG_DESCRIPTION,
UiText("no gateway needed", { 30, 356, 220, 31 }, UIS_CENTER | UIS_MED),
UiText(selconn_Gateway, { 35, 393, 205, 21 }, UIS_CENTER),
UiText("Select Connection", { 300, 211, 295, 33 }, UIS_CENTER | UIS_BIG),
UiArtText("no gateway needed", { 30, 356, 220, 31 }, UIS_CENTER | UIS_MED),
UiArtText(selconn_Gateway, { 35, 393, 205, 21 }, UIS_CENTER),
UiArtText("Select Connection", { 300, 211, 295, 33 }, UIS_CENTER | UIS_BIG),
UiArtTextButton("Change Gateway", nullptr, { 16, 427, 250, 35 }, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD | UIS_HIDDEN),
UiList(SELCONN_DIALOG_ITEMS, 305, 256, 285, 26, UIS_CENTER | UIS_VCENTER | UIS_GOLD),
UiArtTextButton("OK", &UiFocusNavigationSelect, { 299, 427, 140, 35 }, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD),
@ -79,7 +79,7 @@ void selconn_Focus(int value)
}
sprintf(selconn_MaxPlayers, "Players Supported: %d", players);
WordWrap(selconn_Description, SELCONNECT_DIALOG_DESCRIPTION.rect.w);
WordWrapArtStr(selconn_Description, SELCONNECT_DIALOG_DESCRIPTION.rect.w);
}
void selconn_Select(int value)
@ -118,7 +118,7 @@ int UiSelectProvider(
selconn_ReturnValue = true;
selconn_EndMenu = false;
while (!selconn_EndMenu) {
UiRender();
UiPollAndRender();
}
BlackPalette();
selconn_Free();

50
SourceX/DiabloUI/selgame.cpp

@ -1,8 +1,10 @@
#include "selgame.h"
#include "devilution.h"
#include "config.h"
#include "DiabloUI/diabloui.h"
#include "DiabloUI/text.h"
#include "DiabloUI/dialogs.h"
namespace dvl {
@ -23,7 +25,7 @@ constexpr UiImage SELGAME_BACKGROUND = UiImage(&ArtBackground, { 0, 0, 640, 480
constexpr UiArtTextButton SELGAME_OK = UiArtTextButton("OK", &UiFocusNavigationSelect, { 299, 427, 140, 35 }, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD);
constexpr UiArtTextButton SELGAME_CANCEL = UiArtTextButton("CANCEL", &UiFocusNavigationEsc, { 449, 427, 140, 35 }, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD);
UiText SELGAME_DESCRIPTION(selgame_Description, { 35, 256, 205, 192 });
UiArtText SELGAME_DESCRIPTION(selgame_Description, { 35, 256, 205, 192 });
UiListItem SELDIFF_DIALOG_ITEMS[] = {
{ "Normal", DIFF_NORMAL },
@ -32,17 +34,17 @@ UiListItem SELDIFF_DIALOG_ITEMS[] = {
};
UiItem SELDIFF_DIALOG[] = {
SELGAME_BACKGROUND,
UiText("Create Game", { 24, 161, 590, 35 }, UIS_CENTER | UIS_BIG),
UiText(selgame_Label, { 34, 211, 205, 33 }, UIS_CENTER | UIS_BIG), // DIFF
UiArtText("Create Game", { 24, 161, 590, 35 }, UIS_CENTER | UIS_BIG),
UiArtText(selgame_Label, { 34, 211, 205, 33 }, UIS_CENTER | UIS_BIG), // DIFF
SELGAME_DESCRIPTION,
UiText("Select Difficulty", { 299, 211, 295, 35 }, UIS_CENTER | UIS_BIG),
UiArtText("Select Difficulty", { 299, 211, 295, 35 }, UIS_CENTER | UIS_BIG),
UiList(SELDIFF_DIALOG_ITEMS, 300, 282, 295, 26, UIS_CENTER | UIS_MED | UIS_GOLD),
SELGAME_OK,
SELGAME_CANCEL,
};
constexpr UiText SELUDPGAME_TITLE = UiText("Join TCP/UDP Games", { 24, 161, 590, 35 }, UIS_CENTER | UIS_BIG);
constexpr UiText SELUDPGAME_DESCRIPTION_LABEL = UiText("Description:", { 35, 211, 205, 192 }, UIS_MED);
constexpr UiArtText SELUDPGAME_TITLE = UiArtText("Join TCP/UDP Games", { 24, 161, 590, 35 }, UIS_CENTER | UIS_BIG);
constexpr UiArtText SELUDPGAME_DESCRIPTION_LABEL = UiArtText("Description:", { 35, 211, 205, 192 }, UIS_MED);
UiListItem SELUDPGAME_DIALOG_ITEMS[] = {
{ "Create Game", 0 },
@ -53,7 +55,7 @@ UiItem SELUDPGAME_DIALOG[] = {
SELUDPGAME_TITLE,
SELUDPGAME_DESCRIPTION_LABEL,
SELGAME_DESCRIPTION,
UiText("Select Action", { 300, 211, 295, 33 }, UIS_CENTER | UIS_BIG),
UiArtText("Select Action", { 300, 211, 295, 33 }, UIS_CENTER | UIS_BIG),
UiList(SELUDPGAME_DIALOG_ITEMS, 305, 255, 285, 26, UIS_CENTER | UIS_MED | UIS_GOLD),
SELGAME_OK,
SELGAME_CANCEL,
@ -64,8 +66,8 @@ UiItem ENTERIP_DIALOG[] = {
SELUDPGAME_TITLE,
SELUDPGAME_DESCRIPTION_LABEL,
SELGAME_DESCRIPTION,
UiText("Enter IP", { 305, 211, 285, 33 }, UIS_CENTER | UIS_BIG),
UiEdit(selgame_Ip, 128, { 305, 314, 285, 33 }, UIS_LIST | UIS_MED | UIS_GOLD),
UiArtText("Enter IP", { 305, 211, 285, 33 }, UIS_CENTER | UIS_BIG),
UiEdit(selgame_Ip, 128, { 305, 314, 285, 33 }, UIS_MED | UIS_GOLD),
SELGAME_OK,
SELGAME_CANCEL,
};
@ -75,8 +77,8 @@ UiItem ENTERPASSWORD_DIALOG[] = {
SELUDPGAME_TITLE,
SELUDPGAME_DESCRIPTION_LABEL,
SELGAME_DESCRIPTION,
UiText("Enter Password", { 305, 211, 285, 33 }, UIS_CENTER | UIS_BIG),
UiEdit(selgame_Password, 15, { 305, 314, 285, 33 }, UIS_LIST | UIS_MED | UIS_GOLD),
UiArtText("Enter Password", { 305, 211, 285, 33 }, UIS_CENTER | UIS_BIG),
UiEdit(selgame_Password, 15, { 305, 314, 285, 33 }, UIS_MED | UIS_GOLD),
SELGAME_OK,
SELGAME_CANCEL,
};
@ -111,7 +113,7 @@ void selgame_GameSelection_Focus(int value)
strcpy(selgame_Description, "Enter an IP and join a game already in progress at that address.");
break;
}
WordWrap(selgame_Description, SELGAME_DESCRIPTION.rect.w);
WordWrapArtStr(selgame_Description, SELGAME_DESCRIPTION.rect.w);
}
void selgame_GameSelection_Select(int value)
@ -152,7 +154,7 @@ void selgame_Diff_Focus(int value)
strcpy(selgame_Description, "Hell Difficulty\nThe most powerful of the underworld's creatures lurk at the gateway into Hell. Only the most experienced characters should venture in this realm.");
break;
}
WordWrap(selgame_Description, SELGAME_DESCRIPTION.rect.w);
WordWrapArtStr(selgame_Description, SELGAME_DESCRIPTION.rect.w);
}
void selgame_Diff_Select(int value)
@ -185,13 +187,16 @@ void selgame_Password_Init(int value)
void selgame_Password_Select(int value)
{
UiInitList(0, 0, NULL, NULL, NULL, NULL, 0);
selgame_endMenu = true;
if (selgame_selectedGame) {
SRegSaveString("Phone Book", "Entry1", 0, selgame_Ip);
if (!SNetJoinGame(selgame_selectedGame, selgame_Ip, selgame_Password, NULL, NULL, gdwPlayerId)) {
DrawDlg("Unable to establish a connection. A game of Devilution 0.2.0 was not detected at the specified IP address.");
if (SNetJoinGame(selgame_selectedGame, selgame_Ip, selgame_Password, NULL, NULL, gdwPlayerId)) {
UiInitList(0, 0, NULL, NULL, NULL, NULL, 0);
selgame_endMenu = true;
} else {
UiErrorOkDialog(
"Unable to establish a connection.",
PROJECT_NAME " v" PROJECT_VERSION " game not found or password invalid.",
ENTERPASSWORD_DIALOG, size(ENTERPASSWORD_DIALOG));
selgame_Password_Init(selgame_selectedGame);
}
return;
@ -200,8 +205,11 @@ void selgame_Password_Select(int value)
_gamedata *info = m_client_info->initdata;
info->bDiff = gbDifficulty;
if (!SNetCreateGame(NULL, selgame_Password, NULL, 0, (char *)info, sizeof(_gamedata), MAX_PLRS, NULL, NULL, gdwPlayerId)) {
DrawDlg("Unable to create game.");
if (SNetCreateGame(NULL, selgame_Password, NULL, 0, (char *)info, sizeof(_gamedata), MAX_PLRS, NULL, NULL, gdwPlayerId)) {
UiInitList(0, 0, NULL, NULL, NULL, NULL, 0);
selgame_endMenu = true;
} else {
UiErrorOkDialog("Unable to create game.", ENTERPASSWORD_DIALOG, size(ENTERPASSWORD_DIALOG));
selgame_Password_Init(0);
}
}
@ -221,7 +229,7 @@ int UiSelectGame(int a1, _SNETPROGRAMDATA *client_info, _SNETPLAYERDATA *user_in
selgame_endMenu = false;
while (!selgame_endMenu) {
UiRender();
UiPollAndRender();
}
BlackPalette();
selgame_Free();

46
SourceX/DiabloUI/selhero.cpp

@ -5,6 +5,7 @@
#include "scrollbar.h"
#include "selyesno.h"
#include "DiabloUI/diabloui.h"
#include "DiabloUI/dialogs.h"
#include "devilution.h"
namespace dvl {
@ -35,24 +36,24 @@ namespace {
UiItem SELHERO_DIALOG[] = {
UiImage(&ArtBackground, { 0, 0, 640, 480 }),
UiText(title, { 24, 161, 590, 35 }, UIS_CENTER | UIS_BIG),
UiArtText(title, { 24, 161, 590, 35 }, UIS_CENTER | UIS_BIG),
UiImage(&ArtHero, UI_NUM_CLASSES, { 30, 211, 180, 76 }),
UiText("Level:", { 39, 323, 110, 21 }, UIS_RIGHT),
UiText(textStats[0], { 159, 323, 40, 21 }, UIS_CENTER),
UiText("Strength:", { 39, 358, 110, 21 }, UIS_RIGHT),
UiText(textStats[1], { 159, 358, 40, 21 }, UIS_CENTER),
UiText("Magic:", { 39, 380, 110, 21 }, UIS_RIGHT),
UiText(textStats[2], { 159, 380, 40, 21 }, UIS_CENTER),
UiText("Dexterity:", { 39, 401, 110, 21 }, UIS_RIGHT),
UiText(textStats[3], { 159, 401, 40, 21 }, UIS_CENTER),
UiText("Vitality:", { 39, 422, 110, 21 }, UIS_RIGHT),
UiText(textStats[4], { 159, 422, 40, 21 }, UIS_CENTER),
UiArtText("Level:", { 39, 323, 110, 21 }, UIS_RIGHT),
UiArtText(textStats[0], { 159, 323, 40, 21 }, UIS_CENTER),
UiArtText("Strength:", { 39, 358, 110, 21 }, UIS_RIGHT),
UiArtText(textStats[1], { 159, 358, 40, 21 }, UIS_CENTER),
UiArtText("Magic:", { 39, 380, 110, 21 }, UIS_RIGHT),
UiArtText(textStats[2], { 159, 380, 40, 21 }, UIS_CENTER),
UiArtText("Dexterity:", { 39, 401, 110, 21 }, UIS_RIGHT),
UiArtText(textStats[3], { 159, 401, 40, 21 }, UIS_CENTER),
UiArtText("Vitality:", { 39, 422, 110, 21 }, UIS_RIGHT),
UiArtText(textStats[4], { 159, 422, 40, 21 }, UIS_CENTER),
};
UiImage *SELHERO_DIALOG_HERO_IMG = &SELHERO_DIALOG[2].image;
UiListItem SELLIST_DIALOG_ITEMS[kMaxViewportItems];
UiItem SELLIST_DIALOG[] = {
UiText("Select Hero", { 264, 211, 320, 33 }, UIS_CENTER | UIS_BIG),
UiArtText("Select Hero", { 264, 211, 320, 33 }, UIS_CENTER | UIS_BIG),
UiList(SELLIST_DIALOG_ITEMS, 265, 256, 320, 26, UIS_CENTER | UIS_MED | UIS_GOLD),
MakeScrollBar({585, 244, 25, 178}),
UiArtTextButton("OK", &UiFocusNavigationSelect, { 239, 429, 120, 35 }, UIS_CENTER | UIS_BIG | UIS_GOLD),
@ -69,15 +70,15 @@ UiListItem SELCLAS_DIALOG_ITEMS[] = {
{ "Sorcerer", UI_SORCERER }
};
UiItem SELCLASS_DIALOG[] = {
UiText("Choose Class", { 264, 211, 320, 33 }, UIS_CENTER | UIS_BIG),
UiArtText("Choose Class", { 264, 211, 320, 33 }, UIS_CENTER | UIS_BIG),
UiList(SELCLAS_DIALOG_ITEMS, 264, 285, 320, 33, UIS_CENTER | UIS_MED | UIS_GOLD),
UiArtTextButton("OK", &UiFocusNavigationSelect, { 279, 429, 140, 35 }, UIS_CENTER | UIS_BIG | UIS_GOLD),
UiArtTextButton("Cancel", &UiFocusNavigationEsc, { 429, 429, 140, 35 }, UIS_CENTER | UIS_BIG | UIS_GOLD)
};
UiItem ENTERNAME_DIALOG[] = {
UiText("Enter Name", { 264, 211, 320, 33 }, UIS_CENTER | UIS_BIG),
UiEdit(selhero_heroInfo.name, 15, { 265, 317, 320, 33 }, UIS_LIST | UIS_MED | UIS_GOLD),
UiArtText("Enter Name", { 264, 211, 320, 33 }, UIS_CENTER | UIS_BIG),
UiEdit(selhero_heroInfo.name, 15, { 265, 317, 320, 33 }, UIS_MED | UIS_GOLD),
UiArtTextButton("OK", &UiFocusNavigationSelect, { 279, 429, 140, 35 }, UIS_CENTER | UIS_BIG | UIS_GOLD),
UiArtTextButton("Cancel", &UiFocusNavigationEsc, { 429, 429, 140, 35 }, UIS_CENTER | UIS_BIG | UIS_GOLD)
};
@ -87,7 +88,7 @@ UiListItem SELLOAD_DIALOG_ITEMS[] = {
{ "New Game", 1 }
};
UiItem SELLOAD_DIALOG[] = {
UiText("Save File Exists", { 264, 211, 320, 33 }, UIS_CENTER | UIS_BIG),
UiArtText("Save File Exists", { 264, 211, 320, 33 }, UIS_CENTER | UIS_BIG),
UiList(SELLOAD_DIALOG_ITEMS, 265, 285, 320, 33, UIS_CENTER | UIS_MED | UIS_GOLD),
UiArtTextButton("OK", &UiFocusNavigationSelect, { 279, 427, 140, 35 }, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD),
UiArtTextButton("Cancel", &UiFocusNavigationEsc, { 429, 427, 140, 35 }, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD)
@ -254,9 +255,14 @@ void selhero_ClassSelector_Esc()
void selhero_Name_Select(int value)
{
UiInitList(0, 0, NULL, NULL, NULL, NULL, 0);
gfnHeroCreate(&selhero_heroInfo);
selhero_endMenu = true;
if (gfnHeroCreate(&selhero_heroInfo)) {
UiInitList(0, 0, NULL, NULL, NULL, NULL, 0);
selhero_endMenu = true;
} else {
UiErrorOkDialog("Unable to create character.", SELHERO_DIALOG, size(SELHERO_DIALOG));
memset(selhero_heroInfo.name, '\0', sizeof(selhero_heroInfo.name));
selhero_ClassSelector_Select(selhero_heroInfo.heroclass);
}
}
void selhero_Name_Esc()
@ -319,7 +325,7 @@ BOOL UiSelHeroDialog(
selhero_endMenu = false;
while (!selhero_endMenu && !selhero_navigateYesNo) {
UiRenderItems(SELHERO_DIALOG, size(SELHERO_DIALOG));
UiRender();
UiPollAndRender();
}
BlackPalette();
selhero_Free();

10
SourceX/DiabloUI/selyesno.cpp

@ -20,11 +20,11 @@ UiListItem SELYESNO_DIALOG_ITEMS[] = {
UiItem SELYESNO_DIALOG[] = {
UiImage(&ArtBackground, { 0, 0, 640, 480 }),
UiText(selyesno_title, { 24, 161, 590, 35 }, UIS_CENTER | UIS_BIG),
UiText(selyesno_confirmationMessage, { 120, 236, 280, 168 }, UIS_MED),
UiArtText(selyesno_title, { 24, 161, 590, 35 }, UIS_CENTER | UIS_BIG),
UiArtText(selyesno_confirmationMessage, { 120, 236, 280, 168 }, UIS_MED),
UiList(SELYESNO_DIALOG_ITEMS, 230, 390, 180, 35, UIS_CENTER | UIS_BIG | UIS_GOLD)
};
UiText *SELYESNO_DIALOG_CONFIRMATION_MESSAGE = &SELYESNO_DIALOG[2].text;
UiArtText *SELYESNO_DIALOG_CONFIRMATION_MESSAGE = &SELYESNO_DIALOG[2].art_text;
void selyesno_Free()
{
@ -60,14 +60,14 @@ BOOL UiSelHeroDelYesNoDialog(
}
sprintf(selyesno_confirmationMessage, "Are you sure you want to delete the character \"%s\"?", selyesno_heroInfo.name);
WordWrap(selyesno_confirmationMessage, SELYESNO_DIALOG_CONFIRMATION_MESSAGE->rect.w);
WordWrapArtStr(selyesno_confirmationMessage, SELYESNO_DIALOG_CONFIRMATION_MESSAGE->rect.w);
UiInitList(0, 1, NULL, selyesno_Select, selyesno_Esc, SELYESNO_DIALOG, size(SELYESNO_DIALOG), true, NULL);
selyesno_endMenu = false;
while (!selyesno_endMenu) {
UiRenderItems(SELYESNO_DIALOG, size(SELYESNO_DIALOG));
UiRender();
UiPollAndRender();
}
BlackPalette();

6
SourceX/DiabloUI/text.cpp

@ -2,7 +2,7 @@
namespace dvl {
std::size_t GetStrWidth(const char *str, std::size_t size)
std::size_t GetArtStrWidth(const char *str, std::size_t size)
{
int strWidth = 0;
@ -17,7 +17,7 @@ std::size_t GetStrWidth(const char *str, std::size_t size)
return strWidth;
}
void WordWrap(char *text, std::size_t width)
void WordWrapArtStr(char *text, std::size_t width)
{
const std::size_t len = strlen(text);
std::size_t lineStart = 0;
@ -31,7 +31,7 @@ void WordWrap(char *text, std::size_t width)
if (i != len)
text[i] = '\0';
if (GetStrWidth(&text[lineStart], AFT_SMALL) <= width) {
if (GetArtStrWidth(&text[lineStart], AFT_SMALL) <= width) {
if (i != len)
text[i] = ' ';
continue;

10
SourceX/DiabloUI/text.h

@ -6,13 +6,7 @@
namespace dvl {
enum TXT_JUST {
JustLeft = 0,
JustCentre = 1,
JustRight = 2,
};
std::size_t GetStrWidth(const char *str, std::size_t size);
void WordWrap(char *text, std::size_t width);
std::size_t GetArtStrWidth(const char *str, std::size_t size);
void WordWrapArtStr(char *text, std::size_t width);
} // namespace dvl

62
SourceX/DiabloUI/text_draw.cpp

@ -1,3 +1,5 @@
#include "DiabloUI/text_draw.h"
#include "DiabloUI/art_draw.h"
#include "DiabloUI/fonts.h"
#include "DiabloUI/text.h"
@ -5,27 +7,51 @@
namespace dvl {
extern SDL_Surface *pal_surface;
namespace {
int AlignedTextOffsetX(const char *text, std::size_t rect_w, TXT_JUST align, _artFontTables size)
int AlignXOffset(int flags, const SDL_Rect &dest, int w)
{
switch (align) {
case JustCentre:
return (rect_w - GetStrWidth(text, size)) / 2;
case JustRight:
return rect_w - GetStrWidth(text, size);
default:
return 0;
}
if (flags & UIS_CENTER)
return (dest.w - w) / 2;
if (flags & UIS_RIGHT)
return dest.w - w;
return 0;
}
} // namespace
void DrawArtStr(const char *text, const SDL_Rect &rect, int flags, bool drawTextCursor = false)
void DrawTTF(const char *text, const SDL_Rect &rect, int flags,
const SDL_Color &text_color, const SDL_Color &shadow_color,
TtfSurfaceCache **render_cache)
{
if (*render_cache == nullptr) {
*render_cache = new TtfSurfaceCache();
(*render_cache)->text = TTF_RenderUTF8_Solid(font, text, text_color);
(*render_cache)->shadow = TTF_RenderUTF8_Solid(font, text, shadow_color);
}
SDL_Surface *text_surface = (*render_cache)->text;
SDL_Surface *shadow_surface = (*render_cache)->shadow;
SDL_Rect dest_rect = rect;
const int x_offset = AlignXOffset(flags, rect, text_surface->w);
const int y_offset = (flags & UIS_VCENTER) ? (rect.h - text_surface->h) / 2 : 0;
dest_rect.x += static_cast<decltype(SDL_Rect().x)>(SCREEN_X + x_offset);
dest_rect.y += static_cast<decltype(SDL_Rect().y)>(SCREEN_Y + y_offset);
SDL_Rect shadow_rect = dest_rect;
shadow_rect.x += 2;
if (SDL_BlitSurface(shadow_surface, nullptr, pal_surface, &shadow_rect) <= -1)
SDL_Log(SDL_GetError());
if (SDL_BlitSurface(text_surface, nullptr, pal_surface, &dest_rect) <= -1)
SDL_Log(SDL_GetError());
}
void DrawArtStr(const char *text, const SDL_Rect &rect, int flags, bool drawTextCursor)
{
_artFontTables size = AFT_SMALL;
_artFontColors color = flags & UIS_GOLD ? AFC_GOLD : AFC_SILVER;
TXT_JUST align = JustLeft;
if (flags & UIS_MED)
size = AFT_MED;
@ -34,18 +60,10 @@ void DrawArtStr(const char *text, const SDL_Rect &rect, int flags, bool drawText
else if (flags & UIS_HUGE)
size = AFT_HUGE;
if (flags & UIS_CENTER)
align = JustCentre;
else if (flags & UIS_RIGHT)
align = JustRight;
int x = rect.x + AlignedTextOffsetX(text, rect.w, align, size);
int sx = x;
int sy = rect.y;
if (flags & UIS_VCENTER)
sy += (rect.h - ArtFonts[size][color].h()) / 2;
const int x = rect.x + AlignXOffset(flags, rect, GetArtStrWidth(text, size));
const int y = rect.y + ((flags & UIS_VCENTER) ? (rect.h - ArtFonts[size][color].h()) / 2 : 0);
int sx = x, sy = y;
for (size_t i = 0, n = strlen(text); i < n; i++) {
if (text[i] == '\n') {
sx = x;

15
SourceX/DiabloUI/text_draw.h

@ -4,6 +4,21 @@
namespace dvl {
struct TtfSurfaceCache {
~TtfSurfaceCache()
{
mem_free_dbg(text);
mem_free_dbg(shadow);
}
SDL_Surface *text = nullptr;
SDL_Surface *shadow = nullptr;
};
void DrawTTF(const char *text, const SDL_Rect &rect, int flags,
const SDL_Color &text_color, const SDL_Color &shadow_color,
TtfSurfaceCache **surface_cache);
void DrawArtStr(const char *text, const SDL_Rect &rect, int flags, bool drawTextCursor = false);
} // namespace dvl

2
SourceX/DiabloUI/title.cpp

@ -19,7 +19,7 @@ BOOL UiTitleDialog(int a1)
{
UiItem TITLESCREEN_DIALOG[] = {
UiImage(&ArtBackground, { 0, 0, 640, 480 }),
UiText("Copyright \xA9 1996-2001 Blizzard Entertainment", { 49, 410, 550, 26 }, UIS_MED | UIS_CENTER)
UiArtText("Copyright \xA9 1996-2001 Blizzard Entertainment", { 49, 410, 550, 26 }, UIS_MED | UIS_CENTER)
};
title_Load();

124
SourceX/DiabloUI/ui_item.h

@ -1,19 +1,24 @@
#pragma once
#include <cstdint>
#include <cstddef>
#include <string>
#include <vector>
#include "devilution.h"
#include "art.h"
#include "stubs.h"
#include "DiabloUI/art.h"
#include "DiabloUI/text_draw.h"
namespace dvl {
enum UiType {
UI_TEXT,
UI_IMAGE,
UI_ART_TEXT,
UI_ART_TEXT_BUTTON,
UI_IMAGE,
UI_BUTTON,
UI_LIST,
UI_SCROLLBAR,
UI_EDIT,
@ -29,11 +34,8 @@ enum UiFlags {
UIS_VCENTER = 1 << 6,
UIS_SILVER = 1 << 7,
UIS_GOLD = 1 << 8,
UIS_SML1 = 1 << 9,
UIS_SML2 = 1 << 10,
UIS_LIST = 1 << 11,
UIS_DISABLED = 1 << 12,
UIS_HIDDEN = 1 << 13,
UIS_DISABLED = 1 << 9,
UIS_HIDDEN = 1 << 10,
};
struct UiItemBase {
@ -48,6 +50,11 @@ struct UiItemBase {
return flags & flag;
}
bool has_any_flag(int flags) const
{
return (this->flags & flags) != 0;
}
void add_flag(UiFlags flag)
{
flags |= flag;
@ -79,8 +86,34 @@ struct UiImage : public UiItemBase {
int frame;
};
// Plain text (TTF).
struct UiText : public UiItemBase {
constexpr UiText(const char *text, SDL_Rect rect, int flags = 0)
: UiItemBase(rect, flags)
, color{ 243, 243, 243, 0 }
, shadow_color{ 0, 0, 0, 0 }
, text(text)
, render_cache(nullptr)
{
}
SDL_Color color;
SDL_Color shadow_color;
const char *text;
// State:
TtfSurfaceCache *render_cache;
void FreeCache()
{
delete render_cache;
render_cache = nullptr;
}
};
// Text drawn with Diablo sprites.
struct UiArtText : public UiItemBase {
constexpr UiArtText(const char *text, SDL_Rect rect, int flags = 0)
: UiItemBase(rect, flags)
, text(text)
{
@ -89,6 +122,7 @@ struct UiText : public UiItemBase {
const char *text;
};
// Clickable Diablo sprites text.
struct UiArtTextButton : public UiItemBase {
constexpr UiArtTextButton(const char *text, void (*action)(), SDL_Rect rect, int flags = 0)
: UiItemBase(rect, flags)
@ -101,6 +135,43 @@ struct UiArtTextButton : public UiItemBase {
void (*action)();
};
// A button (uses Diablo sprites).
struct UiButton : public UiItemBase {
enum FrameKey {
DEFAULT = 0,
PRESSED,
DISABLED
};
using FrameMap = int[3];
constexpr UiButton(Art *art, const FrameMap &frame_map, const char *text, void (*action)(), SDL_Rect rect, int flags = 0)
: UiItemBase(rect, flags)
, art(art)
, frame_map{ frame_map[0], frame_map[1], frame_map[2] }
, text(text)
, action(action)
, pressed(false)
, render_cache(nullptr)
{
}
Art *art;
const FrameMap frame_map;
const char *text;
void (*action)();
// State
bool pressed;
TtfSurfaceCache *render_cache;
void FreeCache()
{
delete render_cache;
render_cache = nullptr;
}
};
struct UiListItem {
constexpr UiListItem(const char *text = "", int value = 0)
: text(text)
@ -185,9 +256,9 @@ struct UiItem {
{
}
constexpr UiItem(UiImage image)
: type(UI_IMAGE)
, image(image)
constexpr UiItem(UiArtText text)
: type(UI_ART_TEXT)
, art_text(text)
{
}
@ -197,6 +268,18 @@ struct UiItem {
{
}
constexpr UiItem(UiImage image)
: type(UI_IMAGE)
, image(image)
{
}
constexpr UiItem(UiButton button)
: type(UI_BUTTON)
, button(button)
{
}
constexpr UiItem(UiList list)
: type(UI_LIST)
, list(list)
@ -218,8 +301,10 @@ struct UiItem {
UiType type;
union {
UiText text;
UiArtText art_text;
UiImage image;
UiArtTextButton art_text_button;
UiButton button;
UiList list;
UiScrollBar scrollbar;
UiEdit edit;
@ -231,10 +316,29 @@ struct UiItem {
return common.has_flag(flag);
}
bool has_any_flag(int flags) const
{
return common.has_any_flag(flags);
}
const SDL_Rect &rect() const
{
return common.rect;
}
void FreeCache()
{
switch (type) {
case UI_BUTTON:
button.FreeCache();
break;
case UI_TEXT:
text.FreeCache();
break;
default:
break;
}
}
};
} // namespace dvl

Loading…
Cancel
Save