292 lines
9.8 KiB
C++
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
|