#include "ui/CSimpleEditBox.hpp" #include "gx/Gx.hpp" #include "gx/Coordinate.hpp" #include "ui/CSimpleEditBoxScript.hpp" #include "ui/CSimpleFontedFrameFont.hpp" #include "ui/CSimpleFontString.hpp" #include "ui/CSimpleTexture.hpp" #include "ui/CSimpleTop.hpp" #include "ui/LoadXML.hpp" #include "util/Byte.hpp" #include "util/CStatus.hpp" #include "util/Lua.hpp" #include "util/StringTo.hpp" #include #include #include #include CSimpleEditBox* CSimpleEditBox::s_currentFocus; int32_t CSimpleEditBox::s_metatable; int32_t CSimpleEditBox::s_objectType; void CSimpleEditBox::CreateScriptMetaTable() { lua_State* L = FrameScript_GetContext(); int32_t ref = FrameScript_Object::CreateScriptMetaTable(L, &CSimpleEditBox::RegisterScriptMethods); CSimpleEditBox::s_metatable = ref; } int32_t CSimpleEditBox::GetObjectType() { if (!CSimpleEditBox::s_objectType) { CSimpleEditBox::s_objectType = ++FrameScript_Object::s_objectTypes; } return CSimpleEditBox::s_objectType; } void CSimpleEditBox::RegisterScriptMethods(lua_State* L) { CSimpleFrame::RegisterScriptMethods(L); FrameScript_Object::FillScriptMethodTable(L, SimpleEditBoxMethods, NUM_SIMPLE_EDIT_BOX_SCRIPT_METHODS); } void CSimpleEditBox::SetKeyboardFocus(CSimpleEditBox* editBox) { if (!editBox->m_visible) { return; } if (CSimpleEditBox::s_currentFocus == editBox) { return; } if (CSimpleEditBox::s_currentFocus) { // TODO // if (CSimpleEditBox::s_currentFocus->m_password) { // nullsub_3(); // } // TODO // if (!(CSimpleEditBox::s_currentFocus->simpleeditbox_dword4 & 0xC)) { // CSimpleEditBox::s_currentFocus->Sub963390(); // } CSimpleEditBox::s_currentFocus->RunOnEditFocusLostScript(); CSimpleEditBox::s_currentFocus->m_dirtyFlags |= 0x4; } CSimpleEditBox::s_currentFocus = editBox; // TODO // if (editBox->m_password) { // nullsub_3(); // } // TODO // if (!(editBox->simpleeditbox_dword4 & 0xC)) { // OsIMEEnable(1); // } editBox->RunOnEditFocusGainedScript(); editBox->m_dirtyFlags |= 0x4; } CSimpleEditBox::CSimpleEditBox(CSimpleFrame* parent) : CSimpleFrame(parent) { this->m_autoFocus = 1; this->m_multiline = 0; this->m_numeric = 0; this->m_password = 0; this->m_ignoreArrows = 0; this->m_countInvisibleLetters = 0; char* text = static_cast(SMemAlloc(this->m_textSize, __FILE__, __LINE__, 0)); text[0] = 0; this->m_text = text; uint32_t* textInfo = static_cast(SMemAlloc(sizeof(uint32_t) * this->m_textSize, __FILE__, __LINE__, 0)); memset(textInfo, 0, sizeof(uint32_t) * this->m_textSize); this->m_textInfo = textInfo; auto fontMem = SMemAlloc(sizeof(CSimpleFontedFrameFont), __FILE__, __LINE__, 0x0); auto font = new (fontMem) CSimpleFontedFrameFont(this); this->m_font = font; this->m_font->m_attributes.SetJustifyH(0x1); // TODO // auto stringMem = CDataAllocator::GetData(CSimpleFontString::s_allocator, 0, __FILE__, __LINE__); auto stringMem = SMemAlloc(sizeof(CSimpleFontString), __FILE__, __LINE__, 0x0); auto string = new (stringMem) CSimpleFontString(this, DRAWLAYER_ARTWORK, 1); this->m_string = string; this->m_string->SetFontObject(this->m_font); this->SetMultiLine(0); for (int32_t i = 0; i < 3; i++) { // TODO // auto highlightMem = CDataAllocator::GetData(CSimpleTexture::s_allocator, 0, __FILE__, __LINE__); auto highlightMem = SMemAlloc(sizeof(CSimpleTexture), __FILE__, __LINE__, 0x0); auto highlight = new (highlightMem) CSimpleTexture(this, DRAWLAYER_ARTWORK, 0); this->m_highlight[i] = highlight; } CImVector highlightColor = { 0x60, 0x60, 0x60, 0xFF }; for (int32_t i = 0; i < 3; i++) { this->m_highlight[i]->SetTexture(highlightColor); } // TODO // auto cursorMem = CDataAllocator::GetData(CSimpleTexture::s_allocator, 0, __FILE__, __LINE__); auto cursorMem = SMemAlloc(sizeof(CSimpleTexture), __FILE__, __LINE__, 0x0); auto cursor = new (cursorMem) CSimpleTexture(this, DRAWLAYER_ARTWORK_OVERLAY, 1); cursor->Hide(); this->m_cursor = cursor; CImVector cursorColor = { 0xFF, 0xFF, 0xFF, 0xFF }; this->m_cursor->SetTexture(cursorColor); // TODO // - initialize actions this->EnableEvent(SIMPLE_EVENT_CHAR, -1); this->EnableEvent(SIMPLE_EVENT_KEY, -1); this->EnableEvent(SIMPLE_EVENT_MOUSE, -1); } int32_t CSimpleEditBox::ConvertCoordinateToIndex(float a2, float a3, int32_t& a4) { // TODO return 0; } void CSimpleEditBox::DeleteBackward(int32_t a2) { if (this->m_highlightLeft != this->m_highlightRight) { this->DeleteHighlight(a2); return; } if (this->m_cursorPos > 0) { int32_t len = this->GetNumToLen(this->m_cursorPos, -1, true); this->DeleteSubstring(this->m_cursorPos - len, this->m_cursorPos, a2); } } void CSimpleEditBox::DeleteForward(int32_t a2) { if (this->m_highlightLeft != this->m_highlightRight) { this->DeleteHighlight(a2); return; } if (this->m_cursorPos < this->m_textLength) { int32_t len = this->GetNumToLen(this->m_cursorPos, 1, true); this->DeleteSubstring(this->m_cursorPos, this->m_cursorPos + len, a2); } } void CSimpleEditBox::DeleteHighlight(int32_t a2) { // TODO } void CSimpleEditBox::DeleteSubstring(int32_t left, int32_t right, int32_t a4) { auto v5 = this->m_textInfo; auto v6 = left; if ((v5[left] & 0x80000000) != 0) { auto v7 = left; if (left >= 0) { LABEL_3: auto v8 = BYTE2(v5[v6]); auto v9 = &v5[v6]; if (v8 == 1 || v8 == 5 || v8 == 7) { if (v6 >= 0) { LABEL_12: auto v10 = BYTE2(v5[v6]); auto v11 = &v5[v6]; if (v10 == 1 || v10 == 5 || v10 == 7) { v7 = v6; while (1) { v6--; v11--; if (v6 < 0) { break; } if (*v11) { if (v6 >= 0) { goto LABEL_12; } break; } } } } } else { v7 = v6; while (1) { v6--; v9--; if (v6 < 0) { break; } if (*v9) { if (v6 >= 0) { goto LABEL_3; } break; } } } } v6 = v7; } auto v12 = right; if ((v5[right] & 0x80000000) != 0) { auto v13 = this->m_textLength; if (right < v13) { do { auto v14 = BYTE2(v5[v12]); if (v14 == 2 || v14 == 6 || v14 == 8) { break; } v12 += v5[v12] & 0xFFFF; } while (v12 < v13); for (; v12 < v13; v12 += v5[v12] & 0xFFFF) { auto v15 = BYTE2(v5[v12]); if (v15 != 2 && v15 != 6 && v15 != 8) { break; } } } } auto v16 = v12 - v6; auto v17 = &this->m_text[v6]; auto newLength = this->m_textLength - (v12 - v6); this->m_cursorPos = v6; auto v19 = &v5[v6]; if (newLength >= 0) { this->m_textLength = newLength; memcpy(v17, &v17[v16], newLength - v6); memcpy(v19, &v19[v16], 4 * (this->m_textLength - this->m_cursorPos)); this->m_text[this->m_textLength] = '\0'; this->m_textInfo[this->m_textLength] = '\0'; if (this->m_highlightLeft != this->m_highlightRight) { this->m_dirtyFlags |= 0x2; this->m_highlightRight = 0; this->m_highlightLeft = 0; } this->m_dirtyFlags |= 0x1 | 0x4; if (!a4) { this->m_dirtyFlags |= 0x8; } if (!this->m_textLength) { // TODO this->simpleeditbox_dwordC = 0; } } } void CSimpleEditBox::FontUpdated(CSimpleFontedFrameFont* font, int32_t a3) { // TODO } int32_t CSimpleEditBox::GetNumToLen(int32_t offset, int32_t amount, bool a4) { uint32_t len = 0; uint32_t* textInfo = this->m_textInfo + offset; if (amount > 0) { for (int32_t i = 0; i < amount; i++) { if (!*textInfo) { break; } uint32_t v8; uint32_t v9; uint32_t v10; do { do { v8 = *((uint8_t*)textInfo + 2); v9 = (uint16_t)*textInfo; v10 = *textInfo & 0xFF000000; len += v9; textInfo += v9; } while (v8 == 1); } while (v8 == 5 || v8 == 7 || (a4 && v10 < 0 && v8 != 6)); uint32_t v11; uint32_t v12; while (1) { v11 = *((uint8_t*)textInfo + 2); v12 = *textInfo & 0xFFFF; if (v11 != 2 && v11 != 6 && v11 != 8) { break; } len += v12; textInfo += v12; } } } else { for (int32_t i = 0; i < -amount; i++) { if (textInfo <= this->m_textInfo) { break; } uint32_t v14; do { for (--textInfo; textInfo > this->m_textInfo; --textInfo) { if (*textInfo) { break; } } v14 = *((uint8_t*)textInfo + 2); len += (uint16_t)*textInfo; } while (v14 == 2 || v14 == 6 || v14 == 8 || (a4 && (*textInfo & 0x80000000) != 0 && v14 != 5)); if (textInfo > this->m_textInfo) { uint32_t v15; uint32_t v16; while (1) { for (--textInfo; textInfo > this->m_textInfo; --textInfo) { if (*textInfo) { break; } } v15 = *((uint8_t*)textInfo + 2); v16 = *textInfo & 0xFFFF; if (v15 != 1 && v15 != 5 && v15 != 7) { break; } len += v16; if (textInfo <= this->m_textInfo) { break; } } if (textInfo > this->m_textInfo) { textInfo += v16; } } } } return len; } FrameScript_Object::ScriptIx* CSimpleEditBox::GetScriptByName(const char* name, ScriptData& data) { auto parentScript = CSimpleFrame::GetScriptByName(name, data); if (parentScript) { return parentScript; } if (!SStrCmpI(name, "OnEnterPressed", STORM_MAX_STR)) { return &this->m_onEnterPressed; } if (!SStrCmpI(name, "OnEscapePressed", STORM_MAX_STR)) { return &this->m_onEscapePressed; } if (!SStrCmpI(name, "OnSpacePressed", STORM_MAX_STR)) { return &this->m_onSpacePressed; } if (!SStrCmpI(name, "OnTabPressed", STORM_MAX_STR)) { return &this->m_onTabPressed; } if (!SStrCmpI(name, "OnTextChanged", STORM_MAX_STR)) { data.wrapper = "return function(self, userInput) %s end"; return &this->m_onTextChanged; } if (!SStrCmpI(name, "OnTextSet", STORM_MAX_STR)) { return &this->m_onTextSet; } if (!SStrCmpI(name, "OnCursorChanged", STORM_MAX_STR)) { data.wrapper = "return function(self,x,y,w,h) %s end"; return &this->m_onCursorChanged; } if (!SStrCmpI(name, "OnInputLanguageChanged", STORM_MAX_STR)) { data.wrapper = "return function(self,language) %s end"; return &this->m_onInputLanguageChanged; } if (!SStrCmpI(name, "OnEditFocusGained", STORM_MAX_STR)) { return &this->m_onEditFocusGained; } if (!SStrCmpI(name, "OnEditFocusLost", STORM_MAX_STR)) { return &this->m_onEditFocusLost; } if (!SStrCmpI(name, "OnCharComposition", STORM_MAX_STR)) { data.wrapper = "return function(self,text) %s end"; return &this->m_onCharComposition; } return nullptr; } int32_t CSimpleEditBox::GetScriptMetaTable() { return CSimpleEditBox::s_metatable; } void CSimpleEditBox::GrowText(int32_t size) { if (size + 1 <= this->m_textSize) { return; } this->m_textSize = (size + 32) & 0xFFFFFFE0; this->m_text = static_cast( SMemReAlloc(this->m_text, this->m_textSize, __FILE__, __LINE__, 0) ); this->m_textInfo = static_cast( SMemReAlloc(this->m_textInfo, this->m_textSize * sizeof(uint32_t), __FILE__, __LINE__, 0) ); } void CSimpleEditBox::Insert(uint32_t chr) { if (chr == '\t') { this->Insert(" ", nullptr, 1, 0, 0); return; } if (chr == '|') { this->Insert("||", nullptr, 1, 0, 0); return; } if ((this->m_multiline && chr == '\n') || (chr >= ' ' && chr != 127)) { char charBuf[8]; SUniSPutUTF8(chr, charBuf); this->Insert(charBuf, nullptr, 1, 0, 0); return; } } void CSimpleEditBox::Insert(const char* a2, const char* a3, int32_t a4, int32_t a5, int32_t a6) { if ( (this->m_textInfo[this->m_cursorPos] & 0x80000000) && this->m_cursorPos > 0 && (this->m_textInfo[this->PrevCharOffset(this->m_cursorPos)] & 0x80000000) ) { return; } if (this->m_highlightLeft != this->m_highlightRight) { this->DeleteHighlight(a6); } if (!a2) { a2 = ""; } const char* v10 = a2; if (this->m_numeric && !a5 && *v10) { while (*v10) { int32_t v33; if (SUniSGetUTF8(reinterpret_cast(a2), &v33) - 48 > 9) { return; } v10 += v33; } } int32_t v11 = SStrLen(a2); this->GrowText(this->m_textLength + v11); char* v14 = this->m_text + this->m_cursorPos; if (this->m_cursorPos < this->m_textLength) { memcpy(v14 + v11, v14, this->m_textLength - this->m_cursorPos); } memcpy(v14, a2, v11); this->m_textLength += v11; *(this->m_text + this->m_textLength) = 0; int32_t v31 = this->m_cursorPos; this->m_cursorPos += v11; this->UpdateTextInfo(); this->m_dirtyFlags |= (0x1 | 0x4); a6 ? this->m_dirtyFlags &= ~0x8 : this->m_dirtyFlags |= 0x8; if (this->m_textLengthMax >= 0 && this->m_textLength > this->m_textLengthMax) { // TODO } if (this->m_textLettersMax) { // TODO } if (a5) { this->m_highlightLeft = v31; this->m_highlightRight = std::min(this->m_textLength, this->m_cursorPos); this->m_dirtyFlags |= 2u; } // TODO // if (!this->m_intC && this->m_textLength > 0) { // this->m_intC = a3; // } if (a4) { this->RunOnCharScript(a2); // TODO // - check for spaces and run onSpacePressed script } } bool CSimpleEditBox::IsA(int32_t type) { return type == CSimpleEditBox::s_objectType || type == CSimpleFrame::s_objectType || type == CScriptRegion::s_objectType || type == CScriptObject::s_objectType; } int32_t CSimpleEditBox::IsCurrentFocus() { return this == CSimpleEditBox::s_currentFocus; } void CSimpleEditBox::LoadXML(XMLNode* node, CStatus* status) { CSimpleFrame::LoadXML(node, status); const char* fontAttr = node->GetAttributeByName("font"); if (fontAttr && *fontAttr) { auto font = CSimpleFont::GetFont(fontAttr, 0); if (font) { this->m_font->SetFontObject(font); } else { status->Add( STATUS_WARNING, "%s %s: Couldn't find font object named %s", this->GetObjectTypeName(), this->GetDisplayName(), fontAttr ); } } // TODO // - letters const char* countInvisibleLettersAttr = node->GetAttributeByName("countInvisibleLetters"); if (countInvisibleLettersAttr && *countInvisibleLettersAttr) { this->m_countInvisibleLetters = StringToBOOL(countInvisibleLettersAttr); } const char* blinkSpeedAttr = node->GetAttributeByName("blinkSpeed"); if (blinkSpeedAttr && *blinkSpeedAttr) { float blinkSpeed = std::max(SStrToFloat(blinkSpeedAttr), 0.0f); this->m_cursorBlinkSpeed = blinkSpeed; } const char* autoFocusAttr = node->GetAttributeByName("autoFocus"); if (autoFocusAttr && *autoFocusAttr) { this->m_autoFocus = StringToBOOL(autoFocusAttr); } const char* multiLineAttr = node->GetAttributeByName("multiLine"); if (multiLineAttr && *multiLineAttr) { this->m_multiline = StringToBOOL(multiLineAttr); } const char* numericAttr = node->GetAttributeByName("numeric"); if (numericAttr && *numericAttr) { this->m_numeric = StringToBOOL(numericAttr); } const char* passwordAttr = node->GetAttributeByName("password"); if (passwordAttr && *passwordAttr) { this->m_password = StringToBOOL(passwordAttr); } const char* ignoreArrowsAttr = node->GetAttributeByName("ignoreArrows"); if (ignoreArrowsAttr && *ignoreArrowsAttr) { this->m_ignoreArrows = StringToBOOL(ignoreArrowsAttr); } const char* historyLinesAttr = node->GetAttributeByName("historyLines"); if (historyLinesAttr && *historyLinesAttr) { int32_t historyLines = std::max(SStrToInt(historyLinesAttr), 0); this->SetHistoryLines(historyLines); } for (XMLNode* child = node->m_child; child; child = child->m_next) { if (!SStrCmpI(child->GetName(), "FontString", STORM_MAX_STR)) { CSimpleFont* font = this->m_font; if (font->GetFontObject()) { status->Add( STATUS_WARNING, "%s: FontString element overriding font %s", this->GetDisplayName(), font->GetFontObject()->GetName() ); } this->m_font->LoadXML(child, status); const char* bytesAttr = child->GetAttributeByName("bytes"); if (bytesAttr && *bytesAttr) { int32_t bytes = SStrToInt(bytesAttr); // TODO // - set some member of CSimpleEditBox } } else if (!SStrCmpI(child->GetName(), "HighlightColor", STORM_MAX_STR)) { // TODO } else if (!SStrCmpI(child->GetName(), "TextInsets", STORM_MAX_STR)) { float left, right, top, bottom; if (LoadXML_Insets(child, left, right, top, bottom, status)) { this->m_editTextInset.minX = left; this->m_editTextInset.maxX = right; this->m_editTextInset.maxY = top; this->m_editTextInset.minY = bottom; this->UpdateSizes(); } } } } void CSimpleEditBox::Move(int32_t distance, int32_t highlight) { STORM_ASSERT(distance); auto offset = this->GetNumToLen(this->m_cursorPos, distance, true); if (distance < 0) { offset = -offset; } if (highlight) { if (this->m_highlightLeft == this->m_highlightRight) { this->m_highlightLeft = this->m_cursorPos; this->m_highlightRight = this->m_cursorPos; } // TODO this->Sub9631A0(offset); this->m_cursorPos += offset; this->m_dirtyFlags |= 0x4; } else { if (this->m_highlightLeft != this->m_highlightRight) { this->m_dirtyFlags |= 0x2; this->m_highlightLeft = 0; this->m_highlightRight = 0; } this->m_cursorPos += offset; this->m_dirtyFlags |= 0x4; } } void CSimpleEditBox::MoveBackward(int32_t highlight) { if (this->m_cursorPos > 0) { this->Move(-1, highlight); } if (!highlight && this->m_highlightLeft != this->m_highlightRight) { this->m_dirtyFlags |= 0x2; this->m_highlightLeft = 0; this->m_highlightRight = 0; } } void CSimpleEditBox::MoveForward(int32_t highlight) { if (this->m_cursorPos < this->m_textLength) { this->Move(1, highlight); } if (!highlight && this->m_highlightLeft != this->m_highlightRight) { this->m_dirtyFlags |= 0x2; this->m_highlightLeft = 0; this->m_highlightRight = 0; } } void CSimpleEditBox::OnEnterPressed() { this->RunOnEnterPressedScript(); // TODO // - invoke action } void CSimpleEditBox::OnEscapePressed() { // TODO } void CSimpleEditBox::OnFrameSizeChanged(float width, float height) { CSimpleFrame::OnFrameSizeChanged(width, height); this->UpdateSizes(); } int32_t CSimpleEditBox::OnLayerChar(const CCharEvent& evt) { if (!this->m_visible) { return 0; } if (!CSimpleEditBox::s_currentFocus && this->m_autoFocus) { CSimpleEditBox::SetKeyboardFocus(this); } else if (!this->IsCurrentFocus()) { return 0; } this->m_imeInputMode = 0; this->Insert(evt.ch); return 1; } int32_t CSimpleEditBox::OnLayerKeyDown(const CKeyEvent& evt) { if (!this->m_visible) { return 0; } if (evt.key == KEY_PRINTSCREEN || ( this->m_ignoreArrows && (evt.key == KEY_LEFT || evt.key == KEY_RIGHT || evt.key == KEY_UP || evt.key == KEY_DOWN) && !EventIsKeyDown(KEY_LALT) && !EventIsKeyDown(KEY_RALT) ) ) { return 0; } if (!CSimpleEditBox::s_currentFocus && this->m_autoFocus) { CSimpleEditBox::SetKeyboardFocus(this); } else if (!this->IsCurrentFocus()) { return 0; } this->UpdateDirtyBits(); switch (evt.key) { case KEY_ESCAPE: { this->OnEscapePressed(); return 1; } case KEY_ENTER: { if (!this->m_multiline || this->m_onEnterPressed.luaRef) { // TODO // this->m_top->m_int1250 = 1; this->OnEnterPressed(); // TODO // this->m_top->m_int1250 = 0; } else { this->Insert("\n", 0, 1, 0, 0); } return 1; } case KEY_BACKSPACE: { if (EventIsControlKeyDown()) { // TODO this->DeleteBackwardWord(0); } else { this->DeleteBackward(0); } return 1; } case KEY_LEFT: { if (EventIsControlKeyDown()) { // TODO this->MoveBackwardWord(EventIsShiftKeyDown()); } else { this->MoveBackward(EventIsShiftKeyDown()); } return 1; } case KEY_RIGHT: { if (EventIsControlKeyDown()) { // TODO this->MoveForwardWord(EventIsShiftKeyDown()); } else { this->MoveForward(EventIsShiftKeyDown()); } return 1; } case KEY_DELETE: { if (EventIsShiftKeyDown()) { // TODO cut to clipboard } else if (EventIsControlKeyDown()) { // TODO this->DeleteForwardWord(0); } else { this->DeleteForward(0); } return 1; } // TODO // - remaining keys default: return 1; } } int32_t CSimpleEditBox::OnLayerMouseDown(const CMouseEvent& evt, const char* btn) { int32_t eaten = CSimpleFrame::OnLayerMouseDown(evt, btn); if (!eaten) { this->UpdateDirtyBits(); int32_t position; if (!this->m_imeInputMode && this->ConvertCoordinateToIndex(evt.x, evt.y, position)) { if (this->m_highlightLeft != this->m_highlightRight) { this->m_dirtyFlags |= 0x2; this->m_highlightRight = 0; this->m_highlightLeft = 0; } this->SetCursorPosition(position); this->StartHighlight(); this->m_highlightDrag = 1; eaten = 1; } CSimpleEditBox::SetKeyboardFocus(this); } return eaten; } int32_t CSimpleEditBox::OnLayerMouseUp(const CMouseEvent& evt, const char* btn) { int32_t eaten = CSimpleFrame::OnLayerMouseUp(evt, btn); if (!eaten) { if (this->m_highlightDrag) { this->m_highlightDrag = 0; eaten = 1; } } return eaten; } void CSimpleEditBox::OnLayerUpdate(float elapsedSec) { CSimpleFrame::OnLayerUpdate(elapsedSec); this->UpdateDirtyBits(); if (!GxCapsWindowHasFocus(-1)) { this->m_cursor->Hide(); return; } if (this->m_cursorBlinkSpeed == 0.0f) { return; } bool cursorVisible = this->m_cursorPos >= this->m_visiblePos && this->m_cursorPos <= this->m_visiblePos + this->m_visibleLen; if (!cursorVisible) { return; } this->m_blinkElapsedTime += elapsedSec; if (this->IsCurrentFocus() && this->m_blinkElapsedTime > this->m_cursorBlinkSpeed) { if (this->m_cursor->IsShown()) { this->m_cursor->Hide(); } else { this->m_cursor->Show(); } this->m_blinkElapsedTime = 0.0f; } } void CSimpleEditBox::OnTextChanged() { this->RunOnTextChangedScript((this->m_dirtyFlags & 0x8) != 0); // TODO // - invoke action } int32_t CSimpleEditBox::PrevCharOffset(int32_t offset) { int32_t prevCharOffset = offset; while (prevCharOffset > 0 && !this->m_textInfo[prevCharOffset - 1]) { prevCharOffset--; } return prevCharOffset; } void CSimpleEditBox::RunOnCursorChangedScript(float x, float y, float w, float h) { // TODO } void CSimpleEditBox::RunOnEditFocusGainedScript() { if (this->m_onEditFocusGained.luaRef) { this->RunScript(this->m_onEditFocusGained, 0, 0); } } void CSimpleEditBox::RunOnEditFocusLostScript() { if (this->m_onEditFocusLost.luaRef) { this->RunScript(this->m_onEditFocusLost, 0, 0); } } void CSimpleEditBox::RunOnEnterPressedScript() { if (this->m_onEnterPressed.luaRef) { this->RunScript(this->m_onEnterPressed, 0, 0); } } void CSimpleEditBox::RunOnTextChangedScript(int32_t changed) { if (this->m_onTextChanged.luaRef) { auto L = FrameScript_GetContext(); lua_pushboolean(L, changed); this->RunScript(this->m_onTextChanged, 1, 0); } } void CSimpleEditBox::SetCursorPosition(int32_t position) { if (position >= 0) { this->m_cursorPos = std::min(this->m_textLength, position); } else { this->m_cursorPos = 0; } this->m_dirtyFlags |= 0x4; } void CSimpleEditBox::SetHistoryLines(int32_t a2) { // TODO } void CSimpleEditBox::SetMultiLine(int32_t enabled) { if (enabled) { this->m_multiline = 1; } else { this->m_multiline = 0; } this->m_visibleLines.SetCount(2); if (this->m_multiline) { this->m_string->SetJustifyV(0x8); // TODO // - unknown style change (remove flag 0x4000) this->m_string->SetNonWordWrap(0); } else { this->m_string->SetJustifyV(0x10); // TODO // - unknown style change (add flag 0x4000) this->m_string->SetNonWordWrap(1); } this->m_string->SetNonSpaceWrap(1); this->UpdateSizes(); } void CSimpleEditBox::StartHighlight() { // TODO } void CSimpleEditBox::UpdateDirtyBits() { int32_t textChanged = this->m_dirtyFlags & 0x1; if (this->m_dirtyFlags & 0x4) { // TODO } if (this->m_dirtyFlags & 0x1) { this->m_dirtyFlags &= ~0x1; this->UpdateVisibleText(); } if (this->m_dirtyFlags & 0x2) { this->m_dirtyFlags &= ~0x2; this->UpdateVisibleHighlight(); } if (this->m_dirtyFlags & 0x4) { this->m_dirtyFlags &= ~0x4; this->UpdateVisibleCursor(); } if (textChanged) { this->OnTextChanged(); this->m_dirtyFlags &= ~0x8; } } void CSimpleEditBox::UpdateSizes() { if (!(CLayoutFrame::m_flags & 0x1)) { return; } this->m_string->ClearAllPoints(); float stringWidth = ((this->m_rect.maxX - this->m_rect.minX) / this->m_layoutScale) - (this->m_editTextInset.maxX + this->m_editTextInset.minX); this->m_string->SetWidth(stringWidth); float stringHeight = 0.0f; if (!this->m_multiline) { stringHeight = ((this->m_rect.maxY - this->m_rect.minY) / this->m_layoutScale) - (this->m_editTextInset.maxY + this->m_editTextInset.minY); } this->m_string->SetHeight(stringHeight); this->m_string->SetPoint( FRAMEPOINT_TOPLEFT, this, FRAMEPOINT_TOPLEFT, this->m_editTextInset.minX, -this->m_editTextInset.maxY, 1 ); float fontHeight = this->m_string->GetFontHeight(1); for (int32_t i = 0; i < 3; i++) { // TODO // - highlight sizing } // TODO // - clause highlight? sizing float v13 = 4.0f / (CoordinateGetAspectCompensation() * 1024.0f); float cursorWidth = NDCToDDCWidth(v13); this->m_cursor->ClearAllPoints(); this->m_cursor->SetWidth(cursorWidth); this->m_cursor->SetHeight(fontHeight); this->m_dirtyFlags |= (0x1 | 0x2 | 0x4); } void CSimpleEditBox::UpdateTextInfo() { memset(this->m_textInfo, 0, this->m_textSize * sizeof(uint32_t)); uint32_t i = 0; uint32_t v9 = 0; int32_t advance; uint32_t code; const char* text = this->m_text; while (text && *text) { QUOTEDCODE quotedCode = GxuDetermineQuotedCode(text, advance, nullptr, 0, code); if (quotedCode == CODE_HYPERLINKSTART) { v9 |= 0x80000000; } this->m_textInfo[i] = v9 | (quotedCode << 16) | static_cast(advance); if (quotedCode == CODE_HYPERLINKSTOP) { v9 &= 0x7FFFFFFF; } i += advance; text += advance; } } void CSimpleEditBox::UpdateVisibleCursor() { bool cursorVisible = this->m_cursorPos >= this->m_visiblePos && this->m_cursorPos <= this->m_visiblePos + this->m_visibleLen; if (!cursorVisible) { this->m_cursor->Hide(); return; } const char* text = this->m_password ? this->m_textHidden : this->m_text; float cursorX = 0.0f; float cursorY = 0.0f; float v8 = CSimpleTop::RoundToPixelHeight(this->m_string->GetFontHeight(1) * this->m_layoutScale); float v10 = CSimpleTop::RoundToPixelHeight(this->m_string->m_spacing * this->m_layoutScale); float v13 = (v10 + v8) / this->m_layoutScale; int32_t cursorPos = this->m_cursorPos; int32_t line = 0; while (cursorPos >= this->m_visibleLines[line + 1] && line < this->m_visibleLines.Count() - 2) { line++; cursorY -= v13; } this->m_cursor->ClearAllPoints(); // Left aligned if (this->m_string->m_styleFlags & 0x1) { float v36; if (cursorPos == this->m_visibleLines[line]) { v36 = 0.0f; } else { v36 = this->m_string->GetTextWidth( text + this->m_visibleLines[line], cursorPos - this->m_visibleLines[line] ); } cursorX = v36; if (this->m_multiline) { this->m_cursor->SetPoint( FRAMEPOINT_TOPLEFT, this->m_string, FRAMEPOINT_TOPLEFT, cursorX, cursorY, 1 ); } else { this->m_cursor->SetPoint( FRAMEPOINT_LEFT, this->m_string, FRAMEPOINT_LEFT, cursorX, cursorY, 1 ); } // Center aligned } else if (this->m_string->m_styleFlags & 0x2) { float v28; if (cursorPos == this->m_visibleLines[line]) { v28 = 0.0f; } else { v28 = this->m_string->GetTextWidth( text + this->m_visibleLines[line], cursorPos - this->m_visibleLines[line] ); } float v29 = this->m_string->GetTextWidth( text + this->m_visibleLines[line], this->m_visibleLines[line + 1] - this->m_visibleLines[line] ); cursorX = (v28 - v29) / 2.0f; if (this->m_multiline) { this->m_cursor->SetPoint( FRAMEPOINT_TOPLEFT, this->m_string, FRAMEPOINT_TOP, cursorX, cursorY, 1 ); } else { this->m_cursor->SetPoint( FRAMEPOINT_LEFT, this->m_string, FRAMEPOINT_CENTER, cursorX, cursorY, 1 ); } // Right aligned } else if (this->m_string->m_styleFlags & 0x4) { // TODO } this->m_blinkElapsedTime = 0.0f; float cursorWidth = this->m_cursor->GetWidth(); float cursorHeight = this->m_cursor->GetHeight(); this->RunOnCursorChangedScript(cursorX, cursorY, cursorWidth, cursorHeight); if (this->IsCurrentFocus()) { this->m_cursor->Show(); } else { this->m_cursor->Hide(); } } void CSimpleEditBox::UpdateVisibleHighlight() { // TODO } void CSimpleEditBox::UpdateVisibleText() { float stringWidth = this->m_string->GetWidth(); // Update password mask if (this->m_password) { int32_t textLen = SStrLen(this->m_text); this->m_textHidden = static_cast( SMemReAlloc(this->m_textHidden, textLen + 1, __FILE__, __LINE__, 0) ); memset(this->m_textHidden, '*', textLen); this->m_textHidden[textLen] = '\0'; } char* text = this->m_password ? this->m_textHidden : this->m_text; if (this->m_multiline) { // TODO } else { float v16 = 4.0f / (CoordinateGetAspectCompensation() * 1024.0f); float v17 = stringWidth - NDCToDDCWidth(v16); if (v17 <= 0.0) { v17 = stringWidth; } this->m_visibleLen = this->m_string->GetNumCharsWithinWidth( text + this->m_visiblePos, 0, v17 ); if (this->m_text == text) { this->m_visibleLen = this->GetNumToLen( this->m_visiblePos, this->m_visibleLen, 0 ); } this->m_visibleLines[0] = this->m_visiblePos; this->m_visibleLines[1] = this->m_visiblePos + this->m_visibleLen; } if (!this->m_password) { // TODO } int32_t visibleEnd = this->m_visiblePos + this->m_visibleLen; auto v42 = text[visibleEnd]; text[visibleEnd] = '\0'; this->m_string->SetText(text + this->m_visiblePos, 0); text[visibleEnd] = v42; if (this->m_multiline) { auto height = this->m_string->GetStringHeight(); if (height == 0.0f) { height = this->m_string->GetFontHeight(1); } this->SetHeight(height + this->m_editTextInset.minY + this->m_editTextInset.maxY); } }