feat(new_editor): add project panel and polish dock chrome
This commit is contained in:
@@ -31,6 +31,7 @@ enum class UIDrawCommandType : std::uint8_t {
|
|||||||
RectOutline,
|
RectOutline,
|
||||||
FilledRectLinearGradient,
|
FilledRectLinearGradient,
|
||||||
Line,
|
Line,
|
||||||
|
FilledTriangle,
|
||||||
FilledCircle,
|
FilledCircle,
|
||||||
CircleOutline,
|
CircleOutline,
|
||||||
Text,
|
Text,
|
||||||
@@ -149,6 +150,20 @@ public:
|
|||||||
return AddCommand(std::move(command));
|
return AddCommand(std::move(command));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UIDrawCommand& AddFilledTriangle(
|
||||||
|
const UIPoint& a,
|
||||||
|
const UIPoint& b,
|
||||||
|
const UIPoint& c,
|
||||||
|
const UIColor& color) {
|
||||||
|
UIDrawCommand command = {};
|
||||||
|
command.type = UIDrawCommandType::FilledTriangle;
|
||||||
|
command.position = a;
|
||||||
|
command.uvMin = b;
|
||||||
|
command.uvMax = c;
|
||||||
|
command.color = color;
|
||||||
|
return AddCommand(std::move(command));
|
||||||
|
}
|
||||||
|
|
||||||
UIDrawCommand& AddFilledCircle(
|
UIDrawCommand& AddFilledCircle(
|
||||||
const UIPoint& center,
|
const UIPoint& center,
|
||||||
float radius,
|
float radius,
|
||||||
|
|||||||
@@ -147,6 +147,7 @@ if(XCENGINE_BUILD_XCUI_EDITOR_APP)
|
|||||||
add_executable(XCUIEditorApp WIN32
|
add_executable(XCUIEditorApp WIN32
|
||||||
app/main.cpp
|
app/main.cpp
|
||||||
app/Application.cpp
|
app/Application.cpp
|
||||||
|
app/Panels/ProductProjectPanel.cpp
|
||||||
app/Shell/ProductShellAsset.cpp
|
app/Shell/ProductShellAsset.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -38,6 +38,20 @@ D2D1_RECT_F ToD2DRect(const ::XCEngine::UI::UIRect& rect, float dpiScale) {
|
|||||||
return D2D1::RectF(left, top, right, bottom);
|
return D2D1::RectF(left, top, right, bottom);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
D2D1_POINT_2F ToD2DPoint(
|
||||||
|
const ::XCEngine::UI::UIPoint& point,
|
||||||
|
float dpiScale,
|
||||||
|
float pixelOffset = 0.0f) {
|
||||||
|
return D2D1::Point2F(
|
||||||
|
SnapToPixel(point.x, dpiScale) + pixelOffset,
|
||||||
|
SnapToPixel(point.y, dpiScale) + pixelOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
float ResolveStrokePixelOffset(float thickness) {
|
||||||
|
const float roundedThickness = std::round(thickness);
|
||||||
|
return std::fmod(roundedThickness, 2.0f) == 1.0f ? 0.5f : 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
bool NativeRenderer::Initialize(HWND hwnd) {
|
bool NativeRenderer::Initialize(HWND hwnd) {
|
||||||
@@ -458,6 +472,51 @@ void NativeRenderer::RenderCommand(
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case ::XCEngine::UI::UIDrawCommandType::FilledRectLinearGradient: {
|
||||||
|
const D2D1_RECT_F rect = ToD2DRect(command.rect, dpiScale);
|
||||||
|
const float rounding = command.rounding > 0.0f ? command.rounding * dpiScale : 0.0f;
|
||||||
|
|
||||||
|
const D2D1_GRADIENT_STOP stops[2] = {
|
||||||
|
D2D1::GradientStop(0.0f, ToD2DColor(command.color)),
|
||||||
|
D2D1::GradientStop(1.0f, ToD2DColor(command.secondaryColor))
|
||||||
|
};
|
||||||
|
|
||||||
|
Microsoft::WRL::ComPtr<ID2D1GradientStopCollection> stopCollection;
|
||||||
|
HRESULT hr = renderTarget.CreateGradientStopCollection(
|
||||||
|
stops,
|
||||||
|
2u,
|
||||||
|
stopCollection.ReleaseAndGetAddressOf());
|
||||||
|
if (FAILED(hr) || !stopCollection) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const D2D1_POINT_2F startPoint =
|
||||||
|
command.gradientDirection == ::XCEngine::UI::UILinearGradientDirection::Vertical
|
||||||
|
? D2D1::Point2F((rect.left + rect.right) * 0.5f, rect.top)
|
||||||
|
: D2D1::Point2F(rect.left, (rect.top + rect.bottom) * 0.5f);
|
||||||
|
const D2D1_POINT_2F endPoint =
|
||||||
|
command.gradientDirection == ::XCEngine::UI::UILinearGradientDirection::Vertical
|
||||||
|
? D2D1::Point2F((rect.left + rect.right) * 0.5f, rect.bottom)
|
||||||
|
: D2D1::Point2F(rect.right, (rect.top + rect.bottom) * 0.5f);
|
||||||
|
|
||||||
|
Microsoft::WRL::ComPtr<ID2D1LinearGradientBrush> gradientBrush;
|
||||||
|
hr = renderTarget.CreateLinearGradientBrush(
|
||||||
|
D2D1::LinearGradientBrushProperties(startPoint, endPoint),
|
||||||
|
stopCollection.Get(),
|
||||||
|
gradientBrush.ReleaseAndGetAddressOf());
|
||||||
|
if (FAILED(hr) || !gradientBrush) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (command.rounding > 0.0f) {
|
||||||
|
renderTarget.FillRoundedRectangle(
|
||||||
|
D2D1::RoundedRect(rect, rounding, rounding),
|
||||||
|
gradientBrush.Get());
|
||||||
|
} else {
|
||||||
|
renderTarget.FillRectangle(rect, gradientBrush.Get());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
case ::XCEngine::UI::UIDrawCommandType::RectOutline: {
|
case ::XCEngine::UI::UIDrawCommandType::RectOutline: {
|
||||||
const D2D1_RECT_F rect = ToD2DRect(command.rect, dpiScale);
|
const D2D1_RECT_F rect = ToD2DRect(command.rect, dpiScale);
|
||||||
const float thickness = (command.thickness > 0.0f ? command.thickness : 1.0f) * dpiScale;
|
const float thickness = (command.thickness > 0.0f ? command.thickness : 1.0f) * dpiScale;
|
||||||
@@ -472,6 +531,58 @@ void NativeRenderer::RenderCommand(
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case ::XCEngine::UI::UIDrawCommandType::Line: {
|
||||||
|
const float thickness = (command.thickness > 0.0f ? command.thickness : 1.0f) * dpiScale;
|
||||||
|
const float pixelOffset = ResolveStrokePixelOffset(thickness);
|
||||||
|
const D2D1_POINT_2F start = ToD2DPoint(command.position, dpiScale, pixelOffset);
|
||||||
|
const D2D1_POINT_2F end = ToD2DPoint(command.uvMin, dpiScale, pixelOffset);
|
||||||
|
renderTarget.DrawLine(start, end, &solidBrush, thickness);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ::XCEngine::UI::UIDrawCommandType::FilledTriangle: {
|
||||||
|
Microsoft::WRL::ComPtr<ID2D1PathGeometry> geometry;
|
||||||
|
HRESULT hr = m_d2dFactory->CreatePathGeometry(geometry.ReleaseAndGetAddressOf());
|
||||||
|
if (FAILED(hr) || !geometry) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Microsoft::WRL::ComPtr<ID2D1GeometrySink> sink;
|
||||||
|
hr = geometry->Open(sink.ReleaseAndGetAddressOf());
|
||||||
|
if (FAILED(hr) || !sink) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const D2D1_POINT_2F a = ToD2DPoint(command.position, dpiScale);
|
||||||
|
const D2D1_POINT_2F b = ToD2DPoint(command.uvMin, dpiScale);
|
||||||
|
const D2D1_POINT_2F c = ToD2DPoint(command.uvMax, dpiScale);
|
||||||
|
const D2D1_POINT_2F points[2] = { b, c };
|
||||||
|
sink->BeginFigure(a, D2D1_FIGURE_BEGIN_FILLED);
|
||||||
|
sink->AddLines(points, 2u);
|
||||||
|
sink->EndFigure(D2D1_FIGURE_END_CLOSED);
|
||||||
|
hr = sink->Close();
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderTarget.FillGeometry(geometry.Get(), &solidBrush);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ::XCEngine::UI::UIDrawCommandType::FilledCircle: {
|
||||||
|
const float radius = command.radius * dpiScale;
|
||||||
|
renderTarget.FillEllipse(
|
||||||
|
D2D1::Ellipse(ToD2DPoint(command.position, dpiScale), radius, radius),
|
||||||
|
&solidBrush);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ::XCEngine::UI::UIDrawCommandType::CircleOutline: {
|
||||||
|
const float radius = command.radius * dpiScale;
|
||||||
|
const float thickness = (command.thickness > 0.0f ? command.thickness : 1.0f) * dpiScale;
|
||||||
|
renderTarget.DrawEllipse(
|
||||||
|
D2D1::Ellipse(ToD2DPoint(command.position, dpiScale), radius, radius),
|
||||||
|
&solidBrush,
|
||||||
|
thickness);
|
||||||
|
break;
|
||||||
|
}
|
||||||
case ::XCEngine::UI::UIDrawCommandType::Text: {
|
case ::XCEngine::UI::UIDrawCommandType::Text: {
|
||||||
if (command.text.empty()) {
|
if (command.text.empty()) {
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -41,6 +41,45 @@ constexpr const wchar_t* kWindowTitle = L"Main Scene * - Main.xx - XCEngine Edit
|
|||||||
constexpr UINT kDefaultDpi = 96u;
|
constexpr UINT kDefaultDpi = 96u;
|
||||||
constexpr float kBaseDpiScale = 96.0f;
|
constexpr float kBaseDpiScale = 96.0f;
|
||||||
|
|
||||||
|
UIEditorShellComposeModel BuildShellComposeModelFromFrame(
|
||||||
|
const UIEditorShellInteractionFrame& frame) {
|
||||||
|
UIEditorShellComposeModel model = {};
|
||||||
|
model.menuBarItems = frame.request.menuBarItems;
|
||||||
|
model.toolbarButtons = frame.model.toolbarButtons;
|
||||||
|
model.statusSegments = frame.model.statusSegments;
|
||||||
|
model.workspacePresentations = frame.model.workspacePresentations;
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AppendShellPopups(
|
||||||
|
UIDrawList& drawList,
|
||||||
|
const UIEditorShellInteractionFrame& frame,
|
||||||
|
const UIEditorShellInteractionPalette& palette,
|
||||||
|
const UIEditorShellInteractionMetrics& metrics) {
|
||||||
|
const std::size_t popupCount =
|
||||||
|
(std::min)(frame.request.popupRequests.size(), frame.popupFrames.size());
|
||||||
|
for (std::size_t index = 0; index < popupCount; ++index) {
|
||||||
|
const UIEditorShellInteractionPopupRequest& popupRequest =
|
||||||
|
frame.request.popupRequests[index];
|
||||||
|
const UIEditorShellInteractionPopupFrame& popupFrame =
|
||||||
|
frame.popupFrames[index];
|
||||||
|
Widgets::AppendUIEditorMenuPopupBackground(
|
||||||
|
drawList,
|
||||||
|
popupRequest.layout,
|
||||||
|
popupRequest.widgetItems,
|
||||||
|
popupFrame.popupState,
|
||||||
|
palette.popupPalette,
|
||||||
|
metrics.popupMetrics);
|
||||||
|
Widgets::AppendUIEditorMenuPopupForeground(
|
||||||
|
drawList,
|
||||||
|
popupRequest.layout,
|
||||||
|
popupRequest.widgetItems,
|
||||||
|
popupFrame.popupState,
|
||||||
|
palette.popupPalette,
|
||||||
|
metrics.popupMetrics);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Application* GetApplicationFromWindow(HWND hwnd) {
|
Application* GetApplicationFromWindow(HWND hwnd) {
|
||||||
return reinterpret_cast<Application*>(GetWindowLongPtrW(hwnd, GWLP_USERDATA));
|
return reinterpret_cast<Application*>(GetWindowLongPtrW(hwnd, GWLP_USERDATA));
|
||||||
}
|
}
|
||||||
@@ -289,6 +328,27 @@ std::string DescribeInputEventType(const UIInputEvent& event) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<UIInputEvent> FilterShellInputEventsForHostedContentCapture(
|
||||||
|
const std::vector<UIInputEvent>& inputEvents) {
|
||||||
|
std::vector<UIInputEvent> filteredEvents = {};
|
||||||
|
filteredEvents.reserve(inputEvents.size());
|
||||||
|
for (const UIInputEvent& event : inputEvents) {
|
||||||
|
switch (event.type) {
|
||||||
|
case UIInputEventType::PointerMove:
|
||||||
|
case UIInputEventType::PointerEnter:
|
||||||
|
case UIInputEventType::PointerLeave:
|
||||||
|
case UIInputEventType::PointerButtonDown:
|
||||||
|
case UIInputEventType::PointerButtonUp:
|
||||||
|
case UIInputEventType::PointerWheel:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
filteredEvents.push_back(event);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filteredEvents;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
int Application::Run(HINSTANCE hInstance, int nCmdShow) {
|
int Application::Run(HINSTANCE hInstance, int nCmdShow) {
|
||||||
@@ -339,6 +399,7 @@ bool Application::Initialize(HINSTANCE hInstance, int nCmdShow) {
|
|||||||
m_shellServices.commandDispatcher = &m_shortcutManager.GetCommandDispatcher();
|
m_shellServices.commandDispatcher = &m_shortcutManager.GetCommandDispatcher();
|
||||||
m_shellServices.shortcutManager = &m_shortcutManager;
|
m_shellServices.shortcutManager = &m_shortcutManager;
|
||||||
m_shellServices.textMeasurer = &m_renderer;
|
m_shellServices.textMeasurer = &m_renderer;
|
||||||
|
m_projectPanel.Initialize(ResolveRepoRootPath());
|
||||||
m_lastStatus = "Ready";
|
m_lastStatus = "Ready";
|
||||||
m_lastMessage = "Old editor shell baseline loaded.";
|
m_lastMessage = "Old editor shell baseline loaded.";
|
||||||
LogRuntimeTrace("app", "workspace initialized: " + DescribeWorkspaceState());
|
LogRuntimeTrace("app", "workspace initialized: " + DescribeWorkspaceState());
|
||||||
@@ -385,6 +446,7 @@ bool Application::Initialize(HINSTANCE hInstance, int nCmdShow) {
|
|||||||
LogRuntimeTrace("app", "renderer initialization failed");
|
LogRuntimeTrace("app", "renderer initialization failed");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
m_projectPanel.SetTextMeasurer(&m_renderer);
|
||||||
|
|
||||||
ShowWindow(m_hwnd, nCmdShow);
|
ShowWindow(m_hwnd, nCmdShow);
|
||||||
UpdateWindow(m_hwnd);
|
UpdateWindow(m_hwnd);
|
||||||
@@ -448,6 +510,11 @@ void Application::RenderFrame() {
|
|||||||
const UIEditorShellInteractionDefinition definition = BuildShellDefinition();
|
const UIEditorShellInteractionDefinition definition = BuildShellDefinition();
|
||||||
std::vector<UIInputEvent> frameEvents = std::move(m_pendingInputEvents);
|
std::vector<UIInputEvent> frameEvents = std::move(m_pendingInputEvents);
|
||||||
m_pendingInputEvents.clear();
|
m_pendingInputEvents.clear();
|
||||||
|
const std::vector<UIInputEvent> hostedContentEvents = frameEvents;
|
||||||
|
const std::vector<UIInputEvent> shellEvents =
|
||||||
|
m_projectPanel.HasActivePointerCapture()
|
||||||
|
? FilterShellInputEventsForHostedContentCapture(frameEvents)
|
||||||
|
: frameEvents;
|
||||||
if (!frameEvents.empty()) {
|
if (!frameEvents.empty()) {
|
||||||
LogRuntimeTrace(
|
LogRuntimeTrace(
|
||||||
"input",
|
"input",
|
||||||
@@ -459,10 +526,10 @@ void Application::RenderFrame() {
|
|||||||
m_workspaceController,
|
m_workspaceController,
|
||||||
UIRect(0.0f, 0.0f, width, height),
|
UIRect(0.0f, 0.0f, width, height),
|
||||||
definition,
|
definition,
|
||||||
frameEvents,
|
shellEvents,
|
||||||
m_shellServices,
|
m_shellServices,
|
||||||
metrics);
|
metrics);
|
||||||
if (!frameEvents.empty() ||
|
if (!shellEvents.empty() ||
|
||||||
m_shellFrame.result.workspaceResult.dockHostResult.layoutChanged ||
|
m_shellFrame.result.workspaceResult.dockHostResult.layoutChanged ||
|
||||||
m_shellFrame.result.workspaceResult.dockHostResult.commandExecuted) {
|
m_shellFrame.result.workspaceResult.dockHostResult.commandExecuted) {
|
||||||
std::ostringstream frameTrace = {};
|
std::ostringstream frameTrace = {};
|
||||||
@@ -482,13 +549,24 @@ void Application::RenderFrame() {
|
|||||||
}
|
}
|
||||||
ApplyHostCaptureRequests(m_shellFrame.result);
|
ApplyHostCaptureRequests(m_shellFrame.result);
|
||||||
UpdateLastStatus(m_shellFrame.result);
|
UpdateLastStatus(m_shellFrame.result);
|
||||||
|
m_projectPanel.Update(
|
||||||
|
m_shellFrame.workspaceInteractionFrame.composeFrame.contentHostFrame,
|
||||||
|
hostedContentEvents,
|
||||||
|
!m_shellFrame.result.workspaceInputSuppressed,
|
||||||
|
m_workspaceController.GetWorkspace().activePanelId == "project");
|
||||||
|
ApplyHostedContentCaptureRequests();
|
||||||
ApplyCurrentCursor();
|
ApplyCurrentCursor();
|
||||||
AppendUIEditorShellInteraction(
|
const UIEditorShellComposeModel shellComposeModel =
|
||||||
|
BuildShellComposeModelFromFrame(m_shellFrame);
|
||||||
|
AppendUIEditorShellCompose(
|
||||||
drawList,
|
drawList,
|
||||||
m_shellFrame,
|
m_shellFrame.shellFrame,
|
||||||
m_shellInteractionState,
|
shellComposeModel,
|
||||||
palette,
|
m_shellInteractionState.composeState,
|
||||||
metrics);
|
palette.shellPalette,
|
||||||
|
metrics.shellMetrics);
|
||||||
|
m_projectPanel.Append(drawList);
|
||||||
|
AppendShellPopups(drawList, m_shellFrame, palette, metrics);
|
||||||
} else {
|
} else {
|
||||||
drawList.AddText(
|
drawList.AddText(
|
||||||
UIPoint(28.0f, 28.0f),
|
UIPoint(28.0f, 28.0f),
|
||||||
@@ -522,6 +600,14 @@ float Application::PixelsToDips(float pixels) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
LPCWSTR Application::ResolveCurrentCursorResource() const {
|
LPCWSTR Application::ResolveCurrentCursorResource() const {
|
||||||
|
switch (m_projectPanel.GetCursorKind()) {
|
||||||
|
case App::ProductProjectPanel::CursorKind::ResizeEW:
|
||||||
|
return IDC_SIZEWE;
|
||||||
|
case App::ProductProjectPanel::CursorKind::Arrow:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
switch (Widgets::ResolveUIEditorDockHostCursorKind(
|
switch (Widgets::ResolveUIEditorDockHostCursorKind(
|
||||||
m_shellFrame.workspaceInteractionFrame.dockHostFrame.layout)) {
|
m_shellFrame.workspaceInteractionFrame.dockHostFrame.layout)) {
|
||||||
case Widgets::UIEditorDockHostCursorKind::ResizeEW:
|
case Widgets::UIEditorDockHostCursorKind::ResizeEW:
|
||||||
@@ -639,7 +725,19 @@ void Application::ApplyHostCaptureRequests(const UIEditorShellInteractionResult&
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Application::HasInteractiveCaptureState() const {
|
void Application::ApplyHostedContentCaptureRequests() {
|
||||||
|
if (m_projectPanel.WantsHostPointerCapture() && GetCapture() != m_hwnd) {
|
||||||
|
SetCapture(m_hwnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_projectPanel.WantsHostPointerRelease() &&
|
||||||
|
GetCapture() == m_hwnd &&
|
||||||
|
!HasShellInteractiveCaptureState()) {
|
||||||
|
ReleaseCapture();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Application::HasShellInteractiveCaptureState() const {
|
||||||
if (m_shellInteractionState.workspaceInteractionState.dockHostInteractionState.splitterDragState.active) {
|
if (m_shellInteractionState.workspaceInteractionState.dockHostInteractionState.splitterDragState.active) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -657,6 +755,10 @@ bool Application::HasInteractiveCaptureState() const {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Application::HasInteractiveCaptureState() const {
|
||||||
|
return HasShellInteractiveCaptureState() || m_projectPanel.HasActivePointerCapture();
|
||||||
|
}
|
||||||
|
|
||||||
UIEditorShellInteractionDefinition Application::BuildShellDefinition() const {
|
UIEditorShellInteractionDefinition Application::BuildShellDefinition() const {
|
||||||
std::string statusText = m_lastStatus;
|
std::string statusText = m_lastStatus;
|
||||||
if (!m_lastMessage.empty()) {
|
if (!m_lastMessage.empty()) {
|
||||||
|
|||||||
@@ -8,6 +8,8 @@
|
|||||||
#include <Host/InputModifierTracker.h>
|
#include <Host/InputModifierTracker.h>
|
||||||
#include <Host/NativeRenderer.h>
|
#include <Host/NativeRenderer.h>
|
||||||
|
|
||||||
|
#include "Panels/ProductProjectPanel.h"
|
||||||
|
|
||||||
#include <XCEditor/Foundation/UIEditorShortcutManager.h>
|
#include <XCEditor/Foundation/UIEditorShortcutManager.h>
|
||||||
#include <XCEditor/Shell/UIEditorShellAsset.h>
|
#include <XCEditor/Shell/UIEditorShellAsset.h>
|
||||||
#include <XCEditor/Shell/UIEditorShellInteraction.h>
|
#include <XCEditor/Shell/UIEditorShellInteraction.h>
|
||||||
@@ -43,11 +45,15 @@ private:
|
|||||||
void RenderFrame();
|
void RenderFrame();
|
||||||
void OnResize(UINT width, UINT height);
|
void OnResize(UINT width, UINT height);
|
||||||
void OnDpiChanged(UINT dpi, const RECT& suggestedRect);
|
void OnDpiChanged(UINT dpi, const RECT& suggestedRect);
|
||||||
|
bool ApplyCurrentCursor() const;
|
||||||
|
LPCWSTR ResolveCurrentCursorResource() const;
|
||||||
float GetDpiScale() const;
|
float GetDpiScale() const;
|
||||||
float PixelsToDips(float pixels) const;
|
float PixelsToDips(float pixels) const;
|
||||||
::XCEngine::UI::UIPoint ConvertClientPixelsToDips(LONG x, LONG y) const;
|
::XCEngine::UI::UIPoint ConvertClientPixelsToDips(LONG x, LONG y) const;
|
||||||
void LogRuntimeTrace(std::string_view channel, std::string_view message) const;
|
void LogRuntimeTrace(std::string_view channel, std::string_view message) const;
|
||||||
void ApplyHostCaptureRequests(const UIEditorShellInteractionResult& result);
|
void ApplyHostCaptureRequests(const UIEditorShellInteractionResult& result);
|
||||||
|
void ApplyHostedContentCaptureRequests();
|
||||||
|
bool HasShellInteractiveCaptureState() const;
|
||||||
bool HasInteractiveCaptureState() const;
|
bool HasInteractiveCaptureState() const;
|
||||||
UIEditorShellInteractionDefinition BuildShellDefinition() const;
|
UIEditorShellInteractionDefinition BuildShellDefinition() const;
|
||||||
void UpdateLastStatus(const UIEditorShellInteractionResult& result);
|
void UpdateLastStatus(const UIEditorShellInteractionResult& result);
|
||||||
@@ -77,6 +83,7 @@ private:
|
|||||||
EditorShellAssetValidationResult m_shellValidation = {};
|
EditorShellAssetValidationResult m_shellValidation = {};
|
||||||
UIEditorWorkspaceController m_workspaceController = {};
|
UIEditorWorkspaceController m_workspaceController = {};
|
||||||
UIEditorShortcutManager m_shortcutManager = {};
|
UIEditorShortcutManager m_shortcutManager = {};
|
||||||
|
App::ProductProjectPanel m_projectPanel = {};
|
||||||
UIEditorShellInteractionServices m_shellServices = {};
|
UIEditorShellInteractionServices m_shellServices = {};
|
||||||
UIEditorShellInteractionState m_shellInteractionState = {};
|
UIEditorShellInteractionState m_shellInteractionState = {};
|
||||||
UIEditorShellInteractionFrame m_shellFrame = {};
|
UIEditorShellInteractionFrame m_shellFrame = {};
|
||||||
|
|||||||
1032
new_editor/app/Panels/ProductProjectPanel.cpp
Normal file
1032
new_editor/app/Panels/ProductProjectPanel.cpp
Normal file
File diff suppressed because it is too large
Load Diff
121
new_editor/app/Panels/ProductProjectPanel.h
Normal file
121
new_editor/app/Panels/ProductProjectPanel.h
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <XCEditor/Collections/UIEditorTreeViewInteraction.h>
|
||||||
|
#include <XCEditor/Foundation/UIEditorTextMeasurement.h>
|
||||||
|
#include <XCEditor/Shell/UIEditorPanelContentHost.h>
|
||||||
|
|
||||||
|
#include <XCEngine/UI/DrawData.h>
|
||||||
|
#include <XCEngine/UI/Widgets/UIExpansionModel.h>
|
||||||
|
#include <XCEngine/UI/Widgets/UISelectionModel.h>
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace XCEngine::UI::Editor::App {
|
||||||
|
|
||||||
|
class ProductProjectPanel {
|
||||||
|
public:
|
||||||
|
enum class CursorKind : std::uint8_t {
|
||||||
|
Arrow = 0,
|
||||||
|
ResizeEW
|
||||||
|
};
|
||||||
|
|
||||||
|
void Initialize(const std::filesystem::path& repoRoot);
|
||||||
|
void SetTextMeasurer(const ::XCEngine::UI::Editor::UIEditorTextMeasurer* textMeasurer);
|
||||||
|
void Update(
|
||||||
|
const UIEditorPanelContentHostFrame& contentHostFrame,
|
||||||
|
const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents,
|
||||||
|
bool allowInteraction,
|
||||||
|
bool panelActive);
|
||||||
|
void Append(::XCEngine::UI::UIDrawList& drawList) const;
|
||||||
|
|
||||||
|
CursorKind GetCursorKind() const;
|
||||||
|
bool WantsHostPointerCapture() const;
|
||||||
|
bool WantsHostPointerRelease() const;
|
||||||
|
bool HasActivePointerCapture() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct FolderEntry {
|
||||||
|
std::string itemId = {};
|
||||||
|
std::filesystem::path absolutePath = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AssetEntry {
|
||||||
|
std::string itemId = {};
|
||||||
|
std::filesystem::path absolutePath = {};
|
||||||
|
std::string displayName = {};
|
||||||
|
bool directory = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BreadcrumbItemLayout {
|
||||||
|
std::string label = {};
|
||||||
|
std::string targetFolderId = {};
|
||||||
|
::XCEngine::UI::UIRect rect = {};
|
||||||
|
bool separator = false;
|
||||||
|
bool clickable = false;
|
||||||
|
bool current = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AssetTileLayout {
|
||||||
|
std::size_t itemIndex = static_cast<std::size_t>(-1);
|
||||||
|
::XCEngine::UI::UIRect tileRect = {};
|
||||||
|
::XCEngine::UI::UIRect previewRect = {};
|
||||||
|
::XCEngine::UI::UIRect labelRect = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Layout {
|
||||||
|
::XCEngine::UI::UIRect bounds = {};
|
||||||
|
::XCEngine::UI::UIRect leftPaneRect = {};
|
||||||
|
::XCEngine::UI::UIRect treeRect = {};
|
||||||
|
::XCEngine::UI::UIRect dividerRect = {};
|
||||||
|
::XCEngine::UI::UIRect rightPaneRect = {};
|
||||||
|
::XCEngine::UI::UIRect browserHeaderRect = {};
|
||||||
|
::XCEngine::UI::UIRect browserBodyRect = {};
|
||||||
|
::XCEngine::UI::UIRect gridRect = {};
|
||||||
|
std::vector<BreadcrumbItemLayout> breadcrumbItems = {};
|
||||||
|
std::vector<AssetTileLayout> assetTiles = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
const FolderEntry* FindFolderEntry(std::string_view itemId) const;
|
||||||
|
const UIEditorPanelContentHostPanelState* FindMountedProjectPanel(
|
||||||
|
const UIEditorPanelContentHostFrame& contentHostFrame) const;
|
||||||
|
Layout BuildLayout(const ::XCEngine::UI::UIRect& bounds) const;
|
||||||
|
std::size_t HitTestBreadcrumbItem(const ::XCEngine::UI::UIPoint& point) const;
|
||||||
|
std::size_t HitTestAssetTile(const ::XCEngine::UI::UIPoint& point) const;
|
||||||
|
void RefreshFolderTree();
|
||||||
|
void RefreshAssetList();
|
||||||
|
void EnsureValidCurrentFolder();
|
||||||
|
void ExpandFolderAncestors(std::string_view itemId);
|
||||||
|
void SyncCurrentFolderSelection();
|
||||||
|
void NavigateToFolder(std::string_view itemId);
|
||||||
|
void ResetTransientFrames();
|
||||||
|
|
||||||
|
std::filesystem::path m_assetsRootPath = {};
|
||||||
|
std::vector<FolderEntry> m_folderEntries = {};
|
||||||
|
std::vector<Widgets::UIEditorTreeViewItem> m_treeItems = {};
|
||||||
|
std::vector<AssetEntry> m_assetEntries = {};
|
||||||
|
const ::XCEngine::UI::Editor::UIEditorTextMeasurer* m_textMeasurer = nullptr;
|
||||||
|
::XCEngine::UI::Widgets::UISelectionModel m_folderSelection = {};
|
||||||
|
::XCEngine::UI::Widgets::UIExpansionModel m_folderExpansion = {};
|
||||||
|
::XCEngine::UI::Widgets::UISelectionModel m_assetSelection = {};
|
||||||
|
UIEditorTreeViewInteractionState m_treeInteractionState = {};
|
||||||
|
UIEditorTreeViewInteractionFrame m_treeFrame = {};
|
||||||
|
Layout m_layout = {};
|
||||||
|
std::string m_currentFolderId = {};
|
||||||
|
std::string m_hoveredAssetItemId = {};
|
||||||
|
std::string m_lastPrimaryClickedAssetId = {};
|
||||||
|
float m_navigationWidth = 248.0f;
|
||||||
|
std::uint64_t m_lastPrimaryClickTimeMs = 0u;
|
||||||
|
std::size_t m_hoveredBreadcrumbIndex = static_cast<std::size_t>(-1);
|
||||||
|
std::size_t m_pressedBreadcrumbIndex = static_cast<std::size_t>(-1);
|
||||||
|
bool m_visible = false;
|
||||||
|
bool m_splitterHovered = false;
|
||||||
|
bool m_splitterDragging = false;
|
||||||
|
bool m_requestPointerCapture = false;
|
||||||
|
bool m_requestPointerRelease = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace XCEngine::UI::Editor::App
|
||||||
@@ -25,7 +25,7 @@ UIEditorPanelRegistry BuildPanelRegistry() {
|
|||||||
{ "game", "Game", UIEditorPanelPresentationKind::ViewportShell, false, false, false },
|
{ "game", "Game", UIEditorPanelPresentationKind::ViewportShell, false, false, false },
|
||||||
{ "inspector", "Inspector", UIEditorPanelPresentationKind::Placeholder, true, false, false },
|
{ "inspector", "Inspector", UIEditorPanelPresentationKind::Placeholder, true, false, false },
|
||||||
{ "console", "Console", UIEditorPanelPresentationKind::Placeholder, true, false, false },
|
{ "console", "Console", UIEditorPanelPresentationKind::Placeholder, true, false, false },
|
||||||
{ "project", "Project", UIEditorPanelPresentationKind::Placeholder, true, false, false }
|
{ "project", "Project", UIEditorPanelPresentationKind::HostedContent, false, false, false }
|
||||||
};
|
};
|
||||||
return registry;
|
return registry;
|
||||||
}
|
}
|
||||||
@@ -81,9 +81,9 @@ UIEditorWorkspaceModel BuildWorkspace() {
|
|||||||
"project-panel",
|
"project-panel",
|
||||||
"project",
|
"project",
|
||||||
"Project",
|
"Project",
|
||||||
true)
|
false)
|
||||||
},
|
},
|
||||||
0u));
|
1u));
|
||||||
workspace.activePanelId = "scene";
|
workspace.activePanelId = "scene";
|
||||||
return workspace;
|
return workspace;
|
||||||
}
|
}
|
||||||
@@ -434,6 +434,14 @@ UIEditorWorkspacePanelPresentationModel BuildViewportPresentation(
|
|||||||
return presentation;
|
return presentation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UIEditorWorkspacePanelPresentationModel BuildHostedContentPresentation(
|
||||||
|
std::string panelId) {
|
||||||
|
UIEditorWorkspacePanelPresentationModel presentation = {};
|
||||||
|
presentation.panelId = std::move(panelId);
|
||||||
|
presentation.kind = UIEditorPanelPresentationKind::HostedContent;
|
||||||
|
return presentation;
|
||||||
|
}
|
||||||
|
|
||||||
UIEditorShellInteractionDefinition BuildBaseShellDefinition() {
|
UIEditorShellInteractionDefinition BuildBaseShellDefinition() {
|
||||||
UIEditorShellInteractionDefinition definition = {};
|
UIEditorShellInteractionDefinition definition = {};
|
||||||
definition.menuModel = BuildMenuModel();
|
definition.menuModel = BuildMenuModel();
|
||||||
@@ -449,7 +457,7 @@ UIEditorShellInteractionDefinition BuildBaseShellDefinition() {
|
|||||||
BuildViewportPresentation("game", "Game", "Display 1", "Game preview host ready"),
|
BuildViewportPresentation("game", "Game", "Display 1", "Game preview host ready"),
|
||||||
BuildPlaceholderPresentation("inspector"),
|
BuildPlaceholderPresentation("inspector"),
|
||||||
BuildPlaceholderPresentation("console"),
|
BuildPlaceholderPresentation("console"),
|
||||||
BuildPlaceholderPresentation("project")
|
BuildHostedContentPresentation("project")
|
||||||
};
|
};
|
||||||
return definition;
|
return definition;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,9 +65,9 @@ struct UIEditorTabStripPalette {
|
|||||||
::XCEngine::UI::UIColor contentBackgroundColor =
|
::XCEngine::UI::UIColor contentBackgroundColor =
|
||||||
::XCEngine::UI::UIColor(0.18f, 0.18f, 0.18f, 1.0f);
|
::XCEngine::UI::UIColor(0.18f, 0.18f, 0.18f, 1.0f);
|
||||||
::XCEngine::UI::UIColor stripBorderColor =
|
::XCEngine::UI::UIColor stripBorderColor =
|
||||||
::XCEngine::UI::UIColor(0.24f, 0.24f, 0.24f, 1.0f);
|
::XCEngine::UI::UIColor(0.13f, 0.13f, 0.13f, 1.0f);
|
||||||
::XCEngine::UI::UIColor headerContentSeparatorColor =
|
::XCEngine::UI::UIColor headerContentSeparatorColor =
|
||||||
::XCEngine::UI::UIColor(0.27f, 0.27f, 0.27f, 1.0f);
|
::XCEngine::UI::UIColor(0.13f, 0.13f, 0.13f, 1.0f);
|
||||||
::XCEngine::UI::UIColor focusedBorderColor =
|
::XCEngine::UI::UIColor focusedBorderColor =
|
||||||
::XCEngine::UI::UIColor(0.36f, 0.36f, 0.36f, 1.0f);
|
::XCEngine::UI::UIColor(0.36f, 0.36f, 0.36f, 1.0f);
|
||||||
::XCEngine::UI::UIColor tabColor =
|
::XCEngine::UI::UIColor tabColor =
|
||||||
@@ -77,11 +77,11 @@ struct UIEditorTabStripPalette {
|
|||||||
::XCEngine::UI::UIColor tabSelectedColor =
|
::XCEngine::UI::UIColor tabSelectedColor =
|
||||||
::XCEngine::UI::UIColor(0.18f, 0.18f, 0.18f, 1.0f);
|
::XCEngine::UI::UIColor(0.18f, 0.18f, 0.18f, 1.0f);
|
||||||
::XCEngine::UI::UIColor tabBorderColor =
|
::XCEngine::UI::UIColor tabBorderColor =
|
||||||
::XCEngine::UI::UIColor(0.24f, 0.24f, 0.24f, 1.0f);
|
::XCEngine::UI::UIColor(0.13f, 0.13f, 0.13f, 1.0f);
|
||||||
::XCEngine::UI::UIColor tabHoveredBorderColor =
|
::XCEngine::UI::UIColor tabHoveredBorderColor =
|
||||||
::XCEngine::UI::UIColor(0.31f, 0.31f, 0.31f, 1.0f);
|
::XCEngine::UI::UIColor(0.13f, 0.13f, 0.13f, 1.0f);
|
||||||
::XCEngine::UI::UIColor tabSelectedBorderColor =
|
::XCEngine::UI::UIColor tabSelectedBorderColor =
|
||||||
::XCEngine::UI::UIColor(0.34f, 0.34f, 0.34f, 1.0f);
|
::XCEngine::UI::UIColor(0.13f, 0.13f, 0.13f, 1.0f);
|
||||||
::XCEngine::UI::UIColor textPrimary =
|
::XCEngine::UI::UIColor textPrimary =
|
||||||
::XCEngine::UI::UIColor(0.86f, 0.86f, 0.86f, 1.0f);
|
::XCEngine::UI::UIColor(0.86f, 0.86f, 0.86f, 1.0f);
|
||||||
::XCEngine::UI::UIColor textSecondary =
|
::XCEngine::UI::UIColor textSecondary =
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ struct UIEditorDockHostState {
|
|||||||
|
|
||||||
struct UIEditorDockHostMetrics {
|
struct UIEditorDockHostMetrics {
|
||||||
::XCEngine::UI::Layout::UISplitterMetrics splitterMetrics =
|
::XCEngine::UI::Layout::UISplitterMetrics splitterMetrics =
|
||||||
::XCEngine::UI::Layout::UISplitterMetrics{ 1.0f, 10.0f };
|
::XCEngine::UI::Layout::UISplitterMetrics{ 2.0f, 10.0f };
|
||||||
UIEditorTabStripMetrics tabStripMetrics = {};
|
UIEditorTabStripMetrics tabStripMetrics = {};
|
||||||
UIEditorPanelFrameMetrics panelFrameMetrics = {};
|
UIEditorPanelFrameMetrics panelFrameMetrics = {};
|
||||||
::XCEngine::UI::UISize minimumStandalonePanelBodySize =
|
::XCEngine::UI::UISize minimumStandalonePanelBodySize =
|
||||||
@@ -75,11 +75,11 @@ struct UIEditorDockHostPalette {
|
|||||||
UIEditorTabStripPalette tabStripPalette = {};
|
UIEditorTabStripPalette tabStripPalette = {};
|
||||||
UIEditorPanelFramePalette panelFramePalette = {};
|
UIEditorPanelFramePalette panelFramePalette = {};
|
||||||
::XCEngine::UI::UIColor splitterColor =
|
::XCEngine::UI::UIColor splitterColor =
|
||||||
::XCEngine::UI::UIColor(0.22f, 0.22f, 0.22f, 1.0f);
|
::XCEngine::UI::UIColor(0.13f, 0.13f, 0.13f, 1.0f);
|
||||||
::XCEngine::UI::UIColor splitterHoveredColor =
|
::XCEngine::UI::UIColor splitterHoveredColor =
|
||||||
::XCEngine::UI::UIColor(0.29f, 0.29f, 0.29f, 1.0f);
|
::XCEngine::UI::UIColor(0.13f, 0.13f, 0.13f, 1.0f);
|
||||||
::XCEngine::UI::UIColor splitterActiveColor =
|
::XCEngine::UI::UIColor splitterActiveColor =
|
||||||
::XCEngine::UI::UIColor(0.35f, 0.35f, 0.35f, 1.0f);
|
::XCEngine::UI::UIColor(0.13f, 0.13f, 0.13f, 1.0f);
|
||||||
::XCEngine::UI::UIColor placeholderTitleColor =
|
::XCEngine::UI::UIColor placeholderTitleColor =
|
||||||
::XCEngine::UI::UIColor(0.93f, 0.94f, 0.96f, 1.0f);
|
::XCEngine::UI::UIColor(0.93f, 0.94f, 0.96f, 1.0f);
|
||||||
::XCEngine::UI::UIColor placeholderTextColor =
|
::XCEngine::UI::UIColor placeholderTextColor =
|
||||||
|
|||||||
@@ -88,20 +88,6 @@ float ResolveTabTextLeft(
|
|||||||
return availableLeft + (std::max)(0.0f, (availableWidth - labelWidth) * 0.5f);
|
return availableLeft + (std::max)(0.0f, (availableWidth - labelWidth) * 0.5f);
|
||||||
}
|
}
|
||||||
|
|
||||||
UIColor ResolveStripBorderColor(
|
|
||||||
const UIEditorTabStripState& state,
|
|
||||||
const UIEditorTabStripPalette& palette) {
|
|
||||||
(void)state;
|
|
||||||
return palette.stripBorderColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
float ResolveStripBorderThickness(
|
|
||||||
const UIEditorTabStripState& state,
|
|
||||||
const UIEditorTabStripMetrics& metrics) {
|
|
||||||
(void)state;
|
|
||||||
return metrics.baseBorderThickness;
|
|
||||||
}
|
|
||||||
|
|
||||||
UIColor ResolveTabFillColor(
|
UIColor ResolveTabFillColor(
|
||||||
bool selected,
|
bool selected,
|
||||||
bool hovered,
|
bool hovered,
|
||||||
@@ -359,11 +345,6 @@ void AppendUIEditorTabStripBackground(
|
|||||||
if (layout.headerRect.height > 0.0f) {
|
if (layout.headerRect.height > 0.0f) {
|
||||||
drawList.AddFilledRect(layout.headerRect, palette.headerBackgroundColor, stripRounding);
|
drawList.AddFilledRect(layout.headerRect, palette.headerBackgroundColor, stripRounding);
|
||||||
}
|
}
|
||||||
drawList.AddRectOutline(
|
|
||||||
layout.bounds,
|
|
||||||
ResolveStripBorderColor(state, palette),
|
|
||||||
ResolveStripBorderThickness(state, metrics),
|
|
||||||
stripRounding);
|
|
||||||
|
|
||||||
for (std::size_t index = 0; index < layout.tabHeaderRects.size(); ++index) {
|
for (std::size_t index = 0; index < layout.tabHeaderRects.size(); ++index) {
|
||||||
const bool selected = layout.selectedIndex == index;
|
const bool selected = layout.selectedIndex == index;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include <XCEngine/UI/Widgets/UIFlatHierarchyHelpers.h>
|
#include <XCEngine/UI/Widgets/UIFlatHierarchyHelpers.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <cmath>
|
||||||
#include <numeric>
|
#include <numeric>
|
||||||
|
|
||||||
namespace XCEngine::UI::Editor::Widgets {
|
namespace XCEngine::UI::Editor::Widgets {
|
||||||
@@ -19,16 +20,42 @@ std::vector<std::size_t> BuildItemOffsets(std::size_t count) {
|
|||||||
return offsets;
|
return offsets;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constexpr float kTreeFontSize = 12.0f;
|
||||||
float ResolveTreeViewRowHeight(
|
float ResolveTreeViewRowHeight(
|
||||||
const UIEditorTreeViewItem& item,
|
const UIEditorTreeViewItem& item,
|
||||||
const UIEditorTreeViewMetrics& metrics) {
|
const UIEditorTreeViewMetrics& metrics) {
|
||||||
return item.desiredHeight > 0.0f ? item.desiredHeight : metrics.rowHeight;
|
return item.desiredHeight > 0.0f ? item.desiredHeight : metrics.rowHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
::XCEngine::UI::UIPoint ResolveDisclosureGlyphPosition(
|
float ResolveTextTop(const ::XCEngine::UI::UIRect& rect, float fontSize) {
|
||||||
|
const float lineHeight = fontSize * 1.6f;
|
||||||
|
return rect.y + std::floor((rect.height - lineHeight) * 0.5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AppendDisclosureArrow(
|
||||||
|
::XCEngine::UI::UIDrawList& drawList,
|
||||||
const ::XCEngine::UI::UIRect& rect,
|
const ::XCEngine::UI::UIRect& rect,
|
||||||
float insetY) {
|
bool expanded,
|
||||||
return ::XCEngine::UI::UIPoint(rect.x + 2.0f, rect.y + insetY - 1.0f);
|
const ::XCEngine::UI::UIColor& color) {
|
||||||
|
constexpr float kOpticalCenterYOffset = -0.5f;
|
||||||
|
const float centerX = std::floor(rect.x + rect.width * 0.5f) + 0.5f;
|
||||||
|
const float centerY =
|
||||||
|
std::floor(rect.y + rect.height * 0.5f + kOpticalCenterYOffset) + 0.5f;
|
||||||
|
const float halfExtent = (std::max)(2.5f, std::floor((std::min)(rect.width, rect.height) * 0.24f));
|
||||||
|
const float triangleHeight = halfExtent * 1.55f;
|
||||||
|
|
||||||
|
::XCEngine::UI::UIPoint points[3] = {};
|
||||||
|
if (expanded) {
|
||||||
|
points[0] = ::XCEngine::UI::UIPoint(centerX - halfExtent, centerY - triangleHeight * 0.5f);
|
||||||
|
points[1] = ::XCEngine::UI::UIPoint(centerX + halfExtent, centerY - triangleHeight * 0.5f);
|
||||||
|
points[2] = ::XCEngine::UI::UIPoint(centerX, centerY + triangleHeight * 0.5f);
|
||||||
|
} else {
|
||||||
|
points[0] = ::XCEngine::UI::UIPoint(centerX - triangleHeight * 0.5f, centerY - halfExtent);
|
||||||
|
points[1] = ::XCEngine::UI::UIPoint(centerX - triangleHeight * 0.5f, centerY + halfExtent);
|
||||||
|
points[2] = ::XCEngine::UI::UIPoint(centerX + triangleHeight * 0.5f, centerY);
|
||||||
|
}
|
||||||
|
|
||||||
|
drawList.AddFilledTriangle(points[0], points[1], points[2], color);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
@@ -267,23 +294,21 @@ void AppendUIEditorTreeViewForeground(
|
|||||||
for (std::size_t visibleOffset = 0u; visibleOffset < layout.rowRects.size(); ++visibleOffset) {
|
for (std::size_t visibleOffset = 0u; visibleOffset < layout.rowRects.size(); ++visibleOffset) {
|
||||||
const UIEditorTreeViewItem& item = items[layout.visibleItemIndices[visibleOffset]];
|
const UIEditorTreeViewItem& item = items[layout.visibleItemIndices[visibleOffset]];
|
||||||
if (layout.itemHasChildren[visibleOffset]) {
|
if (layout.itemHasChildren[visibleOffset]) {
|
||||||
drawList.AddText(
|
AppendDisclosureArrow(
|
||||||
ResolveDisclosureGlyphPosition(
|
drawList,
|
||||||
layout.disclosureRects[visibleOffset],
|
layout.disclosureRects[visibleOffset],
|
||||||
metrics.labelInsetY),
|
layout.itemExpanded[visibleOffset],
|
||||||
layout.itemExpanded[visibleOffset] ? "v" : ">",
|
palette.disclosureColor);
|
||||||
palette.disclosureColor,
|
|
||||||
12.0f);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
drawList.PushClipRect(layout.labelRects[visibleOffset]);
|
drawList.PushClipRect(layout.labelRects[visibleOffset]);
|
||||||
drawList.AddText(
|
drawList.AddText(
|
||||||
::XCEngine::UI::UIPoint(
|
::XCEngine::UI::UIPoint(
|
||||||
layout.labelRects[visibleOffset].x,
|
layout.labelRects[visibleOffset].x,
|
||||||
layout.labelRects[visibleOffset].y + metrics.labelInsetY),
|
ResolveTextTop(layout.labelRects[visibleOffset], kTreeFontSize)),
|
||||||
item.label,
|
item.label,
|
||||||
palette.textColor,
|
palette.textColor,
|
||||||
12.0f);
|
kTreeFontSize);
|
||||||
drawList.PopClipRect();
|
drawList.PopClipRect();
|
||||||
}
|
}
|
||||||
drawList.PopClipRect();
|
drawList.PopClipRect();
|
||||||
|
|||||||
Reference in New Issue
Block a user