#include "Contents/Items/ItemNaming.h" #include "Render/Text/TextContext.h" #include "UI/Text/ItemLinkDisplay.h" #include "UI/Text/NavigationLink.h" #include "UI/Text/PixelTextWrapping.h" #include "UI/Text/TextMarkup.h" #include "UI/Text/TextWrapping.h" #include "UI/Lists/UIScrollBar.h" #include "UI/Buttons/UITextButton.h" #include "UI/Text/UITextViewer.h" #include // INLINED std::string UITextViewer::ConvertChat(std::string text) { std::string converted; DisplayNavigationLink(converted, text); const auto* owners = this->m_text->chatConversions; if (owners) { for (std::size_t index = 0; index < owners->size(); ++index) { converted = (*owners)[0]->Convert(converted); } } return converted; } // OFFSET: 0x0089C900 // FUNCTION: fn_11589 std::pair UITextViewer::LinkPosition(unsigned position, unsigned addedLines) { unsigned accumulated = 0; const auto count = static_cast(this->lines.size()); const unsigned first = count - addedLines; auto& owner = this->m_text->list; for (unsigned index = first; index < count; ++index) { const std::string plain = RemoveTextColors(this->lines[index].c_str(), owner.codePage); accumulated += static_cast(plain.size()); if (accumulated > position) { const unsigned bytes = position + static_cast(plain.size()) - accumulated; const std::string prefix = plain.substr(0, bytes); return {static_cast(index), this->TextWidth(owner.fonts, owner.locale, prefix.c_str(), 0, 0, 12, 0, 0) + 2}; } } return {0, 0}; } // OFFSET: 0x008760D0 // FUNCTION: fn_11205 bool UITextViewer::AddItemLinks(std::string text, unsigned addedLines) { auto& owner = this->m_text->list; std::string parsed = RemoveTextColors(text.c_str(), owner.codePage); parsed = DisplayTextTag(std::move(parsed), "", ""); parsed = DisplayTextTag(std::move(parsed), "", ""); if (this->m_text->chatConversions) { parsed = this->ConvertChat(std::move(parsed)); } std::string navigation; DisplayNavigationLink(navigation, parsed); parsed = std::move(navigation); parsed = DisplayTextTag(std::move(parsed), "", ""); std::vector extracted; ExtractTextLinks(parsed, "", "", extracted); if (extracted.empty()) { return false; } for (const auto& link : extracted) { auto* button = new UITextButton(*this->m_context, this->m_text->buttons); button->Configure(link.display.c_str(), 0x00F4934A, 0, 12, 0, 1); const int height = button->geometry.height; const int width = this->TextWidth(owner.fonts, owner.locale, link.display.c_str(), 0, 0, 12, 0, 0); button->Resize(width + 1, height); button->m_hoverColor = 0x00FF7864; button->SetId(464); SplitTextLinkArguments(link.metadata, button->m_lines); const auto position = this->LinkPosition(link.position, addedLines); button->m_value = position.first; button->Move(position.second, -100); this->AddChild(button); this->links.push_back(button); } this->SendMsg(nullptr, 7, 0, 0, 0, 0); return true; } // OFFSET: 0x00876700 // FUNCTION: fn_11206 bool UITextViewer::AddNaviLinks(std::string text, unsigned addedLines) { auto& owner = this->m_text->list; std::string parsed = RemoveTextColors(text.c_str(), owner.codePage); parsed = DisplayTextTag(std::move(parsed), "", ""); parsed = DisplayTextTag(std::move(parsed), "", ""); if (this->m_text->chatConversions) { parsed = this->ConvertChat(std::move(parsed)); } parsed = DisplayTextTag(std::move(parsed), "", ""); std::string navigation; DisplayNavigationLink(navigation, parsed); parsed = std::move(navigation); parsed = DisplayTextTag(std::move(parsed), "", ""); std::vector extracted; ExtractTextLinks(parsed, "", "", extracted); if (extracted.empty()) { return false; } for (const auto& link : extracted) { auto* button = new UITextButton(*this->m_context, this->m_text->buttons); button->Configure(link.display.c_str(), 0x00F4934A, 0, 12, 0, 1); const int height = button->geometry.height; const int width = this->TextWidth(owner.fonts, owner.locale, link.display.c_str(), 0, 0, 12, 0, 0); button->Resize(width + 1, height); button->m_hoverColor = 0x00FF7864; button->SetId(438); SplitTextLinkArguments(link.metadata, button->m_lines); const auto position = this->LinkPosition(link.position, addedLines); button->m_value = position.first; button->Move(position.second, -100); this->AddChild(button); this->links.push_back(button); } this->SendMsg(nullptr, 7, 0, 0, 0, 0); return true; } // OFFSET: 0x00877020 // FUNCTION: fn_11209 bool UITextViewer::AddQuestLinks(std::string text, unsigned addedLines) { auto& owner = this->m_text->list; std::string parsed = RemoveTextColors(text.c_str(), owner.codePage); parsed = DisplayTextTag(std::move(parsed), "", ""); parsed = DisplayTextTag(std::move(parsed), "", ""); if (this->m_text->chatConversions) { parsed = this->ConvertChat(std::move(parsed)); } std::string navigation; DisplayNavigationLink(navigation, parsed); parsed = std::move(navigation); parsed = DisplayTextTag(std::move(parsed), "", ""); std::vector extracted; ExtractTextLinks(parsed, "", "", extracted); if (extracted.empty()) { return false; } for (const auto& link : extracted) { auto* button = new UITextButton(*this->m_context, this->m_text->buttons); button->Configure(link.display.c_str(), 0x00F4934A, 0, 12, 0, 1); const int height = button->geometry.height; const int width = this->TextWidth(owner.fonts, owner.locale, link.display.c_str(), 0, 0, 12, 0, 0); button->Resize(width + 1, height); button->m_hoverColor = 0x00FF7864; button->SetId(539); SplitTextLinkArguments(link.metadata, button->m_lines); const auto position = this->LinkPosition(link.position, addedLines); button->m_value = position.first; button->Move(position.second, -100); this->AddChild(button); this->links.push_back(button); } this->SendMsg(nullptr, 7, 0, 0, 0, 0); return true; } // OFFSET: 0x00878750 // OFFSET: 0x00878750 // FUNCTION: fn_11212 bool UITextViewer::AddUrlLinks(std::string text, unsigned addedLines) { auto& owner = this->m_text->list; std::string parsed = RemoveTextColors(text.c_str(), owner.codePage); parsed = DisplayTextTag(std::move(parsed), "", ""); parsed = DisplayTextTag(std::move(parsed), "", ""); if (this->m_text->chatConversions) { parsed = this->ConvertChat(std::move(parsed)); } parsed = DisplayTextTag(std::move(parsed), "", ""); std::string navigation; DisplayNavigationLink(navigation, parsed); parsed = std::move(navigation); parsed = DisplayTextTag(std::move(parsed), "", ""); std::vector extracted; ExtractTextLinks(parsed, "", "", extracted); if (extracted.empty()) { return false; } for (const auto& link : extracted) { auto* button = new UITextButton(*this->m_context, this->m_text->buttons); button->Configure(link.display.c_str(), 0x00E6A300, 0, 12, 0, 1); button->m_hoverColor = 0x00EFB700; button->SetId(437); const int height = button->geometry.height; const int width = this->TextWidth(owner.fonts, owner.locale, link.display.c_str(), 0, 0, 12, 0, 0); button->Resize(width + 1, height); parsed = RemoveTextLinkInfo(parsed); const auto position = this->LinkPosition(link.position, addedLines); button->m_value = position.first; button->Move(position.second, -100); this->AddChild(button); SplitTextLinkArguments(link.metadata, button->m_lines); this->links.push_back(button); } this->SendMsg(nullptr, 7, 0, 0, 0, 0); return true; } // INLINED void UITextViewer::AddLinks(const char* text, unsigned addedLines, int kind) { switch (kind) { case 0: this->AddItemLinks(text, addedLines); return; case 1: this->AddUrlLinks(text, addedLines); return; case 2: this->AddNaviLinks(text, addedLines); return; case 4: this->AddQuestLinks(text, addedLines); return; default: break; } auto& owner = this->m_text->list; std::string parsed = RemoveTextColors(text, owner.codePage); if (kind != 1) { parsed = DisplayTextTag(std::move(parsed), "", ""); } if (kind != 2) { parsed = DisplayTextTag(std::move(parsed), "", ""); } if (kind == 1 || kind == 2 || kind == 3) { parsed = DisplayTextTag(std::move(parsed), "", ""); } if (kind == 3) { parsed = DisplayTextTag(std::move(parsed), "", ""); } if (this->m_text->chatConversions) { parsed = this->ConvertChat(std::move(parsed)); } if (kind == 1 || kind == 2) { parsed = DisplayTextTag(std::move(parsed), "", ""); } std::string navigation; DisplayNavigationLink(navigation, parsed); parsed = std::move(navigation); if (kind == 0 || kind == 4) { parsed = DisplayTextTag(std::move(parsed), "", ""); } if (kind == 1 || kind == 2) { parsed = DisplayTextTag(std::move(parsed), "", ""); } const char* begins[] = {"", "", "", "", ""}; const char* ends[] = {"", "", "", "", ""}; const int ids[] = {464, 437, 438, 517, 539}; std::vector extracted; ExtractTextLinks(std::move(parsed), begins[kind], ends[kind], extracted); for (const auto& link : extracted) { std::vector labels; if (kind == 3 && this->splitTipLinks == 1) { const char* begin = link.display.c_str(); const char* current = begin; const char* end = begin + link.display.size(); const char* breakAt = begin; auto limit = reinterpret_cast(begin) + 32u - link.position; while (current < end) { current = CharNextExA(owner.codePage, current, 0); while (*current == ' ') { breakAt = ++current; } if (reinterpret_cast(current) >= limit || *current == '\n') { if (*current == '\n') { breakAt = current++; } const char* split = breakAt <= begin ? current : breakAt; labels.emplace_back(begin, split); begin = split; limit = reinterpret_cast(begin) + 32u; } } if (begin < current) { labels.emplace_back(begin, current); } } else { labels.push_back(link.display); } for (unsigned fragment = 0; fragment < labels.size(); ++fragment) { if (kind == 1) { labels[fragment] = RemoveTextLinkInfo(std::move(labels[fragment])); } auto* button = new UITextButton(*this->m_context, this->m_text->buttons); button->Configure(labels[fragment].c_str(), kind == 1 ? 0x00E6A300 : 0x00F4934A, 0, 12, 0, 1); const int height = button->geometry.height; const int width = this->TextWidth(owner.fonts, owner.locale, labels[fragment].c_str(), 0, 0, 12, 0, 0); button->Resize(width + 1, height); button->m_hoverColor = kind == 1 ? 0x00E6A300 : 0x00FF7864; button->SetId(ids[kind]); SplitTextLinkArguments(link.metadata, button->m_lines); const auto position = this->LinkPosition(fragment == 0 ? link.position : 0, addedLines - fragment); button->m_value = position.first; button->Move(position.second, -100); this->AddChild(button); this->links.push_back(button); } } if (!extracted.empty()) { this->SendMsg(nullptr, 7, 0, 0, 0, 0); } }