Browse Source

Refactor DiabloUI to dynamic C++03 compatible code

This will help with porting the game to the original Xbox, and also make
it easier to implement dynamic resolutions and translations.
pull/796/head
BDC 6 years ago committed by Anders Jenbo
parent
commit
62de5f1600
  1. 15
      SourceX/DiabloUI/art_draw.cpp
  2. 21
      SourceX/DiabloUI/button.cpp
  3. 11
      SourceX/DiabloUI/button.h
  4. 70
      SourceX/DiabloUI/credits.cpp
  5. 194
      SourceX/DiabloUI/diabloui.cpp
  6. 21
      SourceX/DiabloUI/diabloui.h
  7. 86
      SourceX/DiabloUI/dialogs.cpp
  8. 6
      SourceX/DiabloUI/dialogs.h
  9. 46
      SourceX/DiabloUI/mainmenu.cpp
  10. 13
      SourceX/DiabloUI/progress.cpp
  11. 72
      SourceX/DiabloUI/scrollbar.h
  12. 90
      SourceX/DiabloUI/selconn.cpp
  13. 206
      SourceX/DiabloUI/selgame.cpp
  14. 278
      SourceX/DiabloUI/selhero.cpp
  15. 1
      SourceX/DiabloUI/selhero.h
  16. 66
      SourceX/DiabloUI/selok.cpp
  17. 54
      SourceX/DiabloUI/selyesno.cpp
  18. 2
      SourceX/DiabloUI/text_draw.cpp
  19. 20
      SourceX/DiabloUI/title.cpp
  20. 3
      SourceX/DiabloUI/ttf_render_wrapped.cpp
  21. 2
      SourceX/DiabloUI/ttf_render_wrapped.h
  22. 424
      SourceX/DiabloUI/ui_item.h
  23. 110
      SourceX/platform/switch/keyboard.cpp
  24. 2
      SourceX/platform/switch/keyboard.h

15
SourceX/DiabloUI/art_draw.cpp

