rodecomp/Source/UI/Window/UIWindow.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

728 lines
17 KiB
C++

#include "Client/Mode/ModeCursor.h"
#include "Client/Mode/ModeEvents.h"
#include "Render/DeviceContext/DCBitmap.h"
#include "Render/Primitives/RenderPrimitivePool.h"
#include "Render/Primitives/RenderQueues.h"
#include "Render/Renderer.h"
#include "Render/Text/TextContext.h"
#include "Render/Textures/Texture.h"
#include "Resource/Bitmap/BitmapImage.h"
#include "UI/Window/UIWindow.h"
#include "UI/Window/WindowRegistration.h"
#include <bit>
#include <cstring>
#include <windows.h>
#include <timeapi.h>
// INLINED
inline WindowGeometry::WindowGeometry()
: width(0), height(0), x(0), y(0), surface(nullptr), visible(1), id(600), unknown_0030(0), unknown_0034(0), targetAlpha(255), alphaStart(timeGetTime()),
currentAlpha(255) {
}
// INLINED
inline void WindowGeometry::Show(int state) {
this->visible = state;
}
// INLINED
inline void WindowGeometry::SetId(int value) {
this->id = value;
}
// INLINED
inline std::uint32_t WindowGeometry::Color() {
std::uint32_t value = this->currentAlpha;
if (value != this->targetAlpha) {
const std::uint32_t step = (timeGetTime() - this->alphaStart) >> 4;
const std::uint32_t candidate = value < this->targetAlpha ? value + step : value - step;
const bool keepCandidate = value < this->targetAlpha ? candidate < this->targetAlpha : this->targetAlpha < candidate;
value = keepCandidate ? candidate : this->targetAlpha;
this->currentAlpha = value;
}
if (value > 255) {
value = this->targetAlpha;
this->currentAlpha = value;
}
return (value << 24) + 0xFFFFFF;
}
// STATIC: 0x0145723C
// GLOBAL: gl_24364
std::uint32_t g_windowUid = 0xFFFFFFFFu;
// INLINED
void UIWindowContext::DestroyDeferredChildren() {
for (UIWindow* window : this->deferredDeletion) {
delete window;
}
this->deferredDeletion.clear();
}
// OFFSET: 0x00A79A90
// FUNCTION: fn_16914
UIWindow::UIWindow(UIWindowContext& context, unsigned char cached)
: unknown_0044(0), unknown_0048(0), unknown_004C(4), m_dirty(1), m_uid(++g_windowUid), m_cached(cached), m_clip(0), m_context(&context) {
}
// OFFSET: 0x00A79E90
// OFFSET: 0x00A7A050
// FUNCTION: fn_16917
UIWindow::~UIWindow() {
delete this->geometry.surface;
this->geometry.surface = nullptr;
this->DeleteChildren();
this->m_context->registration.Remove(this);
ForgetWindowReferences(
this, this->m_context->reference_019C, this->m_context->reference_01A0, this->m_context->reference_01B8, this->m_context->reference_0A30,
this->m_context->reference_4F1D4
);
}
// INLINED
UIWindow* UIWindow::Parent() const {
return static_cast<UIWindow*>(this->parent);
}
// OFFSET: 0x00A82EB0
// FUNCTION: fn_16998
void UIWindow::Resize(int width, int height) {
delete this->geometry.surface;
this->geometry.surface = CreateBitmapDC(width, height, true);
this->geometry.width = width;
this->geometry.height = height;
this->Invalidate();
}
// OFFSET: 0x005C4650
// FUNCTION: fn_03571
bool UIWindow::Slot02() {
return false;
}
// OFFSET: 0x005C4670
// FUNCTION: fn_03573
bool UIWindow::Slot03() {
return false;
}
// OFFSET: 0x00A81D40
// FUNCTION: fn_16987
void UIWindow::Move(int x, int y) {
this->geometry.Move(x, y);
}
// OFFSET: 0x005C3220
// FUNCTION: fn_03553
bool UIWindow::Slot05() {
return false;
}
// OFFSET: 0x005C4510
// FUNCTION: fn_03564
UIWindow* UIWindow::Slot06() {
return this;
}
// OFFSET: 0x005C45C0
// FUNCTION: fn_03567
bool UIWindow::Slot07(void*) {
return false;
}
// OFFSET: 0x005C4660
// FUNCTION: fn_03572
bool UIWindow::Slot08() {
return false;
}
// OFFSET: 0x005CA050
// FUNCTION: fn_03638
bool UIWindow::CanHitTest() {
return true;
}
// OFFSET: 0x00A7BB00
// FUNCTION: fn_16933
void UIWindow::Slot10(int, int, std::intptr_t) {
this->m_context->mode->SendModeMessage(18, 0, 0, 0, 0);
}
// OFFSET: 0x005CA0F0
// FUNCTION: fn_03640
void UIWindow::Slot11() {
}
// OFFSET: 0x005C7B40
// FUNCTION: fn_03623
void UIWindow::Slot12() {
}
// OFFSET: 0x005C4680
// FUNCTION: fn_03574
void UIWindow::Slot13(int, int) {
}
// OFFSET: 0x005C9630
// FUNCTION: fn_03632
void UIWindow::Show(int visible) {
this->geometry.Show(visible);
}
// OFFSET: 0x005C6070
// FUNCTION: fn_03584
void UIWindow::Slot15(int, int) {
}
// OFFSET: 0x005C6F10
// FUNCTION: fn_03592
void UIWindow::Slot16() {
}
// OFFSET: 0x005C7340
// FUNCTION: fn_03613
void UIWindow::Grow(int width, int height) {
this->Resize((this->geometry.width + width), (this->geometry.height + height));
}
// OFFSET: 0x005C6080
// FUNCTION: fn_03585
void UIWindow::Slot18() {
}
// OFFSET: 0x005C72D0
// FUNCTION: fn_03606
void UIWindow::Slot19() {
}
// OFFSET: 0x006006C0
// FUNCTION: fn_04159
void UIWindow::Paint() {
}
// OFFSET: 0x005C7320
// FUNCTION: fn_03611
void UIWindow::Slot21() {
}
// OFFSET: 0x005C7330
// FUNCTION: fn_03612
void UIWindow::Slot22(int, int) {
}
// OFFSET: 0x005C4C70
// FUNCTION: fn_03578
void UIWindow::Slot23() {
}
// OFFSET: 0x005C6F00
// FUNCTION: fn_03591
void UIWindow::Slot24(int) {
}
// OFFSET: 0x005FDA50
// FUNCTION: fn_04116
void UIWindow::Slot25(int, int) {
}
// OFFSET: 0x005C6F30
// FUNCTION: fn_03594
void UIWindow::Slot26(int, int) {
}
// OFFSET: 0x005C7280
// FUNCTION: fn_03602
void UIWindow::Slot27(int, int) {
}
// OFFSET: 0x005C7260
// FUNCTION: fn_03600
void UIWindow::Slot28(int, int) {
}
// OFFSET: 0x005C7290
// FUNCTION: fn_03603
void UIWindow::Slot29(int, int) {
}
// OFFSET: 0x00A81D60
// FUNCTION: fn_16988
void UIWindow::ResetCursor(int, int) {
this->m_context->cursor.Set(0, this->m_context->cursorTick);
}
// OFFSET: 0x005C7240
// FUNCTION: fn_03598
void UIWindow::Slot31(int, int) {
}
// OFFSET: 0x005C7300
// FUNCTION: fn_03609
void UIWindow::Slot32(int, int) {
}
// OFFSET: 0x005C7310
// FUNCTION: fn_03610
void UIWindow::Slot33(int, int) {
}
// OFFSET: 0x005C72E0
// FUNCTION: fn_03607
void UIWindow::Slot34(int, int) {
}
// OFFSET: 0x005C73A0
// FUNCTION: fn_03615
void UIWindow::Slot35(int) {
}
// OFFSET: 0x005C7AB0
// FUNCTION: fn_03620
void UIWindow::Slot36() {
}
// OFFSET: 0x00A83660
// FUNCTION: fn_17005
int UIWindow::SendMsg(UIWindow*, int message, std::intptr_t, std::intptr_t, std::intptr_t, std::intptr_t) {
if ((message == 0 || message == 1) && this->parent) {
this->Parent()->SendMsg(this, message, 0, 0, 0, 0);
}
return 0;
}
// OFFSET: 0x00A81C30
// FUNCTION: fn_16983
void UIWindow::Invalidate() {
this->m_dirty = 1;
if (this->parent && this->Parent()->m_cached == 1) {
this->Parent()->Invalidate();
}
}
// OFFSET: 0x00A81C50
// FUNCTION: fn_16984
void UIWindow::InvalidateChildren() {
this->m_dirty = 1;
this->Slot39();
for (UIWindow* child : this->m_children) {
if (child->geometry.visible != 0) {
child->InvalidateChildren();
}
}
}
// OFFSET: 0x005C4630
// FUNCTION: fn_03569
void UIWindow::Slot39() {
}
// OFFSET: 0x00A7D6F0
// FUNCTION: fn_16954
std::uint32_t UIWindow::Color() {
return this->geometry.Color();
}
// OFFSET: 0x00A7B710
// FUNCTION: fn_16931
bool UIWindow::Update(int redraw) {
if (this->m_dirty == 1) {
this->Paint();
this->m_dirty = 0;
redraw = 1;
}
bool changed = false;
for (UIWindow* child : this->m_children) {
if (child->geometry.visible == 1) {
changed = child->Update(redraw) || changed;
}
}
if (this->m_cached == 1) {
if (redraw != 1 && !changed) {
return false;
}
std::list<RenderPrimitive*> primitives(this->m_primitives);
for (UIWindow* child : this->m_children) {
if (child->geometry.visible == 1) {
primitives.push_back(child->DrawPrimitive());
}
}
this->m_context->renderer.RenderToTexture(this->geometry.width, this->geometry.height, primitives, this->m_texture);
return this->parent != nullptr;
}
if (!this->parent || (redraw != 1 && !changed)) {
return false;
}
UIWindow* parentWindow = this->Parent();
if (parentWindow->m_cached != 0) {
return true;
}
CDCBitmap& source = *static_cast<CDCBitmap*>(this->geometry.surface);
if (this->m_clip == 0) {
parentWindow->geometry.surface->BlendFrom(this->geometry.x, this->geometry.y, this->geometry.width, this->geometry.height, source);
return true;
}
int x = this->geometry.x >= 0 ? this->geometry.x : 0;
int y = this->geometry.y >= 0 ? this->geometry.y : 0;
int sourceX = this->geometry.x >= 0 ? 0 : (0 - this->geometry.x);
int sourceY = this->geometry.y >= 0 ? 0 : (0 - this->geometry.y);
int width = this->geometry.x >= 0 ? this->geometry.width : (this->geometry.width + this->geometry.x);
int height = this->geometry.y >= 0 ? this->geometry.height : (this->geometry.height + this->geometry.y);
if (parentWindow->geometry.width < (x + width)) {
width = (parentWindow->geometry.width - x);
}
if (parentWindow->geometry.height < (y + height)) {
height = (parentWindow->geometry.height - y);
}
if (width > 0 && height > 0) {
parentWindow->geometry.surface->CopyFrom(x, y, sourceX, sourceY, width, height, source);
}
return true;
}
// OFFSET: 0x005C45B0
// FUNCTION: fn_03566
bool UIWindow::Slot42(void*) {
return false;
}
// OFFSET: 0x005C33D0
// FUNCTION: fn_03556
void UIWindow::Slot43() {
}
// OFFSET: 0x005C4480
// FUNCTION: fn_03560
std::vector<std::uint32_t> UIWindow::Slot44() {
return {};
}
// OFFSET: 0x005C8F60
// FUNCTION: fn_03629
void UIWindow::SetId(int value) {
this->geometry.SetId(value);
}
// OFFSET: 0x005C4500
// FUNCTION: fn_03563
int UIWindow::Slot46() {
return 0;
}
// OFFSET: 0x005C7250
// FUNCTION: fn_03599
void UIWindow::Slot47() {
}
// OFFSET: 0x005C7270
// FUNCTION: fn_03601
void UIWindow::Slot48() {
}
// OFFSET: 0x00A7FFB0
// FUNCTION: fn_16970
RenderPrimitive* UIWindow::DrawPrimitive() {
if (this->m_cached == 0) {
static_cast<CDCBitmap*>(this->geometry.surface)->UploadTexture(this->m_context->renderSystem, this->m_texture);
}
RenderPrimitive* primitive = this->m_context->primitives.Acquire();
primitive->texture = this->m_texture;
const float width = static_cast<float>(std::bit_cast<std::int32_t>(this->m_texture->TextureWidth()));
const float height = static_cast<float>(std::bit_cast<std::int32_t>(this->m_texture->TextureHeight()));
const float x = static_cast<float>(this->geometry.x);
const float y = static_cast<float>(this->geometry.y);
const float right = static_cast<float>((this->geometry.x + this->geometry.width));
const float bottom = static_cast<float>((this->geometry.y + this->geometry.height));
const float u0 = g_textureOffsetX / width;
const float v0 = g_textureOffsetY / height;
const float u1 = (static_cast<float>(this->geometry.width) + g_textureOffsetX) / width;
const float v1 = (static_cast<float>(this->geometry.height) + g_textureOffsetY) / height;
primitive->quad[0].x = x;
primitive->quad[0].y = y;
primitive->quad[0].u = u0;
primitive->quad[0].v = v0;
primitive->quad[1].x = right;
primitive->quad[1].y = y;
primitive->quad[1].u = u1;
primitive->quad[1].v = v0;
primitive->quad[2].x = x;
primitive->quad[2].y = bottom;
primitive->quad[2].u = u0;
primitive->quad[2].v = v1;
primitive->quad[3].x = right;
primitive->quad[3].y = bottom;
primitive->quad[3].u = u1;
primitive->quad[3].v = v1;
for (RenderVertex& vertex : primitive->quad) {
vertex.z = 0.000001f;
vertex.rhw = 0.999999f;
vertex.diffuse = this->Color();
}
return primitive;
}
// OFFSET: 0x00A81B10
// FUNCTION: fn_16981
UIWindow* UIWindow::HitTest(int x, int y) {
if (!this->CanHitTest() || this->geometry.visible == 0) {
return nullptr;
}
if (x < this->geometry.x || x >= (this->geometry.x + this->geometry.width) || y < this->geometry.y || y >= (this->geometry.y + this->geometry.height)) {
return nullptr;
}
for (auto child = this->m_children.rbegin(); child != this->m_children.rend(); ++child) {
UIWindow* hit = (*child)->HitTest((x - this->geometry.x), (y - this->geometry.y));
if (hit) {
return hit;
}
}
return this;
}
// OFFSET: 0x00A7C4B0
// FUNCTION: fn_16945
void UIWindow::VisitChildren() {
for (UIWindow* child : this->m_children) {
if (child) {
child->VisitChildren();
}
}
}
// OFFSET: 0x005C7AC0
// FUNCTION: fn_03621
void UIWindow::Slot52(int) {
}
// OFFSET: 0x00A7A080
// FUNCTION: fn_16920
void UIWindow::AddChild(UIWindow* child) {
child->parent = this;
this->m_children.push_back(child);
}
// OFFSET: 0x00A7A0A0
// FUNCTION: fn_16921
void UIWindow::AddChildFront(UIWindow* child) {
child->parent = this;
this->m_children.push_front(child);
}
// OFFSET: 0x00A81A30
// FUNCTION: fn_16979
UIWindow* UIWindow::FindUid(std::uint32_t uid) {
if (this->m_uid == uid) {
return this;
}
for (UIWindow* child : this->m_children) {
if (UIWindow* found = child->FindUid(uid)) {
return found;
}
}
return nullptr;
}
// OFFSET: 0x00A7D6D0
// FUNCTION: fn_16953
UIWindow* UIWindow::Root() {
UIWindow* root = this;
while (root->parent) {
root = static_cast<UIWindow*>(root->parent);
}
return root;
}
// OFFSET: 0x00A82DD0
// FUNCTION: fn_16995
void UIWindow::DeleteChildren() {
for (UIWindow* child : this->m_children) {
delete child;
}
this->m_children.clear();
}
// OFFSET: 0x00A82E30
// FUNCTION: fn_16996
void UIWindow::ClearChildren(bool destroy) {
if (destroy) {
this->DeleteChildren();
} else {
this->m_children.clear();
}
}
// OFFSET: 0x00AA5E90
// FUNCTION: fn_17226
void UIWindow::DeferDeletion(UIWindow* window) {
if (!window) {
return;
}
ForgetWindowReferences(
window, this->m_context->reference_019C, this->m_context->reference_01A0, this->m_context->reference_01B8, this->m_context->reference_0A30,
this->m_context->reference_4F1D4
);
this->m_context->deferredDeletion.insert(window);
window->Show(0);
}
// OFFSET: 0x00A82E50
// FUNCTION: fn_16997
void UIWindow::RemoveChild(UIWindow* child, unsigned char destroy) {
if (!child) {
return;
}
for (auto entry = this->m_children.begin(); entry != this->m_children.end(); ++entry) {
if (*entry == child) {
if (destroy == 1) {
this->DeferDeletion(child);
}
this->m_children.erase(entry);
return;
}
}
}
// OFFSET: 0x00A81CC0
// FUNCTION: fn_16986
bool UIWindow::Contains(const UIWindow* window) const {
if (!window) {
return false;
}
if (window == this) {
return true;
}
for (UIWindow* child : this->m_children) {
if (child->Contains(window)) {
return true;
}
}
return false;
}
// OFFSET: 0x00A7B430
// FUNCTION: fn_16927
void UIWindow::ClearSurface(std::uint32_t color) {
if (this->geometry.surface) {
this->geometry.surface->Fill(nullptr, color);
}
}
// OFFSET: 0x00A7B470
// FUNCTION: fn_16929
void UIWindow::SetSize(int width, int height) {
delete this->geometry.surface;
this->geometry.surface = CreateBitmapDC(width, height, true);
this->geometry.width = width;
this->geometry.height = height;
this->Invalidate();
this->Slot15(width, height);
this->Slot22(width, height);
this->Slot16();
}
// OFFSET: 0x00A7BB60
// FUNCTION: fn_16935
void UIWindow::DrawBitmap(int x, int y, const BitmapImage* image, bool transparent) {
if (!this->geometry.surface || !image) {
return;
}
static_cast<CDCBitmap*>(this->geometry.surface)->SetAlpha(image->alpha != 0);
this->geometry.surface->Copy(x, y, image->width, image->height, image->pixels, transparent);
}
// OFFSET: 0x00A7BD60
// FUNCTION: fn_16938
void UIWindow::FillRegion(int x, int y, int width, int height, std::uint32_t color) {
const int right = (x + width);
const int bottom = (y + height);
RECT rectangle = {
x < 0 ? 0 : x, y < 0 ? 0 : y, this->geometry.width < right ? this->geometry.width : right, this->geometry.height < bottom ? this->geometry.height : bottom
};
this->geometry.surface->Fill(&rectangle, color);
}
// OFFSET: 0x00A80580
// FUNCTION: fn_16975
int UIWindow::TextWidth(
FontCache& fonts, const TextLocale& locale, const char* text, unsigned bytes, int family, int height, unsigned char bold, unsigned char italic
) {
if (!text) {
return 0;
}
if (!bytes) {
bytes = static_cast<unsigned>(std::strlen(text));
if (!bytes) {
return 0;
}
}
TextContext context(*this->geometry.surface, fonts, locale);
context.SetFont(family, height, bold, italic);
return context.Measure(text, bytes).cx;
}
// OFFSET: 0x00A84360
// FUNCTION: fn_17009
void UIWindow::DrawTextAt(
FontCache& fonts, const TextLocale& locale, int x, int y, const char* text, int bytes, int family, int height, std::uint32_t color, unsigned char bold,
unsigned char italic
) {
if (!bytes) {
bytes = static_cast<int>(std::strlen(text));
}
TextContext context(*this->geometry.surface, fonts, locale);
context.SetFont(family, height, bold, italic);
context.SetColor(color);
context.Draw(x, y, text, bytes);
}
// OFFSET: 0x00A7C470
// FUNCTION: fn_16944
void UIWindow::FlushPrimitives(RenderQueues& queues, bool depthAlpha, float depthStart, float depthEnd) {
for (RenderPrimitive* primitive : this->m_overlayPrimitives) {
queues.Submit(primitive, 513, depthAlpha, depthStart, depthEnd);
}
this->m_overlayPrimitives.clear();
}