#pragma once #include #include #include #include #include #include #include namespace Game::Object { class UITextBox : public Entity { public: UITextBox(const std::string& name, std::shared_ptr background, std::shared_ptr font, const Transform& transform, float x = 0.f, float y = 0.f); ~UITextBox() override = default; void start() override; void update(float deltaTime) override; // Custom render to show both background and text void render(Game::Renderer::Renderer* renderer, Game::Renderer::RendererConfig config) override; void setText(const std::string& text); std::string getText() const; void setPlaceholder(const std::string& placeholder) { mPlaceholder = placeholder; mLastRenderedText.clear(); } // Insert UTF-8 text at the current cursor position (delivered from Input queue) void insertText(const std::string& utf8); void setMaxLength(size_t maxLen) { mMaxLength = maxLen; } void setPasswordMode(bool enable) { mPasswordMode = enable; } void setOnFocus(void* fn) { mOnFocus = fn; } void setPosition(float x, float y) { mX = x; mY = y; } std::pair getPosition() const { return {mX, mY}; } bool isFocused() const { return mIsFocused; } private: std::shared_ptr mBackground; // Background texture for the textbox std::shared_ptr mFont; // Font used to render text float mX, mY; std::string mText; size_t mCursorIndex = 0; float mCursorBlinkTimer = 0.f; bool mShowCursor = true; size_t mMaxLength = 1024; bool mPasswordMode = false; bool mIsFocused = false; void* mOnFocus = nullptr; // optional function pointer std::string mLastRenderedText; // to avoid rebuilding font unnecessarily std::string mPlaceholder = ""; float mReservedPlaceholderWidth = 0.f; // Reserved pixel width for placeholder to avoid layout shifts std::mutex mRenderMutex; // Protects mLastRenderedText and mReservedPlaceholderWidth from main-thread updates }; }