rodecomp/Source/UI/Lists/UIComboBoxEx.cpp
ragnarok-clanker e168b4e4a6 Restore translation-unit ownership and publish verified source corrections
Owner-authorized source migration: 31 helper-only translation units were merged into their callers or existing inline headers; both build manifests updated. Includes the preserved source payload, Lua configuration matching wowdecomp, and exact malloca expansion without diagnostic pragmas.

449 native units compiled locally and 449 staged units compiled by the pinned server toolchain. No previously byte-matched function regressed. Compiler mapping diagnostics remain recorded as needs_cleanup, not accepted or peer-confirmed. Existing independent review records are preserved. README.md is intentionally unchanged.
2026-10-25 15:42:21 +00:00

333 lines
10 KiB
C++

#include "Render/DeviceContext/DeviceContext.h"
#include "Resource/Bitmap/BitmapResource.h"
#include "Resource/ResourceMgr.h"
#include "UI/Lists/UIComboBoxEx.h"
#include "UI/Lists/UIListBox.h"
#include "UI/Lists/UIScrollBar.h"
#include "UI/Lists/UIScrollBarNavi.h"
#include "UI/Window/WindowManagerWindows.h"
#include <algorithm>
namespace {
unsigned Palette(ResourceMgr& resources, int column, int row) {
constexpr char path[] = "\xC0\xAF\xC0\xFA\xC0\xCE\xC5\xCD\xC6\xE4\xC0\xCC\xBD\xBA\\colorchip.bmp";
auto* chip = static_cast<CBitmapRes*>(resources.Load(path));
const int x = column * 4 + 2;
const int y = row * 4 + 2;
const auto& image = chip->image;
const unsigned pixel = x >= 0 && y >= 0 && x < image.width && y < image.height ? image.pixels[x + y * image.width] : 0x00FF0000u;
return ((pixel & 255) << 16) | (pixel & 0xFF00) | ((pixel >> 16) & 255);
}
}
// OFFSET: 0x0086CD40
// FUNCTION: fn_11037
UIComboBoxEx::UIComboBoxEx(UIWindowContext& windows, UIComboBoxContext& combo) : UIListBoxEx(windows, combo.list), targetId(10041), m_combo(&combo) {
}
// OFFSET: 0x00872C00
UIComboBoxEx::~UIComboBoxEx() = default;
// OFFSET: 0x0087B5A0
// FUNCTION: fn_11243
void UIComboBoxEx::Configure(int x, int y, int width, int height, unsigned char value) {
this->transparent = value;
this->UIWindow::Resize(width, height);
this->Move(x, y);
this->Slot15(width, height);
this->Slot22(width, height);
}
// OFFSET: 0x00886060
// FUNCTION: fn_11321
void UIComboBoxEx::Slot15(int width, int height) {
this->UIListBoxEx::Slot15(width, height);
}
// OFFSET: 0x00897580
// FUNCTION: fn_11530
void UIComboBoxEx::Resize(int width, int height) {
this->UIListBoxEx::Resize(width, height);
this->topLine = 0;
this->verticalBar->position = 0;
this->verticalBar->Move((width - 13), 0);
if (this->verticalOverflow != 0) {
this->verticalBar->Invalidate();
}
}
// OFFSET: 0x00890460
// FUNCTION: fn_11414
void UIComboBoxEx::Slot25(int, int) {
}
// OFFSET: 0x00890160
// FUNCTION: fn_11401
void UIComboBoxEx::Slot26(int, int) {
}
// OFFSET: 0x008920A0
// FUNCTION: fn_11464
void UIComboBoxEx::Slot27(int x, int y) {
const int previous = this->selected;
this->selected = this->LineAt(x, y);
if (previous != this->selected) {
this->Invalidate();
}
}
// OFFSET: 0x00890B70
// FUNCTION: fn_11435
void UIComboBoxEx::Slot31(int x, int y) {
this->selected = this->LineAt(x, y);
auto& windows = this->m_listContext->windows;
if (UIWindow* target = windows.LookupWindow(this->targetId)) {
target->SendMsg(nullptr, 39, this->selected, 0, 0, 0);
} else if (this->parent) {
static_cast<UIWindow*>(this->parent)->SendMsg(nullptr, 39, this->selected, 0, 0, 0);
}
windows.CloseWindow(26);
}
// OFFSET: 0x00895C30
// FUNCTION: fn_11510
void UIComboBoxEx::Refresh() {
const int visible = this->geometry.height / this->lineHeight;
const int count = static_cast<int>(this->lines.size());
const int overflow = std::max((count - visible), 0);
this->verticalOverflow = overflow != 0;
auto* bar = this->verticalBar;
bar->visible = visible;
bar->total = count;
bar->maximum = overflow != 0 ? (overflow + this->bottomMargin) : 0;
bar->Invalidate();
if (bar->position > bar->maximum) {
bar->position = bar->maximum;
}
if (overflow != 0) {
if (this->selected < this->topLine) {
this->topLine = this->selected;
} else if ((this->topLine + visible) <= this->selected) {
this->topLine = ((this->selected - visible) + 1);
}
this->topLine = std::clamp(this->topLine, 0, overflow);
bar->position = this->topLine;
}
bar->Invalidate();
UIWindow* root = this;
while (root->parent) {
root = static_cast<UIWindow*>(root->parent);
}
root->Invalidate();
}
// OFFSET: 0x00898050
// FUNCTION: fn_11545
int UIComboBoxEx::SendMsg(UIWindow* sender, int message, std::intptr_t a, std::intptr_t b, std::intptr_t c, std::intptr_t d) {
const int value = static_cast<int>(a);
const unsigned count = static_cast<unsigned>(this->lines.size());
switch (message) {
case 0:
if (UIWindow* target = this->m_listContext->windows.LookupWindow(this->targetId)) {
target->SendMsg(nullptr, 39, this->selected, 0, 0, 0);
}
if (this->m_combo->popup) {
this->m_listContext->windows.CloseWindow(26);
}
return 0;
case 18:
if (count == 0) {
return 0;
}
this->selected = std::max((this->selected - 1), 0);
if (this->selected < this->topLine) {
this->topLine = this->selected;
}
break;
case 19:
if (count == 0) {
return 0;
}
this->selected = (this->selected + 1);
if (static_cast<unsigned>(this->selected) >= count) {
this->selected = static_cast<int>(count - 1);
}
{
const int edge = ((this->bottomMargin - this->geometry.height / this->lineHeight) + this->selected);
if (this->topLine <= edge) {
this->topLine = (edge + 1);
}
}
break;
case 31:
this->AddLine(reinterpret_cast<const char*>(a), static_cast<unsigned>(b));
this->Invalidate();
return 0;
case 40:
this->selected = std::max(value, 0);
if (static_cast<unsigned>(this->selected) > count - 1) {
this->selected = static_cast<int>(count - 1);
}
break;
case 83:
this->targetId = value;
return 0;
default:
return this->UIListBoxEx::SendMsg(sender, message, a, b, c, d);
}
this->Refresh();
this->Invalidate();
return 0;
}
// INLINED
void UIComboBoxEx::DrawRows(bool centered) {
const unsigned end = std::min(
static_cast<unsigned>(this->lines.size()), static_cast<unsigned>((this->topLine + (this->geometry.height / this->lineHeight - this->bottomMargin)))
);
auto& owner = *this->m_listContext;
for (int index = this->topLine; index < static_cast<int>(end); index = (index + 1)) {
auto& row = this->lines[index];
int x = (3 - this->horizontalOffset);
if (centered) {
const int width = this->TextWidth(owner.fonts, owner.locale, row.text.c_str(), 0, 0, 12, 0, 0);
x = static_cast<int>(static_cast<float>(this->geometry.width) * 0.5f - static_cast<float>(width) * 0.5f);
}
this->DrawTextAt(owner.fonts, owner.locale, x, ((this->lineHeight * (index - this->topLine)) + 2), row.text.c_str(), 0, 0, 12, row.color, 0, 0);
}
}
// OFFSET: 0x0088A360
// FUNCTION: fn_11354
void UIComboBoxEx::Paint() {
auto& resources = this->m_listContext->resources;
this->geometry.surface->Fill(nullptr, Palette(resources, 6, 6));
this->FillRegion(1, 1, (this->geometry.width - 2), (this->geometry.height - 2), Palette(resources, 2, 2));
constexpr char prefix[] = "\xC0\xAF\xC0\xFA\xC0\xCE\xC5\xCD\xC6\xE4\xC0\xCC\xBD\xBA\\basic_interface\\";
const char* names[] = {"sysboxs_lu.bmp", "sysboxs_ld.bmp", "sysboxs_ru.bmp", "sysboxs_rd.bmp"};
for (int corner = 0; corner < 4; ++corner) {
auto* resource = static_cast<CBitmapRes*>(resources.Load((std::string(prefix) + names[corner]).c_str()));
this->DrawBitmap(
corner >= 2 ? (this->geometry.width - resource->image.width) : 0, (corner & 1) ? (this->geometry.height - resource->image.height) : 0, &resource->image,
false
);
}
const unsigned end = std::min(
static_cast<unsigned>(this->lines.size()), static_cast<unsigned>((this->topLine + (this->geometry.height / this->lineHeight - this->bottomMargin)))
);
if (this->selected >= this->topLine && this->selected < static_cast<int>(end)) {
this->FillRegion(
3, ((this->lineHeight * (this->selected - this->topLine)) + 1), (this->geometry.width - 6), (this->lineHeight - 2), Palette(resources, 6, 2)
);
}
this->DrawRows(false);
if (this->verticalOverflow != 0) {
this->FillRegion((this->geometry.width - 16), 0, 16, this->geometry.height, Palette(resources, 2, 2));
}
static_cast<UIWindow*>(this->parent)->Invalidate();
}
// OFFSET: 0x00885250
// FUNCTION: fn_11036
UIComboBoxEx2::UIComboBoxEx2(UIWindowContext& windows, UIComboBoxContext& combo) : UIComboBoxEx(windows, combo), alignment(0) {
}
// OFFSET: 0x00872B50
// FUNCTION: fn_11102
UIComboBoxEx2::~UIComboBoxEx2() = default;
// OFFSET: 0x00885EE0
// FUNCTION: fn_11320
void UIComboBoxEx2::Slot15(int width, int height) {
auto& list = *this->m_listContext;
auto* vertical = new UIScrollBar_Navi(*this->m_context, list.capture, list.resources);
this->verticalBar = vertical;
vertical->vertical = 1;
vertical->UIWindow::Resize(vertical->thickness, height);
vertical->Move((width - 7), 0);
vertical->Slot15(vertical->thickness, height);
vertical->Slot22(vertical->thickness, height);
this->AddChild(vertical);
auto* horizontal = new UIScrollBar(*this->m_context, list.capture, list.resources);
this->horizontalBar = horizontal;
horizontal->vertical = 0;
horizontal->UIWindow::Resize((width - 7), horizontal->thickness);
horizontal->Move(0, (height - 7));
horizontal->Slot15((width - 7), horizontal->thickness);
horizontal->Slot22((width - 7), horizontal->thickness);
this->AddChild(horizontal);
this->lineHeight = 18;
}
// OFFSET: 0x00897510
// FUNCTION: fn_11529
void UIComboBoxEx2::Resize(int width, int height) {
this->UIWindow::Resize(width, height);
this->Refresh();
this->verticalBar->Move((width - 7), 0);
if (this->verticalOverflow != 0) {
this->verticalBar->Invalidate();
} else {
this->verticalBar->Resize(7, height);
this->verticalBar->Move((width - 6), 0);
}
}
// OFFSET: 0x00889E50
// FUNCTION: fn_11353
void UIComboBoxEx2::Paint() {
this->geometry.surface->Fill(nullptr, 0xC59582u);
this->FillRegion(1, 1, (this->geometry.width - 2), (this->geometry.height - 2), 0xFCF4F0u);
const unsigned end = std::min(
static_cast<unsigned>(this->lines.size()), static_cast<unsigned>((this->topLine + (this->geometry.height / this->lineHeight - this->bottomMargin)))
);
if (this->selected >= this->topLine && this->selected < static_cast<int>(end)) {
this->FillRegion(3, ((this->lineHeight * (this->selected - this->topLine)) + 1), (this->geometry.width - 6), (this->lineHeight - 2), 0xF8D5C4u);
}
if (this->alignment == 0 || this->alignment == 1) {
this->DrawRows(this->alignment == 1);
}
if (this->parent) {
static_cast<UIWindow*>(this->parent)->Invalidate();
}
}