Unlink XCNewEditor default shell from ImGui compat slice
This commit is contained in:
126
engine/include/XCEngine/UI/Widgets/UIEditorPanelChrome.h
Normal file
126
engine/include/XCEngine/UI/Widgets/UIEditorPanelChrome.h
Normal file
@@ -0,0 +1,126 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEngine/UI/DrawData.h>
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace UI {
|
||||
namespace Widgets {
|
||||
|
||||
struct UIEditorPanelChromeState {
|
||||
bool active = false;
|
||||
bool hovered = false;
|
||||
};
|
||||
|
||||
struct UIEditorPanelChromeText {
|
||||
std::string_view title = {};
|
||||
std::string_view subtitle = {};
|
||||
std::string_view footer = {};
|
||||
};
|
||||
|
||||
struct UIEditorPanelChromeMetrics {
|
||||
float cornerRounding = 18.0f;
|
||||
float headerHeight = 42.0f;
|
||||
float titleInsetX = 16.0f;
|
||||
float titleInsetY = 12.0f;
|
||||
float subtitleInsetY = 28.0f;
|
||||
float footerInsetX = 16.0f;
|
||||
float footerInsetBottom = 18.0f;
|
||||
float activeBorderThickness = 2.0f;
|
||||
float inactiveBorderThickness = 1.0f;
|
||||
};
|
||||
|
||||
struct UIEditorPanelChromePalette {
|
||||
UIColor surfaceColor = UIColor(9.0f / 255.0f, 13.0f / 255.0f, 18.0f / 255.0f, 212.0f / 255.0f);
|
||||
UIColor borderColor = UIColor(53.0f / 255.0f, 72.0f / 255.0f, 96.0f / 255.0f, 1.0f);
|
||||
UIColor accentColor = UIColor(84.0f / 255.0f, 176.0f / 255.0f, 244.0f / 255.0f, 1.0f);
|
||||
UIColor hoveredAccentColor = UIColor(1.0f, 206.0f / 255.0f, 112.0f / 255.0f, 1.0f);
|
||||
UIColor headerColor = UIColor(13.0f / 255.0f, 20.0f / 255.0f, 28.0f / 255.0f, 242.0f / 255.0f);
|
||||
UIColor textPrimary = UIColor(232.0f / 255.0f, 238.0f / 255.0f, 246.0f / 255.0f, 1.0f);
|
||||
UIColor textSecondary = UIColor(150.0f / 255.0f, 164.0f / 255.0f, 184.0f / 255.0f, 1.0f);
|
||||
UIColor textMuted = UIColor(108.0f / 255.0f, 123.0f / 255.0f, 145.0f / 255.0f, 1.0f);
|
||||
};
|
||||
|
||||
inline UIRect BuildUIEditorPanelChromeHeaderRect(
|
||||
const UIRect& panelRect,
|
||||
const UIEditorPanelChromeMetrics& metrics = {}) {
|
||||
return UIRect(
|
||||
panelRect.x,
|
||||
panelRect.y,
|
||||
panelRect.width,
|
||||
metrics.headerHeight);
|
||||
}
|
||||
|
||||
inline UIColor ResolveUIEditorPanelChromeBorderColor(
|
||||
const UIEditorPanelChromeState& state,
|
||||
const UIEditorPanelChromePalette& palette = {}) {
|
||||
if (state.active) {
|
||||
return palette.accentColor;
|
||||
}
|
||||
|
||||
if (state.hovered) {
|
||||
return palette.hoveredAccentColor;
|
||||
}
|
||||
|
||||
return palette.borderColor;
|
||||
}
|
||||
|
||||
inline float ResolveUIEditorPanelChromeBorderThickness(
|
||||
const UIEditorPanelChromeState& state,
|
||||
const UIEditorPanelChromeMetrics& metrics = {}) {
|
||||
return state.active
|
||||
? metrics.activeBorderThickness
|
||||
: metrics.inactiveBorderThickness;
|
||||
}
|
||||
|
||||
inline void AppendUIEditorPanelChromeBackground(
|
||||
UIDrawList& drawList,
|
||||
const UIRect& panelRect,
|
||||
const UIEditorPanelChromeState& state,
|
||||
const UIEditorPanelChromePalette& palette = {},
|
||||
const UIEditorPanelChromeMetrics& metrics = {}) {
|
||||
drawList.AddFilledRect(panelRect, palette.surfaceColor, metrics.cornerRounding);
|
||||
drawList.AddRectOutline(
|
||||
panelRect,
|
||||
ResolveUIEditorPanelChromeBorderColor(state, palette),
|
||||
ResolveUIEditorPanelChromeBorderThickness(state, metrics),
|
||||
metrics.cornerRounding);
|
||||
drawList.AddFilledRect(
|
||||
BuildUIEditorPanelChromeHeaderRect(panelRect, metrics),
|
||||
palette.headerColor,
|
||||
metrics.cornerRounding);
|
||||
}
|
||||
|
||||
inline void AppendUIEditorPanelChromeForeground(
|
||||
UIDrawList& drawList,
|
||||
const UIRect& panelRect,
|
||||
const UIEditorPanelChromeText& text,
|
||||
const UIEditorPanelChromePalette& palette = {},
|
||||
const UIEditorPanelChromeMetrics& metrics = {}) {
|
||||
if (!text.title.empty()) {
|
||||
drawList.AddText(
|
||||
UIPoint(panelRect.x + metrics.titleInsetX, panelRect.y + metrics.titleInsetY),
|
||||
std::string(text.title),
|
||||
palette.textPrimary);
|
||||
}
|
||||
|
||||
if (!text.subtitle.empty()) {
|
||||
drawList.AddText(
|
||||
UIPoint(panelRect.x + metrics.titleInsetX, panelRect.y + metrics.subtitleInsetY),
|
||||
std::string(text.subtitle),
|
||||
palette.textSecondary);
|
||||
}
|
||||
|
||||
if (!text.footer.empty()) {
|
||||
drawList.AddText(
|
||||
UIPoint(panelRect.x + metrics.footerInsetX, panelRect.y + panelRect.height - metrics.footerInsetBottom),
|
||||
std::string(text.footer),
|
||||
palette.textMuted);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Widgets
|
||||
} // namespace UI
|
||||
} // namespace XCEngine
|
||||
167
engine/include/XCEngine/UI/Widgets/UIFlatHierarchyHelpers.h
Normal file
167
engine/include/XCEngine/UI/Widgets/UIFlatHierarchyHelpers.h
Normal file
@@ -0,0 +1,167 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <span>
|
||||
#include <utility>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace UI {
|
||||
namespace Widgets {
|
||||
|
||||
inline constexpr std::size_t kInvalidUIFlatHierarchyItemOffset = static_cast<std::size_t>(-1);
|
||||
|
||||
namespace Detail {
|
||||
|
||||
template <typename ResolveDepthFn>
|
||||
float ResolveUIFlatHierarchyDepth(
|
||||
std::span<const std::size_t> itemIndices,
|
||||
std::size_t itemOffset,
|
||||
ResolveDepthFn&& resolveDepth) {
|
||||
if (itemOffset >= itemIndices.size()) {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
const float depth = static_cast<float>(resolveDepth(itemIndices[itemOffset]));
|
||||
return depth > 0.0f ? depth : 0.0f;
|
||||
}
|
||||
|
||||
} // namespace Detail
|
||||
|
||||
template <typename ResolveDepthFn>
|
||||
bool UIFlatHierarchyHasChildren(
|
||||
std::span<const std::size_t> itemIndices,
|
||||
std::size_t itemOffset,
|
||||
ResolveDepthFn&& resolveDepth) {
|
||||
if (itemOffset >= itemIndices.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const float itemDepth = Detail::ResolveUIFlatHierarchyDepth(
|
||||
itemIndices,
|
||||
itemOffset,
|
||||
std::forward<ResolveDepthFn>(resolveDepth));
|
||||
for (std::size_t candidateOffset = itemOffset + 1u;
|
||||
candidateOffset < itemIndices.size();
|
||||
++candidateOffset) {
|
||||
const float candidateDepth = Detail::ResolveUIFlatHierarchyDepth(
|
||||
itemIndices,
|
||||
candidateOffset,
|
||||
std::forward<ResolveDepthFn>(resolveDepth));
|
||||
if (candidateDepth <= itemDepth) {
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename ResolveDepthFn>
|
||||
std::size_t UIFlatHierarchyFindParentOffset(
|
||||
std::span<const std::size_t> itemIndices,
|
||||
std::size_t itemOffset,
|
||||
ResolveDepthFn&& resolveDepth) {
|
||||
if (itemOffset >= itemIndices.size()) {
|
||||
return kInvalidUIFlatHierarchyItemOffset;
|
||||
}
|
||||
|
||||
const float itemDepth = Detail::ResolveUIFlatHierarchyDepth(
|
||||
itemIndices,
|
||||
itemOffset,
|
||||
std::forward<ResolveDepthFn>(resolveDepth));
|
||||
if (itemDepth <= 0.0f) {
|
||||
return kInvalidUIFlatHierarchyItemOffset;
|
||||
}
|
||||
|
||||
for (std::size_t candidateOffset = itemOffset; candidateOffset > 0u; --candidateOffset) {
|
||||
const std::size_t previousOffset = candidateOffset - 1u;
|
||||
const float previousDepth = Detail::ResolveUIFlatHierarchyDepth(
|
||||
itemIndices,
|
||||
previousOffset,
|
||||
std::forward<ResolveDepthFn>(resolveDepth));
|
||||
if (previousDepth < itemDepth) {
|
||||
return previousOffset;
|
||||
}
|
||||
}
|
||||
|
||||
return kInvalidUIFlatHierarchyItemOffset;
|
||||
}
|
||||
|
||||
template <typename ResolveDepthFn, typename IsExpandedFn>
|
||||
bool UIFlatHierarchyIsVisible(
|
||||
std::span<const std::size_t> itemIndices,
|
||||
std::size_t itemOffset,
|
||||
ResolveDepthFn&& resolveDepth,
|
||||
IsExpandedFn&& isExpanded) {
|
||||
if (itemOffset >= itemIndices.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
float requiredAncestorDepth = Detail::ResolveUIFlatHierarchyDepth(
|
||||
itemIndices,
|
||||
itemOffset,
|
||||
std::forward<ResolveDepthFn>(resolveDepth));
|
||||
if (requiredAncestorDepth <= 0.0f) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (std::size_t candidateOffset = itemOffset;
|
||||
candidateOffset > 0u && requiredAncestorDepth > 0.0f;
|
||||
--candidateOffset) {
|
||||
const std::size_t previousOffset = candidateOffset - 1u;
|
||||
const float previousDepth = Detail::ResolveUIFlatHierarchyDepth(
|
||||
itemIndices,
|
||||
previousOffset,
|
||||
std::forward<ResolveDepthFn>(resolveDepth));
|
||||
if (previousDepth < requiredAncestorDepth) {
|
||||
if (!isExpanded(itemIndices[previousOffset])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
requiredAncestorDepth = previousDepth;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename ResolveDepthFn, typename IsVisibleFn>
|
||||
std::size_t UIFlatHierarchyFindFirstVisibleChildOffset(
|
||||
std::span<const std::size_t> itemIndices,
|
||||
std::size_t itemOffset,
|
||||
ResolveDepthFn&& resolveDepth,
|
||||
IsVisibleFn&& isVisible) {
|
||||
if (!UIFlatHierarchyHasChildren(
|
||||
itemIndices,
|
||||
itemOffset,
|
||||
std::forward<ResolveDepthFn>(resolveDepth))) {
|
||||
return kInvalidUIFlatHierarchyItemOffset;
|
||||
}
|
||||
|
||||
const float itemDepth = Detail::ResolveUIFlatHierarchyDepth(
|
||||
itemIndices,
|
||||
itemOffset,
|
||||
std::forward<ResolveDepthFn>(resolveDepth));
|
||||
for (std::size_t candidateOffset = itemOffset + 1u;
|
||||
candidateOffset < itemIndices.size();
|
||||
++candidateOffset) {
|
||||
const float candidateDepth = Detail::ResolveUIFlatHierarchyDepth(
|
||||
itemIndices,
|
||||
candidateOffset,
|
||||
std::forward<ResolveDepthFn>(resolveDepth));
|
||||
if (candidateDepth <= itemDepth) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (isVisible(itemIndices[candidateOffset])) {
|
||||
return candidateOffset;
|
||||
}
|
||||
}
|
||||
|
||||
return kInvalidUIFlatHierarchyItemOffset;
|
||||
}
|
||||
|
||||
} // namespace Widgets
|
||||
} // namespace UI
|
||||
} // namespace XCEngine
|
||||
Reference in New Issue
Block a user