Files
XCEngine/new_editor/src/XCUIBackend/NativeXCUIPanelCanvasHost.h

292 lines
9.8 KiB
C++

#pragma once
#include "XCUIBackend/XCUIPanelCanvasHost.h"
#include <algorithm>
#include <memory>
#include <string>
#include <utility>
namespace XCEngine {
namespace Editor {
namespace XCUIBackend {
namespace detail {
inline std::string CopyCanvasLabel(const char* text) {
return text != nullptr ? std::string(text) : std::string();
}
inline ::XCEngine::UI::UIColor NativeCanvasPlaceholderFillColor() {
return ::XCEngine::UI::UIColor(18.0f / 255.0f, 24.0f / 255.0f, 32.0f / 255.0f, 1.0f);
}
inline ::XCEngine::UI::UIColor NativeCanvasPlaceholderStrokeColor() {
return ::XCEngine::UI::UIColor(54.0f / 255.0f, 72.0f / 255.0f, 94.0f / 255.0f, 1.0f);
}
inline ::XCEngine::UI::UIColor NativeCanvasPrimaryTextColor() {
return ::XCEngine::UI::UIColor(191.0f / 255.0f, 205.0f / 255.0f, 224.0f / 255.0f, 1.0f);
}
inline ::XCEngine::UI::UIColor NativeCanvasSecondaryTextColor() {
return ::XCEngine::UI::UIColor(132.0f / 255.0f, 147.0f / 255.0f, 170.0f / 255.0f, 1.0f);
}
inline XCUIPanelCanvasSession NormalizeNativeCanvasSession(
const XCUIPanelCanvasRequest& request,
const XCUIPanelCanvasSession* configuredSession) {
if (configuredSession == nullptr) {
return BuildPassiveXCUIPanelCanvasSession(request);
}
XCUIPanelCanvasSession session = *configuredSession;
const float fallbackHostHeight = request.height > 0.0f ? request.height : 0.0f;
if (session.hostRect.height <= 0.0f && fallbackHostHeight > 0.0f) {
session.hostRect.height = fallbackHostHeight;
}
if (session.canvasRect.width <= 0.0f || session.canvasRect.height <= 0.0f) {
const float topInset = request.topInset > 0.0f ? request.topInset : 0.0f;
const float clampedTopInset =
session.hostRect.height > 0.0f
? (std::min)(topInset, session.hostRect.height)
: 0.0f;
session.canvasRect = ::XCEngine::UI::UIRect(
session.hostRect.x,
session.hostRect.y + clampedTopInset,
session.hostRect.width,
(std::max)(0.0f, session.hostRect.height - clampedTopInset));
}
session.validCanvas =
session.canvasRect.width > 1.0f &&
session.canvasRect.height > 1.0f;
if (!session.validCanvas) {
session.hovered = false;
}
return session;
}
inline void RecordPlaceholder(
::XCEngine::UI::UIDrawList& drawList,
const ::XCEngine::UI::UIRect& rect,
const std::string& title,
const std::string& subtitle) {
if (rect.width <= 1.0f || rect.height <= 1.0f) {
return;
}
drawList.AddFilledRect(rect, NativeCanvasPlaceholderFillColor(), 8.0f);
drawList.AddRectOutline(rect, NativeCanvasPlaceholderStrokeColor(), 1.0f, 8.0f);
if (!title.empty()) {
drawList.AddText(
::XCEngine::UI::UIPoint(rect.x + 14.0f, rect.y + 14.0f),
title,
NativeCanvasPrimaryTextColor());
}
if (!subtitle.empty()) {
drawList.AddText(
::XCEngine::UI::UIPoint(rect.x + 14.0f, rect.y + 36.0f),
subtitle,
NativeCanvasSecondaryTextColor());
}
}
inline void RecordBadge(
::XCEngine::UI::UIDrawList& drawList,
const ::XCEngine::UI::UIRect& canvasRect,
const std::string& title,
const std::string& subtitle) {
if (title.empty()) {
return;
}
const ::XCEngine::UI::UIRect badgeRect(
canvasRect.x + 10.0f,
canvasRect.y + 10.0f,
290.0f,
42.0f);
drawList.AddFilledRect(
badgeRect,
::XCEngine::UI::UIColor(16.0f / 255.0f, 22.0f / 255.0f, 30.0f / 255.0f, 216.0f / 255.0f),
8.0f);
drawList.AddRectOutline(badgeRect, NativeCanvasPlaceholderStrokeColor(), 1.0f, 8.0f);
drawList.AddText(
::XCEngine::UI::UIPoint(badgeRect.x + 10.0f, badgeRect.y + 8.0f),
title,
NativeCanvasPrimaryTextColor());
if (!subtitle.empty()) {
drawList.AddText(
::XCEngine::UI::UIPoint(badgeRect.x + 10.0f, badgeRect.y + 24.0f),
subtitle,
NativeCanvasSecondaryTextColor());
}
}
} // namespace detail
class NativeXCUIPanelCanvasHost final : public IXCUIPanelCanvasHost {
public:
const char* GetDebugName() const override {
return "NativeXCUIPanelCanvasHost";
}
void SetCanvasSession(const XCUIPanelCanvasSession& session) {
m_configuredSession = session;
m_hasConfiguredSession = true;
}
void ClearCanvasSession() {
m_configuredSession = {};
m_hasConfiguredSession = false;
}
bool HasConfiguredSession() const {
return m_hasConfiguredSession;
}
const XCUIPanelCanvasSession& GetConfiguredSession() const {
return m_configuredSession;
}
XCUIPanelCanvasSession BeginCanvas(const XCUIPanelCanvasRequest& request) override {
m_currentFrame = {};
m_currentFrame.childId = ResolveXCUIPanelCanvasChildId(request, "NativeXCUIPanelCanvasHost");
m_currentFrame.session = detail::NormalizeNativeCanvasSession(
request,
m_hasConfiguredSession ? &m_configuredSession : nullptr);
m_currentFrame.bordered = request.bordered;
m_currentFrame.drawPreviewFrame = request.drawPreviewFrame;
m_currentFrame.showingSurfaceImage = request.showSurfaceImage && request.surfaceImage.IsValid();
m_currentFrame.placeholderTitle = detail::CopyCanvasLabel(request.placeholderTitle);
m_currentFrame.placeholderSubtitle = detail::CopyCanvasLabel(request.placeholderSubtitle);
m_currentFrame.badgeTitle = detail::CopyCanvasLabel(request.badgeTitle);
m_currentFrame.badgeSubtitle = detail::CopyCanvasLabel(request.badgeSubtitle);
m_currentFrame.surfaceImage = request.surfaceImage;
m_overlayDrawList = nullptr;
m_clipDepth = 0u;
if (m_currentFrame.session.validCanvas) {
const bool shouldRecordPlaceholder =
!m_currentFrame.showingSurfaceImage &&
(!m_currentFrame.placeholderTitle.empty() || !m_currentFrame.placeholderSubtitle.empty());
if (shouldRecordPlaceholder) {
::XCEngine::UI::UIDrawList& drawList = EnsureOverlayDrawList();
detail::RecordPlaceholder(
drawList,
m_currentFrame.session.canvasRect,
m_currentFrame.placeholderTitle,
m_currentFrame.placeholderSubtitle);
}
if (m_currentFrame.showingSurfaceImage) {
EnsureOverlayDrawList().AddImage(
m_currentFrame.session.canvasRect,
m_currentFrame.surfaceImage.texture,
::XCEngine::UI::UIColor(1.0f, 1.0f, 1.0f, 1.0f),
m_currentFrame.surfaceImage.uvMin,
m_currentFrame.surfaceImage.uvMax);
}
if (request.drawPreviewFrame) {
DrawOutlineRect(
m_currentFrame.session.canvasRect,
detail::NativeCanvasPlaceholderStrokeColor(),
1.0f,
8.0f);
}
if (!m_currentFrame.badgeTitle.empty()) {
::XCEngine::UI::UIDrawList& drawList = EnsureOverlayDrawList();
detail::RecordBadge(
drawList,
m_currentFrame.session.canvasRect,
m_currentFrame.badgeTitle,
m_currentFrame.badgeSubtitle);
}
}
return m_currentFrame.session;
}
void DrawFilledRect(
const ::XCEngine::UI::UIRect& rect,
const ::XCEngine::UI::UIColor& color,
float rounding = 0.0f) override {
if (rect.width <= 0.0f || rect.height <= 0.0f) {
return;
}
EnsureOverlayDrawList().AddFilledRect(rect, color, rounding);
}
void DrawOutlineRect(
const ::XCEngine::UI::UIRect& rect,
const ::XCEngine::UI::UIColor& color,
float thickness = 1.0f,
float rounding = 0.0f) override {
if (rect.width <= 0.0f || rect.height <= 0.0f) {
return;
}
EnsureOverlayDrawList().AddRectOutline(rect, color, thickness, rounding);
}
void DrawText(
const ::XCEngine::UI::UIPoint& position,
std::string_view text,
const ::XCEngine::UI::UIColor& color,
float fontSize = 0.0f) override {
if (text.empty()) {
return;
}
EnsureOverlayDrawList().AddText(position, std::string(text), color, fontSize);
}
void EndCanvas() override {
if (m_overlayDrawList != nullptr) {
while (m_clipDepth > 0u) {
m_overlayDrawList->PopClipRect();
--m_clipDepth;
}
}
m_overlayDrawList = nullptr;
}
bool TryGetLatestFrameSnapshot(XCUIPanelCanvasFrameSnapshot& outSnapshot) const override {
outSnapshot = m_currentFrame;
return !outSnapshot.childId.empty();
}
private:
::XCEngine::UI::UIDrawList& EnsureOverlayDrawList() {
if (m_overlayDrawList == nullptr) {
::XCEngine::UI::UIDrawList& drawList =
m_currentFrame.overlayDrawData.EmplaceDrawList(m_currentFrame.childId + ".overlay");
drawList.PushClipRect(m_currentFrame.session.canvasRect, true);
m_overlayDrawList = &drawList;
m_clipDepth = 1u;
}
return *m_overlayDrawList;
}
bool m_hasConfiguredSession = false;
XCUIPanelCanvasSession m_configuredSession = {};
XCUIPanelCanvasFrameSnapshot m_currentFrame = {};
::XCEngine::UI::UIDrawList* m_overlayDrawList = nullptr;
std::size_t m_clipDepth = 0u;
};
inline std::unique_ptr<IXCUIPanelCanvasHost> CreateNativeXCUIPanelCanvasHost() {
return std::make_unique<NativeXCUIPanelCanvasHost>();
}
} // namespace XCUIBackend
} // namespace Editor
} // namespace XCEngine