#pragma once #include #include #include #include #include #include namespace XCEngine::UI::Editor::Widgets { inline float MeasureUIEditorTextLayoutHeight(float fontSize) { return (std::max)(0.0f, fontSize * 1.35f); } inline float ResolveUIEditorTextTop( const ::XCEngine::UI::UIRect& rect, float fontSize, float offsetY = 0.0f) { const float textHeight = MeasureUIEditorTextLayoutHeight(fontSize); const float centeredTop = rect.y + (std::max)(0.0f, std::floor((rect.height - textHeight) * 0.5f)); return centeredTop - 1.0f + offsetY; } inline ::XCEngine::UI::UIRect ResolveUIEditorTextClipRect( const ::XCEngine::UI::UIRect& rect, float fontSize) { const float extraPadding = (std::max)(2.0f, std::ceil(fontSize * 0.35f)); return ::XCEngine::UI::UIRect( rect.x, rect.y - 1.0f, rect.width, rect.height + extraPadding + 1.0f); } inline float MeasureUIEditorTextWidth( const std::string& text, float fontSize) { return fontSize * 0.56f * static_cast(::XCEngine::UI::Text::CountUtf8Codepoints(text)); } inline float MeasureUIEditorTextWidthToCaret( const std::string& text, std::size_t caretOffset, float fontSize) { return fontSize * 0.56f * static_cast(::XCEngine::UI::Text::CountUtf8CodepointsInRange( text, 0u, (std::min)(caretOffset, text.size()))); } inline std::uint64_t GetUIEditorTextCaretClockNanoseconds() { using Clock = std::chrono::steady_clock; const auto now = Clock::now().time_since_epoch(); return static_cast( std::chrono::duration_cast(now).count()); } inline bool ShouldBlinkUIEditorTextCaret(std::uint64_t blinkStartNanoseconds) { constexpr std::uint64_t kInitialVisibleDurationNanoseconds = 500000000ull; constexpr std::uint64_t kBlinkPhaseDurationNanoseconds = 500000000ull; if (blinkStartNanoseconds == 0u) { return true; } const std::uint64_t nowNanoseconds = GetUIEditorTextCaretClockNanoseconds(); if (nowNanoseconds <= blinkStartNanoseconds) { return true; } const std::uint64_t elapsedNanoseconds = nowNanoseconds - blinkStartNanoseconds; if (elapsedNanoseconds < kInitialVisibleDurationNanoseconds) { return true; } const std::uint64_t blinkElapsedNanoseconds = elapsedNanoseconds - kInitialVisibleDurationNanoseconds; return ((blinkElapsedNanoseconds / kBlinkPhaseDurationNanoseconds) % 2u) == 0u; } inline void AppendUIEditorTextCaret( ::XCEngine::UI::UIDrawList& drawList, const ::XCEngine::UI::UIRect& rect, const std::string& text, std::size_t caretOffset, std::uint64_t blinkStartNanoseconds, const ::XCEngine::UI::UIColor& color, float fontSize, float insetX, float insetY = 0.0f, float thickness = 1.0f) { if (color.a <= 0.0f || !ShouldBlinkUIEditorTextCaret(blinkStartNanoseconds)) { return; } const float caretX = (std::min)( rect.x + rect.width - 1.0f, rect.x + insetX + MeasureUIEditorTextWidthToCaret(text, caretOffset, fontSize)); const float top = ResolveUIEditorTextTop(rect, fontSize, insetY); const float bottom = top + MeasureUIEditorTextLayoutHeight(fontSize); drawList.AddLine( ::XCEngine::UI::UIPoint(caretX, top), ::XCEngine::UI::UIPoint(caretX, bottom), color, thickness); } } // namespace XCEngine::UI::Editor::Widgets