Files
XCEngine/new_editor/include/XCEditor/Widgets/UIEditorTextLayout.h

114 lines
3.5 KiB
C
Raw Normal View History

2026-04-08 02:52:28 +08:00
#pragma once
#include <XCEngine/UI/Text/UITextEditing.h>
2026-04-08 02:52:28 +08:00
#include <XCEngine/UI/DrawData.h>
#include <algorithm>
#include <chrono>
2026-04-08 02:52:28 +08:00
#include <cmath>
#include <string>
2026-04-08 02:52:28 +08:00
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<float>(::XCEngine::UI::Text::CountUtf8Codepoints(text));
}
inline float MeasureUIEditorTextWidthToCaret(
const std::string& text,
std::size_t caretOffset,
float fontSize) {
return fontSize * 0.56f *
static_cast<float>(::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::uint64_t>(
std::chrono::duration_cast<std::chrono::nanoseconds>(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);
}
2026-04-08 02:52:28 +08:00
} // namespace XCEngine::UI::Editor::Widgets