Decompile fn_11520 (UIItemTagOnChat::RemoveTagChild)

Batch #9684 by secret: 0 byte-identical, 1 confirmed by peer review.

- fn_11520 UIItemTagOnChat::RemoveTagChild: logic confirmed by peer review

Peer-reviewed-by: ragnarok-clanker, willerthal, superintelligence, KWDev
Accepted without human review: peers confirmed the logic ("same" ahead by at least 3, with 70% of the votes).
This commit is contained in:
ragnarok-clanker 2026-11-01 18:11:04 +00:00
commit dcda289d48
4 changed files with 177 additions and 53 deletions

View file

@ -1,3 +1,4 @@
#include "Client/Input/Language.h"
#include "Contents/Items/ItemInfo.h"
#include "Contents/Items/ItemNaming.h"
#include "UI/Buttons/UIItemTagButton.h"
@ -26,21 +27,18 @@ struct ItemLinkOption {
};
#pragma pack(pop)
// INLINED
FocusedItemTagRemoval::FocusedItemTagRemoval(InputFocus& focus) : m_focus(focus) {
}
// OFFSET: 0x00896EB0
// FUNCTION: fn_11519
void FocusedItemTagRemoval::Remove(UIWindow* child) {
if (this->m_focus.window) {
this->m_focus.window->RemoveChild(child, 1);
void UIItemTagCtrl::RemoveTagChild(UIWindow* child) {
UIWindow* hoverWindow = g_windowMgr.GetHoverWindow();
if (hoverWindow) {
hoverWindow->RemoveChild(child, 1);
}
}
// OFFSET: 0x00896EE0
// FUNCTION: fn_11520
void ChatItemTagRemoval::Remove(UIWindow* child) {
void UIItemTagOnChat::RemoveTagChild(UIWindow* child) {
if (g_inputSuggestionsEnabled == 1 && g_chatSuggestions->unknown_00BC != nullptr && g_chatSuggestions->unknown_00BC->Contains(child)) {
g_chatSuggestions->unknown_00BC->RemoveChild(child, 1);
return;
@ -156,27 +154,19 @@ bool UIItemTagCtrl::SyncTagsWithText() {
return true;
}
// INLINED
MailItemTagRemoval::MailItemTagRemoval(UIWindow* const& owner) : m_owner(owner) {
}
// OFFSET: 0x00896F60
// FUNCTION: fn_11521
void MailItemTagRemoval::Remove(UIWindow* child) {
if (this->m_owner) {
this->m_owner->RemoveChild(child, 1);
void UIItemTagOnMail::RemoveTagChild(UIWindow* child) {
if (g_windowMgr.unknown_0458) {
g_windowMgr.unknown_0458->RemoveChild(child, 1);
}
}
// INLINED
MegaPhoneItemTagRemoval::MegaPhoneItemTagRemoval(UIWindow* const& owner) : m_owner(owner) {
}
// OFFSET: 0x00896F80
// FUNCTION: fn_11522
void MegaPhoneItemTagRemoval::Remove(UIWindow* child) {
if (this->m_owner) {
this->m_owner->RemoveChild(child, 1);
void UIItemTagOnMegaPhone::RemoveTagChild(UIWindow* child) {
if (g_windowMgr.unknown_0318) {
g_windowMgr.unknown_0318->RemoveChild(child, 1);
}
}
@ -344,3 +334,79 @@ bool UIItemTagOnMegaPhone::CanInsertTag(const std::string& name, int width) {
std::string text = this->taggedText += tag;
return text.size() <= maximum;
}
// OFFSET: 0x008962C0
void UIItemTagCtrl::sub_8962C0() {
this->unknown_0140.clear();
if (this->unknown_011C != static_cast<int>(this->tags.size()) && this->SyncTagsWithText()) {
this->unknown_011C = static_cast<int>(this->tags.size());
}
std::string prefix;
std::string remaining = this->taggedText;
if (!this->tags.empty()) {
for (auto entry = this->tags.begin(); entry != this->tags.end();) {
if (this->RemoveInvalidTag(entry)) {
break;
}
const std::string fitted = FitText(*this->geometry.surface, entry->name, this->tagWidth / 2);
std::string display = "<";
display += fitted;
display += ">";
const std::size_t found = remaining.find(display);
if (found != std::string::npos) {
const std::string head = remaining.substr(0, found);
prefix += head;
std::string visible = head;
std::size_t firstVisible = 0;
int x = 0;
UIWindow* hover = g_windowMgr.GetHoverWindow();
if (hover != nullptr) {
switch (hover->geometry.id) {
case 0x1EA:
case 0x1EC:
case 0x1ED:
case 0x1EE:
firstVisible = static_cast<UIEditCtrl*>(hover)->firstVisible;
break;
}
if (firstVisible <= prefix.size()) {
visible = prefix.substr(firstVisible);
}
if (firstVisible != 0 && prefix.size() <= firstVisible) {
x = 1000;
}
}
this->PlaceTagButton(entry->child, visible, x, 0);
const int bytes = static_cast<int>(std::strlen(fitted.c_str()));
if (!g_windowMgr.unknown_0458 || static_cast<UIRodexComposeWnd*>(g_windowMgr.unknown_0458)->m_contents == g_windowMgr.GetHoverWindow()) {
const int cursor = this->m_edit->input.cursor;
const int start = static_cast<int>(prefix.size());
const int end = start + bytes + 2;
if (cursor > start && cursor < end) {
const int target = start + bytes / 2 + 1 > cursor ? end : start;
this->m_edit->input.Select(target, target);
}
}
this->unknown_0140 += head;
this->unknown_0140 += itemLinkTagOpening;
this->unknown_0140 += entry->link;
this->unknown_0140 += itemLinkTagClosing;
remaining = remaining.substr(found + display.size());
prefix += display;
}
++entry;
}
}
this->unknown_0140 += remaining;
}

View file

@ -24,34 +24,6 @@ protected:
~ItemTagRemoval() = default;
};
struct FocusedItemTagRemoval final : ItemTagRemoval {
explicit FocusedItemTagRemoval(InputFocus& focus);
void Remove(UIWindow* child) override;
private:
InputFocus& m_focus;
};
struct ChatItemTagRemoval final : ItemTagRemoval {
void Remove(UIWindow* child) override;
};
struct MailItemTagRemoval final : ItemTagRemoval {
explicit MailItemTagRemoval(UIWindow* const& owner);
void Remove(UIWindow* child) override;
private:
UIWindow* const& m_owner;
};
struct MegaPhoneItemTagRemoval final : ItemTagRemoval {
explicit MegaPhoneItemTagRemoval(UIWindow* const& owner);
void Remove(UIWindow* child) override;
private:
UIWindow* const& m_owner;
};
struct ItemTagRetirement {
std::list<ItemTagRecord>& records;
ItemTagRemoval& removal;

View file

@ -3,6 +3,7 @@
#include "UI/Edit/ItemTagRetirement.h"
#include "UI/Edit/UIEditCtrl.h"
#include "UI/TextTags.h"
#include <list>
#include <string>
@ -16,10 +17,13 @@ public:
~UIItemTagCtrl() override;
virtual bool CanInsertTag(const std::string& name, int width);
virtual void AddTagButton(UIWindow* button);
virtual void RemoveTagChild(UIWindow* child);
virtual void PlaceTagButton(UIWindow* button, const std::string& name, const int& x, int unused) = 0;
std::string AppendTag(const std::string& name);
int MeasureText(const std::string& text, const unsigned& bytes);
bool SyncTagsWithText();
bool RemoveInvalidTag(std::list<ItemTagRecord>::iterator& entry);
void SetTaggedText(const std::string& text, int width);
void sub_8962C0();
void sub_89CFE0(const ITEM_INFO& item, unsigned int color);
@ -36,7 +40,8 @@ class UIItemTagOnChat : public UIItemTagCtrl {
public:
UIItemTagOnChat();
bool CanInsertTag(const std::string& name, int width) override;
void PlaceTagButton(UIWindow* button, const std::string& name, const int& x, int unused);
void RemoveTagChild(UIWindow* child) override;
void PlaceTagButton(UIWindow* button, const std::string& name, const int& x, int unused) override;
};
class UIItemTagOnMail : public UIItemTagCtrl {
@ -45,7 +50,8 @@ public:
bool CanInsertTag(const std::string& name, int width) override;
void AddTagButton(UIWindow* button) override;
void SetLines(const std::vector<std::string>& lines);
void PlaceTagButton(UIWindow* button, const std::string& name, const int& x, int unused);
void RemoveTagChild(UIWindow* child) override;
void PlaceTagButton(UIWindow* button, const std::string& name, const int& x, int unused) override;
/* 015C */ std::vector<std::string> m_lines;
};
@ -55,7 +61,8 @@ public:
UIItemTagOnMegaPhone();
bool CanInsertTag(const std::string& name, int width) override;
void AddTagButton(UIWindow* button) override;
void PlaceTagButton(UIWindow* button, const std::string& name, const int& x, int unused);
void RemoveTagChild(UIWindow* child) override;
void PlaceTagButton(UIWindow* button, const std::string& name, const int& x, int unused) override;
/* 015C */ int textWidth;
};

View file

@ -44,6 +44,9 @@
#include <cstdlib>
#include <windows.h>
#include <timeapi.h>
#include "UI/Edit/UIItemTagCtrl.h"
#include "UI/Rodex/UIRodexComposeWnd.h"
#include "UI/Text/TextFitting.h"
// OFFSET: 0x0086DDA0
// FUNCTION: fn_11059
UIListBoxTitle::UIListBoxTitle() : titleColor(0x92C0FA) {
@ -1762,3 +1765,79 @@ void UITabControl::Configure(
this->edit->Move(-100, -100);
this->AddChild(this->edit);
}
// OFFSET: 0x0087D240
bool UIItemTagCtrl::RemoveInvalidTag(std::list<ItemTagRecord>::iterator& entry) {
const std::string fitted = FitText(*this->geometry.surface, entry->name, this->tagWidth / 2);
std::string remaining = this->taggedText;
const int bytes = static_cast<int>(std::strlen(fitted.c_str()));
int found = static_cast<int>(remaining.find(fitted));
if (found == -1) {
this->RemoveTagChild(entry->child);
entry = this->tags.erase(entry);
return true;
}
int absolute = found;
int count = 0;
do {
std::string before;
if (found > 0) {
before = remaining.substr(found - 1, 1);
}
std::string after;
if (found + bytes >= 0) {
after = remaining.substr(found + bytes, 1);
}
bool atCursor;
if (before.find("<") != std::string::npos && after.find(">") == std::string::npos) {
atCursor = (!g_windowMgr.unknown_0458 || static_cast<UIRodexComposeWnd*>(g_windowMgr.unknown_0458)->m_contents == g_windowMgr.GetHoverWindow()) &&
absolute + bytes == this->m_edit->input.cursor;
} else if (before.find("<") == std::string::npos && after.find(">") != std::string::npos) {
atCursor = (!g_windowMgr.unknown_0458 || static_cast<UIRodexComposeWnd*>(g_windowMgr.unknown_0458)->m_contents == g_windowMgr.GetHoverWindow()) &&
absolute == this->m_edit->input.cursor;
} else if (before.find("<") != std::string::npos && after.find(">") != std::string::npos) {
++count;
if (count > entry->index) {
return false;
}
atCursor = false;
} else {
remaining = remaining.substr(found + bytes);
absolute = (absolute == found ? absolute : absolute + found) + bytes;
found = static_cast<int>(remaining.find(fitted));
continue;
}
if (atCursor) {
remaining = before.find("<") != std::string::npos ? this->taggedText.substr(0, absolute - 1) : this->taggedText.substr(0, absolute);
int end = absolute;
if (after.find(">") != std::string::npos) {
++end;
}
remaining += this->taggedText.substr(end + bytes);
this->m_edit->input.ResetInput();
this->m_edit->input.Insert(remaining.c_str());
this->RemoveTagChild(entry->child);
entry = this->tags.erase(entry);
return true;
}
if (entry->index == 0) {
break;
}
remaining = remaining.substr(found + bytes);
found = static_cast<int>(remaining.find(fitted));
if (found == -1) {
return false;
}
absolute += found + bytes;
} while (found != -1);
return false;
}