Extract XCUI text editing core and window seam headers
This commit is contained in:
52
new_editor/src/XCUIBackend/IWindowUICompositor.h
Normal file
52
new_editor/src/XCUIBackend/IWindowUICompositor.h
Normal file
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
|
||||
#include "Platform/D3D12WindowRenderer.h"
|
||||
|
||||
#include <XCEngine/RHI/RHIDevice.h>
|
||||
#include <XCEngine/RHI/RHITexture.h>
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <windows.h>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Editor {
|
||||
namespace XCUIBackend {
|
||||
|
||||
class IWindowUICompositor {
|
||||
public:
|
||||
using ConfigureFontsCallback = std::function<void()>;
|
||||
using UiRenderCallback = std::function<void()>;
|
||||
using RenderCallback = ::XCEngine::Editor::Platform::D3D12WindowRenderer::RenderCallback;
|
||||
|
||||
virtual ~IWindowUICompositor() = default;
|
||||
|
||||
virtual bool Initialize(
|
||||
HWND hwnd,
|
||||
::XCEngine::Editor::Platform::D3D12WindowRenderer& windowRenderer,
|
||||
const ConfigureFontsCallback& configureFonts) = 0;
|
||||
virtual void Shutdown() = 0;
|
||||
virtual bool HandleWindowMessage(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) = 0;
|
||||
virtual void RenderFrame(
|
||||
const float clearColor[4],
|
||||
const UiRenderCallback& renderUi,
|
||||
const RenderCallback& beforeUiRender = {},
|
||||
const RenderCallback& afterUiRender = {}) = 0;
|
||||
virtual bool CreateTextureDescriptor(
|
||||
::XCEngine::RHI::RHIDevice* device,
|
||||
::XCEngine::RHI::RHITexture* texture,
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE* outCpuHandle,
|
||||
D3D12_GPU_DESCRIPTOR_HANDLE* outGpuHandle,
|
||||
ImTextureID* outTextureId) = 0;
|
||||
virtual void FreeTextureDescriptor(
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE cpuHandle,
|
||||
D3D12_GPU_DESCRIPTOR_HANDLE gpuHandle) = 0;
|
||||
};
|
||||
|
||||
std::unique_ptr<IWindowUICompositor> CreateImGuiWindowUICompositor();
|
||||
|
||||
} // namespace XCUIBackend
|
||||
} // namespace Editor
|
||||
} // namespace XCEngine
|
||||
94
new_editor/src/XCUIBackend/ImGuiWindowUICompositor.h
Normal file
94
new_editor/src/XCUIBackend/ImGuiWindowUICompositor.h
Normal file
@@ -0,0 +1,94 @@
|
||||
#pragma once
|
||||
|
||||
#include "XCUIBackend/IEditorHostCompositor.h"
|
||||
#include "XCUIBackend/IWindowUICompositor.h"
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Editor {
|
||||
namespace XCUIBackend {
|
||||
|
||||
class ImGuiWindowUICompositor final : public IWindowUICompositor {
|
||||
public:
|
||||
explicit ImGuiWindowUICompositor(
|
||||
std::unique_ptr<IEditorHostCompositor> hostCompositor = CreateImGuiHostCompositor())
|
||||
: m_hostCompositor(std::move(hostCompositor)) {
|
||||
}
|
||||
|
||||
bool Initialize(
|
||||
HWND hwnd,
|
||||
::XCEngine::Editor::Platform::D3D12WindowRenderer& windowRenderer,
|
||||
const ConfigureFontsCallback& configureFonts) override {
|
||||
if (m_hostCompositor == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_windowRenderer = &windowRenderer;
|
||||
return m_hostCompositor->Initialize(hwnd, windowRenderer, configureFonts);
|
||||
}
|
||||
|
||||
void Shutdown() override {
|
||||
if (m_hostCompositor != nullptr) {
|
||||
m_hostCompositor->Shutdown();
|
||||
}
|
||||
m_windowRenderer = nullptr;
|
||||
}
|
||||
|
||||
bool HandleWindowMessage(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) override {
|
||||
return m_hostCompositor != nullptr &&
|
||||
m_hostCompositor->HandleWindowMessage(hwnd, message, wParam, lParam);
|
||||
}
|
||||
|
||||
void RenderFrame(
|
||||
const float clearColor[4],
|
||||
const UiRenderCallback& renderUi,
|
||||
const RenderCallback& beforeUiRender,
|
||||
const RenderCallback& afterUiRender) override {
|
||||
if (m_hostCompositor == nullptr || m_windowRenderer == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_hostCompositor->BeginFrame();
|
||||
if (renderUi) {
|
||||
renderUi();
|
||||
}
|
||||
m_hostCompositor->EndFrameAndPresent(*m_windowRenderer, clearColor, beforeUiRender, afterUiRender);
|
||||
}
|
||||
|
||||
bool CreateTextureDescriptor(
|
||||
::XCEngine::RHI::RHIDevice* device,
|
||||
::XCEngine::RHI::RHITexture* texture,
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE* outCpuHandle,
|
||||
D3D12_GPU_DESCRIPTOR_HANDLE* outGpuHandle,
|
||||
ImTextureID* outTextureId) override {
|
||||
return m_hostCompositor != nullptr &&
|
||||
m_hostCompositor->CreateTextureDescriptor(
|
||||
device,
|
||||
texture,
|
||||
outCpuHandle,
|
||||
outGpuHandle,
|
||||
outTextureId);
|
||||
}
|
||||
|
||||
void FreeTextureDescriptor(
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE cpuHandle,
|
||||
D3D12_GPU_DESCRIPTOR_HANDLE gpuHandle) override {
|
||||
if (m_hostCompositor != nullptr) {
|
||||
m_hostCompositor->FreeTextureDescriptor(cpuHandle, gpuHandle);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
::XCEngine::Editor::Platform::D3D12WindowRenderer* m_windowRenderer = nullptr;
|
||||
std::unique_ptr<IEditorHostCompositor> m_hostCompositor;
|
||||
};
|
||||
|
||||
inline std::unique_ptr<IWindowUICompositor> CreateImGuiWindowUICompositor() {
|
||||
return std::make_unique<ImGuiWindowUICompositor>();
|
||||
}
|
||||
|
||||
} // namespace XCUIBackend
|
||||
} // namespace Editor
|
||||
} // namespace XCEngine
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <XCEngine/UI/Layout/LayoutEngine.h>
|
||||
#include <XCEngine/UI/Style/StyleResolver.h>
|
||||
#include <XCEngine/UI/Style/Theme.h>
|
||||
#include <XCEngine/UI/Text/UITextEditing.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
@@ -58,6 +59,7 @@ using XCEngine::UI::UIRect;
|
||||
using XCEngine::UI::UISize;
|
||||
namespace Layout = XCEngine::UI::Layout;
|
||||
namespace Style = XCEngine::UI::Style;
|
||||
namespace UIText = XCEngine::UI::Text;
|
||||
|
||||
constexpr std::size_t kInvalidIndex = static_cast<std::size_t>(-1);
|
||||
constexpr char kViewRelativePath[] = "new_editor/resources/xcui_demo_view.xcui";
|
||||
@@ -147,77 +149,8 @@ float Clamp01(float value) {
|
||||
return (std::min)(1.0f, (std::max)(0.0f, value));
|
||||
}
|
||||
|
||||
bool IsUtf8ContinuationByte(unsigned char value) {
|
||||
return (value & 0xC0u) == 0x80u;
|
||||
}
|
||||
|
||||
std::size_t CountUtf8Codepoints(const std::string& text) {
|
||||
std::size_t count = 0u;
|
||||
for (unsigned char ch : text) {
|
||||
if (!IsUtf8ContinuationByte(ch)) {
|
||||
++count;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
float MeasureGlyphRunWidth(const std::string& text, float fontSize) {
|
||||
return fontSize * kApproximateTextWidthFactor * static_cast<float>(CountUtf8Codepoints(text));
|
||||
}
|
||||
|
||||
std::size_t AdvanceUtf8Offset(const std::string& text, std::size_t offset) {
|
||||
if (offset >= text.size()) {
|
||||
return text.size();
|
||||
}
|
||||
|
||||
++offset;
|
||||
while (offset < text.size() && IsUtf8ContinuationByte(static_cast<unsigned char>(text[offset]))) {
|
||||
++offset;
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
std::size_t RetreatUtf8Offset(const std::string& text, std::size_t offset) {
|
||||
if (offset == 0u || text.empty()) {
|
||||
return 0u;
|
||||
}
|
||||
|
||||
offset = (std::min)(offset, text.size());
|
||||
do {
|
||||
--offset;
|
||||
} while (offset > 0u && IsUtf8ContinuationByte(static_cast<unsigned char>(text[offset])));
|
||||
return offset;
|
||||
}
|
||||
|
||||
void AppendUtf8Codepoint(std::string& text, std::uint32_t codepoint) {
|
||||
if (codepoint <= 0x7Fu) {
|
||||
text.push_back(static_cast<char>(codepoint));
|
||||
return;
|
||||
}
|
||||
|
||||
if (codepoint <= 0x7FFu) {
|
||||
text.push_back(static_cast<char>(0xC0u | ((codepoint >> 6u) & 0x1Fu)));
|
||||
text.push_back(static_cast<char>(0x80u | (codepoint & 0x3Fu)));
|
||||
return;
|
||||
}
|
||||
|
||||
if (codepoint >= 0xD800u && codepoint <= 0xDFFFu) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (codepoint <= 0xFFFFu) {
|
||||
text.push_back(static_cast<char>(0xE0u | ((codepoint >> 12u) & 0x0Fu)));
|
||||
text.push_back(static_cast<char>(0x80u | ((codepoint >> 6u) & 0x3Fu)));
|
||||
text.push_back(static_cast<char>(0x80u | (codepoint & 0x3Fu)));
|
||||
return;
|
||||
}
|
||||
|
||||
if (codepoint <= 0x10FFFFu) {
|
||||
text.push_back(static_cast<char>(0xF0u | ((codepoint >> 18u) & 0x07u)));
|
||||
text.push_back(static_cast<char>(0x80u | ((codepoint >> 12u) & 0x3Fu)));
|
||||
text.push_back(static_cast<char>(0x80u | ((codepoint >> 6u) & 0x3Fu)));
|
||||
text.push_back(static_cast<char>(0x80u | (codepoint & 0x3Fu)));
|
||||
}
|
||||
return fontSize * kApproximateTextWidthFactor * static_cast<float>(UIText::CountUtf8Codepoints(text));
|
||||
}
|
||||
|
||||
bool ContainsPoint(const UIRect& rect, const UIPoint& point) {
|
||||
@@ -467,26 +400,6 @@ bool IsTextInputNode(const DemoNode& node) {
|
||||
return IsTextFieldNode(node) || IsTextAreaNode(node);
|
||||
}
|
||||
|
||||
std::vector<std::string> SplitLines(const std::string& text) {
|
||||
std::vector<std::string> lines = {};
|
||||
std::size_t lineStart = 0u;
|
||||
for (std::size_t index = 0u; index < text.size(); ++index) {
|
||||
if (text[index] != '\n') {
|
||||
continue;
|
||||
}
|
||||
|
||||
lines.push_back(text.substr(lineStart, index - lineStart));
|
||||
lineStart = index + 1u;
|
||||
}
|
||||
|
||||
lines.push_back(text.substr(lineStart));
|
||||
return lines;
|
||||
}
|
||||
|
||||
std::size_t CountTextLines(const std::string& text) {
|
||||
return SplitLines(text).size();
|
||||
}
|
||||
|
||||
std::size_t CountDecimalDigits(std::size_t value) {
|
||||
std::size_t digits = 1u;
|
||||
while (value >= 10u) {
|
||||
@@ -496,90 +409,6 @@ std::size_t CountDecimalDigits(std::size_t value) {
|
||||
return digits;
|
||||
}
|
||||
|
||||
std::size_t CountUtf8CodepointsInRange(
|
||||
const std::string& text,
|
||||
std::size_t beginOffset,
|
||||
std::size_t endOffset) {
|
||||
beginOffset = (std::min)(beginOffset, text.size());
|
||||
endOffset = (std::min)(endOffset, text.size());
|
||||
if (endOffset <= beginOffset) {
|
||||
return 0u;
|
||||
}
|
||||
|
||||
std::size_t count = 0u;
|
||||
for (std::size_t index = beginOffset; index < endOffset; ++index) {
|
||||
if (!IsUtf8ContinuationByte(static_cast<unsigned char>(text[index]))) {
|
||||
++count;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
std::size_t AdvanceUtf8Codepoints(
|
||||
const std::string& text,
|
||||
std::size_t offset,
|
||||
std::size_t codepointCount) {
|
||||
offset = (std::min)(offset, text.size());
|
||||
for (std::size_t index = 0u; index < codepointCount && offset < text.size(); ++index) {
|
||||
offset = AdvanceUtf8Offset(text, offset);
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
std::size_t FindLineStartOffset(const std::string& text, std::size_t caret) {
|
||||
caret = (std::min)(caret, text.size());
|
||||
while (caret > 0u && text[caret - 1u] != '\n') {
|
||||
--caret;
|
||||
}
|
||||
return caret;
|
||||
}
|
||||
|
||||
std::size_t FindLineEndOffset(const std::string& text, std::size_t caret) {
|
||||
caret = (std::min)(caret, text.size());
|
||||
while (caret < text.size() && text[caret] != '\n') {
|
||||
++caret;
|
||||
}
|
||||
return caret;
|
||||
}
|
||||
|
||||
std::size_t MoveCaretVertically(
|
||||
const std::string& text,
|
||||
std::size_t caret,
|
||||
int direction) {
|
||||
caret = (std::min)(caret, text.size());
|
||||
const std::size_t currentLineStart = FindLineStartOffset(text, caret);
|
||||
const std::size_t currentLineEnd = FindLineEndOffset(text, caret);
|
||||
const std::size_t column = CountUtf8CodepointsInRange(text, currentLineStart, caret);
|
||||
|
||||
if (direction < 0) {
|
||||
if (currentLineStart == 0u) {
|
||||
return caret;
|
||||
}
|
||||
|
||||
const std::size_t previousLineEnd = currentLineStart - 1u;
|
||||
const std::size_t previousLineStart = FindLineStartOffset(text, previousLineEnd);
|
||||
const std::size_t previousLineLength =
|
||||
CountUtf8CodepointsInRange(text, previousLineStart, previousLineEnd);
|
||||
return AdvanceUtf8Codepoints(
|
||||
text,
|
||||
previousLineStart,
|
||||
(std::min)(column, previousLineLength));
|
||||
}
|
||||
|
||||
if (currentLineEnd >= text.size()) {
|
||||
return caret;
|
||||
}
|
||||
|
||||
const std::size_t nextLineStart = currentLineEnd + 1u;
|
||||
const std::size_t nextLineEnd = FindLineEndOffset(text, nextLineStart);
|
||||
const std::size_t nextLineLength =
|
||||
CountUtf8CodepointsInRange(text, nextLineStart, nextLineEnd);
|
||||
return AdvanceUtf8Codepoints(
|
||||
text,
|
||||
nextLineStart,
|
||||
(std::min)(column, nextLineLength));
|
||||
}
|
||||
|
||||
bool ShouldShowTextAreaLineNumbers(const DemoNode& node) {
|
||||
bool showLineNumbers = false;
|
||||
return IsTextAreaNode(node) &&
|
||||
@@ -612,7 +441,7 @@ std::size_t FindCaretOffsetForHorizontalPosition(
|
||||
|
||||
std::size_t caret = lineStart;
|
||||
while (caret < lineEnd) {
|
||||
const std::size_t nextCaret = AdvanceUtf8Offset(text, caret);
|
||||
const std::size_t nextCaret = UIText::AdvanceUtf8Offset(text, caret);
|
||||
const float currentWidth = MeasureGlyphRunWidth(text.substr(lineStart, caret - lineStart), fontSize);
|
||||
const float nextWidth = MeasureGlyphRunWidth(text.substr(lineStart, nextCaret - lineStart), fontSize);
|
||||
if (targetX < (currentWidth + nextWidth) * 0.5f) {
|
||||
@@ -955,7 +784,7 @@ std::string BuildNodeDisplayText(
|
||||
const std::string& promptValue =
|
||||
promptIt != state.textFieldValues.end() ? promptIt->second : node.staticText;
|
||||
return "Single-line input, Enter submits, " +
|
||||
std::to_string(static_cast<unsigned long long>(CountUtf8Codepoints(promptValue))) +
|
||||
std::to_string(static_cast<unsigned long long>(UIText::CountUtf8Codepoints(promptValue))) +
|
||||
" chars";
|
||||
}
|
||||
if (node.elementKey == "notesMeta") {
|
||||
@@ -963,7 +792,7 @@ std::string BuildNodeDisplayText(
|
||||
const std::string& notesValue =
|
||||
notesIt != state.textFieldValues.end() ? notesIt->second : node.staticText;
|
||||
return "Multiline input, click caret, Tab indent, " +
|
||||
std::to_string(static_cast<unsigned long long>(CountTextLines(notesValue))) +
|
||||
std::to_string(static_cast<unsigned long long>(UIText::CountTextLines(notesValue))) +
|
||||
" lines";
|
||||
}
|
||||
if (node.elementKey == "subtitle" && stats.accentEnabled) {
|
||||
@@ -1177,7 +1006,7 @@ std::size_t FindCaretOffsetFromPoint(
|
||||
}
|
||||
|
||||
const float lineHeight = MeasureTextHeight(fontSize);
|
||||
const std::size_t lineCount = (std::max)(std::size_t(1u), CountTextLines(value));
|
||||
const std::size_t lineCount = (std::max)(std::size_t(1u), UIText::CountTextLines(value));
|
||||
const float gutterWidth = ResolveTextAreaGutterWidth(node, lineCount, fontSize);
|
||||
const float localY = point.y - (contentRect.y + kTextInputTextInset);
|
||||
const float clampedY = (std::max)(0.0f, localY);
|
||||
@@ -1187,13 +1016,13 @@ std::size_t FindCaretOffsetFromPoint(
|
||||
|
||||
std::size_t lineStart = 0u;
|
||||
for (std::size_t currentLine = 0u; currentLine < lineIndex && lineStart < value.size(); ++currentLine) {
|
||||
lineStart = FindLineEndOffset(value, lineStart);
|
||||
lineStart = UIText::FindLineEndOffset(value, lineStart);
|
||||
if (lineStart < value.size()) {
|
||||
++lineStart;
|
||||
}
|
||||
}
|
||||
|
||||
const std::size_t lineEnd = FindLineEndOffset(value, lineStart);
|
||||
const std::size_t lineEnd = UIText::FindLineEndOffset(value, lineStart);
|
||||
const float targetX = point.x - (contentRect.x + kTextInputTextInset + gutterWidth);
|
||||
return FindCaretOffsetForHorizontalPosition(value, lineStart, lineEnd, targetX, fontSize);
|
||||
}
|
||||
@@ -1231,7 +1060,7 @@ bool HandleTextInputCharacterInput(
|
||||
caret = (std::min)(caret, value.size());
|
||||
|
||||
std::string encoded = {};
|
||||
AppendUtf8Codepoint(encoded, character);
|
||||
UIText::AppendUtf8Codepoint(encoded, character);
|
||||
if (encoded.empty()) {
|
||||
return false;
|
||||
}
|
||||
@@ -1260,7 +1089,7 @@ bool HandleTextInputKeyDown(
|
||||
|
||||
if (keyCode == static_cast<std::int32_t>(KeyCode::Backspace)) {
|
||||
if (caret > 0u) {
|
||||
const std::size_t previousCaret = RetreatUtf8Offset(value, caret);
|
||||
const std::size_t previousCaret = UIText::RetreatUtf8Offset(value, caret);
|
||||
value.erase(previousCaret, caret - previousCaret);
|
||||
caret = previousCaret;
|
||||
state.lastCommandId = "demo.text.edit." + stateKey;
|
||||
@@ -1270,7 +1099,7 @@ bool HandleTextInputKeyDown(
|
||||
|
||||
if (keyCode == static_cast<std::int32_t>(KeyCode::Delete)) {
|
||||
if (caret < value.size()) {
|
||||
const std::size_t nextCaret = AdvanceUtf8Offset(value, caret);
|
||||
const std::size_t nextCaret = UIText::AdvanceUtf8Offset(value, caret);
|
||||
value.erase(caret, nextCaret - caret);
|
||||
state.lastCommandId = "demo.text.edit." + stateKey;
|
||||
}
|
||||
@@ -1278,35 +1107,35 @@ bool HandleTextInputKeyDown(
|
||||
}
|
||||
|
||||
if (keyCode == static_cast<std::int32_t>(KeyCode::Left)) {
|
||||
caret = RetreatUtf8Offset(value, caret);
|
||||
caret = UIText::RetreatUtf8Offset(value, caret);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (keyCode == static_cast<std::int32_t>(KeyCode::Right)) {
|
||||
caret = AdvanceUtf8Offset(value, caret);
|
||||
caret = UIText::AdvanceUtf8Offset(value, caret);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (keyCode == static_cast<std::int32_t>(KeyCode::Up) && IsTextAreaNode(*node)) {
|
||||
caret = MoveCaretVertically(value, caret, -1);
|
||||
caret = UIText::MoveCaretVertically(value, caret, -1);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (keyCode == static_cast<std::int32_t>(KeyCode::Down) && IsTextAreaNode(*node)) {
|
||||
caret = MoveCaretVertically(value, caret, 1);
|
||||
caret = UIText::MoveCaretVertically(value, caret, 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (keyCode == static_cast<std::int32_t>(KeyCode::Home)) {
|
||||
caret = IsTextAreaNode(*node)
|
||||
? FindLineStartOffset(value, caret)
|
||||
? UIText::FindLineStartOffset(value, caret)
|
||||
: 0u;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (keyCode == static_cast<std::int32_t>(KeyCode::End)) {
|
||||
caret = IsTextAreaNode(*node)
|
||||
? FindLineEndOffset(value, caret)
|
||||
? UIText::FindLineEndOffset(value, caret)
|
||||
: value.size();
|
||||
return true;
|
||||
}
|
||||
@@ -1329,7 +1158,7 @@ bool HandleTextInputKeyDown(
|
||||
!inputModifiers.alt &&
|
||||
!inputModifiers.super) {
|
||||
if (inputModifiers.shift) {
|
||||
const std::size_t lineStart = FindLineStartOffset(value, caret);
|
||||
const std::size_t lineStart = UIText::FindLineStartOffset(value, caret);
|
||||
std::size_t removed = 0u;
|
||||
while (removed < kTextAreaTabWidth &&
|
||||
lineStart + removed < value.size() &&
|
||||
@@ -1539,7 +1368,7 @@ UISize MeasureNode(RuntimeBuildContext& state, std::size_t index) {
|
||||
float minWidth = 220.0f;
|
||||
TryParseFloat(GetNodeAttribute(node, "min-width"), minWidth);
|
||||
const std::string probeText = value.empty() ? placeholder : value;
|
||||
const std::vector<std::string> lines = SplitLines(probeText);
|
||||
const std::vector<std::string> lines = UIText::SplitLines(probeText);
|
||||
float widestLine = 0.0f;
|
||||
for (const std::string& line : lines) {
|
||||
widestLine = (std::max)(
|
||||
@@ -1881,7 +1710,7 @@ void DrawTextAreaNode(RuntimeBuildContext& state, const DemoNode& node, UIDrawLi
|
||||
const std::string value = ResolveTextInputValue(state, node);
|
||||
const std::string placeholder = GetNodeAttribute(node, "placeholder");
|
||||
const bool showingPlaceholder = value.empty() && !placeholder.empty();
|
||||
const std::vector<std::string> lines = SplitLines(showingPlaceholder ? placeholder : value);
|
||||
const std::vector<std::string> lines = UIText::SplitLines(showingPlaceholder ? placeholder : value);
|
||||
const float gutterWidth = ResolveTextAreaGutterWidth(node, (std::max)(std::size_t(1u), lines.size()), fontSize);
|
||||
const Color textColor = showingPlaceholder
|
||||
? ResolveColorToken(state.activeTheme, "color.text.placeholder", Color(0.49f, 0.56f, 0.64f, 1.0f))
|
||||
@@ -1931,7 +1760,7 @@ void DrawTextAreaNode(RuntimeBuildContext& state, const DemoNode& node, UIDrawLi
|
||||
}
|
||||
|
||||
const std::size_t caret = ResolveTextInputCaret(state, node);
|
||||
const std::size_t lineStart = FindLineStartOffset(value, caret);
|
||||
const std::size_t lineStart = UIText::FindLineStartOffset(value, caret);
|
||||
std::size_t lineIndex = 0u;
|
||||
for (std::size_t scan = 0u; scan < lineStart && scan < value.size(); ++scan) {
|
||||
if (value[scan] == '\n') {
|
||||
|
||||
Reference in New Issue
Block a user