diff --git a/Source/UI/Lists/UIListBoxTemplate.h b/Source/UI/Lists/UIListBoxTemplate.h new file mode 100644 index 0000000..c005d62 --- /dev/null +++ b/Source/UI/Lists/UIListBoxTemplate.h @@ -0,0 +1,144 @@ +#ifndef RAGNAROK_SOURCE_UI_LISTS_UILISTBOXTEMPLATE_H +#define RAGNAROK_SOURCE_UI_LISTS_UILISTBOXTEMPLATE_H + +#include "UI/Lists/UIVScrollBar.h" +#include "UI/Window/UIControl.h" + +#include +#include +#include +#include +#include + +class CUIListItem : public CUIControl { +public: + virtual void SetSelected(bool selected); + + /* 0084 */ unsigned char unknown_0084[0x04]; + /* 0088 */ int selected; + /* 008C */ unsigned char unknown_008C[0x04]; + /* 0090 */ unsigned char frozen; + /* 0094 */ int index; +}; + +namespace AdventureGuide { +class CUISubTab : public CUIListItem {}; +class CUITab : public CUIListItem {}; +class CUIItemInfo : public CUIListItem {}; +class CUIMonsterInfo : public CUIListItem {}; +class CUIQuestInfo : public CUIListItem {}; +class CUITabInfo : public CUIListItem {}; +class CItemData {}; +class CMonsterData {}; +class CQuestInfo {}; +} + +namespace Guide_Intro { +struct Contents {}; +} + +template class CUIListBox : public CUIControl { +public: + // OFFSET: 0x009D3FD0 + // OFFSET: 0x009D4040 + // FUNCTION: fn_14037 + // FUNCTION: fn_14038 + void Resize(int width, int height) override { + UIWindow::Resize(width, height); + this->scrollBar->Resize(this->scrollBar->geometry.width, height); + int total = 0; + for (T* item : this->items) { + total += item->geometry.height; + } + this->scrollBar->SetTotal(total); + this->scrollBar->SetVisible(this->geometry.height); + this->scrollBar->SetPosition(this->scrollBar->position); + } + + /* 0084 */ unsigned char unknown_0084[0x2C]; + /* 00B0 */ CUIVScrollBar* scrollBar; + /* 00B4 */ unsigned char unknown_00B4[0x10]; + /* 00C4 */ std::list items; +}; + +template class CUIListBoxEx : public CUIControl { +public: + // OFFSET: 0x009D43D0 + // OFFSET: 0x009D45D0 + // OFFSET: 0x009D47D0 + // OFFSET: 0x009D49D0 + // FUNCTION: fn_14043 + // FUNCTION: fn_14044 + // FUNCTION: fn_14045 + // FUNCTION: fn_14046 + void OnScroll(int position) { + auto it = this->items.begin(); + if (it == this->items.end()) { + return; + } + if (this->data.size() == 0) { + for (; it != this->items.end(); ++it) { + (*it)->index = -1; + (*it)->Show(0); + } + return; + } + + const int first = (*it)->index; + unsigned int row = position / this->rowHeight; + if (first != -1) { + if (first < static_cast(row)) { + if (row - first < static_cast(this->items.size())) { + std::rotate(this->items.begin(), this->items.begin() + (row - first), this->items.end()); + } + } else if (first > static_cast(row)) { + if (first - row < static_cast(this->items.size())) { + std::rotate(this->items.begin(), this->items.end() - (first - row), this->items.end()); + } + } + } + + it = this->items.begin(); + for (int y = this->itemY - position % this->rowHeight; y < this->geometry.height; y += this->rowHeight, ++it, ++row) { + if (it == this->items.end() || row >= this->data.size()) { + break; + } + T* item = *it; + if (item) { + if (this->rebind || row != static_cast(item->index)) { + item->index = row; + if (this->binder) { + auto node = this->data.begin(); + std::advance(node, static_cast(row)); + this->binder(item, *node); + } + } + item->SetSelected(row == this->selected); + item->Move(this->itemX, y); + item->Show(1); + } + } + + for (; it != this->items.end(); ++it) { + (*it)->index = -1; + (*it)->Show(0); + } + } + + /* 0084 */ unsigned char unknown_0084[0x2C]; + /* 00B0 */ CUIVScrollBar* scrollBar; + /* 00B4 */ int itemWidth; + /* 00B8 */ int rowHeight; + /* 00BC */ int itemX; + /* 00C0 */ int itemY; + /* 00C4 */ int unknown_00C4; + /* 00C8 */ std::vector items; + /* 00D4 */ std::list data; + /* 00DC */ unsigned int selected; + /* 00E0 */ unsigned char unknown_00E0; + /* 00E1 */ unsigned char rebind; + /* 00E2 */ unsigned char unknown_00E2[0x2E]; + /* 0110 */ std::function binder; +}; + +#endif diff --git a/Source/UI/Lists/UIVScrollBar.h b/Source/UI/Lists/UIVScrollBar.h new file mode 100644 index 0000000..ce4d316 --- /dev/null +++ b/Source/UI/Lists/UIVScrollBar.h @@ -0,0 +1,37 @@ +#ifndef RAGNAROK_SOURCE_UI_LISTS_UIVSCROLLBAR_H +#define RAGNAROK_SOURCE_UI_LISTS_UIVSCROLLBAR_H + +#include "UI/Window/UIControl.h" + +#include +#include + +class CUIVScrollBar : public CUIControl { +public: + virtual void Slot54(); + + void SetTotal(int value); + void SetVisible(int value); + void SetPosition(int value); + + /* 0084 */ UIWindow* upButton; + /* 0088 */ CUIControl* thumb; + /* 008C */ int unknown_008C; + /* 0090 */ int total; + /* 0094 */ int visible; + /* 0098 */ int unknown_0098; + /* 009C */ int position; + /* 00A0 */ int maximum; + /* 00A4 */ int trackLength; + /* 00A8 */ unsigned char dragging; + /* 00A9 */ unsigned char unknown_00A9[0x1F]; + /* 00C8 */ std::function onChange; +}; + +#if defined(_WIN32) && !defined(_WIN64) +static_assert(offsetof(CUIVScrollBar, total) == 0x90); +static_assert(offsetof(CUIVScrollBar, dragging) == 0xA8); +static_assert(offsetof(CUIVScrollBar, onChange) == 0xC8); +#endif + +#endif diff --git a/Source/UI/Window/UIControl.cpp b/Source/UI/Window/UIControl.cpp index d63cec1..a278594 100644 --- a/Source/UI/Window/UIControl.cpp +++ b/Source/UI/Window/UIControl.cpp @@ -1,4 +1,5 @@ #include "UI/Window/UIControl.h" +#include "UI/Lists/UIListBoxTemplate.h" #include "Resource/Bitmap/BitmapResource.h" #include "Resource/ResourceMgr.h" #include "Resource/TextureNames.h" @@ -150,3 +151,80 @@ void CUIControl::Slot52(int value) { // FUNCTION: fn_14036 void CUIControl::Slot53(int) { } + +// OFFSET: 0x009F2B70 +// FUNCTION: fn_14726 +void CUIVScrollBar::SetTotal(int value) { + const int count = value > 1 ? value : 1; + this->total = count; + const int shown = this->visible <= count ? this->visible : count; + this->visible = shown; + this->maximum = count - shown; + this->Invalidate(); +} + +// OFFSET: 0x009F2E80 +// FUNCTION: fn_14732 +void CUIVScrollBar::SetVisible(int value) { + const int count = this->total; + const int shown = count < value ? count : value; + this->visible = shown; + this->maximum = count - shown; + this->Invalidate(); +} + +// OFFSET: 0x009F2530 +// FUNCTION: fn_14716 +void CUIVScrollBar::SetPosition(int value) { + int zero = 0; + const int* clamped = &this->maximum; + if (value <= this->maximum) { + clamped = value >= 0 ? &value : &zero; + } + if (!this->dragging) { + this->position = *clamped; + } + this->Show(this->total > this->visible); + this->Slot54(); + if (this->onChange) { + this->onChange(this->position); + } + this->Invalidate(); +} + +// OFFSET: 0x009F25C0 +// FUNCTION: fn_14717 +void CUIVScrollBar::Slot54() { + CUIControl* bar = this->thumb; + float offset; + if (bar->hitEnabled) { + const int length = static_cast((static_cast(this->visible) / static_cast(this->total)) * static_cast(this->trackLength)); + bar->Resize(this->geometry.width, length > 1 ? length : 1); + offset = (static_cast(this->trackLength) / static_cast(this->total)) * static_cast(this->position); + } else { + if (this->total - this->visible <= 0) { + bar->Move(0, this->upButton->geometry.height); + return; + } + offset = static_cast(this->position * (this->trackLength - bar->geometry.height)) / static_cast(this->total - this->visible); + } + this->thumb->Move(0, static_cast(offset + static_cast(this->upButton->geometry.height))); +} + +// OFFSET: 0x009F0290 +// FUNCTION: fn_14661 +void CUIListItem::SetSelected(bool selected) { + if (this->selected != selected) { + this->selected = selected; + if (!this->frozen) { + this->Invalidate(); + } + } +} + +template void CUIListBox::Resize(int, int); +template void CUIListBox::Resize(int, int); +template void CUIListBoxEx::OnScroll(int); +template void CUIListBoxEx::OnScroll(int); +template void CUIListBoxEx::OnScroll(int); +template void CUIListBoxEx::OnScroll(int);