@ -11,12 +11,12 @@ void DrawArt(int screenX, int screenY, Art *art, int nFrame, Uint16 srcW, Uint16
if (screenY >= SCREEN_HEIGHT || screenX >= SCREEN_WIDTH || art->surface == NULL)
return;
SDL_Rect src_rect = {
0,
static_cast<Sint16>(nFrame * art->h()),
static_cast<Uint16>(art->w()),
static_cast<Uint16>(art->h())
};
SDL_Rect src_rect;
src_rect.x = 0;
src_rect.y = nFrame * art->h();
src_rect.w = art->w();
src_rect.h = art->h();
ScaleOutputRect(&src_rect);
if (srcW && srcW < src_rect.w)
@ -36,7 +36,8 @@ void DrawArt(int screenX, int screenY, Art *art, int nFrame, Uint16 srcW, Uint16
ErrSdl();
}
void DrawAnimatedArt(Art *art, int screenX, int screenY) {
void DrawAnimatedArt(Art *art, int screenX, int screenY)
{
DrawArt(screenX, screenY, art, GetAnimationFrame(art->frames));
}

21
SourceX/DiabloUI/button.cpp

@ -16,18 +16,21 @@ void LoadSmlButtonArt()
void RenderButton(UiButton *button)
{
int frame;
if (button->pressed) {
if (button->m_pressed) {
frame = UiButton::PRESSED;
} else {
frame = UiButton::DEFAULT;
}
DrawArt(button->rect.x, button->rect.y, button->art, frame, button->rect.w, button->rect.h);
DrawArt(button->m_rect.x, button->m_rect.y, button->m_art, frame, button->m_rect.w, button->m_rect.h);
SDL_Rect text_rect = button->rect;
if (!button->pressed)
SDL_Rect text_rect = button->m_rect;
if (!button->m_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);
SDL_Color color1 = { 243, 243, 243, 0 };
SDL_Color color2 = { 0, 0, 0, 0 };
DrawTTF(button->m_text, text_rect, UIS_CENTER,
color1, color2, &button->m_render_cache);
}
bool HandleMouseEventButton(const SDL_Event &event, UiButton *button)
@ -36,10 +39,10 @@ bool HandleMouseEventButton(const SDL_Event &event, UiButton *button)
return false;
switch (event.type) {
case SDL_MOUSEBUTTONUP:
button->action();
button->m_action();
return true;
case SDL_MOUSEBUTTONDOWN:
button->pressed = true;
button->m_pressed = true;
return true;
default:
return false;
@ -48,7 +51,7 @@ bool HandleMouseEventButton(const SDL_Event &event, UiButton *button)
void HandleGlobalMouseUpButton(UiButton *button)
{
button->pressed = false;
button->m_pressed = false;
}
} // namespace dvl

11
SourceX/DiabloUI/button.h

@ -14,17 +14,6 @@ inline void UnloadSmlButtonArt()
const Uint16 SML_BUTTON_WIDTH = 110;
const Uint16 SML_BUTTON_HEIGHT = 28;
constexpr UiButton MakeSmlButton(
const char *text, void (*action)(), Sint16 x, Sint16 y, int flags = 0)
{
return UiButton(
&SmlButton,
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);

70
SourceX/DiabloUI/credits.cpp

@ -33,31 +33,26 @@ struct SurfaceDeleter {
}
};
using SurfacePtr = std::unique_ptr<SDL_Surface, SurfaceDeleter>;
struct CachedLine {
CachedLine() = default;
explicit CachedLine(std::size_t index, SurfacePtr surface)
: index(index)
, surface(std::move(surface))
, palette_version(pal_surface_palette_version)
CachedLine(std::size_t index, SDL_Surface *surface)
{
m_index = index;
m_surface = surface;
}
std::size_t index;
SurfacePtr surface;
std::size_t m_index;
SDL_Surface *m_surface;
unsigned int palette_version;
};
SurfacePtr RenderText(const char *text, SDL_Color color)
SDL_Surface *RenderText(const char *text, SDL_Color color)
{
if (text[0] == '\0')
return NULL;
SDL_Surface *result = TTF_RenderUTF8_Solid(font, text, color);
if (result == NULL)
SDL_Log(TTF_GetError());
return SurfacePtr(result);
return result;
}
CachedLine PrepareLine(std::size_t index)
@ -67,41 +62,40 @@ CachedLine PrepareLine(std::size_t index)
++contents;
const SDL_Color shadow_color = { 0, 0, 0, 0 };
auto text = RenderText(contents, shadow_color);
SDL_Surface *text = RenderText(contents, shadow_color);
// Precompose shadow and text:
SurfacePtr surface;
SDL_Surface *surface = NULL;
if (text != NULL) {
// Set up the target surface to have 3 colors: mask, text, and shadow.
surface.reset(
SDL_CreateRGBSurfaceWithFormat(0, text->w + SHADOW_OFFSET_X, text->h + SHADOW_OFFSET_Y, 8, SDL_PIXELFORMAT_INDEX8));
const SDL_Color &mask_color = { 0, 255, 0, 0 }; // Any color different from both shadow and text
surface = SDL_CreateRGBSurfaceWithFormat(0, text->w + SHADOW_OFFSET_X, text->h + SHADOW_OFFSET_Y, 8, SDL_PIXELFORMAT_INDEX8);
const SDL_Color mask_color = { 0, 255, 0, 0 }; // Any color different from both shadow and text
const SDL_Color &text_color = palette->colors[224];
SDL_Color colors[3] = { mask_color, text_color, shadow_color };
if (SDLC_SetSurfaceColors(surface.get(), colors, 0, 3) <= -1)
if (SDLC_SetSurfaceColors(surface, colors, 0, 3) <= -1)
SDL_Log(SDL_GetError());
SDLC_SetColorKey(surface.get(), 0);
SDLC_SetColorKey(surface, 0);
// Blit the shadow first:
SDL_Rect shadow_rect = { SHADOW_OFFSET_X, SHADOW_OFFSET_Y, 0, 0 };
if (SDL_BlitSurface(text.get(), NULL, surface.get(), &shadow_rect) <= -1)
if (SDL_BlitSurface(text, NULL, surface, &shadow_rect) <= -1)
ErrSdl();
// Change the text surface color and blit again:
SDL_Color text_colors[2] = { mask_color, text_color };
if (SDLC_SetSurfaceColors(text.get(), text_colors, 0, 2) <= -1)
if (SDLC_SetSurfaceColors(text, text_colors, 0, 2) <= -1)
ErrSdl();
SDLC_SetColorKey(text.get(), 0);
SDLC_SetColorKey(text, 0);
if (SDL_BlitSurface(text.get(), NULL, surface.get(), NULL) <= -1)
if (SDL_BlitSurface(text, NULL, surface, NULL) <= -1)
ErrSdl();
SDL_Surface *surface_ptr = surface.release();
SDL_Surface *surface_ptr = surface;
ScaleSurfaceToOutput(&surface_ptr);
surface.reset(surface_ptr);
surface = surface_ptr;
}
return CachedLine(index, std::move(surface));
return CachedLine(index, surface);
}
/**
@ -154,10 +148,10 @@ public:
empty_ = true;
}
void push_back(CachedLine &&line)
void push_back(CachedLine line)
{
end_ = (end_ + 1) % data_.size();
data_[end_] = std::move(line);
data_[end_] = line;
empty_ = false;
}
@ -222,12 +216,12 @@ void CreditsRenderer::Render()
return;
}
while (!lines_.empty() && lines_.front().index != lines_begin)
while (!lines_.empty() && lines_.front().m_index != lines_begin)
lines_.pop_front();
if (lines_.empty())
lines_.push_back(PrepareLine(lines_begin));
while (lines_.back().index + 1 != lines_end)
lines_.push_back(PrepareLine(lines_.back().index + 1));
while (lines_.back().m_index + 1 != lines_end)
lines_.push_back(PrepareLine(lines_.back().m_index + 1));
SDL_Rect viewport = VIEWPORT;
ScaleOutputRect(&viewport);
@ -236,23 +230,23 @@ void CreditsRenderer::Render()
// We use unscaled coordinates for calculation throughout.
Sint16 dest_y = VIEWPORT.y - (offset_y - lines_begin * LINE_H);
for (std::size_t i = 0; i < lines_.size(); ++i, dest_y += LINE_H) {
auto &line = lines_[i];
if (line.surface == NULL)
CachedLine &line = lines_[i];
if (line.m_surface == NULL)
continue;
// Still fading in: the cached line was drawn with a different fade level.
if (line.palette_version != pal_surface_palette_version)
line = PrepareLine(line.index);
line = PrepareLine(line.m_index);
Sint16 dest_x = PANEL_LEFT + VIEWPORT.x + 31;
if (CREDITS_LINES[line.index][0] == '\t')
if (CREDITS_LINES[line.m_index][0] == '\t')
dest_x += 40;
SDL_Rect dst_rect = { dest_x, dest_y, 0, 0 };
ScaleOutputRect(&dst_rect);
dst_rect.w = line.surface.get()->w;
dst_rect.h = line.surface.get()->h;
if (SDL_BlitSurface(line.surface.get(), NULL, GetOutputSurface(), &dst_rect) < 0)
dst_rect.w = line.m_surface->w;
dst_rect.h = line.m_surface->h;
if (SDL_BlitSurface(line.m_surface, NULL, GetOutputSurface(), &dst_rect) < 0)
ErrSdl();
}
SDL_SetClipRect(GetOutputSurface(), NULL);

194
SourceX/DiabloUI/diabloui.cpp

@ -42,8 +42,7 @@ void (*gfnListFocus)(int value);
void (*gfnListSelect)(int value);
void (*gfnListEsc)();
bool (*gfnListYesNo)();
UiItem *gUiItems;
int gUiItemCnt;
std::vector<UiItemBase *> gUiItems;
bool UiItemsWraps;
char *UiTextInput;
int UiTextInputLen;
@ -75,7 +74,7 @@ void UiDestroy()
UnloadArtFonts();
}
void UiInitList(int min, int max, void (*fnFocus)(int value), void (*fnSelect)(int value), void (*fnEsc)(), UiItem *items, int itemCnt, bool itemsWraps, bool (*fnYesNo)())
void UiInitList(int min, int max, void (*fnFocus)(int value), void (*fnSelect)(int value), void (*fnEsc)(), std::vector<UiItemBase *> items, bool itemsWraps, bool (*fnYesNo)())
{
SelectedItem = min;
SelectedItemMin = min;
@ -86,20 +85,21 @@ void UiInitList(int min, int max, void (*fnFocus)(int value), void (*fnSelect)(i
gfnListEsc = fnEsc;
gfnListYesNo = fnYesNo;
gUiItems = items;
gUiItemCnt = itemCnt;
UiItemsWraps = itemsWraps;
if (fnFocus)
fnFocus(min);
SDL_StopTextInput(); // input is enabled by default
for (int i = 0; i < itemCnt; i++) {
if (items[i].type == UI_EDIT) {
for (std::size_t i = 0; i < items.size(); i++) {
if (items[i]->m_type == UI_EDIT) {
UiEdit *pItemUIEdit = (UiEdit *)items[i];
#ifdef __SWITCH__
switch_start_text_input(items[i - 1].art_text.text, items[i].edit.value, /*multiline=*/0);
switch_start_text_input("", pItemUIEdit->m_value, pItemUIEdit->m_max_length, /*multiline=*/0);
#endif
SDL_StartTextInput();
UiTextInput = items[i].edit.value;
UiTextInputLen = items[i].edit.max_length;
UiTextInput = pItemUIEdit->m_value;
UiTextInputLen = pItemUIEdit->m_max_length;
}
}
}
@ -115,6 +115,20 @@ void UiInitScrollBar(UiScrollBar *ui_sb, std::size_t viewport_size, const std::s
}
}
void UiInitList_clear()
{
SelectedItem = 0;
SelectedItemMin = 1;
SelectedItemMax = 1;
ListViewportSize = 1;
gfnListFocus = NULL;
gfnListSelect = NULL;
gfnListEsc = NULL;
gfnListYesNo = NULL;
gUiItems.clear();
UiItemsWraps = false;
}
void UiPlayMoveSound()
{
if (gfnSoundFunction)
@ -309,7 +323,7 @@ void UiFocusNavigation(SDL_Event *event)
}
if (event->type == SDL_MOUSEBUTTONDOWN || event->type == SDL_MOUSEBUTTONUP) {
if (UiItemMouseEvents(event, gUiItems, gUiItemCnt))
if (UiItemMouseEvents(event, gUiItems))
return;
}
}
@ -580,6 +594,18 @@ void LoadBackgroundArt(const char *pszFile)
RenderPresent();
}
void UiAddBackground(std::vector<UiItemBase *> *vecDialog)
{
SDL_Rect rect = { PANEL_LEFT, 0, 640, 480 };
vecDialog->push_back(new UiImage(&ArtBackground, rect));
}
void UiAddLogo(std::vector<UiItemBase *> *vecDialog, int size, int height)
{
SDL_Rect rect = { 0, height, 0, 0 };
vecDialog->push_back(new UiImage(&ArtLogos[size], /*animated=*/true, /*frame=*/0, rect, UIS_CENTER));
}
void UiFadeIn()
{
if (fadeValue < 256) {
@ -625,7 +651,7 @@ void UiPollAndRender()
UiFocusNavigation(&event);
UiHandleEvents(&event);
}
UiRenderItems(gUiItems, gUiItemCnt);
UiRenderItems(gUiItems);
DrawMouse();
UiFadeIn();
}
@ -634,58 +660,58 @@ namespace {
void Render(UiText *ui_text)
{
DrawTTF(ui_text->text,
ui_text->rect,
ui_text->flags,
ui_text->color,
ui_text->shadow_color,
&ui_text->render_cache);
DrawTTF(ui_text->m_text,
ui_text->m_rect,
ui_text->m_iFlags,
ui_text->m_color,
ui_text->m_shadow_color,
&ui_text->m_render_cache);
}
void Render(const UiArtText &ui_art_text)
void Render(const UiArtText *ui_art_text)
{
DrawArtStr(ui_art_text.text, ui_art_text.rect, ui_art_text.flags);
DrawArtStr(ui_art_text->m_text, ui_art_text->m_rect, ui_art_text->m_iFlags);
}
void Render(const UiImage &ui_image)
void Render(const UiImage *ui_image)
{
int x = ui_image.rect.x;
if ((ui_image.flags & UIS_CENTER) && ui_image.art != NULL) {
const int x_offset = GetCenterOffset(ui_image.art->w(), ui_image.rect.w);
int x = ui_image->m_rect.x;
if ((ui_image->m_iFlags & UIS_CENTER) && ui_image->m_art != NULL) {
const int x_offset = GetCenterOffset(ui_image->m_art->w(), ui_image->m_rect.w);
x += x_offset;
}
if (ui_image.animated) {
DrawAnimatedArt(ui_image.art, x, ui_image.rect.y);
if (ui_image->m_animated) {
DrawAnimatedArt(ui_image->m_art, x, ui_image->m_rect.y);
} else {
DrawArt(x, ui_image.rect.y, ui_image.art, ui_image.frame, ui_image.rect.w);
DrawArt(x, ui_image->m_rect.y, ui_image->m_art, ui_image->m_frame, ui_image->m_rect.w);
}
}
void Render(const UiArtTextButton &ui_button)
void Render(const UiArtTextButton *ui_button)
{
DrawArtStr(ui_button.text, ui_button.rect, ui_button.flags);
DrawArtStr(ui_button->m_text, ui_button->m_rect, ui_button->m_iFlags);
}
void Render(const UiList &ui_list)
void Render(const UiList *ui_list)
{
for (std::size_t i = 0; i < ui_list.length; ++i) {
SDL_Rect rect = ui_list.itemRect(i);
const auto &item = ui_list.items[i];
if (item.value == SelectedItem)
for (std::size_t i = 0; i < ui_list->m_vecItems.size(); ++i) {
SDL_Rect rect = ui_list->itemRect(i);
const UiListItem *item = ui_list->GetItem(i);
if (item->m_value == SelectedItem)
DrawSelector(rect);
DrawArtStr(item.text, rect, ui_list.flags);
DrawArtStr(item->m_text, rect, ui_list->m_iFlags);
}
}
void Render(const UiScrollBar &ui_sb)
void Render(const UiScrollBar *ui_sb)
{
// Bar background (tiled):
{
const std::size_t bg_y_end = DownArrowRect(ui_sb).y;
std::size_t bg_y = ui_sb.rect.y + ui_sb.arrow->h();
std::size_t bg_y = ui_sb->m_rect.y + ui_sb->m_arrow->h();
while (bg_y < bg_y_end) {
std::size_t drawH = std::min(bg_y + ui_sb.bg->h(), bg_y_end) - bg_y;
DrawArt(ui_sb.rect.x, bg_y, ui_sb.bg, 0, SCROLLBAR_BG_WIDTH, drawH);
std::size_t drawH = std::min(bg_y + ui_sb->m_bg->h(), bg_y_end) - bg_y;
DrawArt(ui_sb->m_rect.x, bg_y, ui_sb->m_bg, 0, SCROLLBAR_BG_WIDTH, drawH);
bg_y += drawH;
}
}
@ -694,91 +720,94 @@ void Render(const UiScrollBar &ui_sb)
{
const SDL_Rect rect = UpArrowRect(ui_sb);
const int frame = static_cast<int>(scrollBarState.upArrowPressed ? ScrollBarArrowFrame_UP_ACTIVE : ScrollBarArrowFrame_UP);
DrawArt(rect.x, rect.y, ui_sb.arrow, frame, rect.w);
DrawArt(rect.x, rect.y, ui_sb->m_arrow, frame, rect.w);
}
{
const SDL_Rect rect = DownArrowRect(ui_sb);
const int frame = static_cast<int>(scrollBarState.downArrowPressed ? ScrollBarArrowFrame_DOWN_ACTIVE : ScrollBarArrowFrame_DOWN);
DrawArt(rect.x, rect.y, ui_sb.arrow, frame, rect.w);
DrawArt(rect.x, rect.y, ui_sb->m_arrow, frame, rect.w);
}
// Thumb:
{
if (SelectedItemMax - SelectedItemMin > 0) {
const SDL_Rect rect = ThumbRect(
ui_sb, SelectedItem - SelectedItemMin, SelectedItemMax - SelectedItemMin + 1);
DrawArt(rect.x, rect.y, ui_sb.thumb);
DrawArt(rect.x, rect.y, ui_sb->m_thumb);
}
}
void Render(const UiEdit &ui_edit)
void Render(const UiEdit *ui_edit)
{
DrawSelector(ui_edit.rect);
SDL_Rect rect = ui_edit.rect;
DrawSelector(ui_edit->m_rect);
SDL_Rect rect = ui_edit->m_rect;
rect.x += 43;
rect.y += 1;
rect.w -= 86;
DrawArtStr(ui_edit.value, rect, ui_edit.flags, /*drawTextCursor=*/true);
DrawArtStr(ui_edit->m_value, rect, ui_edit->m_iFlags, /*drawTextCursor=*/true);
}
void RenderItem(UiItem *item)
void RenderItem(UiItemBase *item)
{
if (item->has_flag(UIS_HIDDEN))
return;
switch (item->type) {
switch (item->m_type) {
case UI_TEXT:
Render(&item->text);
Render(static_cast<UiText *>(item));
break;
case UI_ART_TEXT:
Render(item->art_text);
Render(static_cast<UiArtText *>(item));
break;
case UI_IMAGE:
Render(item->image);
Render(static_cast<UiImage *>(item));
break;
case UI_ART_TEXT_BUTTON:
Render(item->art_text_button);
Render(static_cast<UiArtTextButton *>(item));
break;
case UI_BUTTON:
RenderButton(&item->button);
RenderButton(static_cast<UiButton *>(item));
break;
case UI_LIST:
Render(item->list);
Render(static_cast<UiList *>(item));
break;
case UI_SCROLLBAR:
Render(item->scrollbar);
Render(static_cast<UiScrollBar *>(item));
break;
case UI_EDIT:
Render(item->edit);
Render(static_cast<UiEdit *>(item));
break;
}
}
bool HandleMouseEventArtTextButton(const SDL_Event &event, const UiArtTextButton &ui_button)
bool HandleMouseEventArtTextButton(const SDL_Event &event, const UiArtTextButton *ui_button)
{
if (event.type != SDL_MOUSEBUTTONDOWN || event.button.button != SDL_BUTTON_LEFT)
return false;
ui_button.action();
ui_button->m_action();
return true;
}
bool HandleMouseEventList(const SDL_Event &event, const UiList &ui_list)
bool HandleMouseEventList(const SDL_Event &event, UiList *ui_list)
{
if (event.type != SDL_MOUSEBUTTONDOWN || event.button.button != SDL_BUTTON_LEFT)
return false;
const UiListItem *list_item = ui_list.itemAt(event.button.y);
if (gfnListFocus != NULL && SelectedItem != list_item->value) {
UiFocus(list_item->value);
const UiListItem *list_item = ui_list->itemAt(event.button.y);
if (gfnListFocus != NULL && SelectedItem != list_item->m_value) {
UiFocus(list_item->m_value);
#ifdef USE_SDL1
} else if (gfnListFocus == NULL) {
#else
} else if (gfnListFocus == NULL || event.button.clicks >= 2) {
#endif
SelectedItem = list_item->value;
SelectedItem = list_item->m_value;
UiFocusNavigationSelect();
}
return true;
}
bool HandleMouseEventScrollBar(const SDL_Event &event, const UiScrollBar &ui_sb)
bool HandleMouseEventScrollBar(const SDL_Event &event, const UiScrollBar *ui_sb)
{
if (event.button.button != SDL_BUTTON_LEFT)
return false;
@ -812,19 +841,19 @@ bool HandleMouseEventScrollBar(const SDL_Event &event, const UiScrollBar &ui_sb)
return false;
}
bool HandleMouseEvent(const SDL_Event &event, UiItem *item)
bool HandleMouseEvent(const SDL_Event &event, UiItemBase *item)
{
if (item->has_any_flag(UIS_HIDDEN | UIS_DISABLED) || !IsInsideRect(event, item->rect()))
if (item->has_any_flag(UIS_HIDDEN | UIS_DISABLED) || !IsInsideRect(event, item->m_rect))
return false;
switch (item->type) {
switch (item->m_type) {
case UI_ART_TEXT_BUTTON:
return HandleMouseEventArtTextButton(event, item->art_text_button);
return HandleMouseEventArtTextButton(event, (UiArtTextButton *)item);
case UI_BUTTON:
return HandleMouseEventButton(event, &item->button);
return HandleMouseEventButton(event, (UiButton *)item);
case UI_LIST:
return HandleMouseEventList(event, item->list);
return HandleMouseEventList(event, (UiList *)item);
case UI_SCROLLBAR:
return HandleMouseEventScrollBar(event, item->scrollbar);
return HandleMouseEventScrollBar(event, (UiScrollBar *)item);
default:
return false;
}
@ -839,16 +868,17 @@ void LoadPalInMem(const SDL_Color *pPal)
}
}
void UiRenderItems(UiItem *items, std::size_t size)
void UiRenderItems(std::vector<UiItemBase *> items)
{
for (std::size_t i = 0; i < size; i++)
RenderItem(&items[i]);
for (std::size_t i = 0; i < items.size(); i++)
RenderItem((UiItemBase *)items[i]);
}
bool UiItemMouseEvents(SDL_Event *event, UiItem *items, std::size_t size)
bool UiItemMouseEvents(SDL_Event *event, std::vector<UiItemBase *> items)
{
if (!items || size == 0)
if (items.size() == 0) {
return false;
}
// In SDL2 mouse events already use logical coordinates.
#ifdef USE_SDL1
@ -856,8 +886,8 @@ bool UiItemMouseEvents(SDL_Event *event, UiItem *items, std::size_t size)
#endif
bool handled = false;
for (std::size_t i = 0; i < size; i++) {
if (HandleMouseEvent(*event, &items[i])) {
for (std::size_t i = 0; i < items.size(); i++) {
if (HandleMouseEvent(*event, items[i])) {
handled = true;
break;
}
@ -865,10 +895,10 @@ bool UiItemMouseEvents(SDL_Event *event, UiItem *items, std::size_t size)
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);
for (std::size_t i = 0; i < items.size(); ++i) {
UiItemBase *&item = items[i];
if (item->m_type == UI_BUTTON)
HandleGlobalMouseUpButton((UiButton *)item);
}
}

21
SourceX/DiabloUI/diabloui.h

@ -29,36 +29,27 @@ extern Art ArtCursor;
extern Art ArtHero;
extern bool gbSpawned;
constexpr auto MAINMENU_BACKGROUND = UiImage(&ArtBackground, { PANEL_LEFT, 0, 640, 480 });
constexpr auto MAINMENU_LOGO = UiImage(&ArtLogos[LOGO_MED], /*animated=*/true, /*frame=*/0, { 0, 0, 0, 0 }, UIS_CENTER);
template <class T, size_t N>
constexpr size_t size(T (&)[N])
{
return N;
}
extern void (*gfnSoundFunction)(char *file);
extern BOOL (*gfnHeroInfo)(BOOL (*fninfofunc)(_uiheroinfo *));
bool IsInsideRect(const SDL_Event &event, const SDL_Rect &rect);
void UiFadeIn();
void UiFocusNavigation(SDL_Event *event);
void UiHandleEvents(SDL_Event *event);
bool UiItemMouseEvents(SDL_Event *event, UiItem *items, std::size_t size);
bool UiItemMouseEvents(SDL_Event *event, std::vector<UiItemBase *> items);
int GetCenterOffset(int w, int bw = 0);
void LoadPalInMem(const SDL_Color *pPal);
void DrawMouse();
void LoadBackgroundArt(const char *pszFile);
void SetMenu(int MenuId);
void UiAddBackground(std::vector<UiItemBase *> *vecDialog);
void UiAddLogo(std::vector<UiItemBase *> *vecDialog, int size = LOGO_MED, int height = 0);
void UiFocusNavigationSelect();
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 UiInitList(int min, int max, void (*fnFocus)(int value), void (*fnSelect)(int value), void (*fnEsc)(), std::vector<UiItemBase *> items, bool wraps = false, bool (*fnYesNo)() = NULL);
void UiInitScrollBar(UiScrollBar *ui_sb, std::size_t viewport_size, const std::size_t *current_offset);
void UiClearScreen();
void UiPollAndRender();
void UiRenderItems(UiItem *items, std::size_t size);
void UiRenderItems(std::vector<UiItemBase *> items);
void UiInitList_clear();
void DvlIntSetting(const char *valuename, int *value);
void DvlStringSetting(const char *valuename, char *string, int len);

86
SourceX/DiabloUI/dialogs.cpp

@ -17,14 +17,9 @@ extern SDL_Surface *pal_surface;
namespace {
Art dialogArt;
char dialogText[256];
char dialogCaption[1024];
bool fontWasLoaded;
bool textInputWasActive;
UiItem *dialogItems;
std::size_t dialogItemsSize;
bool dialogEnd;
void DialogActionOK()
@ -32,20 +27,8 @@ void DialogActionOK()
dialogEnd = true;
}
const auto DIALOG_ART_L = UiImage(&dialogArt, { PANEL_LEFT + 127, 100, 385, 280 });
UiItem OK_DIALOG[] = {
UiImage(&dialogArt, { PANEL_LEFT + 180, 168, 280, 144 }),
UiText(dialogText, { PANEL_LEFT + 200, 211, 240, 80 }, UIS_CENTER),
MakeSmlButton("OK", &DialogActionOK, PANEL_LEFT + 265, 265),
};
UiItem OK_DIALOG_WITH_CAPTION[] = {
DIALOG_ART_L,
UiText(dialogText, SDL_Color{ 255, 255, 0, 0 }, { PANEL_LEFT + 147, 110, 345, 20 }, UIS_CENTER),
UiText(dialogCaption, { PANEL_LEFT + 147, 141, 345, 190 }, UIS_CENTER),
MakeSmlButton("OK", &DialogActionOK, PANEL_LEFT + 264, 335),
};
std::vector<UiItemBase *> vecNULL;
std::vector<UiItemBase *> vecOkDialog;
// clang-format off
#define BLANKCOLOR { 0, 0xFF, 0, 0 }
@ -176,7 +159,30 @@ void LoadFallbackPalette()
void Init(const char *text, const char *caption, bool error, bool renderBehind)
{
strcpy(dialogText, text);
if (caption == NULL) {
SDL_Rect rect1 = { PANEL_LEFT + 180, 168, 280, 144 };
vecOkDialog.push_back(new UiImage(&dialogArt, rect1));
SDL_Rect rect2 = { PANEL_LEFT + 200, 211, 240, 80 };
vecOkDialog.push_back(new UiText(text, rect2, UIS_CENTER));
SDL_Rect rect3 = { PANEL_LEFT + 265, 265, SML_BUTTON_WIDTH, SML_BUTTON_HEIGHT };
vecOkDialog.push_back(new UiButton(&SmlButton, "OK", &DialogActionOK, rect3, 0));
} else {
SDL_Rect rect1 = { PANEL_LEFT + 127, 100, 385, 280 };
vecOkDialog.push_back(new UiImage(&dialogArt, rect1));
SDL_Color color = { 255, 255, 0, 0 };
SDL_Rect rect2 = { PANEL_LEFT + 147, 110, 345, 20 };
vecOkDialog.push_back(new UiText(text, color, rect2, UIS_CENTER));
SDL_Rect rect3 = { PANEL_LEFT + 147, 141, 345, 190 };
vecOkDialog.push_back(new UiText(caption, rect3, UIS_CENTER));
SDL_Rect rect4 = { PANEL_LEFT + 264, 335, SML_BUTTON_WIDTH, SML_BUTTON_HEIGHT };
vecOkDialog.push_back(new UiButton(&SmlButton, "OK", &DialogActionOK, rect4, 0));
}
if (!renderBehind) {
LoadBackgroundArt("ui_art\\black.pcx");
if (ArtBackground.surface == NULL) {
@ -186,17 +192,12 @@ void Init(const char *text, const char *caption, bool error, bool renderBehind)
SetFadeLevel(256);
if (caption == NULL) {
LoadMaskedArt(error ? "ui_art\\srpopup.pcx" : "ui_art\\spopup.pcx", &dialogArt);
dialogItems = OK_DIALOG;
dialogItemsSize = size(OK_DIALOG);
} else {
if (error) {
LoadArt(&dialogArt, popupData, DIALOG_ART_L.rect.w, DIALOG_ART_L.rect.h);
LoadArt(&dialogArt, popupData, 385, 280);
} else {
LoadMaskedArt("ui_art\\lpopup.pcx", &dialogArt);
}
strcpy(dialogCaption, caption);
dialogItems = OK_DIALOG_WITH_CAPTION;
dialogItemsSize = size(OK_DIALOG_WITH_CAPTION);
}
LoadSmlButtonArt();
@ -215,12 +216,15 @@ void Deinit()
UnloadTtfFont();
if (textInputWasActive)
SDL_StartTextInput();
for (std::size_t i = 0; i < dialogItemsSize; ++i) {
dialogItems[i].FreeCache();
for (std::size_t i = 0; i < vecOkDialog.size(); i++) {
UiItemBase *pUIItem = vecOkDialog[i];
delete pUIItem;
}
vecOkDialog.clear();
}
void DialogLoop(UiItem *items, std::size_t num_items, UiItem *renderBehind, std::size_t renderBehind_size)
void DialogLoop(std::vector<UiItemBase *> items, std::vector<UiItemBase *> renderBehind)
{
SDL_Event event;
dialogEnd = false;
@ -229,7 +233,7 @@ void DialogLoop(UiItem *items, std::size_t num_items, UiItem *renderBehind, std:
switch (event.type) {
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
UiItemMouseEvents(&event, items, num_items);
UiItemMouseEvents(&event, items);
break;
default:
switch (GetMenuAction(event)) {
@ -245,12 +249,12 @@ void DialogLoop(UiItem *items, std::size_t num_items, UiItem *renderBehind, std:
UiHandleEvents(&event);
}
if (renderBehind_size == 0) {
if (renderBehind.size() == 0) {
SDL_FillRect(GetOutputSurface(), NULL, 0);
} else {
UiRenderItems(renderBehind, renderBehind_size);
UiRenderItems(renderBehind);
}
UiRenderItems(items, num_items);
UiRenderItems(items);
DrawMouse();
UiFadeIn();
} while (!dialogEnd);
@ -258,7 +262,7 @@ void DialogLoop(UiItem *items, std::size_t num_items, UiItem *renderBehind, std:
} // namespace
void UiOkDialog(const char *text, const char *caption, bool error, UiItem *renderBehind, std::size_t renderBehind_size)
void UiOkDialog(const char *text, const char *caption, bool error, std::vector<UiItemBase *> renderBehind)
{
static bool inDialog = false;
@ -279,25 +283,25 @@ void UiOkDialog(const char *text, const char *caption, bool error, UiItem *rende
}
inDialog = true;
Init(text, caption, error, renderBehind_size > 0);
DialogLoop(dialogItems, dialogItemsSize, renderBehind, renderBehind_size);
Init(text, caption, error, renderBehind.size() > 0);
DialogLoop(vecOkDialog, renderBehind);
Deinit();
inDialog = false;
}
void UiErrorOkDialog(const char *text, const char *caption, UiItem *renderBehind, std::size_t renderBehind_size)
void UiErrorOkDialog(const char *text, const char *caption, std::vector<UiItemBase *> renderBehind)
{
UiOkDialog(text, caption, /*error=*/true, renderBehind, renderBehind_size);
UiOkDialog(text, caption, /*error=*/true, renderBehind);
}
void UiErrorOkDialog(const char *text, const char *caption, bool error)
{
UiOkDialog(text, caption, error, NULL, 0);
UiOkDialog(text, caption, error, vecNULL);
}
void UiErrorOkDialog(const char *text, UiItem *renderBehind, std::size_t renderBehind_size)
void UiErrorOkDialog(const char *text, std::vector<UiItemBase *> renderBehind)
{
UiErrorOkDialog(text, NULL, renderBehind, renderBehind_size);
UiErrorOkDialog(text, NULL, renderBehind);
}
} // namespace dvl

6
SourceX/DiabloUI/dialogs.h

@ -6,8 +6,8 @@
namespace dvl {
void UiErrorOkDialog(const char *text, UiItem *renderBehind, std::size_t renderBehindSize);
void UiErrorOkDialog(const char *text, const char *caption, UiItem *renderBehind, std::size_t renderBehind_size);
void UiOkDialog(const char *text, const char *caption, bool error, UiItem *renderBehind, std::size_t renderBehind_size);
void UiErrorOkDialog(const char *text, std::vector<UiItemBase *> renderBehind);
void UiErrorOkDialog(const char *text, const char *caption, std::vector<UiItemBase *> renderBehind);
void UiOkDialog(const char *text, const char *caption, bool error, std::vector<UiItemBase *> renderBehind);
} // namespace dvl

46
SourceX/DiabloUI/mainmenu.cpp

@ -7,20 +7,10 @@ namespace dvl {
int mainmenu_attract_time_out; //seconds
DWORD dwAttractTicks;
std::vector<UiItemBase *> vecMainMenuDialog;
std::vector<UiListItem *> vecMenuItems;
int MainMenuResult;
UiListItem MAINMENU_DIALOG_ITEMS[] = {
{ "Single Player", MAINMENU_SINGLE_PLAYER },
{ "Multi Player", MAINMENU_MULTIPLAYER },
{ "Replay Intro", MAINMENU_REPLAY_INTRO },
{ "Show Credits", MAINMENU_SHOW_CREDITS },
{ "Exit Diablo", MAINMENU_EXIT_DIABLO }
};
UiItem MAINMENU_DIALOG[] = {
MAINMENU_BACKGROUND,
MAINMENU_LOGO,
UiList(MAINMENU_DIALOG_ITEMS, PANEL_LEFT + 64, 192, 510, 43, UIS_HUGE | UIS_GOLD | UIS_CENTER),
UiArtText(NULL, { 17, 444, 605, 21 }, UIS_SMALL)
};
void UiMainMenuSelect(int value)
{
@ -44,7 +34,20 @@ void mainmenu_restart_repintro()
void mainmenu_Load(char *name, void (*fnSound)(char *file))
{
gfnSoundFunction = fnSound;
MAINMENU_DIALOG[size(MAINMENU_DIALOG) - 1].art_text.text = name;
vecMenuItems.push_back(new UiListItem("Single Player", MAINMENU_SINGLE_PLAYER));
vecMenuItems.push_back(new UiListItem("Multi Player", MAINMENU_MULTIPLAYER));
vecMenuItems.push_back(new UiListItem("Replay Intro", MAINMENU_REPLAY_INTRO));
vecMenuItems.push_back(new UiListItem("Show Credits", MAINMENU_SHOW_CREDITS));
vecMenuItems.push_back(new UiListItem("Exit Diablo", MAINMENU_EXIT_DIABLO));
UiAddBackground(&vecMainMenuDialog);
UiAddLogo(&vecMainMenuDialog);
vecMainMenuDialog.push_back(new UiList(vecMenuItems, PANEL_LEFT + 64, 192, 510, 43, UIS_HUGE | UIS_GOLD | UIS_CENTER));
SDL_Rect rect = { 17, 444, 605, 21 };
vecMainMenuDialog.push_back(new UiArtText(name, rect, UIS_SMALL));
if (!gbSpawned) {
LoadBackgroundArt("ui_art\\mainmenu.pcx");
@ -52,12 +55,25 @@ void mainmenu_Load(char *name, void (*fnSound)(char *file))
LoadBackgroundArt("ui_art\\swmmenu.pcx");
}
UiInitList(MAINMENU_SINGLE_PLAYER, MAINMENU_EXIT_DIABLO, NULL, UiMainMenuSelect, mainmenu_Esc, MAINMENU_DIALOG, size(MAINMENU_DIALOG), true);
UiInitList(MAINMENU_SINGLE_PLAYER, MAINMENU_EXIT_DIABLO, NULL, UiMainMenuSelect, mainmenu_Esc, vecMainMenuDialog, true);
}
void mainmenu_Free()
{
ArtBackground.Unload();
for (std::size_t i = 0; i < vecMainMenuDialog.size(); i++) {
UiItemBase *pUIItem = vecMainMenuDialog[i];
delete pUIItem;
}
vecMainMenuDialog.clear();
for (std::size_t i = 0; i < vecMenuItems.size(); i++) {
UiListItem *pUIMenuItem = vecMenuItems[i];
if (pUIMenuItem)
delete pUIMenuItem;
}
vecMenuItems.clear();
}
BOOL UiMainMenuDialog(char *name, int *pdwResult, void (*fnSound)(char *file), int attractTimeOut)

13
SourceX/DiabloUI/progress.cpp

@ -24,13 +24,12 @@ void DialogActionCancel()
endMenu = true;
}
// TODO use PROGRESS_DIALOG for rendering the progressbar or delete it
UiItem PROGRESS_DIALOG[] = {
UiImage(&dialogArt, { PANEL_LEFT + 180, 168, 280, 144 }),
UiText(dialogText, { PANEL_LEFT + 180, 177, 280, 43 }, UIS_CENTER),
UiImage(&progressArt, { PANEL_LEFT + 205, 220, 228, 38 }),
MakeSmlButton("Cancel", &DialogActionCancel, 330, 265),
};
//// TODO use PROGRESS_DIALOG for rendering the progressbar or delete it
//UiItem PROGRESS_DIALOG[] = {
// UiImage(&dialogArt, { PANEL_LEFT + 180, 168, 280, 144 }),
// UiText(dialogText, { PANEL_LEFT + 180, 177, 280, 43 }, UIS_CENTER),
// UiImage(&progressArt, { PANEL_LEFT + 205, 220, 228, 38 }),
//};
void progress_Load(char *msg)
{

72
SourceX/DiabloUI/scrollbar.h

@ -6,6 +6,8 @@
namespace dvl {
extern Art ArtScrollBarBackground;
extern Art ArtScrollBarThumb;
extern Art ArtScrollBarArrow;
const Uint16 SCROLLBAR_BG_WIDTH = 25;
extern Art ArtScrollBarArrow;
@ -19,57 +21,57 @@ enum ScrollBarArrowFrame {
extern Art ArtScrollBarThumb;
const Uint16 SCROLLBAR_ARROW_WIDTH = 25;
inline SDL_Rect UpArrowRect(const UiScrollBar &sb)
inline SDL_Rect UpArrowRect(const UiScrollBar *sb)
{
return {
sb.rect.x,
sb.rect.y,
SCROLLBAR_ARROW_WIDTH,
static_cast<Uint16>(sb.arrow->h()),
};
SDL_Rect Tmp;
Tmp.x = sb->m_rect.x;
Tmp.y = sb->m_rect.y;
Tmp.w = SCROLLBAR_ARROW_WIDTH;
Tmp.h = static_cast<Uint16>(sb->m_arrow->h());
return Tmp;
}
inline SDL_Rect DownArrowRect(const UiScrollBar &sb)
inline SDL_Rect DownArrowRect(const UiScrollBar *sb)
{
return {
sb.rect.x,
static_cast<Sint16>(sb.rect.y + sb.rect.h) - sb.arrow->h(),
SCROLLBAR_ARROW_WIDTH,
static_cast<Uint16>(sb.arrow->h()),
};
SDL_Rect Tmp;
Tmp.x = sb->m_rect.x;
Tmp.y = static_cast<Sint16>(sb->m_rect.y + sb->m_rect.h - sb->m_arrow->h());
Tmp.w = SCROLLBAR_ARROW_WIDTH,
Tmp.h = static_cast<Uint16>(sb->m_arrow->h());
return Tmp;
}
inline Uint16 BarHeight(const UiScrollBar &sb)
inline Uint16 BarHeight(const UiScrollBar *sb)
{
return sb.rect.h - 2 * sb.arrow->h();
return sb->m_rect.h - 2 * sb->m_arrow->h();
}
inline SDL_Rect BarRect(const UiScrollBar &sb)
inline SDL_Rect BarRect(const UiScrollBar *sb)
{
return {
sb.rect.x,
static_cast<Sint16>(sb.rect.y + sb.arrow->h()),
SCROLLBAR_BG_WIDTH,
BarHeight(sb),
};
SDL_Rect Tmp;
Tmp.x = sb->m_rect.x;
Tmp.y = static_cast<Sint16>(sb->m_rect.y + sb->m_arrow->h());
Tmp.w = SCROLLBAR_ARROW_WIDTH,
Tmp.h = BarHeight(sb);
return Tmp;
}
inline SDL_Rect ThumbRect(const UiScrollBar &sb, std::size_t selected_index, std::size_t num_items)
inline SDL_Rect ThumbRect(const UiScrollBar *sb, std::size_t selected_index, std::size_t num_items)
{
const int THUMB_OFFSET_X = 3;
const int thumb_max_y = BarHeight(sb) - sb.thumb->h();
const int thumb_max_y = BarHeight(sb) - sb->m_thumb->h();
const int thumb_y = (selected_index * thumb_max_y / (num_items - 1));
return {
static_cast<Sint16>(sb.rect.x + THUMB_OFFSET_X),
static_cast<Sint16>(sb.rect.y + sb.arrow->h() + thumb_y),
static_cast<Uint16>(sb.rect.w - THUMB_OFFSET_X),
static_cast<Uint16>(sb.thumb->h()),
};
}
constexpr UiScrollBar MakeScrollBar(SDL_Rect rect)
{
return UiScrollBar(&ArtScrollBarBackground, &ArtScrollBarThumb, &ArtScrollBarArrow, rect);
SDL_Rect Tmp;
Tmp.x = static_cast<Sint16>(sb->m_rect.x + THUMB_OFFSET_X);
Tmp.y = static_cast<Sint16>(sb->m_rect.y + sb->m_arrow->h() + thumb_y);
Tmp.w = static_cast<Uint16>(sb->m_rect.w - THUMB_OFFSET_X);
Tmp.h = static_cast<Uint16>(sb->m_thumb->h());
return Tmp;
}
void LoadScrollBar();

90
SourceX/DiabloUI/selconn.cpp

@ -18,44 +18,78 @@ _SNETVERSIONDATA *selconn_FileInfo;
int provider;
UiArtText SELCONNECT_DIALOG_DESCRIPTION(selconn_Description, { PANEL_LEFT + 35, 275, 205, 66 });
std::vector<UiListItem *> vecConnItems;
std::vector<UiItemBase *> vecSelConnDlg;
// Should be in the same order than conn_type (See enums.h)
UiListItem SELCONN_DIALOG_ITEMS[] = {
#define DESCRIPTION_WIDTH 205
void selconn_Load()
{
LoadBackgroundArt("ui_art\\selconn.pcx");
// vecConnItems Should be in the same order as conn_type (See enums.h)
#ifndef NONET
{ "Client-Server (TCP)", SELCONN_TCP },
vecConnItems.push_back(new UiListItem("Client-Server (TCP)", SELCONN_TCP));
#ifdef BUGGY
{ "Peer-to-Peer (UDP)", SELCONN_UDP },
vecConnItems.push_back(new UiListItem("Peer-to-Peer (UDP)", SELCONN_UDP));
#endif
#endif
{ "Loopback", SELCONN_LOOPBACK },
};
UiItem SELCONNECT_DIALOG[] = {
MAINMENU_BACKGROUND,
MAINMENU_LOGO,
UiArtText("Multi Player Game", { PANEL_LEFT + 24, 161, 590, 35 }, UIS_CENTER | UIS_BIG),
UiArtText(selconn_MaxPlayers, { PANEL_LEFT + 35, 218, 205, 21 }),
UiArtText("Requirements:", { PANEL_LEFT + 35, 256, 205, 21 }),
SELCONNECT_DIALOG_DESCRIPTION,
UiArtText("no gateway needed", { PANEL_LEFT + 30, 356, 220, 31 }, UIS_CENTER | UIS_MED),
UiArtText(selconn_Gateway, { PANEL_LEFT + 35, 393, 205, 21 }, UIS_CENTER),
UiArtText("Select Connection", { PANEL_LEFT + 300, 211, 295, 33 }, UIS_CENTER | UIS_BIG),
UiArtTextButton("Change Gateway", NULL, { PANEL_LEFT + 16, 427, 250, 35 }, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD | UIS_HIDDEN),
UiList(SELCONN_DIALOG_ITEMS, PANEL_LEFT + 305, 256, 285, 26, UIS_CENTER | UIS_VCENTER | UIS_GOLD),
UiArtTextButton("OK", &UiFocusNavigationSelect, { PANEL_LEFT + 299, 427, 140, 35 }, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD),
UiArtTextButton("Cancel", &UiFocusNavigationEsc, { PANEL_LEFT + 454, 427, 140, 35 }, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD)
};
vecConnItems.push_back(new UiListItem("Loopback", SELCONN_LOOPBACK));
void selconn_Load()
{
LoadBackgroundArt("ui_art\\selconn.pcx");
UiInitList(0, size(SELCONN_DIALOG_ITEMS) - 1, selconn_Focus, selconn_Select, selconn_Esc, SELCONNECT_DIALOG, size(SELCONNECT_DIALOG));
UiAddBackground(&vecSelConnDlg);
UiAddLogo(&vecSelConnDlg);
SDL_Rect rect1 = { PANEL_LEFT + 24, 161, 590, 35 };
vecSelConnDlg.push_back(new UiArtText("Multi Player Game", rect1, UIS_CENTER | UIS_BIG));
SDL_Rect rect2 = { PANEL_LEFT + 35, 218, DESCRIPTION_WIDTH, 21 };
vecSelConnDlg.push_back(new UiArtText(selconn_MaxPlayers, rect2));
SDL_Rect rect3 = { PANEL_LEFT + 35, 256, DESCRIPTION_WIDTH, 21 };
vecSelConnDlg.push_back(new UiArtText("Requirements:", rect3));
SDL_Rect rect4 = { PANEL_LEFT + 35, 275, DESCRIPTION_WIDTH, 66 };
vecSelConnDlg.push_back(new UiArtText(selconn_Description, rect4));
SDL_Rect rect5 = { PANEL_LEFT + 30, 356, 220, 31 };
vecSelConnDlg.push_back(new UiArtText("no gateway needed", rect5, UIS_CENTER | UIS_MED));
SDL_Rect rect6 = { PANEL_LEFT + 35, 393, DESCRIPTION_WIDTH, 21 };
vecSelConnDlg.push_back(new UiArtText(selconn_Gateway, rect6, UIS_CENTER));
SDL_Rect rect7 = { PANEL_LEFT + 300, 211, 295, 33 };
vecSelConnDlg.push_back(new UiArtText("Select Connection", rect7, UIS_CENTER | UIS_BIG));
SDL_Rect rect8 = { PANEL_LEFT + 16, 427, 250, 35 };
vecSelConnDlg.push_back(new UiArtTextButton("Change Gateway", NULL, rect8, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD | UIS_HIDDEN));
vecSelConnDlg.push_back(new UiList(vecConnItems, PANEL_LEFT + 305, 256, 285, 26, UIS_CENTER | UIS_VCENTER | UIS_GOLD));
SDL_Rect rect9 = { PANEL_LEFT + 299, 427, 140, 35 };
vecSelConnDlg.push_back(new UiArtTextButton("OK", &UiFocusNavigationSelect, rect9, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD));
SDL_Rect rect10 = { PANEL_LEFT + 454, 427, 140, 35 };
vecSelConnDlg.push_back(new UiArtTextButton("Cancel", &UiFocusNavigationEsc, rect10, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD));
UiInitList(0, vecConnItems.size() - 1, selconn_Focus, selconn_Select, selconn_Esc, vecSelConnDlg);
}
void selconn_Free()
{
ArtBackground.Unload();
for (std::size_t i = 0; i < vecConnItems.size(); i++) {
UiListItem *pUIItem = vecConnItems[i];
delete pUIItem;
}
vecConnItems.clear();
for (std::size_t i = 0; i < vecSelConnDlg.size(); i++) {
UiItemBase *pUIMenuItem = vecSelConnDlg[i];
if (pUIMenuItem)
delete pUIMenuItem;
}
vecSelConnDlg.clear();
}
void selconn_Esc()
@ -87,7 +121,7 @@ void selconn_Focus(int value)
}
sprintf(selconn_MaxPlayers, "Players Supported: %d", players);
WordWrapArtStr(selconn_Description, SELCONNECT_DIALOG_DESCRIPTION.rect.w);
WordWrapArtStr(selconn_Description, DESCRIPTION_WIDTH);
}
void selconn_Select(int value)

206
SourceX/DiabloUI/selgame.cpp

@ -23,80 +23,37 @@ int heroLevel;
static _SNETPROGRAMDATA *m_client_info;
extern int provider;
constexpr UiArtTextButton SELGAME_OK = UiArtTextButton("OK", &UiFocusNavigationSelect, { PANEL_LEFT + 299, 427, 140, 35 }, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD);
constexpr UiArtTextButton SELGAME_CANCEL = UiArtTextButton("CANCEL", &UiFocusNavigationEsc, { PANEL_LEFT + 449, 427, 140, 35 }, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD);
UiArtText SELGAME_DESCRIPTION(selgame_Description, { PANEL_LEFT + 35, 256, 205, 192 });
#define DESCRIPTION_WIDTH 205
namespace {
char title[32];
UiListItem SELDIFF_DIALOG_ITEMS[] = {
{ "Normal", DIFF_NORMAL },
{ "Nightmare", DIFF_NIGHTMARE },
{ "Hell", DIFF_HELL }
};
UiItem SELDIFF_DIALOG[] = {
MAINMENU_BACKGROUND,
MAINMENU_LOGO,
UiArtText(title, { PANEL_LEFT + 24, 161, 590, 35 }, UIS_CENTER | UIS_BIG),
UiArtText(selgame_Label, { PANEL_LEFT + 34, 211, 205, 33 }, UIS_CENTER | UIS_BIG), // DIFF
SELGAME_DESCRIPTION,
UiArtText("Select Difficulty", { PANEL_LEFT + 299, 211, 295, 35 }, UIS_CENTER | UIS_BIG),
UiList(SELDIFF_DIALOG_ITEMS, PANEL_LEFT + 300, 282, 295, 26, UIS_CENTER | UIS_MED | UIS_GOLD),
SELGAME_OK,
SELGAME_CANCEL,
};
constexpr UiArtText SELUDPGAME_TITLE = UiArtText(title, { PANEL_LEFT + 24, 161, 590, 35 }, UIS_CENTER | UIS_BIG);
constexpr UiArtText SELUDPGAME_DESCRIPTION_LABEL = UiArtText("Description:", { PANEL_LEFT + 35, 211, 205, 192 }, UIS_MED);
UiListItem SELUDPGAME_DIALOG_ITEMS[] = {
{ "Create Game", 0 },
{ "Join Game", 1 },
};
UiItem SELUDPGAME_DIALOG[] = {
MAINMENU_BACKGROUND,
MAINMENU_LOGO,
SELUDPGAME_TITLE,
SELUDPGAME_DESCRIPTION_LABEL,
SELGAME_DESCRIPTION,
UiArtText("Select Action", { PANEL_LEFT + 300, 211, 295, 33 }, UIS_CENTER | UIS_BIG),
UiList(SELUDPGAME_DIALOG_ITEMS, PANEL_LEFT + 305, 255, 285, 26, UIS_CENTER | UIS_MED | UIS_GOLD),
SELGAME_OK,
SELGAME_CANCEL,
};
UiItem ENTERIP_DIALOG[] = {
MAINMENU_BACKGROUND,
MAINMENU_LOGO,
SELUDPGAME_TITLE,
SELUDPGAME_DESCRIPTION_LABEL,
SELGAME_DESCRIPTION,
UiArtText("Enter address", { PANEL_LEFT + 305, 211, 285, 33 }, UIS_CENTER | UIS_BIG),
UiEdit(selgame_Ip, 128, { PANEL_LEFT + 305, 314, 285, 33 }, UIS_MED | UIS_GOLD),
SELGAME_OK,
SELGAME_CANCEL,
};
UiItem ENTERPASSWORD_DIALOG[] = {
MAINMENU_BACKGROUND,
MAINMENU_LOGO,
SELUDPGAME_TITLE,
SELUDPGAME_DESCRIPTION_LABEL,
SELGAME_DESCRIPTION,
UiArtText("Enter Password", { PANEL_LEFT + 305, 211, 285, 33 }, UIS_CENTER | UIS_BIG),
UiEdit(selgame_Password, 15, { PANEL_LEFT + 305, 314, 285, 33 }, UIS_MED | UIS_GOLD),
SELGAME_OK,
SELGAME_CANCEL,
};
std::vector<UiListItem *> vecSelGameDlgItems;
std::vector<UiItemBase *> vecSelGameDialog;
} // namespace
void selgame_FreeVectors()
{
for (std::size_t i = 0; i < vecSelGameDlgItems.size(); i++) {
UiListItem *pUIItem = vecSelGameDlgItems[i];
delete pUIItem;
}
vecSelGameDlgItems.clear();
for (std::size_t i = 0; i < vecSelGameDialog.size(); i++) {
UiItemBase *pUIItem = vecSelGameDialog[i];
delete pUIItem;
}
vecSelGameDialog.clear();
}
void selgame_Free()
{
ArtBackground.Unload();
selgame_FreeVectors();
}
void selgame_GameSelection_Init()
@ -111,8 +68,36 @@ void selgame_GameSelection_Init()
}
getIniValue("Phone Book", "Entry1", selgame_Ip, 128);
strcpy(title, "Client-Server (TCP)");
UiInitList(0, 1, selgame_GameSelection_Focus, selgame_GameSelection_Select, selgame_GameSelection_Esc, SELUDPGAME_DIALOG, size(SELUDPGAME_DIALOG));
selgame_FreeVectors();
UiAddBackground(&vecSelGameDialog);
UiAddLogo(&vecSelGameDialog);
SDL_Rect rect1 = { PANEL_LEFT + 24, 161, 590, 35 };
vecSelGameDialog.push_back(new UiArtText("Client-Server (TCP)", rect1, UIS_CENTER | UIS_BIG));
SDL_Rect rect2 = { PANEL_LEFT + 35, 211, 205, 192 };
vecSelGameDialog.push_back(new UiArtText("Description:", rect2, UIS_MED));
SDL_Rect rect3 = { PANEL_LEFT + 35, 256, DESCRIPTION_WIDTH, 192 };
vecSelGameDialog.push_back(new UiArtText(selgame_Description, rect3));
SDL_Rect rect4 = { PANEL_LEFT + 300, 211, 295, 33 };
vecSelGameDialog.push_back(new UiArtText("Select Action", rect4, UIS_CENTER | UIS_BIG));
vecSelGameDlgItems.push_back(new UiListItem("Create Game", 0));
vecSelGameDlgItems.push_back(new UiListItem("Join Game", 1));
vecSelGameDialog.push_back(new UiList(vecSelGameDlgItems, PANEL_LEFT + 305, 255, 285, 26, UIS_CENTER | UIS_MED | UIS_GOLD));
SDL_Rect rect5 = { PANEL_LEFT + 299, 427, 140, 35 };
vecSelGameDialog.push_back(new UiArtTextButton("OK", &UiFocusNavigationSelect, rect5, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD));
SDL_Rect rect6 = { PANEL_LEFT + 449, 427, 140, 35 };
vecSelGameDialog.push_back(new UiArtTextButton("CANCEL", &UiFocusNavigationEsc, rect6, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD));
UiInitList(0, 1, selgame_GameSelection_Focus, selgame_GameSelection_Select, selgame_GameSelection_Esc, vecSelGameDialog);
}
void selgame_GameSelection_Focus(int value)
@ -125,7 +110,7 @@ void selgame_GameSelection_Focus(int value)
strcpy(selgame_Description, "Enter an IP or a hostname and join a game already in progress at that address.");
break;
}
WordWrapArtStr(selgame_Description, SELGAME_DESCRIPTION.rect.w);
WordWrapArtStr(selgame_Description, DESCRIPTION_WIDTH);
}
/**
@ -148,21 +133,65 @@ void selgame_GameSelection_Select(int value)
gfnHeroInfo(UpdateHeroLevel);
selgame_FreeVectors();
UiAddBackground(&vecSelGameDialog);
UiAddLogo(&vecSelGameDialog);
SDL_Rect rect1 = { PANEL_LEFT + 24, 161, 590, 35 };
vecSelGameDialog.push_back(new UiArtText(title, rect1, UIS_CENTER | UIS_BIG));
SDL_Rect rect2 = { PANEL_LEFT + 34, 211, 205, 33 };
vecSelGameDialog.push_back(new UiArtText(selgame_Label, rect2, UIS_CENTER | UIS_BIG));
SDL_Rect rect3 = { PANEL_LEFT + 35, 256, DESCRIPTION_WIDTH, 192 };
vecSelGameDialog.push_back(new UiArtText(selgame_Description, rect3));
switch (value) {
case 0:
case 0: {
strcpy(title, "Create Game");
UiInitList(0, NUM_DIFFICULTIES - 1, selgame_Diff_Focus, selgame_Diff_Select, selgame_Diff_Esc, SELDIFF_DIALOG, size(SELDIFF_DIALOG));
SDL_Rect rect4 = { PANEL_LEFT + 299, 211, 295, 35 };
vecSelGameDialog.push_back(new UiArtText("Select Difficulty", rect4, UIS_CENTER | UIS_BIG));
vecSelGameDlgItems.push_back(new UiListItem("Normal", DIFF_NORMAL));
vecSelGameDlgItems.push_back(new UiListItem("Nightmare", DIFF_NIGHTMARE));
vecSelGameDlgItems.push_back(new UiListItem("Hell", DIFF_HELL));
vecSelGameDialog.push_back(new UiList(vecSelGameDlgItems, PANEL_LEFT + 300, 282, 295, 26, UIS_CENTER | UIS_MED | UIS_GOLD));
SDL_Rect rect5 = { PANEL_LEFT + 299, 427, 140, 35 };
vecSelGameDialog.push_back(new UiArtTextButton("OK", &UiFocusNavigationSelect, rect5, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD));
SDL_Rect rect6 = { PANEL_LEFT + 449, 427, 140, 35 };
vecSelGameDialog.push_back(new UiArtTextButton("CANCEL", &UiFocusNavigationEsc, rect6, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD));
UiInitList(0, NUM_DIFFICULTIES - 1, selgame_Diff_Focus, selgame_Diff_Select, selgame_Diff_Esc, vecSelGameDialog);
break;
}
case 1:
strcpy(title, "Join TCP Games");
UiInitList(0, 0, NULL, selgame_Password_Init, selgame_GameSelection_Init, ENTERIP_DIALOG, size(ENTERIP_DIALOG));
SDL_Rect rect4 = { PANEL_LEFT + 305, 211, 285, 33 };
vecSelGameDialog.push_back(new UiArtText("Enter address", rect4, UIS_CENTER | UIS_BIG));
SDL_Rect rect5 = { PANEL_LEFT + 305, 314, 285, 33 };
vecSelGameDialog.push_back(new UiEdit(selgame_Ip, 128, rect5, UIS_MED | UIS_GOLD));
SDL_Rect rect6 = { PANEL_LEFT + 299, 427, 140, 35 };
vecSelGameDialog.push_back(new UiArtTextButton("OK", &UiFocusNavigationSelect, rect6, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD));
SDL_Rect rect7 = { PANEL_LEFT + 449, 427, 140, 35 };
vecSelGameDialog.push_back(new UiArtTextButton("CANCEL", &UiFocusNavigationEsc, rect7, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD));
UiInitList(0, 0, NULL, selgame_Password_Init, selgame_GameSelection_Init, vecSelGameDialog);
break;
}
}
void selgame_GameSelection_Esc()
{
UiInitList(0, 0, NULL, NULL, NULL, NULL, 0);
UiInitList_clear();
selgame_enteringGame = false;
selgame_endMenu = true;
}
@ -183,7 +212,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;
}
WordWrapArtStr(selgame_Description, SELGAME_DESCRIPTION.rect.w);
WordWrapArtStr(selgame_Description, DESCRIPTION_WIDTH);
}
bool IsDifficultyAllowed(int value)
@ -234,7 +263,34 @@ void selgame_Diff_Esc()
void selgame_Password_Init(int value)
{
memset(&selgame_Password, 0, sizeof(selgame_Password));
UiInitList(0, 0, NULL, selgame_Password_Select, selgame_Password_Esc, ENTERPASSWORD_DIALOG, size(ENTERPASSWORD_DIALOG));
selgame_FreeVectors();
UiAddBackground(&vecSelGameDialog);
UiAddLogo(&vecSelGameDialog);
SDL_Rect rect1 = { PANEL_LEFT + 24, 161, 590, 35 };
vecSelGameDialog.push_back(new UiArtText("Client-Server (TCP)", rect1, UIS_CENTER | UIS_BIG));
SDL_Rect rect2 = { PANEL_LEFT + 35, 211, 205, 192 };
vecSelGameDialog.push_back(new UiArtText("Description:", rect2, UIS_MED));
SDL_Rect rect3 = { PANEL_LEFT + 35, 256, DESCRIPTION_WIDTH, 192 };
vecSelGameDialog.push_back(new UiArtText(selgame_Description, rect3));
SDL_Rect rect4 = { PANEL_LEFT + 305, 211, 285, 33 };
vecSelGameDialog.push_back(new UiArtText("Enter Password", rect4, UIS_CENTER | UIS_BIG));
SDL_Rect rect5 = { PANEL_LEFT + 305, 314, 285, 33 };
vecSelGameDialog.push_back(new UiEdit(selgame_Password, 15, rect5, UIS_MED | UIS_GOLD));
SDL_Rect rect6 = { PANEL_LEFT + 299, 427, 140, 35 };
vecSelGameDialog.push_back(new UiArtTextButton("OK", &UiFocusNavigationSelect, rect6, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD));
SDL_Rect rect7 = { PANEL_LEFT + 449, 427, 140, 35 };
vecSelGameDialog.push_back(new UiArtTextButton("CANCEL", &UiFocusNavigationEsc, rect7, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD));
UiInitList(0, 0, NULL, selgame_Password_Select, selgame_Password_Esc, vecSelGameDialog);
}
void selgame_Password_Select(int value)
@ -247,7 +303,7 @@ void selgame_Password_Select(int value)
return;
}
UiInitList(0, 0, NULL, NULL, NULL, NULL, 0);
UiInitList_clear();
selgame_endMenu = true;
} else {
selgame_Free();
@ -262,7 +318,7 @@ void selgame_Password_Select(int value)
info->bDiff = gbDifficulty;
if (SNetCreateGame(NULL, selgame_Password, NULL, 0, (char *)info, sizeof(_gamedata), MAX_PLRS, NULL, NULL, gdwPlayerId)) {
UiInitList(0, 0, NULL, NULL, NULL, NULL, 0);
UiInitList_clear();
selgame_endMenu = true;
} else {
selgame_Free();

278
SourceX/DiabloUI/selhero.cpp

@ -14,10 +14,11 @@
namespace dvl {
const char *selhero_GenerateName(uint8_t hero_class);
std::size_t selhero_SaveCount = 0;
_uiheroinfo selhero_heros[MAX_CHARACTERS];
_uiheroinfo selhero_heroInfo;
std::size_t listOffset = 0;
const size_t kMaxViewportItems = 6;
char textStats[5][4];
char title[32];
@ -29,7 +30,7 @@ bool selhero_isMultiPlayer;
bool selhero_navigateYesNo;
bool selhero_deleteEnabled;
BOOL (*gfnHeroInfo)
BOOL(*gfnHeroInfo)
(BOOL (*fninfofunc)(_uiheroinfo *));
BOOL(*gfnHeroCreate)
(_uiheroinfo *);
@ -38,83 +39,58 @@ BOOL(*gfnHeroStats)
namespace {
UiItem SELHERO_DIALOG[] = {
MAINMENU_BACKGROUND,
MAINMENU_LOGO,
UiArtText(title, { PANEL_LEFT + 24, 161, 590, 35 }, UIS_CENTER | UIS_BIG),
UiImage(&ArtHero, UI_NUM_CLASSES, { PANEL_LEFT + 30, 211, 180, 76 }),
UiArtText("Level:", { PANEL_LEFT + 39, 323, 110, 21 }, UIS_RIGHT),
UiArtText(textStats[0], { PANEL_LEFT + 159, 323, 40, 21 }, UIS_CENTER),
UiArtText("Strength:", { PANEL_LEFT + 39, 358, 110, 21 }, UIS_RIGHT),
UiArtText(textStats[1], { PANEL_LEFT + 159, 358, 40, 21 }, UIS_CENTER),
UiArtText("Magic:", { PANEL_LEFT + 39, 380, 110, 21 }, UIS_RIGHT),
UiArtText(textStats[2], { PANEL_LEFT + 159, 380, 40, 21 }, UIS_CENTER),
UiArtText("Dexterity:", { PANEL_LEFT + 39, 401, 110, 21 }, UIS_RIGHT),
UiArtText(textStats[3], { PANEL_LEFT + 159, 401, 40, 21 }, UIS_CENTER),
UiArtText("Vitality:", { PANEL_LEFT + 39, 422, 110, 21 }, UIS_RIGHT),
UiArtText(textStats[4], { PANEL_LEFT + 159, 422, 40, 21 }, UIS_CENTER),
};
UiImage *SELHERO_DIALOG_HERO_IMG = &SELHERO_DIALOG[3].image;
UiListItem SELLIST_DIALOG_ITEMS[kMaxViewportItems];
UiItem SELLIST_DIALOG[] = {
UiArtText("Select Hero", { PANEL_LEFT + 264, 211, 320, 33 }, UIS_CENTER | UIS_BIG),
UiList(SELLIST_DIALOG_ITEMS, PANEL_LEFT + 265, 256, 320, 26, UIS_CENTER | UIS_MED | UIS_GOLD),
MakeScrollBar({ PANEL_LEFT + 585, 244, 25, 178 }),
UiArtTextButton("OK", &UiFocusNavigationSelect, { PANEL_LEFT + 239, 429, 120, 35 }, UIS_CENTER | UIS_BIG | UIS_GOLD),
UiArtTextButton("Delete", &selhero_UiFocusNavigationYesNo, { PANEL_LEFT + 364, 429, 120, 35 }, UIS_CENTER | UIS_BIG | UIS_DISABLED),
UiArtTextButton("Cancel", &UiFocusNavigationEsc, { PANEL_LEFT + 489, 429, 120, 35 }, UIS_CENTER | UIS_BIG | UIS_GOLD)
};
UiList *SELLIST_DIALOG_LIST = &SELLIST_DIALOG[1].list;
UiScrollBar *SELLIST_SCROLLBAR = &SELLIST_DIALOG[2].scrollbar;
UiArtTextButton *SELLIST_DIALOG_DELETE_BUTTON = &SELLIST_DIALOG[4].art_text_button;
UiListItem SELCLAS_DIALOG_ITEMS[] = {
{ "Warrior", UI_WARRIOR },
{ "Rogue", UI_ROGUE },
{ "Sorcerer", UI_SORCERER }
};
UiItem SELCLASS_DIALOG[] = {
UiArtText("Choose Class", { PANEL_LEFT + 264, 211, 320, 33 }, UIS_CENTER | UIS_BIG),
UiList(SELCLAS_DIALOG_ITEMS, PANEL_LEFT + 264, 285, 320, 33, UIS_CENTER | UIS_MED | UIS_GOLD),
UiArtTextButton("OK", &UiFocusNavigationSelect, { PANEL_LEFT + 279, 429, 140, 35 }, UIS_CENTER | UIS_BIG | UIS_GOLD),
UiArtTextButton("Cancel", &UiFocusNavigationEsc, { PANEL_LEFT + 429, 429, 140, 35 }, UIS_CENTER | UIS_BIG | UIS_GOLD)
};
UiItem ENTERNAME_DIALOG[] = {
UiArtText("Enter Name", { PANEL_LEFT + 264, 211, 320, 33 }, UIS_CENTER | UIS_BIG),
UiEdit(selhero_heroInfo.name, 15, { PANEL_LEFT + 265, 317, 320, 33 }, UIS_MED | UIS_GOLD),
UiArtTextButton("OK", &UiFocusNavigationSelect, { PANEL_LEFT + 279, 429, 140, 35 }, UIS_CENTER | UIS_BIG | UIS_GOLD),
UiArtTextButton("Cancel", &UiFocusNavigationEsc, { PANEL_LEFT + 429, 429, 140, 35 }, UIS_CENTER | UIS_BIG | UIS_GOLD)
};
UiListItem SELLOAD_DIALOG_ITEMS[] = {
{ "Load Game", 0 },
{ "New Game", 1 }
};
UiItem SELLOAD_DIALOG[] = {
UiArtText("Save File Exists", { PANEL_LEFT + 264, 211, 320, 33 }, UIS_CENTER | UIS_BIG),
UiList(SELLOAD_DIALOG_ITEMS, PANEL_LEFT + 265, 285, 320, 33, UIS_CENTER | UIS_MED | UIS_GOLD),
UiArtTextButton("OK", &UiFocusNavigationSelect, { PANEL_LEFT + 279, 427, 140, 35 }, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD),
UiArtTextButton("Cancel", &UiFocusNavigationEsc, { PANEL_LEFT + 429, 427, 140, 35 }, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD)
};
std::vector<UiItemBase *> vecSelHeroDialog;
std::vector<UiListItem *> vecSelHeroDlgItems;
std::vector<UiItemBase *> vecSelDlgItems;
UiImage *SELHERO_DIALOG_HERO_IMG;
} // namespace
bool bUIElementsLoaded = false;
void selhero_UiFocusNavigationYesNo()
{
if (selhero_deleteEnabled)
UiFocusNavigationYesNo();
}
void selhero_FreeListItems()
{
for (std::size_t i = 0; i < vecSelHeroDlgItems.size(); i++) {
UiListItem *pUIItem = vecSelHeroDlgItems[i];
delete pUIItem;
}
vecSelHeroDlgItems.clear();
}
void selhero_FreeDlgItems()
{
for (std::size_t i = 0; i < vecSelDlgItems.size(); i++) {
UiItemBase *pUIItem = vecSelDlgItems[i];
delete pUIItem;
}
vecSelDlgItems.clear();
}
void selhero_Free()
{
ArtBackground.Unload();
for (std::size_t i = 0; i < vecSelHeroDialog.size(); i++) {
UiItemBase *pUIItem = vecSelHeroDialog[i];
delete pUIItem;
}
vecSelHeroDialog.clear();
selhero_FreeDlgItems();
selhero_FreeListItems();
bUIElementsLoaded = false;
}
void selhero_SetStats()
{
SELHERO_DIALOG_HERO_IMG->frame = selhero_heroInfo.heroclass;
SELHERO_DIALOG_HERO_IMG->m_frame = selhero_heroInfo.heroclass;
sprintf(textStats[0], "%d", selhero_heroInfo.level);
sprintf(textStats[1], "%d", selhero_heroInfo.strength);
sprintf(textStats[2], "%d", selhero_heroInfo.magic);
@ -124,17 +100,20 @@ void selhero_SetStats()
namespace {
std::size_t listOffset = 0;
UiArtTextButton *SELLIST_DIALOG_DELETE_BUTTON;
void selhero_UpdateViewportItems()
{
const std::size_t num_viewport_heroes = std::min(selhero_SaveCount - listOffset, kMaxViewportItems);
SELLIST_DIALOG_LIST->length = num_viewport_heroes;
for (std::size_t i = 0; i < num_viewport_heroes; i++) {
const std::size_t index = i + listOffset;
SELLIST_DIALOG_ITEMS[i] = { selhero_heros[index].name, static_cast<int>(index) };
vecSelHeroDlgItems[i]->m_text = selhero_heros[index].name;
vecSelHeroDlgItems[i]->m_value = static_cast<int>(index);
}
if (num_viewport_heroes < kMaxViewportItems) {
SELLIST_DIALOG_ITEMS[num_viewport_heroes] = { "New Hero", static_cast<int>(selhero_SaveCount) };
++SELLIST_DIALOG_LIST->length;
vecSelHeroDlgItems[num_viewport_heroes]->m_text = "New Hero";
vecSelHeroDlgItems[num_viewport_heroes]->m_value = static_cast<int>(selhero_SaveCount);
}
}
@ -156,9 +135,36 @@ void selhero_ScrollIntoView(std::size_t index)
void selhero_List_Init()
{
listOffset = 0;
selhero_FreeDlgItems();
SDL_Rect rect1 = { PANEL_LEFT + 264, 211, 320, 33 };
vecSelDlgItems.push_back(new UiArtText("Select Hero", rect1, UIS_CENTER | UIS_BIG));
selhero_FreeListItems();
const size_t num_viewport_heroes = std::min(selhero_SaveCount + 1, kMaxViewportItems);
for (std::size_t i = 0; i < num_viewport_heroes; i++) {
vecSelHeroDlgItems.push_back(new UiListItem("", -1));
}
selhero_UpdateViewportItems();
UiInitList(0, selhero_SaveCount, selhero_List_Focus, selhero_List_Select, selhero_List_Esc, SELLIST_DIALOG, size(SELLIST_DIALOG), false, selhero_List_DeleteYesNo);
UiInitScrollBar(SELLIST_SCROLLBAR, kMaxViewportItems, &listOffset);
vecSelDlgItems.push_back(new UiList(vecSelHeroDlgItems, PANEL_LEFT + 265, 256, 320, 26, UIS_CENTER | UIS_MED | UIS_GOLD));
SDL_Rect rect2 = { PANEL_LEFT + 585, 244, 25, 178 };
UiScrollBar *scrollBar = new UiScrollBar(&ArtScrollBarBackground, &ArtScrollBarThumb, &ArtScrollBarArrow, rect2);
vecSelDlgItems.push_back(scrollBar);
SDL_Rect rect3 = { PANEL_LEFT + 239, 429, 120, 35 };
vecSelDlgItems.push_back(new UiArtTextButton("OK", &UiFocusNavigationSelect, rect3, UIS_CENTER | UIS_BIG | UIS_GOLD));
SDL_Rect rect4 = { PANEL_LEFT + 364, 429, 120, 35 };
SELLIST_DIALOG_DELETE_BUTTON = new UiArtTextButton("Delete", &selhero_UiFocusNavigationYesNo, rect4, UIS_CENTER | UIS_BIG | UIS_DISABLED);
vecSelDlgItems.push_back(SELLIST_DIALOG_DELETE_BUTTON);
SDL_Rect rect5 = { PANEL_LEFT + 489, 429, 120, 35 };
vecSelDlgItems.push_back(new UiArtTextButton("Cancel", &UiFocusNavigationEsc, rect5, UIS_CENTER | UIS_BIG | UIS_GOLD));
UiInitList(0, selhero_SaveCount, selhero_List_Focus, selhero_List_Select, selhero_List_Esc, vecSelDlgItems, false, selhero_List_DeleteYesNo);
UiInitScrollBar(scrollBar, kMaxViewportItems, &listOffset);
if (selhero_isMultiPlayer) {
strcpy(title, "Multi Player Characters");
} else {
@ -174,18 +180,18 @@ void selhero_List_Focus(int value)
if (selhero_SaveCount && index < selhero_SaveCount) {
memcpy(&selhero_heroInfo, &selhero_heros[index], sizeof(selhero_heroInfo));
selhero_SetStats();
SELLIST_DIALOG_DELETE_BUTTON->flags = baseFlags | UIS_GOLD;
SELLIST_DIALOG_DELETE_BUTTON->m_iFlags = baseFlags | UIS_GOLD;
selhero_deleteEnabled = true;
return;
}
SELHERO_DIALOG_HERO_IMG->frame = UI_NUM_CLASSES;
SELHERO_DIALOG_HERO_IMG->m_frame = UI_NUM_CLASSES;
strcpy(textStats[0], "--");
strcpy(textStats[1], "--");
strcpy(textStats[2], "--");
strcpy(textStats[3], "--");
strcpy(textStats[4], "--");
SELLIST_DIALOG_DELETE_BUTTON->flags = baseFlags | UIS_DISABLED;
SELLIST_DIALOG_DELETE_BUTTON->m_iFlags = baseFlags | UIS_DISABLED;
selhero_deleteEnabled = false;
}
@ -199,26 +205,62 @@ bool selhero_List_DeleteYesNo()
void selhero_List_Select(int value)
{
if (static_cast<std::size_t>(value) == selhero_SaveCount) {
UiInitList(0, 2, selhero_ClassSelector_Focus, selhero_ClassSelector_Select, selhero_ClassSelector_Esc, SELCLASS_DIALOG, size(SELCLASS_DIALOG));
selhero_FreeDlgItems();
SDL_Rect rect1 = { PANEL_LEFT + 264, 211, 320, 33 };
vecSelDlgItems.push_back(new UiArtText("Choose Class", rect1, UIS_CENTER | UIS_BIG));
selhero_FreeListItems();
vecSelHeroDlgItems.push_back(new UiListItem("Warrior", UI_WARRIOR));
vecSelHeroDlgItems.push_back(new UiListItem("Rogue", UI_ROGUE));
vecSelHeroDlgItems.push_back(new UiListItem("Sorcerer", UI_SORCERER));
vecSelDlgItems.push_back(new UiList(vecSelHeroDlgItems, PANEL_LEFT + 264, 285, 320, 33, UIS_CENTER | UIS_MED | UIS_GOLD));
SDL_Rect rect2 = { PANEL_LEFT + 279, 429, 140, 35 };
vecSelDlgItems.push_back(new UiArtTextButton("OK", &UiFocusNavigationSelect, rect2, UIS_CENTER | UIS_BIG | UIS_GOLD));
SDL_Rect rect3 = { PANEL_LEFT + 429, 429, 140, 35 };
vecSelDlgItems.push_back(new UiArtTextButton("Cancel", &UiFocusNavigationEsc, rect3, UIS_CENTER | UIS_BIG | UIS_GOLD));
UiInitList(0, 2, selhero_ClassSelector_Focus, selhero_ClassSelector_Select, selhero_ClassSelector_Esc, vecSelDlgItems);
memset(&selhero_heroInfo.name, 0, sizeof(selhero_heroInfo.name));
strcpy(title, "New Single Player Hero");
if (selhero_isMultiPlayer) {
strcpy(title, "New Multi Player Hero");
}
return;
} else if (selhero_heroInfo.hassaved) {
UiInitList(0, 1, selhero_Load_Focus, selhero_Load_Select, selhero_List_Init, SELLOAD_DIALOG, size(SELLOAD_DIALOG), true);
}
if (selhero_heroInfo.hassaved) {
selhero_FreeDlgItems();
SDL_Rect rect1 = { PANEL_LEFT + 264, 211, 320, 33 };
vecSelDlgItems.push_back(new UiArtText("Save File Exists", rect1, UIS_CENTER | UIS_BIG));
selhero_FreeListItems();
vecSelHeroDlgItems.push_back(new UiListItem("Load Game", 0));
vecSelHeroDlgItems.push_back(new UiListItem("New Game", 1));
vecSelDlgItems.push_back(new UiList(vecSelHeroDlgItems, PANEL_LEFT + 265, 285, 320, 33, UIS_CENTER | UIS_MED | UIS_GOLD));
SDL_Rect rect2 = { PANEL_LEFT + 279, 427, 140, 35 };
vecSelDlgItems.push_back(new UiArtTextButton("OK", &UiFocusNavigationSelect, rect2, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD));
SDL_Rect rect3 = { PANEL_LEFT + 429, 427, 140, 35 };
vecSelDlgItems.push_back(new UiArtTextButton("Cancel", &UiFocusNavigationEsc, rect3, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD));
UiInitList(0, 1, selhero_Load_Focus, selhero_Load_Select, selhero_List_Init, vecSelDlgItems, true);
strcpy(title, "Single Player Characters");
return;
}
UiInitList(0, 0, NULL, NULL, NULL, NULL, 0);
UiInitList_clear();
selhero_endMenu = true;
}
void selhero_List_Esc()
{
UiInitList(0, 0, NULL, NULL, NULL, NULL, 0);
UiInitList_clear();
selhero_endMenu = true;
selhero_result = SELHERO_PREVIOUS;
}
@ -241,7 +283,7 @@ void selhero_ClassSelector_Focus(int value)
void selhero_ClassSelector_Select(int value)
{
if (gbSpawned && (value == 1 || value == 2)) {
selhero_Free();
ArtBackground.Unload();
UiSelOkDialog(NULL, "The Rogue and Sorcerer are only available in the full retail version of Diablo. Visit https://www.gog.com/game/diablo to purchase.", false);
LoadBackgroundArt("ui_art\\selhero.pcx");
selhero_List_Select(selhero_SaveCount);
@ -256,11 +298,27 @@ void selhero_ClassSelector_Select(int value)
#ifdef PREFILL_PLAYER_NAME
strcpy(selhero_heroInfo.name, selhero_GenerateName(selhero_heroInfo.heroclass));
#endif
UiInitList(0, 0, NULL, selhero_Name_Select, selhero_Name_Esc, ENTERNAME_DIALOG, size(ENTERNAME_DIALOG));
selhero_FreeDlgItems();
SDL_Rect rect1 = { PANEL_LEFT + 264, 211, 320, 33 };
vecSelDlgItems.push_back(new UiArtText("Enter Name", rect1, UIS_CENTER | UIS_BIG));
SDL_Rect rect2 = { PANEL_LEFT + 265, 317, 320, 33 };
vecSelDlgItems.push_back(new UiEdit(selhero_heroInfo.name, 15, rect2, UIS_MED | UIS_GOLD));
SDL_Rect rect3 = { PANEL_LEFT + 279, 429, 140, 35 };
vecSelDlgItems.push_back(new UiArtTextButton("OK", &UiFocusNavigationSelect, rect3, UIS_CENTER | UIS_BIG | UIS_GOLD));
SDL_Rect rect4 = { PANEL_LEFT + 429, 429, 140, 35 };
vecSelDlgItems.push_back(new UiArtTextButton("Cancel", &UiFocusNavigationEsc, rect4, UIS_CENTER | UIS_BIG | UIS_GOLD));
UiInitList(0, 0, NULL, selhero_Name_Select, selhero_Name_Esc, vecSelDlgItems);
}
void selhero_ClassSelector_Esc()
{
selhero_FreeDlgItems();
selhero_FreeListItems();
if (selhero_SaveCount) {
selhero_List_Init();
return;
@ -273,14 +331,14 @@ void selhero_Name_Select(int value)
{
if (!UiValidPlayerName(selhero_heroInfo.name)) {
selhero_Free();
ArtBackground.Unload();
UiSelOkDialog(title, "Invalid name. A name cannot contain spaces, reserved characters, or reserved words.\n", false);
LoadBackgroundArt("ui_art\\selhero.pcx");
} else {
bool overwrite = true;
for (std::size_t i = 0; i < selhero_SaveCount; i++) {
if (strcasecmp(selhero_heros[i].name, selhero_heroInfo.name) == 0) {
selhero_Free();
ArtBackground.Unload();
char dialogText[256];
sprintf(dialogText, "Character already exists. Do you want to overwrite \"%s\"?", selhero_heroInfo.name);
overwrite = UiSelHeroYesNoDialog(title, dialogText);
@ -291,11 +349,11 @@ void selhero_Name_Select(int value)
if (overwrite) {
if (gfnHeroCreate(&selhero_heroInfo)) {
UiInitList(0, 0, NULL, NULL, NULL, NULL, 0);
UiInitList_clear();
selhero_endMenu = true;
return;
} else {
UiErrorOkDialog("Unable to create character.", SELHERO_DIALOG, size(SELHERO_DIALOG));
UiErrorOkDialog("Unable to create character.", vecSelDlgItems);
}
}
}
@ -318,7 +376,7 @@ void selhero_Load_Focus(int value)
void selhero_Load_Select(int value)
{
UiInitList(0, 0, NULL, NULL, NULL, NULL, 0);
UiInitList_clear();
selhero_endMenu = true;
if (value == 0) {
selhero_result = SELHERO_CONTINUE;
@ -344,10 +402,50 @@ BOOL UiSelHeroDialog(
int *dlgresult,
char *name)
{
bUIElementsLoaded = true;
do {
LoadBackgroundArt("ui_art\\selhero.pcx");
UiAddBackground(&vecSelHeroDialog);
UiAddLogo(&vecSelHeroDialog);
LoadScrollBar();
selhero_FreeDlgItems();
SDL_Rect rect1 = { PANEL_LEFT + 24, 161, 590, 35 };
vecSelHeroDialog.push_back(new UiArtText(title, rect1, UIS_CENTER | UIS_BIG));
SDL_Rect rect2 = { PANEL_LEFT + 30, 211, 180, 76 };
SELHERO_DIALOG_HERO_IMG = new UiImage(&ArtHero, UI_NUM_CLASSES, rect2);
vecSelHeroDialog.push_back(SELHERO_DIALOG_HERO_IMG);
SDL_Rect rect3 = { PANEL_LEFT + 39, 323, 110, 21 };
vecSelHeroDialog.push_back(new UiArtText("Level:", rect3, UIS_RIGHT));
SDL_Rect rect4 = { PANEL_LEFT + 39, 323, 110, 21 };
vecSelHeroDialog.push_back(new UiArtText("Level:", rect4, UIS_RIGHT));
SDL_Rect rect5 = { PANEL_LEFT + 159, 323, 40, 21 };
vecSelHeroDialog.push_back(new UiArtText(textStats[0], rect5, UIS_CENTER));
SDL_Rect rect6 = { PANEL_LEFT + 39, 358, 110, 21 };
vecSelHeroDialog.push_back(new UiArtText("Strength:", rect6, UIS_RIGHT));
SDL_Rect rect7 = { PANEL_LEFT + 159, 358, 40, 21 };
vecSelHeroDialog.push_back(new UiArtText(textStats[1], rect7, UIS_CENTER));
SDL_Rect rect8 = { PANEL_LEFT + 39, 380, 110, 21 };
vecSelHeroDialog.push_back(new UiArtText("Magic:", rect8, UIS_RIGHT));
SDL_Rect rect9 = { PANEL_LEFT + 159, 380, 40, 21 };
vecSelHeroDialog.push_back(new UiArtText(textStats[2], rect9, UIS_CENTER));
SDL_Rect rect10 = { PANEL_LEFT + 39, 401, 110, 21 };
vecSelHeroDialog.push_back(new UiArtText("Dexterity:", rect10, UIS_RIGHT));
SDL_Rect rect11 = { PANEL_LEFT + 159, 401, 40, 21 };
vecSelHeroDialog.push_back(new UiArtText(textStats[3], rect11, UIS_CENTER));
SDL_Rect rect12 = { PANEL_LEFT + 39, 422, 110, 21 };
vecSelHeroDialog.push_back(new UiArtText("Vitality:", rect12, UIS_RIGHT));
SDL_Rect rect13 = { PANEL_LEFT + 159, 422, 40, 21 };
vecSelHeroDialog.push_back(new UiArtText(textStats[4], rect13, UIS_CENTER));
gfnHeroInfo = fninfo;
gfnHeroCreate = fncreate;
gfnHeroStats = fnstats;
@ -368,7 +466,7 @@ BOOL UiSelHeroDialog(
selhero_endMenu = false;
while (!selhero_endMenu && !selhero_navigateYesNo) {
UiClearScreen();
UiRenderItems(SELHERO_DIALOG, size(SELHERO_DIALOG));
UiRenderItems(vecSelHeroDialog);
UiPollAndRender();
}
selhero_Free();
@ -421,7 +519,7 @@ BOOL UiSelHeroMultDialog(
return UiSelHeroDialog(fninfo, fncreate, fnstats, fnremove, dlgresult, name);
}
const char *selhero_GenerateName(std::uint8_t hero_class)
const char *selhero_GenerateName(uint8_t hero_class)
{
static const char *const kNames[3][10] = {
{
@ -461,10 +559,10 @@ const char *selhero_GenerateName(std::uint8_t hero_class)
"Horazon",
}
};
const int seed = std::chrono::system_clock::now().time_since_epoch().count();
std::default_random_engine generator(seed);
std::uniform_int_distribution<std::size_t> dist(0, sizeof(kNames[0]) / sizeof(kNames[0][0]) - 1);
return kNames[hero_class][dist(generator)];
int iRand = rand() % 9;
return kNames[hero_class][iRand];
}
} // namespace dvl

1
SourceX/DiabloUI/selhero.h

@ -17,6 +17,5 @@ void selhero_Name_Select(int value);
void selhero_Name_Esc();
void selhero_Load_Focus(int value);
void selhero_Load_Select(int value);
const char *selhero_GenerateName(std::uint8_t hero_class);
} // namespace dvl

66
SourceX/DiabloUI/selok.cpp

@ -12,11 +12,28 @@ char dialogText[256];
} // namespace
int selok_endMenu;
char selok_title[32];
std::vector<UiListItem *> vecSelOkDialogItems;
std::vector<UiItemBase *> vecSelOkDialog;
#define MESSAGE_WIDTH 280
void selok_Free()
{
ArtBackground.Unload();
for (std::size_t i = 0; i < vecSelOkDialogItems.size(); i++) {
UiListItem *pUIListItem = vecSelOkDialogItems[i];
if (pUIListItem)
delete pUIListItem;
}
vecSelOkDialogItems.clear();
for (std::size_t i = 0; i < vecSelOkDialog.size(); i++) {
UiItemBase *pUIItem = vecSelOkDialog[i];
delete pUIItem;
}
vecSelOkDialog.clear();
}
void selok_Select(int value)
@ -29,25 +46,6 @@ void selok_Esc()
selok_endMenu = true;
}
UiListItem SELOK_DIALOG_ITEMS[] = {
{ "OK", 0 }
};
UiItem SELOK_DIALOG[] = {
MAINMENU_BACKGROUND,
MAINMENU_LOGO,
UiArtText(selok_title, { PANEL_LEFT + 24, 161, 590, 35 }, UIS_CENTER | UIS_BIG),
UiArtText(dialogText, { PANEL_LEFT + 140, 210, 560, 168 }, UIS_MED),
UiList(SELOK_DIALOG_ITEMS, PANEL_LEFT + 230, 390, 180, 35, UIS_CENTER | UIS_BIG | UIS_GOLD)
};
UiItem SPAWNERR_DIALOG[] = {
MAINMENU_BACKGROUND,
MAINMENU_LOGO,
UiArtText(dialogText, { PANEL_LEFT + 140, 197, 560, 168 }, UIS_MED),
UiList(SELOK_DIALOG_ITEMS, PANEL_LEFT + 230, 390, 180, 35, UIS_CENTER | UIS_BIG | UIS_GOLD)
};
void UiSelOkDialog(const char *title, const char *body, bool background)
{
if (!background) {
@ -60,23 +58,35 @@ void UiSelOkDialog(const char *title, const char *body, bool background)
}
}
UiItem *items = SPAWNERR_DIALOG;
int itemCnt = size(SPAWNERR_DIALOG);
UiAddBackground(&vecSelOkDialog);
UiAddLogo(&vecSelOkDialog);
if (title != NULL) {
strcpy(selok_title, title);
items = SELOK_DIALOG;
itemCnt = size(SELOK_DIALOG);
SDL_Rect rect1 = { PANEL_LEFT + 24, 161, 590, 35 };
vecSelOkDialog.push_back(new UiArtText(title, rect1, UIS_CENTER | UIS_BIG));
SDL_Rect rect2 = { PANEL_LEFT + 140, 210, 560, 168 };
vecSelOkDialog.push_back(new UiArtText(dialogText, rect2, UIS_MED));
} else {
SDL_Rect rect1 = { PANEL_LEFT + 140, 197, 560, 168 };
vecSelOkDialog.push_back(new UiArtText(dialogText, rect1, UIS_MED));
}
SDL_Rect rect3 = { PANEL_LEFT + 140, 210, 560, 168 };
vecSelOkDialog.push_back(new UiArtText(dialogText, rect3, UIS_MED));
vecSelOkDialogItems.push_back(new UiListItem("OK", 0));
vecSelOkDialog.push_back(new UiList(vecSelOkDialogItems, PANEL_LEFT + 230, 390, 180, 35, UIS_CENTER | UIS_BIG | UIS_GOLD));
strcpy(dialogText, body);
WordWrapArtStr(dialogText, 280);
WordWrapArtStr(dialogText, MESSAGE_WIDTH);
UiInitList(0, 0, NULL, selok_Select, selok_Esc, items, itemCnt, false, NULL);
UiInitList(0, 0, NULL, selok_Select, selok_Esc, vecSelOkDialog, false, NULL);
selok_endMenu = false;
while (!selok_endMenu) {
UiClearScreen();
UiRenderItems(items, itemCnt);
UiRenderItems(vecSelOkDialog);
UiPollAndRender();
}

54
SourceX/DiabloUI/selyesno.cpp

@ -9,25 +9,28 @@ namespace dvl {
bool selyesno_endMenu;
bool selyesno_value;
char selyesno_confirmationMessage[256];
char selyesno_title[32];
UiListItem SELYESNO_DIALOG_ITEMS[] = {
{ "Yes", 0 },
{ "No", 1 }
};
UiItem SELYESNO_DIALOG[] = {
MAINMENU_BACKGROUND,
MAINMENU_LOGO,
UiArtText(selyesno_title, { PANEL_LEFT + 24, 161, 590, 35 }, UIS_CENTER | UIS_BIG),
UiArtText(selyesno_confirmationMessage, { PANEL_LEFT + 120, 236, 280, 168 }, UIS_MED),
UiList(SELYESNO_DIALOG_ITEMS, PANEL_LEFT + 230, 390, 180, 35, UIS_CENTER | UIS_BIG | UIS_GOLD)
};
UiArtText *SELYESNO_DIALOG_CONFIRMATION_MESSAGE = &SELYESNO_DIALOG[3].art_text;
std::vector<UiListItem *> vecSelYesNoDialogItems;
std::vector<UiItemBase *> vecSelYesNoDialog;
#define MESSAGE_WIDTH 280
void selyesno_Free()
{
ArtBackground.Unload();
for (std::size_t i = 0; i < vecSelYesNoDialogItems.size(); i++) {
UiListItem *pUIListItem = vecSelYesNoDialogItems[i];
if (pUIListItem)
delete pUIListItem;
}
vecSelYesNoDialogItems.clear();
for (std::size_t i = 0; i < vecSelYesNoDialog.size(); i++) {
UiItemBase *pUIItem = vecSelYesNoDialog[i];
delete pUIItem;
}
vecSelYesNoDialog.clear();
}
void selyesno_Select(int value)
@ -45,18 +48,29 @@ void selyesno_Esc()
bool UiSelHeroYesNoDialog(const char *title, const char *body)
{
LoadBackgroundArt("ui_art\\black.pcx");
UiAddBackground(&vecSelYesNoDialog);
UiAddLogo(&vecSelYesNoDialog);
SDL_Rect rect1 = { PANEL_LEFT + 24, 161, 590, 35 };
vecSelYesNoDialog.push_back(new UiArtText(title, rect1, UIS_CENTER | UIS_BIG));
SDL_Rect rect2 = { PANEL_LEFT + 120, 236, MESSAGE_WIDTH, 168 };
vecSelYesNoDialog.push_back(new UiArtText(selyesno_confirmationMessage, rect2, UIS_MED));
vecSelYesNoDialogItems.push_back(new UiListItem("Yes", 0));
vecSelYesNoDialogItems.push_back(new UiListItem("No", 1));
vecSelYesNoDialog.push_back(new UiList(vecSelYesNoDialogItems, PANEL_LEFT + 230, 390, 180, 35, UIS_CENTER | UIS_BIG | UIS_GOLD));
strcpy(selyesno_title, title);
strcpy(selyesno_confirmationMessage, body);
WordWrapArtStr(selyesno_confirmationMessage, SELYESNO_DIALOG_CONFIRMATION_MESSAGE->rect.w);
WordWrapArtStr(selyesno_confirmationMessage, MESSAGE_WIDTH);
UiInitList(0, 1, NULL, selyesno_Select, selyesno_Esc, SELYESNO_DIALOG, size(SELYESNO_DIALOG), true, NULL);
UiInitList(0, 1, NULL, selyesno_Select, selyesno_Esc, vecSelYesNoDialog, true, NULL);
selyesno_value = true;
selyesno_endMenu = false;
while (!selyesno_endMenu) {
UiClearScreen();
UiRenderItems(SELYESNO_DIALOG, size(SELYESNO_DIALOG));
UiRenderItems(vecSelYesNoDialog);
UiPollAndRender();
}
@ -64,4 +78,4 @@ bool UiSelHeroYesNoDialog(const char *title, const char *body)
return selyesno_value;
}
}
} // namespace dvl

2
SourceX/DiabloUI/text_draw.cpp

@ -42,7 +42,7 @@ void DrawTTF(const char *text, const SDL_Rect &rectIn, int flags,
return;
if (*render_cache == NULL) {
*render_cache = new TtfSurfaceCache();
const TextAlignment x_align = XAlignmentFromFlags(flags);
const auto x_align = XAlignmentFromFlags(flags);
(*render_cache)->text = RenderUTF8_Solid_Wrapped(font, text, text_color, rect.w, x_align);
ScaleSurfaceToOutput(&(*render_cache)->text);
(*render_cache)->shadow = RenderUTF8_Solid_Wrapped(font, text, shadow_color, rect.w, x_align);

20
SourceX/DiabloUI/title.cpp

@ -4,6 +4,8 @@
namespace dvl {
std::vector<UiItemBase *> vecTitleScreen;
void title_Load()
{
LoadBackgroundArt("ui_art\\title.pcx");
@ -14,15 +16,21 @@ void title_Free()
{
ArtBackground.Unload();
ArtLogos[LOGO_BIG].Unload();
for (std::size_t i = 0; i < vecTitleScreen.size(); i++) {
UiItemBase *pUIItem = vecTitleScreen[i];
delete pUIItem;
}
vecTitleScreen.clear();
}
void UiTitleDialog()
{
UiItem TITLESCREEN_DIALOG[] = {
MAINMENU_BACKGROUND,
UiImage(&ArtLogos[LOGO_BIG], /*animated=*/true, /*frame=*/0, { 0, 182, 0, 0 }, UIS_CENTER),
UiArtText("Copyright \xA9 1996-2001 Blizzard Entertainment", { PANEL_LEFT + 49, 410, 550, 26 }, UIS_MED | UIS_CENTER)
};
UiAddBackground(&vecTitleScreen);
UiAddLogo(&vecTitleScreen, LOGO_BIG, 182);
SDL_Rect rect = { PANEL_LEFT + 49, 410, 550, 26 };
vecTitleScreen.push_back(new UiArtText("Copyright \xA9 1996-2001 Blizzard Entertainment", rect, UIS_MED | UIS_CENTER));
title_Load();
@ -31,7 +39,7 @@ void UiTitleDialog()
SDL_Event event;
while (!endMenu && SDL_GetTicks() < timeOut) {
UiRenderItems(TITLESCREEN_DIALOG, size(TITLESCREEN_DIALOG));
UiRenderItems(vecTitleScreen);
UiFadeIn();
while (SDL_PollEvent(&event)) {

3
SourceX/DiabloUI/ttf_render_wrapped.cpp

@ -19,7 +19,7 @@ SDL_bool CharacterIsDelimiter(char c, const char *delimiters)
} // namespace
// Based on SDL 2.0.12 TTF_RenderUTF8_Blended_Wrapped
SDL_Surface *RenderUTF8_Solid_Wrapped(TTF_Font *font, const char *text, SDL_Color fg, Uint32 wrapLength, TextAlignment x_align)
SDL_Surface *RenderUTF8_Solid_Wrapped(TTF_Font *font, const char *text, SDL_Color fg, Uint32 wrapLength, const int x_align)
{
int width, height;
SDL_Surface *textbuf;
@ -149,6 +149,7 @@ SDL_Surface *RenderUTF8_Solid_Wrapped(TTF_Font *font, const char *text, SDL_Colo
SDL_stack_free(str);
return NULL;
}
dest.w = static_cast<Uint16>(tmp->w);
dest.h = static_cast<Uint16>(tmp->h);

2
SourceX/DiabloUI/ttf_render_wrapped.h

@ -19,6 +19,6 @@ enum TextAlignment {
* This method is slow. Caching the result is recommended.
*/
SDL_Surface *RenderUTF8_Solid_Wrapped(
TTF_Font *font, const char *text, SDL_Color fg, Uint32 wrapLength, TextAlignment x_align = TextAlignment_BEGIN);
TTF_Font *font, const char *text, SDL_Color fg, Uint32 wrapLength, const int x_align = TextAlignment_BEGIN);
} // namespace dvl

424
SourceX/DiabloUI/ui_item.h

@ -38,316 +38,314 @@ enum UiFlags {
UIS_HIDDEN = 1 << 10,
};
struct UiItemBase {
constexpr UiItemBase(SDL_Rect rect, int flags)
: rect(rect)
, flags(flags)
class UiItemBase {
public:
UiItemBase(SDL_Rect rect, int flags)
{
}
m_rect = rect;
m_iFlags = flags;
};
UiItemBase(Sint16 x, Sint16 y, Uint16 item_width, Uint16 item_height, int flags)
{
SDL_Rect tmp;
tmp.x = x;
tmp.y = y;
tmp.w = item_width;
tmp.h = item_height;
m_rect = tmp;
m_iFlags = flags;
};
virtual ~UiItemBase() {};
bool has_flag(UiFlags flag) const
{
return flags & flag;
return m_iFlags & flag;
}
bool has_any_flag(int flags) const
{
return (this->flags & flags) != 0;
return (m_iFlags & flags) != 0;
}
void add_flag(UiFlags flag)
{
flags |= flag;
m_iFlags |= flag;
}
void remove_flag(UiFlags flag)
{
flags &= ~flag;
m_iFlags &= ~flag;
}
SDL_Rect rect;
int flags;
//protected:
UiType m_type;
SDL_Rect m_rect;
int m_iFlags;
};
struct UiImage : public UiItemBase {
constexpr UiImage(Art *art, bool animated, int frame, SDL_Rect rect, int flags = 0)
//=============================================================================
class UiImage : public UiItemBase {
public:
UiImage(Art *art, SDL_Rect rect, int flags = 0)
: UiItemBase(rect, flags)
, art(art)
, animated(animated)
, frame(frame)
{
}
m_type = UI_IMAGE;
m_art = art;
m_animated = false;
m_frame = 0;
};
constexpr UiImage(Art *art, int frame, SDL_Rect rect, int flags = 0)
: UiImage(art, /*animated=*/false, frame, rect, flags)
UiImage(Art *art, bool bAnimated, int iFrame, SDL_Rect rect, int flags)
: UiItemBase(rect, flags)
{
}
m_type = UI_IMAGE;
m_art = art;
m_animated = bAnimated;
m_frame = iFrame;
};
constexpr UiImage(Art *art, SDL_Rect rect, int flags = 0)
: UiImage(art, /*frame=*/0, rect, flags)
UiImage(Art *art, int frame, SDL_Rect rect, int flags = 0)
: UiItemBase(rect, flags)
{
m_type = UI_IMAGE;
m_art = art;
m_animated = false;
m_frame = frame;
}
Art *art;
bool animated;
int frame;
~UiImage() {};
//private:
Art *m_art;
bool m_animated;
int m_frame;
};
// Plain text (TTF).
struct UiText : public UiItemBase {
constexpr UiText(const char *text, SDL_Color color, SDL_Rect rect, int flags = 0)
: UiItemBase(rect, flags)
, color(color)
, shadow_color { 0, 0, 0, 0 }
, text(text)
, render_cache(NULL)
{
}
//=============================================================================
constexpr UiText(const char *text, SDL_Rect rect, int flags = 0)
: UiText(text, SDL_Color { 243, 243, 243, 0 }, rect, flags)
class UiArtText : public UiItemBase {
public:
UiArtText(const char *text, SDL_Rect rect, int flags = 0)
: UiItemBase(rect, flags)
{
}
SDL_Color color;
SDL_Color shadow_color;
const char *text;
m_type = UI_ART_TEXT;
m_text = text;
};
// State:
TtfSurfaceCache *render_cache;
~UiArtText() {};
void FreeCache()
{
delete render_cache;
render_cache = NULL;
}
//private:
const char *m_text;
};
// Text drawn with Diablo sprites.
struct UiArtText : public UiItemBase {
constexpr UiArtText(const char *text, SDL_Rect rect, int flags = 0)
//=============================================================================
class UiScrollBar : public UiItemBase {
public:
UiScrollBar(Art *bg, Art *thumb, Art *arrow, SDL_Rect rect, int flags = 0)
: UiItemBase(rect, flags)
, text(text)
{
}
m_type = UI_SCROLLBAR;
m_bg = bg;
m_thumb = thumb;
m_arrow = arrow;
};
const char *text;
//private:
Art *m_bg;
Art *m_thumb;
Art *m_arrow;
};
// Clickable Diablo sprites text.
struct UiArtTextButton : public UiItemBase {
constexpr UiArtTextButton(const char *text, void (*action)(), SDL_Rect rect, int flags = 0)
//=============================================================================
class UiArtTextButton : public UiItemBase {
public:
UiArtTextButton(const char *text, void (*action)(), SDL_Rect rect, int flags = 0)
: UiItemBase(rect, flags)
, text(text)
, action(action)
{
}
const char *text;
void (*action)();
m_type = UI_ART_TEXT_BUTTON;
m_text = text;
m_action = action;
};
//private:
const char *m_text;
void (*m_action)();
};
// A button (uses Diablo sprites).
struct UiButton : public UiItemBase {
enum FrameKey {
DEFAULT = 0,
PRESSED,
DISABLED
};
//=============================================================================
constexpr UiButton(Art *art, const char *text, void (*action)(), SDL_Rect rect, int flags = 0)
class UiEdit : public UiItemBase {
public:
UiEdit(char *value, std::size_t max_length, SDL_Rect rect, int flags = 0)
: UiItemBase(rect, flags)
, art(art)
, text(text)
, action(action)
, pressed(false)
, render_cache(NULL)
{
m_type = UI_EDIT;
m_value = value;
m_max_length = max_length;
}
Art *art;
//private:
char *m_value;
std::size_t m_max_length;
};
const char *text;
void (*action)();
//=============================================================================
// State
bool pressed;
TtfSurfaceCache *render_cache;
// Plain text (TTF)
void FreeCache()
class UiText : public UiItemBase {
public:
UiText(const char *text, SDL_Color color1, SDL_Rect rect, int flags = 0)
: UiItemBase(rect, flags)
{
delete render_cache;
render_cache = NULL;
}
};
m_type = UI_TEXT;
m_color = color1;
struct UiListItem {
constexpr UiListItem(const char *text = "", int value = 0)
: text(text)
, value(value)
{
SDL_Color color2 = { 0, 0, 0, 0 };
m_shadow_color = color2;
m_text = text;
m_render_cache = NULL;
}
const char *text;
int value;
};
struct UiList : public UiItemBase {
template <std::size_t N>
constexpr UiList(
UiListItem (&items)[N],
Sint16 x,
Sint16 y,
Uint16 item_width,
Uint16 item_height,
int flags)
: UiItemBase({ x, y, item_width, static_cast<Uint16>(item_height * N) }, flags)
, x(x)
, y(y)
, item_width(item_width)
, item_height(item_height)
, items(items)
, length(N)
UiText(const char *text, SDL_Rect rect, int flags = 0)
: UiItemBase(rect, flags)
{
}
m_type = UI_TEXT;
Sint16 x;
Sint16 y;
Uint16 item_width;
Uint16 item_height;
UiListItem *items;
std::size_t length;
SDL_Color color1 = { 243, 243, 243, 0 };
m_color = color1;
SDL_Rect itemRect(std::size_t i) const
{
return { x, static_cast<Sint16>(y + item_height * i), item_width, item_height };
SDL_Color color2 = { 0, 0, 0, 0 };
m_shadow_color = color2;
m_text = text;
m_render_cache = NULL;
}
UiListItem *itemAt(Sint16 y) const
//private:
SDL_Color m_color;
SDL_Color m_shadow_color;
const char *m_text;
// State:
TtfSurfaceCache *m_render_cache;
virtual void FreeCache()
{
ASSERT(y >= rect.y);
const std::size_t index = (y - rect.y) / item_height;
ASSERT(index < length);
return &items[index];
delete m_render_cache;
m_render_cache = NULL;
}
};
struct UiScrollBar : public UiItemBase {
constexpr UiScrollBar(Art *bg, Art *thumb, Art *arrow, SDL_Rect rect, int flags = 0)
: UiItemBase(rect, flags)
, bg(bg)
, thumb(thumb)
, arrow(arrow)
{
}
//=============================================================================
Art *bg;
Art *thumb;
Art *arrow;
};
// A button (uses Diablo sprites)
struct UiEdit : public UiItemBase {
constexpr UiEdit(char *value, std::size_t max_length, SDL_Rect rect, int flags)
class UiButton : public UiItemBase {
public:
UiButton(Art *art, const char *text, void (*action)(), SDL_Rect rect, int flags = 0)
: UiItemBase(rect, flags)
, value(value)
, max_length(max_length)
{
m_type = UI_BUTTON;
m_art = art;
m_text = text;
m_action = action;
m_pressed = false;
m_render_cache = NULL;
}
char *value;
std::size_t max_length;
};
enum FrameKey {
DEFAULT = 0,
PRESSED,
DISABLED
};
struct UiItem {
constexpr UiItem(UiText text)
: type(UI_TEXT)
, text(text)
{
}
//private:
Art *m_art;
constexpr UiItem(UiArtText text)
: type(UI_ART_TEXT)
, art_text(text)
{
}
const char *m_text;
void (*m_action)();
constexpr UiItem(UiArtTextButton art_text_button)
: type(UI_ART_TEXT_BUTTON)
, art_text_button(art_text_button)
{
}
// State
bool m_pressed;
TtfSurfaceCache *m_render_cache;
constexpr UiItem(UiImage image)
: type(UI_IMAGE)
, image(image)
virtual void FreeCache()
{
delete m_render_cache;
m_render_cache = NULL;
}
};
constexpr UiItem(UiButton button)
: type(UI_BUTTON)
, button(button)
{
}
//=============================================================================
constexpr UiItem(UiList list)
: type(UI_LIST)
, list(list)
class UiListItem {
public:
UiListItem(const char *text = "", int value = 0)
{
m_text = text;
m_value = value;
}
constexpr UiItem(UiScrollBar scrollbar)
: type(UI_SCROLLBAR)
, scrollbar(scrollbar)
~UiListItem()
{
}
constexpr UiItem(UiEdit edit)
: type(UI_EDIT)
, edit(edit)
{
}
//private:
const char *m_text;
int m_value;
};
UiType type;
union {
UiText text;
UiArtText art_text;
UiImage image;
UiArtTextButton art_text_button;
UiButton button;
UiList list;
UiScrollBar scrollbar;
UiEdit edit;
UiItemBase common;
};
typedef std::vector<UiListItem *> vUiListItem;
bool has_flag(UiFlags flag) const
class UiList : public UiItemBase {
public:
UiList(vUiListItem vItems, Sint16 x, Sint16 y, Uint16 item_width, Uint16 item_height, int flags = 0)
: UiItemBase(x, y, item_width, item_height * vItems.size(), flags)
{
return common.has_flag(flag);
}
m_type = UI_LIST;
m_vecItems = vItems;
m_x = x;
m_y = y;
m_width = item_width;
m_height = item_height;
};
bool has_any_flag(int flags) const
~UiList() {};
SDL_Rect itemRect(int i) const
{
return common.has_any_flag(flags);
SDL_Rect tmp;
tmp.x = m_x;
tmp.y = m_y + m_height * i;
tmp.w = m_width;
tmp.h = m_height;
return tmp;
}
const SDL_Rect &rect() const
UiListItem *itemAt(Sint16 y) const
{
return common.rect;
ASSERT(y >= m_rect.y);
const std::size_t index = (y - m_rect.y) / m_height;
ASSERT(index < m_vecItems.size());
return m_vecItems[index];
}
void FreeCache()
UiListItem *GetItem(int i) const
{
switch (type) {
case UI_BUTTON:
button.FreeCache();
break;
case UI_TEXT:
text.FreeCache();
break;
default:
break;
}
return m_vecItems[i];
}
};
//private:
Sint16 m_x, m_y;
Uint16 m_width, m_height;
std::vector<UiListItem *> m_vecItems;
};
} // namespace dvl

110
SourceX/platform/switch/keyboard.cpp

@ -7,73 +7,73 @@
static void switch_keyboard_get(const char *guide_text, char *initial_text, int max_len, int multiline, char *buf)
{
Result rc = 0;
Result rc = 0;
SwkbdConfig kbd;
SwkbdConfig kbd;
rc = swkbdCreate(&kbd, 0);
rc = swkbdCreate(&kbd, 0);
if (R_SUCCEEDED(rc)) {
swkbdConfigMakePresetDefault(&kbd);
swkbdConfigSetGuideText(&kbd, guide_text);
swkbdConfigSetInitialText(&kbd, initial_text);
swkbdConfigSetStringLenMax(&kbd, max_len);
rc = swkbdShow(&kbd, buf, max_len);
swkbdClose(&kbd);
}
if (R_SUCCEEDED(rc)) {
swkbdConfigMakePresetDefault(&kbd);
swkbdConfigSetGuideText(&kbd, guide_text);
swkbdConfigSetInitialText(&kbd, initial_text);
swkbdConfigSetStringLenMax(&kbd, max_len);
rc = swkbdShow(&kbd, buf, max_len);
swkbdClose(&kbd);
}
}
static int get_utf8_character_bytes(const uint8_t *uc)
{
if (uc[0] < 0x80) {
return 1;
} else if ((uc[0] & 0xe0) == 0xc0 && (uc[1] & 0xc0) == 0x80) {
return 2;
} else if ((uc[0] & 0xf0) == 0xe0 && (uc[1] & 0xc0) == 0x80 && (uc[2] & 0xc0) == 0x80) {
return 3;
} else if ((uc[0] & 0xf8) == 0xf0 && (uc[1] & 0xc0) == 0x80 && (uc[2] & 0xc0) == 0x80 && (uc[3] & 0xc0) == 0x80) {
return 4;
} else {
return 1;
}
if (uc[0] < 0x80) {
return 1;
} else if ((uc[0] & 0xe0) == 0xc0 && (uc[1] & 0xc0) == 0x80) {
return 2;
} else if ((uc[0] & 0xf0) == 0xe0 && (uc[1] & 0xc0) == 0x80 && (uc[2] & 0xc0) == 0x80) {
return 3;
} else if ((uc[0] & 0xf8) == 0xf0 && (uc[1] & 0xc0) == 0x80 && (uc[2] & 0xc0) == 0x80 && (uc[3] & 0xc0) == 0x80) {
return 4;
} else {
return 1;
}
}
static void switch_create_and_push_sdlkey_event(uint32_t event_type, SDL_Scancode scan, SDL_Keycode key)
{
SDL_Event event;
event.type = event_type;
event.key.keysym.scancode = scan;
event.key.keysym.sym = key;
event.key.keysym.mod = 0;
SDL_PushEvent(&event);
SDL_Event event;
event.type = event_type;
event.key.keysym.scancode = scan;
event.key.keysym.sym = key;
event.key.keysym.mod = 0;
SDL_PushEvent(&event);
}
void switch_start_text_input(const char *guide_text, char *initial_text, int multiline)
void switch_start_text_input(const char *guide_text, char *initial_text, int max_length, int multiline)
{
char text[65] = {'\0'};
switch_keyboard_get(guide_text, initial_text, 64, multiline, text);
for (int i = 0; i < 600; i++) {
switch_create_and_push_sdlkey_event(SDL_KEYDOWN, SDL_SCANCODE_BACKSPACE, SDLK_BACKSPACE);
switch_create_and_push_sdlkey_event(SDL_KEYUP, SDL_SCANCODE_BACKSPACE, SDLK_BACKSPACE);
}
for (int i = 0; i < 600; i++) {
switch_create_and_push_sdlkey_event(SDL_KEYDOWN, SDL_SCANCODE_DELETE, SDLK_DELETE);
switch_create_and_push_sdlkey_event(SDL_KEYUP, SDL_SCANCODE_DELETE, SDLK_DELETE);
}
if (text[0] == '\0') {
strncpy(text, initial_text, 63);
text[64] = {'\0'};
}
const uint8_t *utf8_text = (uint8_t*) text;
for (int i = 0; i < 599 && utf8_text[i];) {
int bytes_in_char = get_utf8_character_bytes(&utf8_text[i]);
SDL_Event textinput_event;
textinput_event.type = SDL_TEXTINPUT;
for (int n = 0; n < bytes_in_char; n++) {
textinput_event.text.text[n] = text[i + n];
}
textinput_event.text.text[bytes_in_char] = 0;
SDL_PushEvent(&textinput_event);
i += bytes_in_char;
}
char text[max_length] = { '\0' };
switch_keyboard_get(guide_text, initial_text, max_length, multiline, text);
for (int i = 0; i < 600; i++) {
switch_create_and_push_sdlkey_event(SDL_KEYDOWN, SDL_SCANCODE_BACKSPACE, SDLK_BACKSPACE);
switch_create_and_push_sdlkey_event(SDL_KEYUP, SDL_SCANCODE_BACKSPACE, SDLK_BACKSPACE);
}
for (int i = 0; i < 600; i++) {
switch_create_and_push_sdlkey_event(SDL_KEYDOWN, SDL_SCANCODE_DELETE, SDLK_DELETE);
switch_create_and_push_sdlkey_event(SDL_KEYUP, SDL_SCANCODE_DELETE, SDLK_DELETE);
}
if (text[0] == '\0') {
strncpy(text, initial_text, max_length - 1);
text[max_length] = { '\0' };
}
const uint8_t *utf8_text = (uint8_t *)text;
for (int i = 0; i < 599 && utf8_text[i];) {
int bytes_in_char = get_utf8_character_bytes(&utf8_text[i]);
SDL_Event textinput_event;
textinput_event.type = SDL_TEXTINPUT;
for (int n = 0; n < bytes_in_char; n++) {
textinput_event.text.text[n] = text[i + n];
}
textinput_event.text.text[bytes_in_char] = 0;
SDL_PushEvent(&textinput_event);
i += bytes_in_char;
}
}

2
SourceX/platform/switch/keyboard.h

@ -1,3 +1,3 @@
#pragma once
void switch_start_text_input(const char *guide_text, char *initial_text, int multiline);
void switch_start_text_input(const char *guide_text, char *initial_text, int max_length, int multiline);

Loading…
Cancel
Save