#include "Client/ModeCursor.h" #include "Client/ModeEvents.h" #include "Render/DCBitmap.h" #include "Render/RenderPrimitivePool.h" #include "Render/RenderQueues.h" #include "Render/Renderer.h" #include "Render/TextContext.h" #include "Render/Texture.h" #include "Resource/BitmapImage.h" #include "UI/UIWindow.h" #include "UI/WindowRegistration.h" #include #include // STATIC: 0x0145723C // GLOBAL: gl_24364 std::uint32_t g_windowUid = 0xFFFFFFFFu; namespace { int Add(int left, int right) { return std::bit_cast(static_cast(left) + static_cast(right)); } int Subtract(int left, int right) { return std::bit_cast(static_cast(left) - static_cast(right)); } } // ORIGINAL_SITE: 0x00AA4730 in fn_17222 (fragment) void UIWindowContext::DestroyDeferredChildren() { for (UIWindow* window : this->deferredDeletion) { delete window; } this->deferredDeletion.clear(); } // OFFSET: 0x00A79A90 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 // FUNCTION: fn_16917 // OFFSET: 0x00A7A050 // FUNCTION: fn_16919 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 ); } UIWindow* UIWindow::Parent() const { return static_cast(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; } // ORIGINAL_SITE: 0x00A81D40 in fn_16987 (body) 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) { } // ORIGINAL_SITE: 0x005C9630 in fn_03632 (body) 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(Add(this->geometry.width, width), Add(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() { } // ORIGINAL_SITE: 0x00A7D6F0 in fn_16954 (body) 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 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(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 : Subtract(0, this->geometry.x); int sourceY = this->geometry.y >= 0 ? 0 : Subtract(0, this->geometry.y); int width = this->geometry.x >= 0 ? this->geometry.width : Add(this->geometry.width, this->geometry.x); int height = this->geometry.y >= 0 ? this->geometry.height : Add(this->geometry.height, this->geometry.y); if (parentWindow->geometry.width < Add(x, width)) { width = Subtract(parentWindow->geometry.width, x); } if (parentWindow->geometry.height < Add(y, height)) { height = Subtract(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 UIWindow::Slot44() { return {}; } // ORIGINAL_SITE: 0x005C8F60 in fn_03629 (body) 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(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(std::bit_cast(this->m_texture->TextureWidth())); const float height = static_cast(std::bit_cast(this->m_texture->TextureHeight())); const float x = static_cast(this->geometry.x); const float y = static_cast(this->geometry.y); const float right = static_cast(Add(this->geometry.x, this->geometry.width)); const float bottom = static_cast(Add(this->geometry.y, this->geometry.height)); const float u0 = g_textureOffsetX / width; const float v0 = g_textureOffsetY / height; const float u1 = (static_cast(this->geometry.width) + g_textureOffsetX) / width; const float v1 = (static_cast(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 >= Add(this->geometry.x, this->geometry.width) || y < this->geometry.y || y >= Add(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(Subtract(x, this->geometry.x), Subtract(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(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(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 = Add(x, width); const int bottom = Add(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(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(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(); }