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

145 lines
4.6 KiB
C
Raw Normal View History

2026-04-08 02:52:28 +08:00
#pragma once
2026-04-23 01:43:23 +08:00
#include <XCEditor/Foundation/UIEditorTextMeasurement.h>
#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,
2026-04-23 01:43:23 +08:00
float fontSize,
const ::XCEngine::UI::Editor::UIEditorTextMeasurer* textMeasurer = nullptr) {
if (textMeasurer != nullptr &&
!text.empty() &&
fontSize > 0.0f) {
return textMeasurer->MeasureTextWidth(
::XCEngine::UI::Editor::UIEditorTextMeasureRequest{
text,
fontSize
});
}
return fontSize * 0.56f *
static_cast<float>(::XCEngine::UI::Text::CountUtf8Codepoints(text));
}
inline float MeasureUIEditorTextWidthToCaret(
const std::string& text,
std::size_t caretOffset,
2026-04-23 01:43:23 +08:00
float fontSize,
const ::XCEngine::UI::Editor::UIEditorTextMeasurer* textMeasurer = nullptr) {
const std::size_t clampedCaretOffset = (std::min)(caretOffset, text.size());
if (textMeasurer != nullptr &&
clampedCaretOffset > 0u &&
fontSize > 0.0f) {
return textMeasurer->MeasureTextWidth(
::XCEngine::UI::Editor::UIEditorTextMeasureRequest{
std::string_view(text.data(), clampedCaretOffset),
fontSize
});
}
return fontSize * 0.56f *
static_cast<float>(::XCEngine::UI::Text::CountUtf8CodepointsInRange(
text,
0u,
2026-04-23 01:43:23 +08:00
clampedCaretOffset));
}
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,
2026-04-23 01:43:23 +08:00
float thickness = 1.0f,
const ::XCEngine::UI::Editor::UIEditorTextMeasurer* textMeasurer = nullptr) {
if (color.a <= 0.0f || !ShouldBlinkUIEditorTextCaret(blinkStartNanoseconds)) {
return;
}
const float caretX = (std::min)(
rect.x + rect.width - 1.0f,
2026-04-23 01:43:23 +08:00
rect.x + insetX +
MeasureUIEditorTextWidthToCaret(
text,
caretOffset,
fontSize,
textMeasurer));
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