Refactor XCUI editor module layout

This commit is contained in:
2026-04-10 00:41:28 +08:00
parent 4b47764f26
commit 02a0e626fe
263 changed files with 12396 additions and 7592 deletions

View File

@@ -6,64 +6,101 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
file(TO_CMAKE_PATH "${CMAKE_SOURCE_DIR}" XCUIEDITOR_REPO_ROOT_PATH)
set(XCUI_EDITOR_RESOURCE_FILES
ui/views/editor_shell.xcui
ui/themes/editor_shell.xctheme
function(xcui_editor_apply_common_target_settings target visibility)
target_compile_definitions(${target} ${visibility}
UNICODE
_UNICODE
)
if(MSVC)
target_compile_options(${target} PRIVATE /utf-8 /FS)
set_property(TARGET ${target} PROPERTY
MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL")
endif()
endfunction()
set(XCUI_EDITOR_FOUNDATION_SOURCES
src/Foundation/UIEditorCommandDispatcher.cpp
src/Foundation/UIEditorCommandRegistry.cpp
src/Foundation/UIEditorShortcutManager.cpp
src/Foundation/UIEditorTheme.cpp
)
set(XCUI_EDITOR_FIELD_SOURCES
src/Fields/UIEditorAssetField.cpp
src/Fields/UIEditorAssetFieldInteraction.cpp
src/Fields/UIEditorBoolField.cpp
src/Fields/UIEditorBoolFieldInteraction.cpp
src/Fields/UIEditorColorField.cpp
src/Fields/UIEditorColorFieldInteraction.cpp
src/Fields/UIEditorEnumField.cpp
src/Fields/UIEditorEnumFieldInteraction.cpp
src/Fields/UIEditorFieldStyle.cpp
src/Fields/UIEditorNumberField.cpp
src/Fields/UIEditorNumberFieldInteraction.cpp
src/Fields/UIEditorObjectField.cpp
src/Fields/UIEditorObjectFieldInteraction.cpp
src/Fields/UIEditorPropertyGrid.cpp
src/Fields/UIEditorPropertyGridInteraction.cpp
src/Fields/UIEditorTextField.cpp
src/Fields/UIEditorTextFieldInteraction.cpp
src/Fields/UIEditorVector2Field.cpp
src/Fields/UIEditorVector2FieldInteraction.cpp
src/Fields/UIEditorVector3Field.cpp
src/Fields/UIEditorVector3FieldInteraction.cpp
src/Fields/UIEditorVector4Field.cpp
src/Fields/UIEditorVector4FieldInteraction.cpp
)
set(XCUI_EDITOR_COLLECTION_SOURCES
src/Collections/UIEditorInlineRenameSession.cpp
src/Collections/UIEditorListView.cpp
src/Collections/UIEditorListViewInteraction.cpp
src/Collections/UIEditorScrollView.cpp
src/Collections/UIEditorScrollViewInteraction.cpp
src/Collections/UIEditorTabStrip.cpp
src/Collections/UIEditorTabStripInteraction.cpp
src/Collections/UIEditorTreeView.cpp
src/Collections/UIEditorTreeViewInteraction.cpp
)
set(XCUI_EDITOR_SHELL_SOURCES
src/Shell/EditorShellAsset.cpp
src/Shell/UIEditorDockHost.cpp
src/Shell/UIEditorDockHostInteraction.cpp
src/Shell/UIEditorMenuBar.cpp
src/Shell/UIEditorMenuModel.cpp
src/Shell/UIEditorMenuPopup.cpp
src/Shell/UIEditorMenuSession.cpp
src/Shell/UIEditorPanelContentHost.cpp
src/Shell/UIEditorPanelFrame.cpp
src/Shell/UIEditorPanelHostLifecycle.cpp
src/Shell/UIEditorPanelRegistry.cpp
src/Shell/UIEditorShellCompose.cpp
src/Shell/UIEditorShellInteraction.cpp
src/Shell/UIEditorStatusBar.cpp
src/Shell/UIEditorViewportInputBridge.cpp
src/Shell/UIEditorViewportShell.cpp
src/Shell/UIEditorViewportSlot.cpp
src/Shell/UIEditorWorkspaceCompose.cpp
src/Shell/UIEditorWorkspaceController.cpp
src/Shell/UIEditorWorkspaceInteraction.cpp
src/Shell/UIEditorWorkspaceLayoutPersistence.cpp
src/Shell/UIEditorWorkspaceModel.cpp
src/Shell/UIEditorWorkspaceSession.cpp
)
set(XCUI_EDITOR_WIDGET_SUPPORT_SOURCES
src/Widgets/UIEditorCollectionPrimitives.cpp
src/Widgets/UIEditorFieldRowLayout.cpp
)
add_library(XCUIEditorLib STATIC
src/Core/EditorShellAsset.cpp
src/Core/UIEditorCommandDispatcher.cpp
src/Core/UIEditorCommandRegistry.cpp
src/Core/UIEditorBoolFieldInteraction.cpp
src/Core/UIEditorDockHostInteraction.cpp
src/Core/UIEditorEnumFieldInteraction.cpp
src/Core/UIEditorListViewInteraction.cpp
src/Core/UIEditorMenuModel.cpp
src/Core/UIEditorMenuSession.cpp
src/Core/UIEditorNumberFieldInteraction.cpp
src/Core/UIEditorPanelContentHost.cpp
src/Core/UIEditorPanelHostLifecycle.cpp
src/Core/UIEditorPanelRegistry.cpp
src/Core/UIEditorPropertyGridInteraction.cpp
src/Core/UIEditorScrollViewInteraction.cpp
src/Core/UIEditorShellCompose.cpp
src/Core/UIEditorShellInteraction.cpp
src/Core/UIEditorShortcutManager.cpp
src/Core/UIEditorTextFieldInteraction.cpp
src/Core/UIEditorTheme.cpp
src/Core/UIEditorTreeViewInteraction.cpp
src/Core/UIEditorVector2FieldInteraction.cpp
src/Core/UIEditorVector3FieldInteraction.cpp
src/Core/UIEditorVector4FieldInteraction.cpp
src/Core/UIEditorViewportInputBridge.cpp
src/Core/UIEditorViewportShell.cpp
src/Core/UIEditorWorkspaceCompose.cpp
src/Core/UIEditorWorkspaceInteraction.cpp
src/Core/UIEditorWorkspaceLayoutPersistence.cpp
src/Core/UIEditorWorkspaceController.cpp
src/Core/UIEditorWorkspaceModel.cpp
src/Core/UIEditorWorkspaceSession.cpp
src/Widgets/UIEditorCollectionPrimitives.cpp
src/Widgets/UIEditorFieldRowLayout.cpp
src/Widgets/UIEditorBoolField.cpp
src/Widgets/UIEditorDockHost.cpp
src/Widgets/UIEditorEnumField.cpp
src/Widgets/UIEditorListView.cpp
src/Widgets/UIEditorMenuBar.cpp
src/Widgets/UIEditorMenuPopup.cpp
src/Widgets/UIEditorNumberField.cpp
src/Widgets/UIEditorPanelFrame.cpp
src/Widgets/UIEditorPropertyGrid.cpp
src/Widgets/UIEditorScrollView.cpp
src/Widgets/UIEditorStatusBar.cpp
src/Widgets/UIEditorTabStrip.cpp
src/Widgets/UIEditorTextField.cpp
src/Widgets/UIEditorTreeView.cpp
src/Widgets/UIEditorVector2Field.cpp
src/Widgets/UIEditorVector3Field.cpp
src/Widgets/UIEditorVector4Field.cpp
src/Widgets/UIEditorViewportSlot.cpp
${XCUI_EDITOR_FOUNDATION_SOURCES}
${XCUI_EDITOR_FIELD_SOURCES}
${XCUI_EDITOR_COLLECTION_SOURCES}
${XCUI_EDITOR_SHELL_SOURCES}
${XCUI_EDITOR_WIDGET_SUPPORT_SOURCES}
)
target_include_directories(XCUIEditorLib
@@ -75,16 +112,10 @@ target_include_directories(XCUIEditorLib
)
target_compile_definitions(XCUIEditorLib PUBLIC
UNICODE
_UNICODE
XCUIEDITOR_REPO_ROOT="${XCUIEDITOR_REPO_ROOT_PATH}"
)
if(MSVC)
target_compile_options(XCUIEditorLib PRIVATE /utf-8 /FS)
set_property(TARGET XCUIEditorLib PROPERTY
MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL")
endif()
xcui_editor_apply_common_target_settings(XCUIEditorLib PUBLIC)
target_link_libraries(XCUIEditorLib PUBLIC
XCEngine
@@ -99,21 +130,9 @@ target_include_directories(XCUIEditorHost
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/app
${CMAKE_SOURCE_DIR}/engine/include
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/include
${CMAKE_CURRENT_SOURCE_DIR}/src
)
target_compile_definitions(XCUIEditorHost PUBLIC
UNICODE
_UNICODE
)
if(MSVC)
target_compile_options(XCUIEditorHost PRIVATE /utf-8 /FS)
set_property(TARGET XCUIEditorHost PROPERTY
MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL")
endif()
xcui_editor_apply_common_target_settings(XCUIEditorHost PUBLIC)
target_link_libraries(XCUIEditorHost PUBLIC
XCEngine
@@ -126,27 +145,18 @@ if(XCENGINE_BUILD_XCUI_EDITOR_APP)
add_executable(XCUIEditorApp WIN32
app/main.cpp
app/Application.cpp
${XCUI_EDITOR_RESOURCE_FILES}
)
target_include_directories(XCUIEditorApp PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/app
${CMAKE_CURRENT_SOURCE_DIR}/include
${CMAKE_CURRENT_SOURCE_DIR}/src
${CMAKE_SOURCE_DIR}/engine/include
)
target_compile_definitions(XCUIEditorApp PRIVATE
UNICODE
_UNICODE
XCUIEDITOR_REPO_ROOT="${XCUIEDITOR_REPO_ROOT_PATH}"
)
if(MSVC)
target_compile_options(XCUIEditorApp PRIVATE /utf-8 /FS)
set_property(TARGET XCUIEditorApp PROPERTY
MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL")
endif()
xcui_editor_apply_common_target_settings(XCUIEditorApp PRIVATE)
target_link_libraries(XCUIEditorApp PRIVATE
XCUIEditorLib
@@ -158,5 +168,3 @@ if(XCENGINE_BUILD_XCUI_EDITOR_APP)
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/bin"
)
endif()
source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" FILES ${XCUI_EDITOR_RESOURCE_FILES})

View File

@@ -292,7 +292,8 @@ void Application::RenderFrame() {
m_pendingInputEvents.clear();
UIDrawData drawData = {};
if (m_useStructuredScreen && m_screenPlayer.IsLoaded()) {
const bool hasAuthoredScreenDocument = !m_screenAsset.documentPath.empty();
if (hasAuthoredScreenDocument && m_useStructuredScreen && m_screenPlayer.IsLoaded()) {
UIScreenFrameInput input = {};
input.viewportRect = UIRect(0.0f, 0.0f, width, height);
input.events = std::move(frameEvents);
@@ -307,6 +308,10 @@ void Application::RenderFrame() {
m_runtimeStatus = "XCUI Editor Shell";
m_runtimeError = frame.errorMessage;
} else if (!hasAuthoredScreenDocument) {
++m_frameIndex;
m_runtimeStatus = "XCUI Editor Shell | Code-driven";
m_runtimeError.clear();
} else {
m_runtimeStatus = "Editor Shell | Load Error";
if (m_runtimeError.empty() && !m_screenPlayer.IsLoaded()) {
@@ -401,14 +406,19 @@ void Application::QueueWindowFocusEvent(UIInputEventType type) {
bool Application::LoadStructuredScreen(const char* triggerReason) {
(void)triggerReason;
m_screenAsset = m_structuredShell.screenAsset;
const bool loaded = m_screenPlayer.Load(m_screenAsset);
const EditorShellAssetValidationResult& shellAssetValidation =
m_structuredShell.assetValidation;
const auto shortcutValidation = m_structuredShell.shortcutManager.ValidateConfiguration();
m_useStructuredScreen = loaded;
m_runtimeStatus = loaded ? "XCUI Editor Shell" : "Editor Shell | Load Error";
const bool hasAuthoredScreenDocument = !m_screenAsset.documentPath.empty();
const bool loaded =
hasAuthoredScreenDocument ? m_screenPlayer.Load(m_screenAsset) : shellAssetValidation.IsValid();
m_useStructuredScreen = hasAuthoredScreenDocument && loaded;
m_runtimeStatus =
hasAuthoredScreenDocument
? (loaded ? "XCUI Editor Shell" : "Editor Shell | Load Error")
: "XCUI Editor Shell | Code-driven";
m_runtimeError.clear();
if (!loaded) {
if (hasAuthoredScreenDocument && !loaded) {
AppendErrorMessage(m_runtimeError, m_screenPlayer.GetLastError());
}
if (!shellAssetValidation.IsValid()) {
@@ -468,7 +478,6 @@ void Application::RebuildTrackedFileStates() {
};
appendTrackedPath(m_screenAsset.documentPath);
appendTrackedPath(m_screenAsset.themePath);
if (const auto* document = m_screenPlayer.GetDocument(); document != nullptr) {
for (const std::string& dependency : document->dependencies) {
@@ -503,14 +512,18 @@ bool Application::DetectTrackedFileChange() const {
}
void Application::AppendRuntimeOverlay(UIDrawData& drawData, float width, float height) const {
const bool authoredMode = m_useStructuredScreen && m_screenPlayer.IsLoaded();
const bool authoredMode =
!m_screenAsset.documentPath.empty() && m_useStructuredScreen && m_screenPlayer.IsLoaded();
const float panelWidth = authoredMode ? 430.0f : 380.0f;
std::vector<std::string> detailLines = {};
detailLines.push_back(
authoredMode
? "Hot reload watches editor shell resources."
: "Authored editor shell failed to load.");
detailLines.push_back("Document: editor_shell.xcui");
? "Hot reload watches editor shell document and dependencies."
: "Editor shell is composed directly from fixed code definitions.");
detailLines.push_back(
authoredMode
? "Document: " + std::filesystem::path(m_screenAsset.documentPath).filename().string()
: "Document: (none)");
if (authoredMode) {
const auto& inputDebug = m_documentHost.GetInputDebugSnapshot();
@@ -560,7 +573,7 @@ void Application::AppendRuntimeOverlay(UIDrawData& drawData, float width, float
} else if (!m_autoScreenshot.GetLastCaptureError().empty()) {
detailLines.push_back(TruncateText(m_autoScreenshot.GetLastCaptureError(), 78u));
} else if (!authoredMode) {
detailLines.push_back("No fallback sandbox is rendered in this host.");
detailLines.push_back("No authored XCUI document is used by the editor shell host.");
}
const float panelHeight = 38.0f + static_cast<float>(detailLines.size()) * 18.0f;

View File

@@ -8,11 +8,11 @@
#include "Host/InputModifierTracker.h"
#include "Host/NativeRenderer.h"
#include "Core/EditorShellAsset.h"
#include "Shell/EditorShellAsset.h"
#include <XCEditor/Core/UIEditorShellInteraction.h>
#include <XCEditor/Core/UIEditorShortcutManager.h>
#include <XCEditor/Core/UIEditorWorkspaceController.h>
#include <XCEditor/Shell/UIEditorShellInteraction.h>
#include <XCEditor/Foundation/UIEditorShortcutManager.h>
#include <XCEditor/Shell/UIEditorWorkspaceController.h>
#include <XCEngine/UI/Runtime/UIScreenDocumentHost.h>
#include <XCEngine/UI/Runtime/UIScreenPlayer.h>
@@ -50,7 +50,6 @@ inline StructuredEditorShellBinding BuildStructuredEditorShellBinding(
StructuredEditorShellBinding binding = {};
binding.screenAsset.screenId = asset.screenId;
binding.screenAsset.documentPath = asset.documentPath.string();
binding.screenAsset.themePath = asset.themePath.string();
binding.workspaceController =
UIEditorWorkspaceController(asset.panelRegistry, asset.workspace, asset.workspaceSession);
binding.shellDefinition = asset.shellDefinition;

View File

@@ -8,6 +8,9 @@
#include <cstdio>
#include <sstream>
#include <system_error>
#include <vector>
#include <windows.h>
namespace XCEngine::UI::Editor::Host {
@@ -30,16 +33,47 @@ bool IsAutoCaptureOnStartupEnabled() {
normalized != "no";
}
std::filesystem::path GetExecutableDirectory() {
std::vector<wchar_t> buffer(MAX_PATH);
while (true) {
const DWORD copied = ::GetModuleFileNameW(
nullptr,
buffer.data(),
static_cast<DWORD>(buffer.size()));
if (copied == 0u) {
return std::filesystem::current_path().lexically_normal();
}
if (copied < buffer.size() - 1u) {
return std::filesystem::path(std::wstring(buffer.data(), copied))
.parent_path()
.lexically_normal();
}
buffer.resize(buffer.size() * 2u);
}
}
std::filesystem::path ResolveBuildCaptureRoot(const std::filesystem::path& requestedCaptureRoot) {
std::filesystem::path captureRoot = GetExecutableDirectory() / "captures";
const std::filesystem::path scenarioPath = requestedCaptureRoot.parent_path().filename();
if (!scenarioPath.empty() && scenarioPath != "captures") {
captureRoot /= scenarioPath;
}
return captureRoot.lexically_normal();
}
} // namespace
void AutoScreenshotController::Initialize(const std::filesystem::path& captureRoot) {
m_captureRoot = captureRoot.lexically_normal();
m_captureRoot = ResolveBuildCaptureRoot(captureRoot);
m_historyRoot = (m_captureRoot / "history").lexically_normal();
m_latestCapturePath = (m_captureRoot / "latest.png").lexically_normal();
m_captureCount = 0;
m_capturePending = false;
m_pendingReason.clear();
m_lastCaptureSummary.clear();
m_lastCaptureSummary = "Output: " + m_captureRoot.string();
m_lastCaptureError.clear();
if (IsAutoCaptureOnStartupEnabled()) {
RequestCapture("startup");

View File

@@ -0,0 +1,54 @@
#pragma once
#include <XCEditor/Fields/UIEditorTextFieldInteraction.h>
#include <XCEngine/UI/Types.h>
#include <string>
#include <vector>
namespace XCEngine::UI::Editor {
struct UIEditorInlineRenameSessionState {
bool active = false;
std::string itemId = {};
Widgets::UIEditorTextFieldSpec textFieldSpec = {};
UIEditorTextFieldInteractionState textFieldInteraction = {};
};
struct UIEditorInlineRenameSessionRequest {
bool beginSession = false;
std::string itemId = {};
std::string initialText = {};
::XCEngine::UI::UIRect bounds = {};
};
struct UIEditorInlineRenameSessionResult {
bool consumed = false;
bool sessionStarted = false;
bool sessionCommitted = false;
bool sessionCanceled = false;
bool valueChanged = false;
bool active = false;
std::string itemId = {};
std::string valueBefore = {};
std::string valueAfter = {};
UIEditorTextFieldInteractionResult textFieldResult = {};
};
struct UIEditorInlineRenameSessionFrame {
Widgets::UIEditorTextFieldLayout layout = {};
UIEditorInlineRenameSessionResult result = {};
};
Widgets::UIEditorTextFieldMetrics BuildUIEditorInlineRenameTextFieldMetrics(
const ::XCEngine::UI::UIRect& bounds,
const Widgets::UIEditorTextFieldMetrics& metrics = {});
UIEditorInlineRenameSessionFrame UpdateUIEditorInlineRenameSession(
UIEditorInlineRenameSessionState& state,
const UIEditorInlineRenameSessionRequest& request,
const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents,
const Widgets::UIEditorTextFieldMetrics& metrics = {});
} // namespace XCEngine::UI::Editor

View File

@@ -1,6 +1,6 @@
#pragma once
#include <XCEditor/Widgets/UIEditorListView.h>
#include <XCEditor/Collections/UIEditorListView.h>
#include <XCEngine/UI/Types.h>
#include <XCEngine/UI/Widgets/UIKeyboardNavigationModel.h>
@@ -14,6 +14,7 @@ namespace XCEngine::UI::Editor {
struct UIEditorListViewInteractionState {
Widgets::UIEditorListViewState listViewState = {};
::XCEngine::UI::Widgets::UIKeyboardNavigationModel keyboardNavigation = {};
std::string selectionAnchorId = {};
::XCEngine::UI::UIPoint pointerPosition = {};
bool hasPointerPosition = false;
};
@@ -23,8 +24,10 @@ struct UIEditorListViewInteractionResult {
bool selectionChanged = false;
bool keyboardNavigated = false;
bool secondaryClicked = false;
bool renameRequested = false;
Widgets::UIEditorListViewHitTarget hitTarget = {};
std::string selectedItemId = {};
std::string renameItemId = {};
std::size_t selectedIndex = Widgets::UIEditorListViewInvalidIndex;
};

View File

@@ -25,7 +25,7 @@ struct UIEditorScrollViewMetrics {
float scrollbarWidth = 10.0f;
float scrollbarInset = 4.0f;
float minThumbHeight = 28.0f;
float cornerRounding = 6.0f;
float cornerRounding = 8.0f;
float borderThickness = 1.0f;
float focusedBorderThickness = 2.0f;
float wheelStep = 48.0f;
@@ -33,19 +33,19 @@ struct UIEditorScrollViewMetrics {
struct UIEditorScrollViewPalette {
::XCEngine::UI::UIColor surfaceColor =
::XCEngine::UI::UIColor(0.14f, 0.14f, 0.14f, 1.0f);
::XCEngine::UI::UIColor(0.15f, 0.15f, 0.16f, 1.0f);
::XCEngine::UI::UIColor borderColor =
::XCEngine::UI::UIColor(0.29f, 0.29f, 0.29f, 1.0f);
::XCEngine::UI::UIColor(0.30f, 0.32f, 0.34f, 1.0f);
::XCEngine::UI::UIColor focusedBorderColor =
::XCEngine::UI::UIColor(0.84f, 0.84f, 0.84f, 1.0f);
::XCEngine::UI::UIColor(0.78f, 0.80f, 0.84f, 1.0f);
::XCEngine::UI::UIColor scrollbarTrackColor =
::XCEngine::UI::UIColor(0.18f, 0.18f, 0.18f, 1.0f);
::XCEngine::UI::UIColor(0.19f, 0.19f, 0.21f, 1.0f);
::XCEngine::UI::UIColor scrollbarThumbColor =
::XCEngine::UI::UIColor(0.31f, 0.31f, 0.31f, 1.0f);
::XCEngine::UI::UIColor(0.32f, 0.34f, 0.36f, 1.0f);
::XCEngine::UI::UIColor scrollbarThumbHoverColor =
::XCEngine::UI::UIColor(0.38f, 0.38f, 0.38f, 1.0f);
::XCEngine::UI::UIColor(0.42f, 0.44f, 0.47f, 1.0f);
::XCEngine::UI::UIColor scrollbarThumbActiveColor =
::XCEngine::UI::UIColor(0.46f, 0.46f, 0.46f, 1.0f);
::XCEngine::UI::UIColor(0.50f, 0.52f, 0.56f, 1.0f);
};
struct UIEditorScrollViewLayout {

View File

@@ -1,6 +1,6 @@
#pragma once
#include <XCEditor/Widgets/UIEditorScrollView.h>
#include <XCEditor/Collections/UIEditorScrollView.h>
#include <XCEngine/UI/Types.h>

View File

@@ -46,41 +46,41 @@ struct UIEditorTabStripMetrics {
struct UIEditorTabStripPalette {
::XCEngine::UI::UIColor stripBackgroundColor =
::XCEngine::UI::UIColor(0.17f, 0.17f, 0.17f, 1.0f);
::XCEngine::UI::UIColor(0.15f, 0.15f, 0.16f, 1.0f);
::XCEngine::UI::UIColor headerBackgroundColor =
::XCEngine::UI::UIColor(0.20f, 0.20f, 0.20f, 1.0f);
::XCEngine::UI::UIColor(0.18f, 0.18f, 0.19f, 1.0f);
::XCEngine::UI::UIColor contentBackgroundColor =
::XCEngine::UI::UIColor(0.21f, 0.21f, 0.21f, 1.0f);
::XCEngine::UI::UIColor(0.15f, 0.15f, 0.16f, 1.0f);
::XCEngine::UI::UIColor stripBorderColor =
::XCEngine::UI::UIColor(0.30f, 0.30f, 0.30f, 1.0f);
::XCEngine::UI::UIColor(0.30f, 0.32f, 0.34f, 1.0f);
::XCEngine::UI::UIColor focusedBorderColor =
::XCEngine::UI::UIColor(0.86f, 0.86f, 0.86f, 1.0f);
::XCEngine::UI::UIColor(0.78f, 0.80f, 0.84f, 1.0f);
::XCEngine::UI::UIColor tabColor =
::XCEngine::UI::UIColor(0.23f, 0.23f, 0.23f, 1.0f);
::XCEngine::UI::UIColor(0.21f, 0.22f, 0.24f, 1.0f);
::XCEngine::UI::UIColor tabHoveredColor =
::XCEngine::UI::UIColor(0.27f, 0.27f, 0.27f, 1.0f);
::XCEngine::UI::UIColor(0.27f, 0.28f, 0.30f, 1.0f);
::XCEngine::UI::UIColor tabSelectedColor =
::XCEngine::UI::UIColor(0.29f, 0.29f, 0.29f, 1.0f);
::XCEngine::UI::UIColor(0.33f, 0.35f, 0.38f, 1.0f);
::XCEngine::UI::UIColor tabBorderColor =
::XCEngine::UI::UIColor(0.32f, 0.32f, 0.32f, 1.0f);
::XCEngine::UI::UIColor(0.32f, 0.34f, 0.36f, 1.0f);
::XCEngine::UI::UIColor tabHoveredBorderColor =
::XCEngine::UI::UIColor(0.46f, 0.46f, 0.46f, 1.0f);
::XCEngine::UI::UIColor(0.42f, 0.44f, 0.47f, 1.0f);
::XCEngine::UI::UIColor tabSelectedBorderColor =
::XCEngine::UI::UIColor(0.72f, 0.72f, 0.72f, 1.0f);
::XCEngine::UI::UIColor(0.50f, 0.52f, 0.56f, 1.0f);
::XCEngine::UI::UIColor textPrimary =
::XCEngine::UI::UIColor(0.94f, 0.94f, 0.94f, 1.0f);
::XCEngine::UI::UIColor(0.93f, 0.94f, 0.96f, 1.0f);
::XCEngine::UI::UIColor textSecondary =
::XCEngine::UI::UIColor(0.76f, 0.76f, 0.76f, 1.0f);
::XCEngine::UI::UIColor(0.70f, 0.72f, 0.74f, 1.0f);
::XCEngine::UI::UIColor textMuted =
::XCEngine::UI::UIColor(0.60f, 0.60f, 0.60f, 1.0f);
::XCEngine::UI::UIColor(0.58f, 0.59f, 0.62f, 1.0f);
::XCEngine::UI::UIColor closeButtonColor =
::XCEngine::UI::UIColor(0.25f, 0.25f, 0.25f, 1.0f);
::XCEngine::UI::UIColor(0.21f, 0.22f, 0.24f, 1.0f);
::XCEngine::UI::UIColor closeButtonHoveredColor =
::XCEngine::UI::UIColor(0.36f, 0.36f, 0.36f, 1.0f);
::XCEngine::UI::UIColor(0.27f, 0.28f, 0.30f, 1.0f);
::XCEngine::UI::UIColor closeButtonBorderColor =
::XCEngine::UI::UIColor(0.44f, 0.44f, 0.44f, 1.0f);
::XCEngine::UI::UIColor(0.42f, 0.44f, 0.47f, 1.0f);
::XCEngine::UI::UIColor closeGlyphColor =
::XCEngine::UI::UIColor(0.92f, 0.92f, 0.92f, 1.0f);
::XCEngine::UI::UIColor(0.93f, 0.94f, 0.96f, 1.0f);
};
struct UIEditorTabStripLayout {

View File

@@ -0,0 +1,47 @@
#pragma once
#include <XCEditor/Collections/UIEditorTabStrip.h>
#include <XCEngine/UI/DrawData.h>
#include <XCEngine/UI/Widgets/UITabStripModel.h>
#include <string>
#include <vector>
namespace XCEngine::UI::Editor {
struct UIEditorTabStripInteractionState {
Widgets::UIEditorTabStripState tabStripState = {};
::XCEngine::UI::Widgets::UITabStripModel navigationModel = {};
Widgets::UIEditorTabStripHitTarget pressedTarget = {};
::XCEngine::UI::UIPoint pointerPosition = {};
bool hasPointerPosition = false;
};
struct UIEditorTabStripInteractionResult {
bool consumed = false;
bool selectionChanged = false;
bool closeRequested = false;
bool keyboardNavigated = false;
Widgets::UIEditorTabStripHitTarget hitTarget = {};
std::string selectedTabId = {};
std::size_t selectedIndex = Widgets::UIEditorTabStripInvalidIndex;
std::string closedTabId = {};
std::size_t closedIndex = Widgets::UIEditorTabStripInvalidIndex;
};
struct UIEditorTabStripInteractionFrame {
Widgets::UIEditorTabStripLayout layout = {};
UIEditorTabStripInteractionResult result = {};
bool focused = false;
};
UIEditorTabStripInteractionFrame UpdateUIEditorTabStripInteraction(
UIEditorTabStripInteractionState& state,
std::string& selectedTabId,
const ::XCEngine::UI::UIRect& bounds,
const std::vector<Widgets::UIEditorTabStripItem>& items,
const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents,
const Widgets::UIEditorTabStripMetrics& metrics = {});
} // namespace XCEngine::UI::Editor

View File

@@ -1,9 +1,10 @@
#pragma once
#include <XCEditor/Widgets/UIEditorTreeView.h>
#include <XCEditor/Collections/UIEditorTreeView.h>
#include <XCEngine/UI/Types.h>
#include <XCEngine/UI/Widgets/UIExpansionModel.h>
#include <XCEngine/UI/Widgets/UIKeyboardNavigationModel.h>
#include <XCEngine/UI/Widgets/UISelectionModel.h>
#include <string>
@@ -13,6 +14,8 @@ namespace XCEngine::UI::Editor {
struct UIEditorTreeViewInteractionState {
Widgets::UIEditorTreeViewState treeViewState = {};
::XCEngine::UI::Widgets::UIKeyboardNavigationModel keyboardNavigation = {};
std::string selectionAnchorId = {};
::XCEngine::UI::UIPoint pointerPosition = {};
bool hasPointerPosition = false;
};
@@ -21,10 +24,14 @@ struct UIEditorTreeViewInteractionResult {
bool consumed = false;
bool selectionChanged = false;
bool expansionChanged = false;
bool keyboardNavigated = false;
bool secondaryClicked = false;
bool renameRequested = false;
Widgets::UIEditorTreeViewHitTarget hitTarget = {};
std::string selectedItemId = {};
std::string renameItemId = {};
std::string toggledItemId = {};
std::size_t selectedVisibleIndex = Widgets::UIEditorTreeViewInvalidIndex;
};
struct UIEditorTreeViewInteractionFrame {

View File

@@ -1,157 +0,0 @@
#pragma once
#include <XCEditor/Widgets/UIEditorBoolField.h>
#include <XCEditor/Widgets/UIEditorEnumField.h>
#include <XCEditor/Widgets/UIEditorMenuPopup.h>
#include <XCEditor/Widgets/UIEditorNumberField.h>
#include <XCEditor/Widgets/UIEditorPropertyGrid.h>
#include <XCEditor/Widgets/UIEditorTextField.h>
#include <XCEditor/Widgets/UIEditorVector2Field.h>
#include <XCEditor/Widgets/UIEditorVector3Field.h>
#include <XCEditor/Widgets/UIEditorVector4Field.h>
#include <XCEngine/UI/Style/Theme.h>
#include <string_view>
namespace XCEngine::UI::Editor {
float ResolveUIEditorThemeFloat(
const ::XCEngine::UI::Style::UITheme& theme,
std::string_view tokenName,
float fallbackValue);
::XCEngine::UI::UIColor ResolveUIEditorThemeColor(
const ::XCEngine::UI::Style::UITheme& theme,
std::string_view tokenName,
const ::XCEngine::UI::UIColor& fallbackValue);
Widgets::UIEditorBoolFieldMetrics ResolveUIEditorBoolFieldMetrics(
const ::XCEngine::UI::Style::UITheme& theme,
const Widgets::UIEditorBoolFieldMetrics& fallback = {});
Widgets::UIEditorBoolFieldPalette ResolveUIEditorBoolFieldPalette(
const ::XCEngine::UI::Style::UITheme& theme,
const Widgets::UIEditorBoolFieldPalette& fallback = {});
Widgets::UIEditorNumberFieldMetrics ResolveUIEditorNumberFieldMetrics(
const ::XCEngine::UI::Style::UITheme& theme,
const Widgets::UIEditorNumberFieldMetrics& fallback = {});
Widgets::UIEditorNumberFieldPalette ResolveUIEditorNumberFieldPalette(
const ::XCEngine::UI::Style::UITheme& theme,
const Widgets::UIEditorNumberFieldPalette& fallback = {});
Widgets::UIEditorTextFieldMetrics ResolveUIEditorTextFieldMetrics(
const ::XCEngine::UI::Style::UITheme& theme,
const Widgets::UIEditorTextFieldMetrics& fallback = {});
Widgets::UIEditorTextFieldPalette ResolveUIEditorTextFieldPalette(
const ::XCEngine::UI::Style::UITheme& theme,
const Widgets::UIEditorTextFieldPalette& fallback = {});
Widgets::UIEditorVector2FieldMetrics ResolveUIEditorVector2FieldMetrics(
const ::XCEngine::UI::Style::UITheme& theme,
const Widgets::UIEditorVector2FieldMetrics& fallback = {});
Widgets::UIEditorVector2FieldPalette ResolveUIEditorVector2FieldPalette(
const ::XCEngine::UI::Style::UITheme& theme,
const Widgets::UIEditorVector2FieldPalette& fallback = {});
Widgets::UIEditorVector3FieldMetrics ResolveUIEditorVector3FieldMetrics(
const ::XCEngine::UI::Style::UITheme& theme,
const Widgets::UIEditorVector3FieldMetrics& fallback = {});
Widgets::UIEditorVector3FieldPalette ResolveUIEditorVector3FieldPalette(
const ::XCEngine::UI::Style::UITheme& theme,
const Widgets::UIEditorVector3FieldPalette& fallback = {});
Widgets::UIEditorVector4FieldMetrics ResolveUIEditorVector4FieldMetrics(
const ::XCEngine::UI::Style::UITheme& theme,
const Widgets::UIEditorVector4FieldMetrics& fallback = {});
Widgets::UIEditorVector4FieldPalette ResolveUIEditorVector4FieldPalette(
const ::XCEngine::UI::Style::UITheme& theme,
const Widgets::UIEditorVector4FieldPalette& fallback = {});
Widgets::UIEditorEnumFieldMetrics ResolveUIEditorEnumFieldMetrics(
const ::XCEngine::UI::Style::UITheme& theme,
const Widgets::UIEditorEnumFieldMetrics& fallback = {});
Widgets::UIEditorEnumFieldPalette ResolveUIEditorEnumFieldPalette(
const ::XCEngine::UI::Style::UITheme& theme,
const Widgets::UIEditorEnumFieldPalette& fallback = {});
Widgets::UIEditorMenuPopupMetrics ResolveUIEditorMenuPopupMetrics(
const ::XCEngine::UI::Style::UITheme& theme,
const Widgets::UIEditorMenuPopupMetrics& fallback = {});
Widgets::UIEditorMenuPopupPalette ResolveUIEditorMenuPopupPalette(
const ::XCEngine::UI::Style::UITheme& theme,
const Widgets::UIEditorMenuPopupPalette& fallback = {});
Widgets::UIEditorPropertyGridMetrics ResolveUIEditorPropertyGridMetrics(
const ::XCEngine::UI::Style::UITheme& theme,
const Widgets::UIEditorPropertyGridMetrics& fallback = {});
Widgets::UIEditorPropertyGridPalette ResolveUIEditorPropertyGridPalette(
const ::XCEngine::UI::Style::UITheme& theme,
const Widgets::UIEditorPropertyGridPalette& fallback = {});
Widgets::UIEditorBoolFieldMetrics BuildUIEditorHostedBoolFieldMetrics(
const Widgets::UIEditorPropertyGridMetrics& propertyGridMetrics,
const Widgets::UIEditorBoolFieldMetrics& fallback = {});
Widgets::UIEditorBoolFieldPalette BuildUIEditorHostedBoolFieldPalette(
const Widgets::UIEditorPropertyGridPalette& propertyGridPalette,
const Widgets::UIEditorBoolFieldPalette& fallback = {});
Widgets::UIEditorNumberFieldMetrics BuildUIEditorHostedNumberFieldMetrics(
const Widgets::UIEditorPropertyGridMetrics& propertyGridMetrics,
const Widgets::UIEditorNumberFieldMetrics& fallback = {});
Widgets::UIEditorNumberFieldPalette BuildUIEditorHostedNumberFieldPalette(
const Widgets::UIEditorPropertyGridPalette& propertyGridPalette,
const Widgets::UIEditorNumberFieldPalette& fallback = {});
Widgets::UIEditorTextFieldMetrics BuildUIEditorHostedTextFieldMetrics(
const Widgets::UIEditorPropertyGridMetrics& propertyGridMetrics,
const Widgets::UIEditorTextFieldMetrics& fallback = {});
Widgets::UIEditorTextFieldPalette BuildUIEditorHostedTextFieldPalette(
const Widgets::UIEditorPropertyGridPalette& propertyGridPalette,
const Widgets::UIEditorTextFieldPalette& fallback = {});
Widgets::UIEditorVector2FieldMetrics BuildUIEditorHostedVector2FieldMetrics(
const Widgets::UIEditorPropertyGridMetrics& propertyGridMetrics,
const Widgets::UIEditorVector2FieldMetrics& fallback = {});
Widgets::UIEditorVector2FieldPalette BuildUIEditorHostedVector2FieldPalette(
const Widgets::UIEditorPropertyGridPalette& propertyGridPalette,
const Widgets::UIEditorVector2FieldPalette& fallback = {});
Widgets::UIEditorVector3FieldMetrics BuildUIEditorHostedVector3FieldMetrics(
const Widgets::UIEditorPropertyGridMetrics& propertyGridMetrics,
const Widgets::UIEditorVector3FieldMetrics& fallback = {});
Widgets::UIEditorVector3FieldPalette BuildUIEditorHostedVector3FieldPalette(
const Widgets::UIEditorPropertyGridPalette& propertyGridPalette,
const Widgets::UIEditorVector3FieldPalette& fallback = {});
Widgets::UIEditorVector4FieldMetrics BuildUIEditorHostedVector4FieldMetrics(
const Widgets::UIEditorPropertyGridMetrics& propertyGridMetrics,
const Widgets::UIEditorVector4FieldMetrics& fallback = {});
Widgets::UIEditorVector4FieldPalette BuildUIEditorHostedVector4FieldPalette(
const Widgets::UIEditorPropertyGridPalette& propertyGridPalette,
const Widgets::UIEditorVector4FieldPalette& fallback = {});
Widgets::UIEditorEnumFieldMetrics BuildUIEditorHostedEnumFieldMetrics(
const Widgets::UIEditorPropertyGridMetrics& propertyGridMetrics,
const Widgets::UIEditorEnumFieldMetrics& fallback = {});
Widgets::UIEditorEnumFieldPalette BuildUIEditorHostedEnumFieldPalette(
const Widgets::UIEditorPropertyGridPalette& propertyGridPalette,
const Widgets::UIEditorEnumFieldPalette& fallback = {});
} // namespace XCEngine::UI::Editor

View File

@@ -0,0 +1,188 @@
#pragma once
#include <XCEngine/UI/DrawData.h>
#include <cstdint>
#include <string>
namespace XCEngine::UI::Editor::Widgets {
enum class UIEditorAssetFieldHitTargetKind : std::uint8_t {
None = 0,
Row,
ValueBox,
PickerButton,
ClearButton
};
struct UIEditorAssetFieldSpec {
std::string fieldId = {};
std::string label = {};
std::string assetId = {};
std::string displayName = {};
std::string statusText = {};
std::string emptyText = "None";
::XCEngine::UI::UIColor tint = ::XCEngine::UI::UIColor(0.28f, 0.50f, 0.83f, 1.0f);
bool readOnly = false;
bool showPickerButton = true;
bool allowClear = true;
bool showStatusBadge = true;
};
struct UIEditorAssetFieldState {
UIEditorAssetFieldHitTargetKind hoveredTarget = UIEditorAssetFieldHitTargetKind::None;
UIEditorAssetFieldHitTargetKind activeTarget = UIEditorAssetFieldHitTargetKind::None;
bool focused = false;
};
struct UIEditorAssetFieldMetrics {
float rowHeight = 22.0f;
float horizontalPadding = 12.0f;
float labelControlGap = 20.0f;
float controlColumnStart = 236.0f;
float controlTrailingInset = 8.0f;
float valueBoxMinWidth = 116.0f;
float controlInsetY = 1.0f;
float labelTextInsetY = 0.0f;
float labelFontSize = 11.0f;
float valueTextInsetX = 6.0f;
float valueTextInsetY = 0.0f;
float valueFontSize = 12.0f;
float previewSize = 16.0f;
float previewInsetX = 4.0f;
float previewGap = 6.0f;
float previewGlyphFontSize = 10.0f;
float statusBadgeGap = 6.0f;
float statusBadgeMinWidth = 44.0f;
float statusBadgePaddingX = 6.0f;
float statusBadgeHeight = 14.0f;
float statusBadgeFontSize = 10.0f;
float actionButtonGap = 2.0f;
float actionButtonWidth = 20.0f;
float actionGlyphFontSize = 10.0f;
float actionGlyphInsetY = -1.0f;
float cornerRounding = 0.0f;
float valueBoxRounding = 2.0f;
float previewRounding = 2.0f;
float badgeRounding = 2.0f;
float borderThickness = 1.0f;
float focusedBorderThickness = 1.0f;
};
struct UIEditorAssetFieldPalette {
::XCEngine::UI::UIColor surfaceColor =
::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.0f);
::XCEngine::UI::UIColor borderColor =
::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.0f);
::XCEngine::UI::UIColor focusedBorderColor =
::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.0f);
::XCEngine::UI::UIColor rowHoverColor =
::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.0f);
::XCEngine::UI::UIColor rowActiveColor =
::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.0f);
::XCEngine::UI::UIColor valueBoxColor =
::XCEngine::UI::UIColor(0.18f, 0.18f, 0.18f, 1.0f);
::XCEngine::UI::UIColor valueBoxHoverColor =
::XCEngine::UI::UIColor(0.22f, 0.22f, 0.22f, 1.0f);
::XCEngine::UI::UIColor valueBoxActiveColor =
::XCEngine::UI::UIColor(0.24f, 0.24f, 0.24f, 1.0f);
::XCEngine::UI::UIColor readOnlyColor =
::XCEngine::UI::UIColor(0.16f, 0.16f, 0.16f, 1.0f);
::XCEngine::UI::UIColor controlBorderColor =
::XCEngine::UI::UIColor(0.12f, 0.12f, 0.12f, 1.0f);
::XCEngine::UI::UIColor controlFocusedBorderColor =
::XCEngine::UI::UIColor(0.42f, 0.42f, 0.42f, 1.0f);
::XCEngine::UI::UIColor labelColor =
::XCEngine::UI::UIColor(0.80f, 0.80f, 0.80f, 1.0f);
::XCEngine::UI::UIColor valueColor =
::XCEngine::UI::UIColor(0.92f, 0.92f, 0.92f, 1.0f);
::XCEngine::UI::UIColor emptyValueColor =
::XCEngine::UI::UIColor(0.60f, 0.60f, 0.60f, 1.0f);
::XCEngine::UI::UIColor previewBaseColor =
::XCEngine::UI::UIColor(0.23f, 0.25f, 0.28f, 1.0f);
::XCEngine::UI::UIColor previewEmptyColor =
::XCEngine::UI::UIColor(0.26f, 0.26f, 0.26f, 1.0f);
::XCEngine::UI::UIColor previewBorderColor =
::XCEngine::UI::UIColor(0.08f, 0.08f, 0.08f, 1.0f);
::XCEngine::UI::UIColor previewGlyphColor =
::XCEngine::UI::UIColor(0.94f, 0.94f, 0.94f, 1.0f);
::XCEngine::UI::UIColor statusBadgeColor =
::XCEngine::UI::UIColor(0.25f, 0.29f, 0.34f, 1.0f);
::XCEngine::UI::UIColor statusBadgeBorderColor =
::XCEngine::UI::UIColor(0.14f, 0.14f, 0.14f, 1.0f);
::XCEngine::UI::UIColor statusBadgeTextColor =
::XCEngine::UI::UIColor(0.82f, 0.87f, 0.96f, 1.0f);
::XCEngine::UI::UIColor actionButtonHoverColor =
::XCEngine::UI::UIColor(0.28f, 0.28f, 0.28f, 1.0f);
::XCEngine::UI::UIColor actionButtonActiveColor =
::XCEngine::UI::UIColor(0.32f, 0.32f, 0.32f, 1.0f);
::XCEngine::UI::UIColor actionButtonColor =
::XCEngine::UI::UIColor(0.16f, 0.16f, 0.16f, 1.0f);
::XCEngine::UI::UIColor separatorColor =
::XCEngine::UI::UIColor(0.24f, 0.24f, 0.24f, 1.0f);
::XCEngine::UI::UIColor pickerGlyphColor =
::XCEngine::UI::UIColor(0.88f, 0.88f, 0.88f, 1.0f);
::XCEngine::UI::UIColor clearGlyphColor =
::XCEngine::UI::UIColor(0.95f, 0.68f, 0.68f, 1.0f);
};
struct UIEditorAssetFieldLayout {
::XCEngine::UI::UIRect bounds = {};
::XCEngine::UI::UIRect labelRect = {};
::XCEngine::UI::UIRect controlRect = {};
::XCEngine::UI::UIRect valueRect = {};
::XCEngine::UI::UIRect previewRect = {};
::XCEngine::UI::UIRect textRect = {};
::XCEngine::UI::UIRect statusBadgeRect = {};
::XCEngine::UI::UIRect pickerRect = {};
::XCEngine::UI::UIRect clearRect = {};
};
struct UIEditorAssetFieldHitTarget {
UIEditorAssetFieldHitTargetKind kind = UIEditorAssetFieldHitTargetKind::None;
};
bool HasUIEditorAssetFieldValue(const UIEditorAssetFieldSpec& spec);
std::string ResolveUIEditorAssetFieldValueText(const UIEditorAssetFieldSpec& spec);
std::string ResolveUIEditorAssetFieldPreviewGlyph(const UIEditorAssetFieldSpec& spec);
bool IsUIEditorAssetFieldPointInside(
const ::XCEngine::UI::UIRect& rect,
const ::XCEngine::UI::UIPoint& point);
UIEditorAssetFieldLayout BuildUIEditorAssetFieldLayout(
const ::XCEngine::UI::UIRect& bounds,
const UIEditorAssetFieldSpec& spec,
const UIEditorAssetFieldMetrics& metrics = {});
UIEditorAssetFieldHitTarget HitTestUIEditorAssetField(
const UIEditorAssetFieldLayout& layout,
const ::XCEngine::UI::UIPoint& point);
void AppendUIEditorAssetFieldBackground(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorAssetFieldLayout& layout,
const UIEditorAssetFieldSpec& spec,
const UIEditorAssetFieldState& state,
const UIEditorAssetFieldPalette& palette = {},
const UIEditorAssetFieldMetrics& metrics = {});
void AppendUIEditorAssetFieldForeground(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorAssetFieldLayout& layout,
const UIEditorAssetFieldSpec& spec,
const UIEditorAssetFieldState& state,
const UIEditorAssetFieldPalette& palette = {},
const UIEditorAssetFieldMetrics& metrics = {});
void AppendUIEditorAssetField(
::XCEngine::UI::UIDrawList& drawList,
const ::XCEngine::UI::UIRect& bounds,
const UIEditorAssetFieldSpec& spec,
const UIEditorAssetFieldState& state,
const UIEditorAssetFieldPalette& palette = {},
const UIEditorAssetFieldMetrics& metrics = {});
} // namespace XCEngine::UI::Editor::Widgets

View File

@@ -0,0 +1,44 @@
#pragma once
#include <XCEditor/Fields/UIEditorAssetField.h>
#include <XCEngine/UI/Types.h>
#include <string>
#include <vector>
namespace XCEngine::UI::Editor {
struct UIEditorAssetFieldInteractionState {
Widgets::UIEditorAssetFieldState fieldState = {};
::XCEngine::UI::UIPoint pointerPosition = {};
bool hasPointerPosition = false;
};
struct UIEditorAssetFieldInteractionResult {
bool consumed = false;
bool focusChanged = false;
bool valueChanged = false;
bool activateRequested = false;
bool pickerRequested = false;
bool clearRequested = false;
Widgets::UIEditorAssetFieldHitTarget hitTarget = {};
std::string assetIdBefore = {};
std::string assetIdAfter = {};
std::string displayNameBefore = {};
std::string displayNameAfter = {};
};
struct UIEditorAssetFieldInteractionFrame {
Widgets::UIEditorAssetFieldLayout layout = {};
UIEditorAssetFieldInteractionResult result = {};
};
UIEditorAssetFieldInteractionFrame UpdateUIEditorAssetFieldInteraction(
UIEditorAssetFieldInteractionState& state,
Widgets::UIEditorAssetFieldSpec& spec,
const ::XCEngine::UI::UIRect& bounds,
const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents,
const Widgets::UIEditorAssetFieldMetrics& metrics = {});
} // namespace XCEngine::UI::Editor

View File

@@ -1,6 +1,6 @@
#pragma once
#include <XCEditor/Widgets/UIEditorBoolField.h>
#include <XCEditor/Fields/UIEditorBoolField.h>
#include <XCEngine/UI/Types.h>

View File

@@ -0,0 +1,220 @@
#pragma once
#include <XCEngine/UI/DrawData.h>
#include <cstdint>
#include <string>
namespace XCEngine::UI::Editor::Widgets {
enum class UIEditorColorFieldHitTargetKind : std::uint8_t {
None = 0,
Row,
Swatch,
PopupSurface,
PopupCloseButton,
HueWheel,
SaturationValue,
RedChannel,
GreenChannel,
BlueChannel,
AlphaChannel
};
struct UIEditorColorFieldSpec {
std::string fieldId = {};
std::string label = {};
::XCEngine::UI::UIColor value = {};
bool showAlpha = true;
bool readOnly = false;
};
struct UIEditorColorFieldState {
UIEditorColorFieldHitTargetKind hoveredTarget = UIEditorColorFieldHitTargetKind::None;
UIEditorColorFieldHitTargetKind activeTarget = UIEditorColorFieldHitTargetKind::None;
bool focused = false;
bool popupOpen = false;
float hue = 0.0f;
bool hueValid = false;
};
struct UIEditorColorFieldMetrics {
float rowHeight = 22.0f;
float horizontalPadding = 12.0f;
float labelControlGap = 20.0f;
float controlColumnStart = 236.0f;
float controlTrailingInset = 8.0f;
float swatchWidth = 54.0f;
float swatchInsetY = 1.0f;
float labelTextInsetY = 0.0f;
float labelFontSize = 11.0f;
float popupWidth = 292.0f;
float popupPadding = 10.0f;
float popupGapY = 6.0f;
float popupHeaderHeight = 30.0f;
float popupTopRowHeight = 34.0f;
float popupPreviewWidth = 96.0f;
float popupPreviewHeight = 28.0f;
float popupCloseButtonSize = 18.0f;
float borderThickness = 1.0f;
float focusedBorderThickness = 1.0f;
float swatchRounding = 2.0f;
float popupBorderRounding = 3.0f;
float wheelOuterRadius = 110.0f;
float wheelRingThickness = 24.0f;
float saturationValueSize = 114.0f;
float wheelRegionHeight = 220.0f;
float channelRowHeight = 20.0f;
float numericBoxWidth = 62.0f;
float channelLabelWidth = 12.0f;
float hexLabelWidth = 84.0f;
float controlRowSpacing = 8.0f;
float popupFieldInset = 6.0f;
float titleFontSize = 12.0f;
float valueFontSize = 11.0f;
float valueTextInsetX = 6.0f;
float valueTextInsetY = 0.0f;
float checkerSize = 6.0f;
float handleRadius = 8.0f;
float ringHandleRadius = 12.0f;
};
struct UIEditorColorFieldPalette {
::XCEngine::UI::UIColor surfaceColor =
::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.0f);
::XCEngine::UI::UIColor borderColor =
::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.0f);
::XCEngine::UI::UIColor focusedBorderColor =
::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.0f);
::XCEngine::UI::UIColor rowHoverColor =
::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.0f);
::XCEngine::UI::UIColor rowActiveColor =
::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.0f);
::XCEngine::UI::UIColor swatchBorderColor =
::XCEngine::UI::UIColor(0.14f, 0.14f, 0.14f, 1.0f);
::XCEngine::UI::UIColor swatchHoverBorderColor =
::XCEngine::UI::UIColor(0.34f, 0.34f, 0.34f, 1.0f);
::XCEngine::UI::UIColor swatchReadOnlyOverlayColor =
::XCEngine::UI::UIColor(0.08f, 0.08f, 0.08f, 0.18f);
::XCEngine::UI::UIColor popupColor =
::XCEngine::UI::UIColor(0.24f, 0.24f, 0.24f, 1.0f);
::XCEngine::UI::UIColor popupBorderColor =
::XCEngine::UI::UIColor(0.15f, 0.15f, 0.15f, 1.0f);
::XCEngine::UI::UIColor popupHeaderColor =
::XCEngine::UI::UIColor(0.43f, 0.24f, 0.05f, 1.0f);
::XCEngine::UI::UIColor popupTitleColor =
::XCEngine::UI::UIColor(0.95f, 0.95f, 0.95f, 1.0f);
::XCEngine::UI::UIColor closeButtonColor =
::XCEngine::UI::UIColor(0.76f, 0.35f, 0.34f, 1.0f);
::XCEngine::UI::UIColor closeButtonHoverColor =
::XCEngine::UI::UIColor(0.82f, 0.40f, 0.39f, 1.0f);
::XCEngine::UI::UIColor closeGlyphColor =
::XCEngine::UI::UIColor(0.95f, 0.95f, 0.95f, 1.0f);
::XCEngine::UI::UIColor labelColor =
::XCEngine::UI::UIColor(0.88f, 0.88f, 0.88f, 1.0f);
::XCEngine::UI::UIColor popupTextColor =
::XCEngine::UI::UIColor(0.86f, 0.86f, 0.86f, 1.0f);
::XCEngine::UI::UIColor popupTextMutedColor =
::XCEngine::UI::UIColor(0.72f, 0.72f, 0.72f, 1.0f);
::XCEngine::UI::UIColor previewBorderColor =
::XCEngine::UI::UIColor(0.12f, 0.12f, 0.12f, 1.0f);
::XCEngine::UI::UIColor previewBaseColor =
::XCEngine::UI::UIColor(0.19f, 0.19f, 0.19f, 1.0f);
::XCEngine::UI::UIColor checkerLightColor =
::XCEngine::UI::UIColor(0.84f, 0.84f, 0.84f, 1.0f);
::XCEngine::UI::UIColor checkerDarkColor =
::XCEngine::UI::UIColor(0.55f, 0.55f, 0.55f, 1.0f);
::XCEngine::UI::UIColor sliderBorderColor =
::XCEngine::UI::UIColor(0.12f, 0.12f, 0.12f, 1.0f);
::XCEngine::UI::UIColor numericBoxColor =
::XCEngine::UI::UIColor(0.19f, 0.19f, 0.19f, 1.0f);
::XCEngine::UI::UIColor numericBoxBorderColor =
::XCEngine::UI::UIColor(0.12f, 0.12f, 0.12f, 1.0f);
::XCEngine::UI::UIColor numericBoxTextColor =
::XCEngine::UI::UIColor(0.90f, 0.90f, 0.90f, 1.0f);
::XCEngine::UI::UIColor handleFillColor =
::XCEngine::UI::UIColor(1.0f, 1.0f, 1.0f, 1.0f);
::XCEngine::UI::UIColor handleStrokeColor =
::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.4f);
};
struct UIEditorColorFieldLayout {
::XCEngine::UI::UIRect bounds = {};
::XCEngine::UI::UIRect labelRect = {};
::XCEngine::UI::UIRect controlRect = {};
::XCEngine::UI::UIRect swatchRect = {};
::XCEngine::UI::UIRect popupRect = {};
::XCEngine::UI::UIRect popupHeaderRect = {};
::XCEngine::UI::UIRect popupTitleRect = {};
::XCEngine::UI::UIRect popupCloseButtonRect = {};
::XCEngine::UI::UIRect popupBodyRect = {};
::XCEngine::UI::UIRect popupTopRowRect = {};
::XCEngine::UI::UIRect popupPreviewRect = {};
::XCEngine::UI::UIRect popupWheelRect = {};
::XCEngine::UI::UIPoint hueWheelCenter = {};
float hueWheelOuterRadius = 0.0f;
float hueWheelInnerRadius = 0.0f;
::XCEngine::UI::UIRect saturationValueRect = {};
::XCEngine::UI::UIRect redLabelRect = {};
::XCEngine::UI::UIRect redSliderRect = {};
::XCEngine::UI::UIRect redValueRect = {};
::XCEngine::UI::UIRect greenLabelRect = {};
::XCEngine::UI::UIRect greenSliderRect = {};
::XCEngine::UI::UIRect greenValueRect = {};
::XCEngine::UI::UIRect blueLabelRect = {};
::XCEngine::UI::UIRect blueSliderRect = {};
::XCEngine::UI::UIRect blueValueRect = {};
::XCEngine::UI::UIRect alphaLabelRect = {};
::XCEngine::UI::UIRect alphaSliderRect = {};
::XCEngine::UI::UIRect alphaValueRect = {};
::XCEngine::UI::UIRect hexLabelRect = {};
::XCEngine::UI::UIRect hexValueRect = {};
};
struct UIEditorColorFieldHitTarget {
UIEditorColorFieldHitTargetKind kind = UIEditorColorFieldHitTargetKind::None;
};
std::string FormatUIEditorColorFieldRgbaText(
const UIEditorColorFieldSpec& spec);
std::string FormatUIEditorColorFieldHexText(
const UIEditorColorFieldSpec& spec);
UIEditorColorFieldLayout BuildUIEditorColorFieldLayout(
const ::XCEngine::UI::UIRect& bounds,
const UIEditorColorFieldSpec& spec,
const UIEditorColorFieldMetrics& metrics = {},
const ::XCEngine::UI::UIRect& viewportRect = {});
UIEditorColorFieldHitTarget HitTestUIEditorColorField(
const UIEditorColorFieldLayout& layout,
bool popupOpen,
const ::XCEngine::UI::UIPoint& point);
void AppendUIEditorColorFieldBackground(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorColorFieldLayout& layout,
const UIEditorColorFieldSpec& spec,
const UIEditorColorFieldState& state,
const UIEditorColorFieldPalette& palette = {},
const UIEditorColorFieldMetrics& metrics = {});
void AppendUIEditorColorFieldForeground(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorColorFieldLayout& layout,
const UIEditorColorFieldSpec& spec,
const UIEditorColorFieldState& state,
const UIEditorColorFieldPalette& palette = {},
const UIEditorColorFieldMetrics& metrics = {});
void AppendUIEditorColorField(
::XCEngine::UI::UIDrawList& drawList,
const ::XCEngine::UI::UIRect& bounds,
const UIEditorColorFieldSpec& spec,
const UIEditorColorFieldState& state,
const UIEditorColorFieldPalette& palette = {},
const UIEditorColorFieldMetrics& metrics = {},
const ::XCEngine::UI::UIRect& viewportRect = {});
} // namespace XCEngine::UI::Editor::Widgets

View File

@@ -0,0 +1,41 @@
#pragma once
#include <XCEditor/Fields/UIEditorColorField.h>
#include <XCEngine/UI/Types.h>
#include <vector>
namespace XCEngine::UI::Editor {
struct UIEditorColorFieldInteractionState {
Widgets::UIEditorColorFieldState colorFieldState = {};
::XCEngine::UI::UIPoint pointerPosition = {};
bool hasPointerPosition = false;
};
struct UIEditorColorFieldInteractionResult {
bool consumed = false;
bool focusChanged = false;
bool popupOpened = false;
bool popupClosed = false;
bool colorChanged = false;
Widgets::UIEditorColorFieldHitTarget hitTarget = {};
::XCEngine::UI::UIColor valueBefore = {};
::XCEngine::UI::UIColor valueAfter = {};
};
struct UIEditorColorFieldInteractionFrame {
Widgets::UIEditorColorFieldLayout layout = {};
UIEditorColorFieldInteractionResult result = {};
};
UIEditorColorFieldInteractionFrame UpdateUIEditorColorFieldInteraction(
UIEditorColorFieldInteractionState& state,
Widgets::UIEditorColorFieldSpec& spec,
const ::XCEngine::UI::UIRect& bounds,
const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents,
const Widgets::UIEditorColorFieldMetrics& metrics = {},
const ::XCEngine::UI::UIRect& viewportRect = {});
} // namespace XCEngine::UI::Editor

View File

@@ -1,7 +1,7 @@
#pragma once
#include <XCEditor/Widgets/UIEditorMenuPopup.h>
#include <XCEditor/Widgets/UIEditorEnumField.h>
#include <XCEditor/Shell/UIEditorMenuPopup.h>
#include <XCEditor/Fields/UIEditorEnumField.h>
#include <XCEngine/UI/Types.h>

View File

@@ -0,0 +1,101 @@
#pragma once
#include <XCEditor/Fields/UIEditorAssetField.h>
#include <XCEditor/Fields/UIEditorBoolField.h>
#include <XCEditor/Fields/UIEditorColorField.h>
#include <XCEditor/Fields/UIEditorEnumField.h>
#include <XCEditor/Fields/UIEditorNumberField.h>
#include <XCEditor/Fields/UIEditorObjectField.h>
#include <XCEditor/Fields/UIEditorPropertyGrid.h>
#include <XCEditor/Fields/UIEditorTextField.h>
#include <XCEditor/Fields/UIEditorVector2Field.h>
#include <XCEditor/Fields/UIEditorVector3Field.h>
#include <XCEditor/Fields/UIEditorVector4Field.h>
namespace XCEngine::UI::Editor {
const Widgets::UIEditorPropertyGridMetrics& GetUIEditorFixedPropertyGridMetrics();
const Widgets::UIEditorPropertyGridPalette& GetUIEditorFixedPropertyGridPalette();
Widgets::UIEditorBoolFieldMetrics BuildUIEditorPropertyGridBoolFieldMetrics(
const Widgets::UIEditorPropertyGridMetrics& propertyGridMetrics,
const Widgets::UIEditorBoolFieldMetrics& fallback = {});
Widgets::UIEditorBoolFieldPalette BuildUIEditorPropertyGridBoolFieldPalette(
const Widgets::UIEditorPropertyGridPalette& propertyGridPalette,
const Widgets::UIEditorBoolFieldPalette& fallback = {});
Widgets::UIEditorNumberFieldMetrics BuildUIEditorPropertyGridNumberFieldMetrics(
const Widgets::UIEditorPropertyGridMetrics& propertyGridMetrics,
const Widgets::UIEditorNumberFieldMetrics& fallback = {});
Widgets::UIEditorNumberFieldPalette BuildUIEditorPropertyGridNumberFieldPalette(
const Widgets::UIEditorPropertyGridPalette& propertyGridPalette,
const Widgets::UIEditorNumberFieldPalette& fallback = {});
Widgets::UIEditorTextFieldMetrics BuildUIEditorPropertyGridTextFieldMetrics(
const Widgets::UIEditorPropertyGridMetrics& propertyGridMetrics,
const Widgets::UIEditorTextFieldMetrics& fallback = {});
Widgets::UIEditorTextFieldPalette BuildUIEditorPropertyGridTextFieldPalette(
const Widgets::UIEditorPropertyGridPalette& propertyGridPalette,
const Widgets::UIEditorTextFieldPalette& fallback = {});
Widgets::UIEditorVector2FieldMetrics BuildUIEditorPropertyGridVector2FieldMetrics(
const Widgets::UIEditorPropertyGridMetrics& propertyGridMetrics,
const Widgets::UIEditorVector2FieldMetrics& fallback = {});
Widgets::UIEditorVector2FieldPalette BuildUIEditorPropertyGridVector2FieldPalette(
const Widgets::UIEditorPropertyGridPalette& propertyGridPalette,
const Widgets::UIEditorVector2FieldPalette& fallback = {});
Widgets::UIEditorVector3FieldMetrics BuildUIEditorPropertyGridVector3FieldMetrics(
const Widgets::UIEditorPropertyGridMetrics& propertyGridMetrics,
const Widgets::UIEditorVector3FieldMetrics& fallback = {});
Widgets::UIEditorVector3FieldPalette BuildUIEditorPropertyGridVector3FieldPalette(
const Widgets::UIEditorPropertyGridPalette& propertyGridPalette,
const Widgets::UIEditorVector3FieldPalette& fallback = {});
Widgets::UIEditorVector4FieldMetrics BuildUIEditorPropertyGridVector4FieldMetrics(
const Widgets::UIEditorPropertyGridMetrics& propertyGridMetrics,
const Widgets::UIEditorVector4FieldMetrics& fallback = {});
Widgets::UIEditorVector4FieldPalette BuildUIEditorPropertyGridVector4FieldPalette(
const Widgets::UIEditorPropertyGridPalette& propertyGridPalette,
const Widgets::UIEditorVector4FieldPalette& fallback = {});
Widgets::UIEditorEnumFieldMetrics BuildUIEditorPropertyGridEnumFieldMetrics(
const Widgets::UIEditorPropertyGridMetrics& propertyGridMetrics,
const Widgets::UIEditorEnumFieldMetrics& fallback = {});
Widgets::UIEditorEnumFieldPalette BuildUIEditorPropertyGridEnumFieldPalette(
const Widgets::UIEditorPropertyGridPalette& propertyGridPalette,
const Widgets::UIEditorEnumFieldPalette& fallback = {});
Widgets::UIEditorColorFieldMetrics BuildUIEditorPropertyGridColorFieldMetrics(
const Widgets::UIEditorPropertyGridMetrics& propertyGridMetrics,
const Widgets::UIEditorColorFieldMetrics& fallback = {});
Widgets::UIEditorColorFieldPalette BuildUIEditorPropertyGridColorFieldPalette(
const Widgets::UIEditorPropertyGridPalette& propertyGridPalette,
const Widgets::UIEditorColorFieldPalette& fallback = {});
Widgets::UIEditorObjectFieldMetrics BuildUIEditorPropertyGridObjectFieldMetrics(
const Widgets::UIEditorPropertyGridMetrics& propertyGridMetrics,
const Widgets::UIEditorObjectFieldMetrics& fallback = {});
Widgets::UIEditorObjectFieldPalette BuildUIEditorPropertyGridObjectFieldPalette(
const Widgets::UIEditorPropertyGridPalette& propertyGridPalette,
const Widgets::UIEditorObjectFieldPalette& fallback = {});
Widgets::UIEditorAssetFieldMetrics BuildUIEditorPropertyGridAssetFieldMetrics(
const Widgets::UIEditorPropertyGridMetrics& propertyGridMetrics,
const Widgets::UIEditorAssetFieldMetrics& fallback = {});
Widgets::UIEditorAssetFieldPalette BuildUIEditorPropertyGridAssetFieldPalette(
const Widgets::UIEditorPropertyGridPalette& propertyGridPalette,
const Widgets::UIEditorAssetFieldPalette& fallback = {});
} // namespace XCEngine::UI::Editor

View File

@@ -1,6 +1,6 @@
#pragma once
#include <XCEditor/Widgets/UIEditorNumberField.h>
#include <XCEditor/Fields/UIEditorNumberField.h>
#include <XCEngine/UI/Text/UITextInputController.h>
#include <XCEngine/UI/Types.h>

View File

@@ -0,0 +1,153 @@
#pragma once
#include <XCEngine/UI/DrawData.h>
#include <cstdint>
#include <string>
namespace XCEngine::UI::Editor::Widgets {
enum class UIEditorObjectFieldHitTargetKind : std::uint8_t {
None = 0,
Row,
ValueBox,
ClearButton,
PickerButton
};
struct UIEditorObjectFieldSpec {
std::string fieldId = {};
std::string label = {};
std::string objectName = {};
std::string objectTypeName = {};
std::string emptyText = "(none)";
bool hasValue = false;
bool readOnly = false;
bool showClearButton = true;
bool showPickerButton = true;
};
struct UIEditorObjectFieldState {
UIEditorObjectFieldHitTargetKind hoveredTarget = UIEditorObjectFieldHitTargetKind::None;
UIEditorObjectFieldHitTargetKind activeTarget = UIEditorObjectFieldHitTargetKind::None;
bool focused = false;
};
struct UIEditorObjectFieldMetrics {
float rowHeight = 22.0f;
float horizontalPadding = 12.0f;
float labelControlGap = 20.0f;
float controlColumnStart = 236.0f;
float controlTrailingInset = 8.0f;
float valueBoxMinWidth = 96.0f;
float controlInsetY = 1.0f;
float labelTextInsetY = 0.0f;
float labelFontSize = 11.0f;
float valueTextInsetX = 5.0f;
float valueTextInsetY = 0.0f;
float valueFontSize = 12.0f;
float typeTextInsetX = 5.0f;
float typeTextInsetY = 0.0f;
float typeFontSize = 10.0f;
float typeMaxWidth = 96.0f;
float typeMinWidth = 44.0f;
float valueTypeGap = 6.0f;
float buttonWidth = 20.0f;
float buttonGlyphInsetY = -1.0f;
float buttonGlyphFontSize = 10.0f;
float cornerRounding = 0.0f;
float valueBoxRounding = 2.0f;
float borderThickness = 1.0f;
float focusedBorderThickness = 1.0f;
};
struct UIEditorObjectFieldPalette {
::XCEngine::UI::UIColor surfaceColor =
::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.0f);
::XCEngine::UI::UIColor borderColor =
::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.0f);
::XCEngine::UI::UIColor focusedBorderColor =
::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.0f);
::XCEngine::UI::UIColor rowHoverColor =
::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.0f);
::XCEngine::UI::UIColor rowActiveColor =
::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.0f);
::XCEngine::UI::UIColor valueBoxColor =
::XCEngine::UI::UIColor(0.18f, 0.18f, 0.18f, 1.0f);
::XCEngine::UI::UIColor valueBoxHoverColor =
::XCEngine::UI::UIColor(0.21f, 0.21f, 0.21f, 1.0f);
::XCEngine::UI::UIColor readOnlyColor =
::XCEngine::UI::UIColor(0.17f, 0.17f, 0.17f, 1.0f);
::XCEngine::UI::UIColor controlBorderColor =
::XCEngine::UI::UIColor(0.14f, 0.14f, 0.14f, 1.0f);
::XCEngine::UI::UIColor controlFocusedBorderColor =
::XCEngine::UI::UIColor(0.46f, 0.46f, 0.46f, 1.0f);
::XCEngine::UI::UIColor buttonColor =
::XCEngine::UI::UIColor(0.16f, 0.16f, 0.16f, 1.0f);
::XCEngine::UI::UIColor buttonHoverColor =
::XCEngine::UI::UIColor(0.22f, 0.22f, 0.22f, 1.0f);
::XCEngine::UI::UIColor buttonActiveColor =
::XCEngine::UI::UIColor(0.26f, 0.26f, 0.26f, 1.0f);
::XCEngine::UI::UIColor buttonGlyphColor =
::XCEngine::UI::UIColor(0.86f, 0.86f, 0.86f, 1.0f);
::XCEngine::UI::UIColor separatorColor =
::XCEngine::UI::UIColor(0.24f, 0.24f, 0.24f, 1.0f);
::XCEngine::UI::UIColor labelColor =
::XCEngine::UI::UIColor(0.80f, 0.80f, 0.80f, 1.0f);
::XCEngine::UI::UIColor valueColor =
::XCEngine::UI::UIColor(0.92f, 0.92f, 0.92f, 1.0f);
::XCEngine::UI::UIColor emptyValueColor =
::XCEngine::UI::UIColor(0.62f, 0.62f, 0.62f, 1.0f);
::XCEngine::UI::UIColor typeColor =
::XCEngine::UI::UIColor(0.68f, 0.68f, 0.68f, 1.0f);
};
struct UIEditorObjectFieldLayout {
::XCEngine::UI::UIRect bounds = {};
::XCEngine::UI::UIRect labelRect = {};
::XCEngine::UI::UIRect controlRect = {};
::XCEngine::UI::UIRect valueRect = {};
::XCEngine::UI::UIRect typeRect = {};
::XCEngine::UI::UIRect clearButtonRect = {};
::XCEngine::UI::UIRect pickerButtonRect = {};
};
struct UIEditorObjectFieldHitTarget {
UIEditorObjectFieldHitTargetKind kind = UIEditorObjectFieldHitTargetKind::None;
};
std::string ResolveUIEditorObjectFieldDisplayText(const UIEditorObjectFieldSpec& spec);
UIEditorObjectFieldLayout BuildUIEditorObjectFieldLayout(
const ::XCEngine::UI::UIRect& bounds,
const UIEditorObjectFieldSpec& spec,
const UIEditorObjectFieldMetrics& metrics = {});
UIEditorObjectFieldHitTarget HitTestUIEditorObjectField(
const UIEditorObjectFieldLayout& layout,
const ::XCEngine::UI::UIPoint& point);
void AppendUIEditorObjectFieldBackground(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorObjectFieldLayout& layout,
const UIEditorObjectFieldSpec& spec,
const UIEditorObjectFieldState& state,
const UIEditorObjectFieldPalette& palette = {},
const UIEditorObjectFieldMetrics& metrics = {});
void AppendUIEditorObjectFieldForeground(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorObjectFieldLayout& layout,
const UIEditorObjectFieldSpec& spec,
const UIEditorObjectFieldPalette& palette = {},
const UIEditorObjectFieldMetrics& metrics = {});
void AppendUIEditorObjectField(
::XCEngine::UI::UIDrawList& drawList,
const ::XCEngine::UI::UIRect& bounds,
const UIEditorObjectFieldSpec& spec,
const UIEditorObjectFieldState& state,
const UIEditorObjectFieldPalette& palette = {},
const UIEditorObjectFieldMetrics& metrics = {});
} // namespace XCEngine::UI::Editor::Widgets

View File

@@ -0,0 +1,37 @@
#pragma once
#include <XCEditor/Fields/UIEditorObjectField.h>
#include <XCEngine/UI/Types.h>
#include <vector>
namespace XCEngine::UI::Editor {
struct UIEditorObjectFieldInteractionState {
Widgets::UIEditorObjectFieldState fieldState = {};
::XCEngine::UI::UIPoint pointerPosition = {};
bool hasPointerPosition = false;
};
struct UIEditorObjectFieldInteractionResult {
bool consumed = false;
bool focusChanged = false;
bool activateRequested = false;
bool clearRequested = false;
Widgets::UIEditorObjectFieldHitTarget hitTarget = {};
};
struct UIEditorObjectFieldInteractionFrame {
Widgets::UIEditorObjectFieldLayout layout = {};
UIEditorObjectFieldInteractionResult result = {};
};
UIEditorObjectFieldInteractionFrame UpdateUIEditorObjectFieldInteraction(
UIEditorObjectFieldInteractionState& state,
const Widgets::UIEditorObjectFieldSpec& spec,
const ::XCEngine::UI::UIRect& bounds,
const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents,
const Widgets::UIEditorObjectFieldMetrics& metrics = {});
} // namespace XCEngine::UI::Editor

View File

@@ -5,7 +5,8 @@
#include <XCEngine/UI/Widgets/UIPropertyEditModel.h>
#include <XCEngine/UI/Widgets/UISelectionModel.h>
#include <XCEditor/Widgets/UIEditorMenuPopup.h>
#include <XCEditor/Fields/UIEditorColorField.h>
#include <XCEditor/Shell/UIEditorMenuPopup.h>
#include <array>
#include <cstddef>
@@ -23,6 +24,7 @@ enum class UIEditorPropertyGridFieldKind : std::uint8_t {
Bool,
Number,
Enum,
Color,
Vector2,
Vector3,
Vector4
@@ -58,6 +60,11 @@ struct UIEditorPropertyGridEnumFieldValue {
std::size_t selectedIndex = 0u;
};
struct UIEditorPropertyGridColorFieldValue {
::XCEngine::UI::UIColor value = {};
bool showAlpha = true;
};
struct UIEditorPropertyGridVector2FieldValue {
std::array<double, 2u> values = { 0.0, 0.0 };
std::array<std::string, 2u> componentLabels = { std::string("X"), std::string("Y") };
@@ -104,6 +111,7 @@ struct UIEditorPropertyGridField {
bool boolValue = false;
UIEditorPropertyGridNumberFieldValue numberValue = {};
UIEditorPropertyGridEnumFieldValue enumValue = {};
UIEditorPropertyGridColorFieldValue colorValue = {};
UIEditorPropertyGridVector2FieldValue vector2Value = {};
UIEditorPropertyGridVector3FieldValue vector3Value = {};
UIEditorPropertyGridVector4FieldValue vector4Value = {};
@@ -116,6 +124,11 @@ struct UIEditorPropertyGridSection {
float desiredHeaderHeight = 0.0f;
};
struct UIEditorPropertyGridColorFieldVisualState {
std::string fieldId = {};
UIEditorColorFieldState state = {};
};
struct UIEditorPropertyGridState {
std::string hoveredSectionId = {};
std::string hoveredFieldId = {};
@@ -124,6 +137,7 @@ struct UIEditorPropertyGridState {
std::string pressedFieldId = {};
std::string popupFieldId = {};
std::size_t popupHighlightedIndex = UIEditorPropertyGridInvalidIndex;
std::vector<UIEditorPropertyGridColorFieldVisualState> colorFieldStates = {};
};
struct UIEditorPropertyGridMetrics {

View File

@@ -1,6 +1,6 @@
#pragma once
#include <XCEditor/Widgets/UIEditorPropertyGrid.h>
#include <XCEditor/Fields/UIEditorPropertyGrid.h>
#include <XCEngine/UI/Text/UITextInputController.h>
#include <XCEngine/UI/Types.h>

View File

@@ -1,6 +1,6 @@
#pragma once
#include <XCEditor/Widgets/UIEditorTextField.h>
#include <XCEditor/Fields/UIEditorTextField.h>
#include <XCEngine/UI/Text/UITextInputController.h>
#include <XCEngine/UI/Types.h>

View File

@@ -1,6 +1,6 @@
#pragma once
#include <XCEditor/Widgets/UIEditorVector2Field.h>
#include <XCEditor/Fields/UIEditorVector2Field.h>
#include <XCEngine/UI/Text/UITextInputController.h>
#include <XCEngine/UI/Types.h>

View File

@@ -1,6 +1,6 @@
#pragma once
#include <XCEditor/Widgets/UIEditorVector3Field.h>
#include <XCEditor/Fields/UIEditorVector3Field.h>
#include <XCEngine/UI/Text/UITextInputController.h>
#include <XCEngine/UI/Types.h>

View File

@@ -1,6 +1,6 @@
#pragma once
#include <XCEditor/Widgets/UIEditorVector4Field.h>
#include <XCEditor/Fields/UIEditorVector4Field.h>
#include <XCEngine/UI/Text/UITextInputController.h>
#include <XCEngine/UI/Types.h>

View File

@@ -1,6 +1,6 @@
#pragma once
#include <XCEditor/Core/UIEditorCommandRegistry.h>
#include <XCEditor/Foundation/UIEditorCommandRegistry.h>
#include <cstdint>
#include <string>

View File

@@ -1,6 +1,6 @@
#pragma once
#include <XCEditor/Core/UIEditorWorkspaceController.h>
#include <XCEditor/Shell/UIEditorWorkspaceController.h>
#include <cstdint>
#include <string>

View File

@@ -1,6 +1,6 @@
#pragma once
#include <XCEditor/Core/UIEditorCommandDispatcher.h>
#include <XCEditor/Foundation/UIEditorCommandDispatcher.h>
#include <XCEngine/UI/Input/UIShortcutRegistry.h>

View File

@@ -0,0 +1,76 @@
#pragma once
#include <XCEditor/Collections/UIEditorListView.h>
#include <XCEditor/Collections/UIEditorScrollView.h>
#include <XCEditor/Collections/UIEditorTabStrip.h>
#include <XCEditor/Collections/UIEditorTreeView.h>
#include <XCEditor/Fields/UIEditorAssetField.h>
#include <XCEditor/Fields/UIEditorBoolField.h>
#include <XCEditor/Fields/UIEditorColorField.h>
#include <XCEditor/Fields/UIEditorEnumField.h>
#include <XCEditor/Fields/UIEditorNumberField.h>
#include <XCEditor/Fields/UIEditorObjectField.h>
#include <XCEditor/Fields/UIEditorPropertyGrid.h>
#include <XCEditor/Fields/UIEditorTextField.h>
#include <XCEditor/Fields/UIEditorVector2Field.h>
#include <XCEditor/Fields/UIEditorVector3Field.h>
#include <XCEditor/Fields/UIEditorVector4Field.h>
#include <XCEditor/Shell/UIEditorDockHost.h>
#include <XCEditor/Shell/UIEditorMenuBar.h>
#include <XCEditor/Shell/UIEditorMenuPopup.h>
#include <XCEditor/Shell/UIEditorPanelFrame.h>
#include <XCEditor/Shell/UIEditorShellCompose.h>
#include <XCEditor/Shell/UIEditorShellInteraction.h>
#include <XCEditor/Shell/UIEditorStatusBar.h>
#include <XCEditor/Shell/UIEditorViewportSlot.h>
namespace XCEngine::UI::Editor {
const Widgets::UIEditorBoolFieldMetrics& ResolveUIEditorBoolFieldMetrics();
const Widgets::UIEditorBoolFieldPalette& ResolveUIEditorBoolFieldPalette();
const Widgets::UIEditorNumberFieldMetrics& ResolveUIEditorNumberFieldMetrics();
const Widgets::UIEditorNumberFieldPalette& ResolveUIEditorNumberFieldPalette();
const Widgets::UIEditorTextFieldMetrics& ResolveUIEditorTextFieldMetrics();
const Widgets::UIEditorTextFieldPalette& ResolveUIEditorTextFieldPalette();
const Widgets::UIEditorVector2FieldMetrics& ResolveUIEditorVector2FieldMetrics();
const Widgets::UIEditorVector2FieldPalette& ResolveUIEditorVector2FieldPalette();
const Widgets::UIEditorVector3FieldMetrics& ResolveUIEditorVector3FieldMetrics();
const Widgets::UIEditorVector3FieldPalette& ResolveUIEditorVector3FieldPalette();
const Widgets::UIEditorVector4FieldMetrics& ResolveUIEditorVector4FieldMetrics();
const Widgets::UIEditorVector4FieldPalette& ResolveUIEditorVector4FieldPalette();
const Widgets::UIEditorEnumFieldMetrics& ResolveUIEditorEnumFieldMetrics();
const Widgets::UIEditorEnumFieldPalette& ResolveUIEditorEnumFieldPalette();
const Widgets::UIEditorColorFieldMetrics& ResolveUIEditorColorFieldMetrics();
const Widgets::UIEditorColorFieldPalette& ResolveUIEditorColorFieldPalette();
const Widgets::UIEditorObjectFieldMetrics& ResolveUIEditorObjectFieldMetrics();
const Widgets::UIEditorObjectFieldPalette& ResolveUIEditorObjectFieldPalette();
const Widgets::UIEditorAssetFieldMetrics& ResolveUIEditorAssetFieldMetrics();
const Widgets::UIEditorAssetFieldPalette& ResolveUIEditorAssetFieldPalette();
const Widgets::UIEditorMenuPopupMetrics& ResolveUIEditorMenuPopupMetrics();
const Widgets::UIEditorMenuPopupPalette& ResolveUIEditorMenuPopupPalette();
const Widgets::UIEditorListViewMetrics& ResolveUIEditorListViewMetrics();
const Widgets::UIEditorListViewPalette& ResolveUIEditorListViewPalette();
const Widgets::UIEditorTreeViewMetrics& ResolveUIEditorTreeViewMetrics();
const Widgets::UIEditorTreeViewPalette& ResolveUIEditorTreeViewPalette();
const Widgets::UIEditorScrollViewMetrics& ResolveUIEditorScrollViewMetrics();
const Widgets::UIEditorScrollViewPalette& ResolveUIEditorScrollViewPalette();
const Widgets::UIEditorTabStripMetrics& ResolveUIEditorTabStripMetrics();
const Widgets::UIEditorTabStripPalette& ResolveUIEditorTabStripPalette();
const Widgets::UIEditorMenuBarMetrics& ResolveUIEditorMenuBarMetrics();
const Widgets::UIEditorMenuBarPalette& ResolveUIEditorMenuBarPalette();
const Widgets::UIEditorStatusBarMetrics& ResolveUIEditorStatusBarMetrics();
const Widgets::UIEditorStatusBarPalette& ResolveUIEditorStatusBarPalette();
const Widgets::UIEditorPanelFrameMetrics& ResolveUIEditorPanelFrameMetrics();
const Widgets::UIEditorPanelFramePalette& ResolveUIEditorPanelFramePalette();
const Widgets::UIEditorDockHostMetrics& ResolveUIEditorDockHostMetrics();
const Widgets::UIEditorDockHostPalette& ResolveUIEditorDockHostPalette();
const Widgets::UIEditorViewportSlotMetrics& ResolveUIEditorViewportSlotMetrics();
const Widgets::UIEditorViewportSlotPalette& ResolveUIEditorViewportSlotPalette();
const UIEditorShellComposeMetrics& ResolveUIEditorShellComposeMetrics();
const UIEditorShellComposePalette& ResolveUIEditorShellComposePalette();
const UIEditorShellInteractionMetrics& ResolveUIEditorShellInteractionMetrics();
const UIEditorShellInteractionPalette& ResolveUIEditorShellInteractionPalette();
const Widgets::UIEditorPropertyGridMetrics& ResolveUIEditorPropertyGridMetrics();
const Widgets::UIEditorPropertyGridPalette& ResolveUIEditorPropertyGridPalette();
} // namespace XCEngine::UI::Editor

View File

@@ -1,10 +1,10 @@
#pragma once
#include <XCEditor/Core/UIEditorPanelRegistry.h>
#include <XCEditor/Core/UIEditorWorkspaceModel.h>
#include <XCEditor/Core/UIEditorWorkspaceSession.h>
#include <XCEditor/Widgets/UIEditorPanelFrame.h>
#include <XCEditor/Widgets/UIEditorTabStrip.h>
#include <XCEditor/Shell/UIEditorPanelRegistry.h>
#include <XCEditor/Shell/UIEditorWorkspaceModel.h>
#include <XCEditor/Shell/UIEditorWorkspaceSession.h>
#include <XCEditor/Shell/UIEditorPanelFrame.h>
#include <XCEditor/Collections/UIEditorTabStrip.h>
#include <XCEngine/UI/Layout/UISplitterLayout.h>
@@ -35,10 +35,16 @@ struct UIEditorDockHostHitTarget {
std::size_t index = UIEditorTabStripInvalidIndex;
};
struct UIEditorDockHostTabStripVisualState {
std::string nodeId = {};
UIEditorTabStripState state = {};
};
struct UIEditorDockHostState {
bool focused = false;
UIEditorDockHostHitTarget hoveredTarget = {};
std::string activeSplitterNodeId = {};
std::vector<UIEditorDockHostTabStripVisualState> tabStripStates = {};
};
struct UIEditorDockHostMetrics {
@@ -58,17 +64,17 @@ struct UIEditorDockHostPalette {
UIEditorTabStripPalette tabStripPalette = {};
UIEditorPanelFramePalette panelFramePalette = {};
::XCEngine::UI::UIColor splitterColor =
::XCEngine::UI::UIColor(0.26f, 0.26f, 0.26f, 1.0f);
::XCEngine::UI::UIColor(0.22f, 0.23f, 0.25f, 1.0f);
::XCEngine::UI::UIColor splitterHoveredColor =
::XCEngine::UI::UIColor(0.36f, 0.36f, 0.36f, 1.0f);
::XCEngine::UI::UIColor(0.32f, 0.34f, 0.36f, 1.0f);
::XCEngine::UI::UIColor splitterActiveColor =
::XCEngine::UI::UIColor(0.86f, 0.86f, 0.86f, 1.0f);
::XCEngine::UI::UIColor(0.50f, 0.52f, 0.56f, 1.0f);
::XCEngine::UI::UIColor placeholderTitleColor =
::XCEngine::UI::UIColor(0.94f, 0.94f, 0.94f, 1.0f);
::XCEngine::UI::UIColor(0.93f, 0.94f, 0.96f, 1.0f);
::XCEngine::UI::UIColor placeholderTextColor =
::XCEngine::UI::UIColor(0.72f, 0.72f, 0.72f, 1.0f);
::XCEngine::UI::UIColor(0.70f, 0.72f, 0.74f, 1.0f);
::XCEngine::UI::UIColor placeholderMutedColor =
::XCEngine::UI::UIColor(0.58f, 0.58f, 0.58f, 1.0f);
::XCEngine::UI::UIColor(0.58f, 0.59f, 0.62f, 1.0f);
};
struct UIEditorDockHostTabItemLayout {

View File

@@ -1,7 +1,8 @@
#pragma once
#include <XCEditor/Core/UIEditorWorkspaceController.h>
#include <XCEditor/Widgets/UIEditorDockHost.h>
#include <XCEditor/Collections/UIEditorTabStripInteraction.h>
#include <XCEditor/Shell/UIEditorWorkspaceController.h>
#include <XCEditor/Shell/UIEditorDockHost.h>
#include <XCEngine/UI/DrawData.h>
#include <XCEngine/UI/Widgets/UISplitterInteraction.h>
@@ -11,9 +12,15 @@
namespace XCEngine::UI::Editor {
struct UIEditorDockHostTabStripInteractionEntry {
std::string nodeId = {};
UIEditorTabStripInteractionState state = {};
};
struct UIEditorDockHostInteractionState {
Widgets::UIEditorDockHostState dockHostState = {};
::XCEngine::UI::Widgets::UISplitterDragState splitterDragState = {};
std::vector<UIEditorDockHostTabStripInteractionEntry> tabStripInteractions = {};
::XCEngine::UI::UIPoint pointerPosition = {};
bool hasPointerPosition = false;
};

View File

@@ -34,7 +34,7 @@ struct UIEditorMenuBarMetrics {
float estimatedGlyphWidth = 7.0f;
float labelInsetY = -1.0f;
float barCornerRounding = 8.0f;
float buttonCornerRounding = 7.0f;
float buttonCornerRounding = 6.0f;
float baseBorderThickness = 1.0f;
float focusedBorderThickness = 2.0f;
float openBorderThickness = 1.5f;
@@ -42,25 +42,25 @@ struct UIEditorMenuBarMetrics {
struct UIEditorMenuBarPalette {
::XCEngine::UI::UIColor barColor =
::XCEngine::UI::UIColor(0.16f, 0.16f, 0.16f, 1.0f);
::XCEngine::UI::UIColor(0.18f, 0.18f, 0.19f, 1.0f);
::XCEngine::UI::UIColor buttonColor =
::XCEngine::UI::UIColor(0.23f, 0.23f, 0.23f, 1.0f);
::XCEngine::UI::UIColor(0.21f, 0.22f, 0.24f, 1.0f);
::XCEngine::UI::UIColor buttonHoveredColor =
::XCEngine::UI::UIColor(0.29f, 0.29f, 0.29f, 1.0f);
::XCEngine::UI::UIColor(0.27f, 0.28f, 0.30f, 1.0f);
::XCEngine::UI::UIColor buttonOpenColor =
::XCEngine::UI::UIColor(0.35f, 0.35f, 0.35f, 1.0f);
::XCEngine::UI::UIColor(0.33f, 0.35f, 0.38f, 1.0f);
::XCEngine::UI::UIColor borderColor =
::XCEngine::UI::UIColor(0.30f, 0.30f, 0.30f, 1.0f);
::XCEngine::UI::UIColor(0.30f, 0.32f, 0.34f, 1.0f);
::XCEngine::UI::UIColor focusedBorderColor =
::XCEngine::UI::UIColor(0.84f, 0.84f, 0.84f, 1.0f);
::XCEngine::UI::UIColor(0.78f, 0.80f, 0.84f, 1.0f);
::XCEngine::UI::UIColor openBorderColor =
::XCEngine::UI::UIColor(0.68f, 0.68f, 0.68f, 1.0f);
::XCEngine::UI::UIColor(0.50f, 0.52f, 0.56f, 1.0f);
::XCEngine::UI::UIColor textPrimary =
::XCEngine::UI::UIColor(0.94f, 0.94f, 0.94f, 1.0f);
::XCEngine::UI::UIColor(0.93f, 0.94f, 0.96f, 1.0f);
::XCEngine::UI::UIColor textMuted =
::XCEngine::UI::UIColor(0.74f, 0.74f, 0.74f, 1.0f);
::XCEngine::UI::UIColor(0.70f, 0.72f, 0.74f, 1.0f);
::XCEngine::UI::UIColor textDisabled =
::XCEngine::UI::UIColor(0.52f, 0.52f, 0.52f, 1.0f);
::XCEngine::UI::UIColor(0.54f, 0.55f, 0.58f, 1.0f);
};
struct UIEditorMenuBarLayout {

View File

@@ -1,6 +1,6 @@
#pragma once
#include <XCEditor/Core/UIEditorCommandDispatcher.h>
#include <XCEditor/Foundation/UIEditorCommandDispatcher.h>
#include <cstdint>
#include <string>

View File

@@ -1,6 +1,6 @@
#pragma once
#include <XCEditor/Core/UIEditorMenuModel.h>
#include <XCEditor/Shell/UIEditorMenuModel.h>
#include <XCEngine/UI/DrawData.h>
#include <cstddef>

View File

@@ -24,8 +24,8 @@ struct UIEditorMenuSessionMutationResult {
std::string openRootMenuId = {};
std::string openedPopupId = {};
std::vector<std::string> closedPopupIds = {};
Widgets::UIPopupDismissReason dismissReason =
Widgets::UIPopupDismissReason::None;
::XCEngine::UI::Widgets::UIPopupDismissReason dismissReason =
::XCEngine::UI::Widgets::UIPopupDismissReason::None;
[[nodiscard]] bool HasOpenMenu() const {
return !openRootMenuId.empty();
@@ -34,7 +34,7 @@ struct UIEditorMenuSessionMutationResult {
class UIEditorMenuSession {
public:
const Widgets::UIPopupOverlayModel& GetPopupOverlayModel() const {
const ::XCEngine::UI::Widgets::UIPopupOverlayModel& GetPopupOverlayModel() const {
return m_popupOverlayModel;
}
@@ -66,23 +66,23 @@ public:
UIEditorMenuSessionMutationResult OpenRootMenu(
std::string_view menuId,
Widgets::UIPopupOverlayEntry entry);
::XCEngine::UI::Widgets::UIPopupOverlayEntry entry);
UIEditorMenuSessionMutationResult OpenMenuBarRoot(
std::string_view menuId,
Widgets::UIPopupOverlayEntry entry);
::XCEngine::UI::Widgets::UIPopupOverlayEntry entry);
UIEditorMenuSessionMutationResult HoverMenuBarRoot(
std::string_view menuId,
Widgets::UIPopupOverlayEntry entry);
::XCEngine::UI::Widgets::UIPopupOverlayEntry entry);
UIEditorMenuSessionMutationResult HoverSubmenu(
std::string_view itemId,
Widgets::UIPopupOverlayEntry entry);
::XCEngine::UI::Widgets::UIPopupOverlayEntry entry);
UIEditorMenuSessionMutationResult CloseAll(
Widgets::UIPopupDismissReason dismissReason =
Widgets::UIPopupDismissReason::Programmatic);
::XCEngine::UI::Widgets::UIPopupDismissReason dismissReason =
::XCEngine::UI::Widgets::UIPopupDismissReason::Programmatic);
UIEditorMenuSessionMutationResult DismissFromEscape();
UIEditorMenuSessionMutationResult DismissFromPointerDown(
@@ -92,13 +92,13 @@ public:
private:
UIEditorMenuSessionMutationResult BuildResult(
const Widgets::UIPopupOverlayMutationResult& mutation) const;
const ::XCEngine::UI::Widgets::UIPopupOverlayMutationResult& mutation) const;
void RemoveClosedPopupStates(const std::vector<std::string>& closedPopupIds);
void RebuildDerivedState();
std::string m_openRootMenuId = {};
Widgets::UIPopupOverlayModel m_popupOverlayModel = {};
::XCEngine::UI::Widgets::UIPopupOverlayModel m_popupOverlayModel = {};
std::vector<UIEditorMenuPopupState> m_popupStates = {};
std::vector<std::string> m_openSubmenuItemIds = {};
};

View File

@@ -1,7 +1,7 @@
#pragma once
#include <XCEditor/Core/UIEditorPanelRegistry.h>
#include <XCEditor/Widgets/UIEditorDockHost.h>
#include <XCEditor/Shell/UIEditorPanelRegistry.h>
#include <XCEditor/Shell/UIEditorDockHost.h>
#include <cstdint>
#include <string>

View File

@@ -41,7 +41,7 @@ struct UIEditorPanelFrameText {
};
struct UIEditorPanelFrameMetrics {
float cornerRounding = 10.0f;
float cornerRounding = 8.0f;
float headerHeight = 36.0f;
float footerHeight = 24.0f;
float contentPadding = 12.0f;
@@ -61,35 +61,35 @@ struct UIEditorPanelFrameMetrics {
struct UIEditorPanelFramePalette {
::XCEngine::UI::UIColor surfaceColor =
::XCEngine::UI::UIColor(0.16f, 0.16f, 0.16f, 1.0f);
::XCEngine::UI::UIColor(0.15f, 0.15f, 0.16f, 1.0f);
::XCEngine::UI::UIColor headerColor =
::XCEngine::UI::UIColor(0.20f, 0.20f, 0.20f, 1.0f);
::XCEngine::UI::UIColor(0.18f, 0.18f, 0.19f, 1.0f);
::XCEngine::UI::UIColor footerColor =
::XCEngine::UI::UIColor(0.19f, 0.19f, 0.19f, 1.0f);
::XCEngine::UI::UIColor(0.17f, 0.17f, 0.18f, 1.0f);
::XCEngine::UI::UIColor borderColor =
::XCEngine::UI::UIColor(0.30f, 0.30f, 0.30f, 1.0f);
::XCEngine::UI::UIColor(0.30f, 0.32f, 0.34f, 1.0f);
::XCEngine::UI::UIColor hoveredBorderColor =
::XCEngine::UI::UIColor(0.42f, 0.42f, 0.42f, 1.0f);
::XCEngine::UI::UIColor(0.39f, 0.41f, 0.43f, 1.0f);
::XCEngine::UI::UIColor activeBorderColor =
::XCEngine::UI::UIColor(0.58f, 0.58f, 0.58f, 1.0f);
::XCEngine::UI::UIColor(0.50f, 0.52f, 0.56f, 1.0f);
::XCEngine::UI::UIColor focusedBorderColor =
::XCEngine::UI::UIColor(0.84f, 0.84f, 0.84f, 1.0f);
::XCEngine::UI::UIColor(0.78f, 0.80f, 0.84f, 1.0f);
::XCEngine::UI::UIColor textPrimary =
::XCEngine::UI::UIColor(0.94f, 0.94f, 0.94f, 1.0f);
::XCEngine::UI::UIColor(0.93f, 0.94f, 0.96f, 1.0f);
::XCEngine::UI::UIColor textSecondary =
::XCEngine::UI::UIColor(0.71f, 0.71f, 0.71f, 1.0f);
::XCEngine::UI::UIColor(0.70f, 0.72f, 0.74f, 1.0f);
::XCEngine::UI::UIColor textMuted =
::XCEngine::UI::UIColor(0.60f, 0.60f, 0.60f, 1.0f);
::XCEngine::UI::UIColor(0.58f, 0.59f, 0.62f, 1.0f);
::XCEngine::UI::UIColor actionButtonColor =
::XCEngine::UI::UIColor(0.24f, 0.24f, 0.24f, 1.0f);
::XCEngine::UI::UIColor(0.21f, 0.22f, 0.24f, 1.0f);
::XCEngine::UI::UIColor actionButtonHoveredColor =
::XCEngine::UI::UIColor(0.34f, 0.34f, 0.34f, 1.0f);
::XCEngine::UI::UIColor(0.27f, 0.28f, 0.30f, 1.0f);
::XCEngine::UI::UIColor actionButtonSelectedColor =
::XCEngine::UI::UIColor(0.48f, 0.48f, 0.48f, 1.0f);
::XCEngine::UI::UIColor(0.33f, 0.35f, 0.38f, 1.0f);
::XCEngine::UI::UIColor actionButtonBorderColor =
::XCEngine::UI::UIColor(0.52f, 0.52f, 0.52f, 1.0f);
::XCEngine::UI::UIColor(0.42f, 0.44f, 0.47f, 1.0f);
::XCEngine::UI::UIColor actionGlyphColor =
::XCEngine::UI::UIColor(0.92f, 0.92f, 0.92f, 1.0f);
::XCEngine::UI::UIColor(0.93f, 0.94f, 0.96f, 1.0f);
};
struct UIEditorPanelFrameLayout {

View File

@@ -1,7 +1,7 @@
#pragma once
#include <XCEditor/Core/UIEditorPanelRegistry.h>
#include <XCEditor/Core/UIEditorWorkspaceSession.h>
#include <XCEditor/Shell/UIEditorPanelRegistry.h>
#include <XCEditor/Shell/UIEditorWorkspaceSession.h>
#include <cstdint>
#include <string>

View File

@@ -1,8 +1,8 @@
#pragma once
#include <XCEditor/Core/UIEditorWorkspaceCompose.h>
#include <XCEditor/Widgets/UIEditorMenuBar.h>
#include <XCEditor/Widgets/UIEditorStatusBar.h>
#include <XCEditor/Shell/UIEditorWorkspaceCompose.h>
#include <XCEditor/Shell/UIEditorMenuBar.h>
#include <XCEditor/Shell/UIEditorStatusBar.h>
namespace XCEngine::UI::Editor {
@@ -24,6 +24,7 @@ struct UIEditorShellComposeMetrics {
float surfaceCornerRounding = 10.0f;
Widgets::UIEditorMenuBarMetrics menuBarMetrics = {};
Widgets::UIEditorDockHostMetrics dockHostMetrics = {};
Widgets::UIEditorViewportSlotMetrics viewportMetrics = {};
Widgets::UIEditorStatusBarMetrics statusBarMetrics = {};
};
@@ -34,6 +35,7 @@ struct UIEditorShellComposePalette {
::XCEngine::UI::UIColor(0.27f, 0.27f, 0.27f, 1.0f);
Widgets::UIEditorMenuBarPalette menuBarPalette = {};
Widgets::UIEditorDockHostPalette dockHostPalette = {};
Widgets::UIEditorViewportSlotPalette viewportPalette = {};
Widgets::UIEditorStatusBarPalette statusBarPalette = {};
};

View File

@@ -1,11 +1,11 @@
#pragma once
#include <XCEditor/Core/UIEditorMenuModel.h>
#include <XCEditor/Core/UIEditorMenuSession.h>
#include <XCEditor/Core/UIEditorShellCompose.h>
#include <XCEditor/Core/UIEditorWorkspaceController.h>
#include <XCEditor/Core/UIEditorWorkspaceInteraction.h>
#include <XCEditor/Widgets/UIEditorMenuPopup.h>
#include <XCEditor/Shell/UIEditorMenuModel.h>
#include <XCEditor/Shell/UIEditorMenuSession.h>
#include <XCEditor/Shell/UIEditorShellCompose.h>
#include <XCEditor/Shell/UIEditorWorkspaceController.h>
#include <XCEditor/Shell/UIEditorWorkspaceInteraction.h>
#include <XCEditor/Shell/UIEditorMenuPopup.h>
#include <XCEngine/UI/DrawData.h>

View File

@@ -59,32 +59,32 @@ struct UIEditorStatusBarMetrics {
float cornerRounding = 8.0f;
float estimatedGlyphWidth = 7.0f;
float borderThickness = 1.0f;
float focusedBorderThickness = 1.5f;
float focusedBorderThickness = 2.0f;
};
struct UIEditorStatusBarPalette {
::XCEngine::UI::UIColor surfaceColor =
::XCEngine::UI::UIColor(0.15f, 0.15f, 0.15f, 1.0f);
::XCEngine::UI::UIColor(0.15f, 0.15f, 0.16f, 1.0f);
::XCEngine::UI::UIColor borderColor =
::XCEngine::UI::UIColor(0.28f, 0.28f, 0.28f, 1.0f);
::XCEngine::UI::UIColor(0.30f, 0.32f, 0.34f, 1.0f);
::XCEngine::UI::UIColor focusedBorderColor =
::XCEngine::UI::UIColor(0.78f, 0.78f, 0.78f, 1.0f);
::XCEngine::UI::UIColor(0.78f, 0.80f, 0.84f, 1.0f);
::XCEngine::UI::UIColor segmentColor =
::XCEngine::UI::UIColor(0.17f, 0.17f, 0.17f, 1.0f);
::XCEngine::UI::UIColor(0.19f, 0.19f, 0.21f, 1.0f);
::XCEngine::UI::UIColor segmentHoveredColor =
::XCEngine::UI::UIColor(0.23f, 0.23f, 0.23f, 1.0f);
::XCEngine::UI::UIColor(0.24f, 0.26f, 0.28f, 1.0f);
::XCEngine::UI::UIColor segmentActiveColor =
::XCEngine::UI::UIColor(0.29f, 0.29f, 0.29f, 1.0f);
::XCEngine::UI::UIColor(0.30f, 0.32f, 0.35f, 1.0f);
::XCEngine::UI::UIColor segmentBorderColor =
::XCEngine::UI::UIColor(0.35f, 0.35f, 0.35f, 1.0f);
::XCEngine::UI::UIColor(0.42f, 0.44f, 0.47f, 1.0f);
::XCEngine::UI::UIColor separatorColor =
::XCEngine::UI::UIColor(0.32f, 0.32f, 0.32f, 1.0f);
::XCEngine::UI::UIColor(0.32f, 0.34f, 0.36f, 1.0f);
::XCEngine::UI::UIColor textPrimary =
::XCEngine::UI::UIColor(0.92f, 0.92f, 0.92f, 1.0f);
::XCEngine::UI::UIColor(0.93f, 0.94f, 0.96f, 1.0f);
::XCEngine::UI::UIColor textMuted =
::XCEngine::UI::UIColor(0.66f, 0.66f, 0.66f, 1.0f);
::XCEngine::UI::UIColor(0.58f, 0.59f, 0.62f, 1.0f);
::XCEngine::UI::UIColor textAccent =
::XCEngine::UI::UIColor(0.94f, 0.94f, 0.94f, 1.0f);
::XCEngine::UI::UIColor(0.82f, 0.86f, 0.93f, 1.0f);
};
struct UIEditorStatusBarLayout {

View File

@@ -1,7 +1,7 @@
#pragma once
#include <XCEditor/Core/UIEditorViewportInputBridge.h>
#include <XCEditor/Widgets/UIEditorViewportSlot.h>
#include <XCEditor/Shell/UIEditorViewportInputBridge.h>
#include <XCEditor/Shell/UIEditorViewportSlot.h>
#include <vector>

View File

@@ -1,7 +1,7 @@
#pragma once
#include <XCEngine/UI/DrawData.h>
#include <XCEditor/Widgets/UIEditorStatusBar.h>
#include <XCEditor/Shell/UIEditorStatusBar.h>
#include <cstddef>
#include <cstdint>

View File

@@ -1,10 +1,10 @@
#pragma once
#include <XCEditor/Core/UIEditorPanelContentHost.h>
#include <XCEditor/Core/UIEditorPanelRegistry.h>
#include <XCEditor/Core/UIEditorViewportShell.h>
#include <XCEditor/Core/UIEditorWorkspaceSession.h>
#include <XCEditor/Widgets/UIEditorDockHost.h>
#include <XCEditor/Shell/UIEditorPanelContentHost.h>
#include <XCEditor/Shell/UIEditorPanelRegistry.h>
#include <XCEditor/Shell/UIEditorViewportShell.h>
#include <XCEditor/Shell/UIEditorWorkspaceSession.h>
#include <XCEditor/Shell/UIEditorDockHost.h>
#include <XCEngine/UI/DrawData.h>
@@ -78,7 +78,8 @@ UIEditorWorkspaceComposeRequest ResolveUIEditorWorkspaceComposeRequest(
const UIEditorWorkspaceSession& session,
const std::vector<UIEditorWorkspacePanelPresentationModel>& presentations,
const Widgets::UIEditorDockHostState& dockHostState = {},
const Widgets::UIEditorDockHostMetrics& dockHostMetrics = {});
const Widgets::UIEditorDockHostMetrics& dockHostMetrics = {},
const Widgets::UIEditorViewportSlotMetrics& viewportMetrics = {});
UIEditorWorkspaceComposeFrame UpdateUIEditorWorkspaceCompose(
UIEditorWorkspaceComposeState& state,
@@ -89,7 +90,8 @@ UIEditorWorkspaceComposeFrame UpdateUIEditorWorkspaceCompose(
const std::vector<UIEditorWorkspacePanelPresentationModel>& presentations,
const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents,
const Widgets::UIEditorDockHostState& dockHostState = {},
const Widgets::UIEditorDockHostMetrics& dockHostMetrics = {});
const Widgets::UIEditorDockHostMetrics& dockHostMetrics = {},
const Widgets::UIEditorViewportSlotMetrics& viewportMetrics = {});
std::vector<std::string> CollectUIEditorWorkspaceComposeExternalBodyPanelIds(
const UIEditorWorkspaceComposeFrame& frame);
@@ -98,6 +100,8 @@ void AppendUIEditorWorkspaceCompose(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorWorkspaceComposeFrame& frame,
const Widgets::UIEditorDockHostPalette& dockHostPalette = {},
const Widgets::UIEditorDockHostMetrics& dockHostMetrics = {});
const Widgets::UIEditorDockHostMetrics& dockHostMetrics = {},
const Widgets::UIEditorViewportSlotPalette& viewportPalette = {},
const Widgets::UIEditorViewportSlotMetrics& viewportMetrics = {});
} // namespace XCEngine::UI::Editor

View File

@@ -1,7 +1,7 @@
#pragma once
#include <XCEditor/Core/UIEditorWorkspaceLayoutPersistence.h>
#include <XCEditor/Core/UIEditorWorkspaceSession.h>
#include <XCEditor/Shell/UIEditorWorkspaceLayoutPersistence.h>
#include <XCEditor/Shell/UIEditorWorkspaceSession.h>
#include <cstdint>
#include <string>

View File

@@ -1,7 +1,7 @@
#pragma once
#include <XCEditor/Core/UIEditorDockHostInteraction.h>
#include <XCEditor/Core/UIEditorWorkspaceCompose.h>
#include <XCEditor/Shell/UIEditorDockHostInteraction.h>
#include <XCEditor/Shell/UIEditorWorkspaceCompose.h>
#include <XCEngine/UI/Types.h>
@@ -41,6 +41,7 @@ UIEditorWorkspaceInteractionFrame UpdateUIEditorWorkspaceInteraction(
const ::XCEngine::UI::UIRect& bounds,
const UIEditorWorkspaceInteractionModel& model,
const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents,
const Widgets::UIEditorDockHostMetrics& dockHostMetrics = {});
const Widgets::UIEditorDockHostMetrics& dockHostMetrics = {},
const Widgets::UIEditorViewportSlotMetrics& viewportMetrics = {});
} // namespace XCEngine::UI::Editor

View File

@@ -1,6 +1,6 @@
#pragma once
#include <XCEditor/Core/UIEditorWorkspaceSession.h>
#include <XCEditor/Shell/UIEditorWorkspaceSession.h>
#include <cstdint>
#include <string>

View File

@@ -1,7 +1,7 @@
#pragma once
#include <XCEditor/Core/UIEditorPanelRegistry.h>
#include <XCEditor/Core/UIEditorWorkspaceModel.h>
#include <XCEditor/Shell/UIEditorPanelRegistry.h>
#include <XCEditor/Shell/UIEditorWorkspaceModel.h>
#include <cstdint>
#include <string>

View File

@@ -1,7 +1,5 @@
#pragma once
#include <XCEngine/UI/Style/Theme.h>
#include <cstdint>
#include <string_view>
@@ -24,14 +22,11 @@ bool UsesUIEditorCollectionPrimitiveColumnLayout(UIEditorCollectionPrimitiveKind
bool IsUIEditorCollectionPrimitiveHoverable(UIEditorCollectionPrimitiveKind kind);
bool DoesUIEditorCollectionPrimitiveClipChildren(UIEditorCollectionPrimitiveKind kind);
float ResolveUIEditorCollectionPrimitivePadding(
UIEditorCollectionPrimitiveKind kind,
const ::XCEngine::UI::Style::UITheme& theme);
UIEditorCollectionPrimitiveKind kind);
float ResolveUIEditorCollectionPrimitiveDefaultHeight(
UIEditorCollectionPrimitiveKind kind,
const ::XCEngine::UI::Style::UITheme& theme);
UIEditorCollectionPrimitiveKind kind);
float ResolveUIEditorCollectionPrimitiveIndent(
UIEditorCollectionPrimitiveKind kind,
const ::XCEngine::UI::Style::UITheme& theme,
float indentLevel);
} // namespace XCEngine::UI::Editor::Widgets

View File

@@ -0,0 +1,178 @@
#pragma once
#include <XCEngine/UI/DrawData.h>
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <string>
namespace XCEngine::UI::Editor::Widgets {
struct UIEditorHsvColor {
float hue = 0.0f;
float saturation = 0.0f;
float value = 0.0f;
float alpha = 1.0f;
};
inline float ClampUIEditorColorUnit(float value) {
return (std::clamp)(value, 0.0f, 1.0f);
}
inline int ToUIEditorColorByte(float value) {
return static_cast<int>(std::lround(ClampUIEditorColorUnit(value) * 255.0f));
}
inline UIEditorHsvColor ConvertUIEditorColorToHsv(
const ::XCEngine::UI::UIColor& color,
float fallbackHue = 0.0f) {
const float red = ClampUIEditorColorUnit(color.r);
const float green = ClampUIEditorColorUnit(color.g);
const float blue = ClampUIEditorColorUnit(color.b);
const float maxChannel = (std::max)({ red, green, blue });
const float minChannel = (std::min)({ red, green, blue });
const float delta = maxChannel - minChannel;
UIEditorHsvColor hsv = {};
hsv.hue = ClampUIEditorColorUnit(fallbackHue);
hsv.saturation = maxChannel <= 0.0f ? 0.0f : delta / maxChannel;
hsv.value = maxChannel;
hsv.alpha = ClampUIEditorColorUnit(color.a);
if (delta <= 0.00001f) {
return hsv;
}
if (maxChannel == red) {
hsv.hue = std::fmod(((green - blue) / delta), 6.0f) / 6.0f;
} else if (maxChannel == green) {
hsv.hue = (((blue - red) / delta) + 2.0f) / 6.0f;
} else {
hsv.hue = (((red - green) / delta) + 4.0f) / 6.0f;
}
if (hsv.hue < 0.0f) {
hsv.hue += 1.0f;
}
return hsv;
}
inline ::XCEngine::UI::UIColor ConvertUIEditorHsvToColor(const UIEditorHsvColor& hsv) {
const float hue = ClampUIEditorColorUnit(hsv.hue);
const float saturation = ClampUIEditorColorUnit(hsv.saturation);
const float value = ClampUIEditorColorUnit(hsv.value);
if (saturation <= 0.00001f) {
return ::XCEngine::UI::UIColor(value, value, value, ClampUIEditorColorUnit(hsv.alpha));
}
const float sector = hue * 6.0f;
const int sectorIndex = static_cast<int>(std::floor(sector)) % 6;
const float fraction = sector - std::floor(sector);
const float p = value * (1.0f - saturation);
const float q = value * (1.0f - saturation * fraction);
const float t = value * (1.0f - saturation * (1.0f - fraction));
float red = value;
float green = t;
float blue = p;
switch (sectorIndex) {
case 0:
red = value;
green = t;
blue = p;
break;
case 1:
red = q;
green = value;
blue = p;
break;
case 2:
red = p;
green = value;
blue = t;
break;
case 3:
red = p;
green = q;
blue = value;
break;
case 4:
red = t;
green = p;
blue = value;
break;
case 5:
default:
red = value;
green = p;
blue = q;
break;
}
return ::XCEngine::UI::UIColor(red, green, blue, ClampUIEditorColorUnit(hsv.alpha));
}
inline UIEditorHsvColor ResolveUIEditorDisplayHsv(
const ::XCEngine::UI::UIColor& color,
float rememberedHue,
bool hueValid) {
UIEditorHsvColor hsv = ConvertUIEditorColorToHsv(color, hueValid ? rememberedHue : 0.0f);
if (hsv.saturation <= 0.00001f && hueValid) {
hsv.hue = ClampUIEditorColorUnit(rememberedHue);
}
return hsv;
}
inline std::string FormatUIEditorColorHex(
const ::XCEngine::UI::UIColor& color,
bool includeAlpha = true) {
char buffer[16] = {};
if (includeAlpha) {
std::snprintf(
buffer,
sizeof(buffer),
"#%02X%02X%02X%02X",
ToUIEditorColorByte(color.r),
ToUIEditorColorByte(color.g),
ToUIEditorColorByte(color.b),
ToUIEditorColorByte(color.a));
} else {
std::snprintf(
buffer,
sizeof(buffer),
"#%02X%02X%02X",
ToUIEditorColorByte(color.r),
ToUIEditorColorByte(color.g),
ToUIEditorColorByte(color.b));
}
return std::string(buffer);
}
inline std::string FormatUIEditorColorChannelsText(
const ::XCEngine::UI::UIColor& color,
bool includeAlpha = true) {
char buffer[64] = {};
if (includeAlpha) {
std::snprintf(
buffer,
sizeof(buffer),
"RGBA %d, %d, %d, %d",
ToUIEditorColorByte(color.r),
ToUIEditorColorByte(color.g),
ToUIEditorColorByte(color.b),
ToUIEditorColorByte(color.a));
} else {
std::snprintf(
buffer,
sizeof(buffer),
"RGB %d, %d, %d",
ToUIEditorColorByte(color.r),
ToUIEditorColorByte(color.g),
ToUIEditorColorByte(color.b));
}
return std::string(buffer);
}
} // namespace XCEngine::UI::Editor::Widgets

View File

@@ -4,6 +4,95 @@
namespace XCEngine::UI::Editor::Widgets {
struct UIEditorInspectorFieldStyleTokens {
::XCEngine::UI::UIColor rowHoverColor =
::XCEngine::UI::UIColor(0.20f, 0.20f, 0.20f, 1.0f);
::XCEngine::UI::UIColor rowActiveColor =
::XCEngine::UI::UIColor(0.24f, 0.24f, 0.24f, 1.0f);
::XCEngine::UI::UIColor labelColor =
::XCEngine::UI::UIColor(0.80f, 0.80f, 0.80f, 1.0f);
::XCEngine::UI::UIColor valueColor =
::XCEngine::UI::UIColor(0.92f, 0.92f, 0.92f, 1.0f);
::XCEngine::UI::UIColor readOnlyValueColor =
::XCEngine::UI::UIColor(0.60f, 0.60f, 0.60f, 1.0f);
::XCEngine::UI::UIColor controlColor =
::XCEngine::UI::UIColor(0.18f, 0.18f, 0.18f, 1.0f);
::XCEngine::UI::UIColor controlHoverColor =
::XCEngine::UI::UIColor(0.21f, 0.21f, 0.21f, 1.0f);
::XCEngine::UI::UIColor controlEditingColor =
::XCEngine::UI::UIColor(0.24f, 0.24f, 0.24f, 1.0f);
::XCEngine::UI::UIColor controlReadOnlyColor =
::XCEngine::UI::UIColor(0.15f, 0.15f, 0.15f, 1.0f);
::XCEngine::UI::UIColor controlBorderColor =
::XCEngine::UI::UIColor(0.30f, 0.30f, 0.30f, 1.0f);
::XCEngine::UI::UIColor controlFocusedBorderColor =
::XCEngine::UI::UIColor(0.64f, 0.64f, 0.64f, 1.0f);
::XCEngine::UI::UIColor prefixColor =
::XCEngine::UI::UIColor(0.20f, 0.20f, 0.20f, 1.0f);
::XCEngine::UI::UIColor prefixBorderColor =
::XCEngine::UI::UIColor(0.31f, 0.31f, 0.31f, 1.0f);
::XCEngine::UI::UIColor axisXColor =
::XCEngine::UI::UIColor(0.78f, 0.42f, 0.42f, 1.0f);
::XCEngine::UI::UIColor axisYColor =
::XCEngine::UI::UIColor(0.56f, 0.72f, 0.46f, 1.0f);
::XCEngine::UI::UIColor axisZColor =
::XCEngine::UI::UIColor(0.45f, 0.62f, 0.82f, 1.0f);
::XCEngine::UI::UIColor axisWColor =
::XCEngine::UI::UIColor(0.76f, 0.66f, 0.42f, 1.0f);
::XCEngine::UI::UIColor arrowColor =
::XCEngine::UI::UIColor(0.88f, 0.88f, 0.88f, 1.0f);
::XCEngine::UI::UIColor swatchBorderColor =
::XCEngine::UI::UIColor(0.30f, 0.30f, 0.30f, 1.0f);
::XCEngine::UI::UIColor swatchHoverBorderColor =
::XCEngine::UI::UIColor(0.64f, 0.64f, 0.64f, 1.0f);
::XCEngine::UI::UIColor swatchReadOnlyOverlayColor =
::XCEngine::UI::UIColor(0.08f, 0.08f, 0.08f, 0.18f);
::XCEngine::UI::UIColor popupColor =
::XCEngine::UI::UIColor(0.20f, 0.20f, 0.20f, 1.0f);
::XCEngine::UI::UIColor popupBorderColor =
::XCEngine::UI::UIColor(0.12f, 0.12f, 0.12f, 1.0f);
::XCEngine::UI::UIColor popupHeaderColor =
::XCEngine::UI::UIColor(0.17f, 0.17f, 0.17f, 1.0f);
::XCEngine::UI::UIColor popupTitleColor =
::XCEngine::UI::UIColor(0.93f, 0.93f, 0.93f, 1.0f);
::XCEngine::UI::UIColor popupTextColor =
::XCEngine::UI::UIColor(0.86f, 0.86f, 0.86f, 1.0f);
::XCEngine::UI::UIColor popupTextMutedColor =
::XCEngine::UI::UIColor(0.72f, 0.72f, 0.72f, 1.0f);
::XCEngine::UI::UIColor previewBorderColor =
::XCEngine::UI::UIColor(0.11f, 0.11f, 0.11f, 1.0f);
::XCEngine::UI::UIColor previewBaseColor =
::XCEngine::UI::UIColor(0.19f, 0.19f, 0.19f, 1.0f);
::XCEngine::UI::UIColor checkerLightColor =
::XCEngine::UI::UIColor(0.84f, 0.84f, 0.84f, 1.0f);
::XCEngine::UI::UIColor checkerDarkColor =
::XCEngine::UI::UIColor(0.55f, 0.55f, 0.55f, 1.0f);
::XCEngine::UI::UIColor sliderBorderColor =
::XCEngine::UI::UIColor(0.11f, 0.11f, 0.11f, 1.0f);
::XCEngine::UI::UIColor numericBoxColor =
::XCEngine::UI::UIColor(0.17f, 0.17f, 0.17f, 1.0f);
::XCEngine::UI::UIColor numericBoxBorderColor =
::XCEngine::UI::UIColor(0.11f, 0.11f, 0.11f, 1.0f);
::XCEngine::UI::UIColor numericBoxTextColor =
::XCEngine::UI::UIColor(0.93f, 0.93f, 0.93f, 1.0f);
::XCEngine::UI::UIColor closeButtonColor =
::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.0f);
::XCEngine::UI::UIColor closeButtonHoverColor =
::XCEngine::UI::UIColor(0.25f, 0.25f, 0.25f, 1.0f);
::XCEngine::UI::UIColor closeGlyphColor =
::XCEngine::UI::UIColor(0.85f, 0.85f, 0.85f, 1.0f);
::XCEngine::UI::UIColor handleFillColor =
::XCEngine::UI::UIColor(1.0f, 1.0f, 1.0f, 1.0f);
::XCEngine::UI::UIColor handleStrokeColor =
::XCEngine::UI::UIColor(0.10f, 0.10f, 0.10f, 0.5f);
float controlTrailingInset = 9.0f;
float controlMinWidth = 88.0f;
float dropdownArrowWidth = 14.0f;
float vectorComponentMinWidth = 74.0f;
float vectorPrefixWidth = 18.0f;
float vectorPrefixGap = 5.0f;
};
struct UIEditorFieldRowLayoutMetrics {
float rowHeight = 22.0f;
float horizontalPadding = 12.0f;
@@ -19,6 +108,14 @@ struct UIEditorFieldRowLayout {
::XCEngine::UI::UIRect controlRect = {};
};
const UIEditorInspectorFieldStyleTokens& GetUIEditorInspectorFieldStyleTokens();
bool AreUIEditorFieldMetricsEqual(float lhs, float rhs);
bool AreUIEditorFieldColorsEqual(
const ::XCEngine::UI::UIColor& lhs,
const ::XCEngine::UI::UIColor& rhs);
UIEditorFieldRowLayout BuildUIEditorFieldRowLayout(
const ::XCEngine::UI::UIRect& bounds,
float minimumControlWidth,

View File

@@ -1,132 +0,0 @@
#pragma once
#include <XCEngine/UI/DrawData.h>
#include <string>
#include <string_view>
namespace XCEngine::UI::Editor::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 {
::XCEngine::UI::UIColor surfaceColor =
::XCEngine::UI::UIColor(9.0f / 255.0f, 13.0f / 255.0f, 18.0f / 255.0f, 212.0f / 255.0f);
::XCEngine::UI::UIColor borderColor =
::XCEngine::UI::UIColor(53.0f / 255.0f, 72.0f / 255.0f, 96.0f / 255.0f, 1.0f);
::XCEngine::UI::UIColor accentColor =
::XCEngine::UI::UIColor(84.0f / 255.0f, 176.0f / 255.0f, 244.0f / 255.0f, 1.0f);
::XCEngine::UI::UIColor hoveredAccentColor =
::XCEngine::UI::UIColor(1.0f, 206.0f / 255.0f, 112.0f / 255.0f, 1.0f);
::XCEngine::UI::UIColor headerColor =
::XCEngine::UI::UIColor(13.0f / 255.0f, 20.0f / 255.0f, 28.0f / 255.0f, 242.0f / 255.0f);
::XCEngine::UI::UIColor textPrimary =
::XCEngine::UI::UIColor(232.0f / 255.0f, 238.0f / 255.0f, 246.0f / 255.0f, 1.0f);
::XCEngine::UI::UIColor textSecondary =
::XCEngine::UI::UIColor(150.0f / 255.0f, 164.0f / 255.0f, 184.0f / 255.0f, 1.0f);
::XCEngine::UI::UIColor textMuted =
::XCEngine::UI::UIColor(108.0f / 255.0f, 123.0f / 255.0f, 145.0f / 255.0f, 1.0f);
};
inline ::XCEngine::UI::UIRect BuildUIEditorPanelChromeHeaderRect(
const ::XCEngine::UI::UIRect& panelRect,
const UIEditorPanelChromeMetrics& metrics = {}) {
return ::XCEngine::UI::UIRect(
panelRect.x,
panelRect.y,
panelRect.width,
metrics.headerHeight);
}
inline ::XCEngine::UI::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(
::XCEngine::UI::UIDrawList& drawList,
const ::XCEngine::UI::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(
::XCEngine::UI::UIDrawList& drawList,
const ::XCEngine::UI::UIRect& panelRect,
const UIEditorPanelChromeText& text,
const UIEditorPanelChromePalette& palette = {},
const UIEditorPanelChromeMetrics& metrics = {}) {
if (!text.title.empty()) {
drawList.AddText(
::XCEngine::UI::UIPoint(panelRect.x + metrics.titleInsetX, panelRect.y + metrics.titleInsetY),
std::string(text.title),
palette.textPrimary);
}
if (!text.subtitle.empty()) {
drawList.AddText(
::XCEngine::UI::UIPoint(panelRect.x + metrics.titleInsetX, panelRect.y + metrics.subtitleInsetY),
std::string(text.subtitle),
palette.textSecondary);
}
if (!text.footer.empty()) {
drawList.AddText(
::XCEngine::UI::UIPoint(
panelRect.x + metrics.footerInsetX,
panelRect.y + panelRect.height - metrics.footerInsetBottom),
std::string(text.footer),
palette.textMuted);
}
}
} // namespace XCEngine::UI::Editor::Widgets

View File

@@ -0,0 +1,114 @@
#include <XCEditor/Collections/UIEditorInlineRenameSession.h>
#include <utility>
namespace XCEngine::UI::Editor {
Widgets::UIEditorTextFieldMetrics BuildUIEditorInlineRenameTextFieldMetrics(
const ::XCEngine::UI::UIRect& bounds,
const Widgets::UIEditorTextFieldMetrics& metrics) {
Widgets::UIEditorTextFieldMetrics resolved = metrics;
resolved.rowHeight = bounds.height > 0.0f ? bounds.height : metrics.rowHeight;
resolved.horizontalPadding = 0.0f;
resolved.labelControlGap = 0.0f;
resolved.controlColumnStart = 0.0f;
resolved.controlTrailingInset = 0.0f;
resolved.valueBoxMinWidth = bounds.width;
return resolved;
}
namespace {
void ResetSession(UIEditorInlineRenameSessionState& state) {
state = {};
}
void BeginSession(
UIEditorInlineRenameSessionState& state,
const UIEditorInlineRenameSessionRequest& request,
UIEditorInlineRenameSessionResult& result) {
ResetSession(state);
state.active = true;
state.itemId = request.itemId;
state.textFieldSpec.fieldId = request.itemId;
state.textFieldSpec.label.clear();
state.textFieldSpec.value = request.initialText;
state.textFieldSpec.readOnly = false;
state.textFieldInteraction.textFieldState.focused = true;
state.textFieldInteraction.textFieldState.editing = true;
state.textFieldInteraction.textFieldState.displayText = request.initialText;
state.textFieldInteraction.textInputState.value = request.initialText;
state.textFieldInteraction.textInputState.caret = request.initialText.size();
state.textFieldInteraction.editModel.BeginEdit(request.itemId, request.initialText);
result.sessionStarted = true;
result.active = true;
result.itemId = request.itemId;
result.valueBefore = request.initialText;
result.valueAfter = request.initialText;
}
} // namespace
UIEditorInlineRenameSessionFrame UpdateUIEditorInlineRenameSession(
UIEditorInlineRenameSessionState& state,
const UIEditorInlineRenameSessionRequest& request,
const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents,
const Widgets::UIEditorTextFieldMetrics& metrics) {
UIEditorInlineRenameSessionResult result = {};
if (request.beginSession &&
!request.itemId.empty() &&
(!state.active || state.itemId != request.itemId)) {
BeginSession(state, request, result);
}
Widgets::UIEditorTextFieldLayout layout = {};
if (!state.active) {
return {
std::move(layout),
std::move(result)
};
}
const Widgets::UIEditorTextFieldMetrics inlineMetrics =
BuildUIEditorInlineRenameTextFieldMetrics(request.bounds, metrics);
const std::string valueBefore = state.textFieldSpec.value;
UIEditorTextFieldInteractionFrame textFieldFrame =
UpdateUIEditorTextFieldInteraction(
state.textFieldInteraction,
state.textFieldSpec,
request.bounds,
inputEvents,
inlineMetrics);
layout = textFieldFrame.layout;
result.textFieldResult = textFieldFrame.result;
result.itemId = state.itemId;
result.active = state.active;
result.consumed = textFieldFrame.result.consumed;
result.valueBefore = textFieldFrame.result.valueBefore.empty()
? valueBefore
: textFieldFrame.result.valueBefore;
result.valueAfter = state.textFieldSpec.value;
if (textFieldFrame.result.editCommitted) {
result.sessionCommitted = true;
result.valueChanged = textFieldFrame.result.valueChanged;
result.valueAfter = state.textFieldSpec.value;
ResetSession(state);
result.active = false;
} else if (textFieldFrame.result.editCanceled) {
result.sessionCanceled = true;
result.valueAfter = result.valueBefore;
ResetSession(state);
result.active = false;
}
return {
std::move(layout),
std::move(result)
};
}
} // namespace XCEngine::UI::Editor

View File

@@ -1,4 +1,4 @@
#include <XCEditor/Widgets/UIEditorListView.h>
#include <XCEditor/Collections/UIEditorListView.h>
#include <algorithm>

View File

@@ -1,7 +1,8 @@
#include <XCEditor/Core/UIEditorListViewInteraction.h>
#include <XCEditor/Collections/UIEditorListViewInteraction.h>
#include <XCEngine/Input/InputTypes.h>
#include <algorithm>
#include <utility>
namespace XCEngine::UI::Editor {
@@ -33,10 +34,6 @@ bool ShouldUsePointerPosition(const UIInputEvent& event) {
}
}
bool HasNavigationModifiers(const ::XCEngine::UI::UIInputModifiers& modifiers) {
return modifiers.shift || modifiers.control || modifiers.alt || modifiers.super;
}
void SyncHoverTarget(
UIEditorListViewInteractionState& state,
const Widgets::UIEditorListViewLayout& layout,
@@ -76,21 +73,156 @@ void SyncKeyboardNavigation(
}
}
void SyncSelectionAnchor(
UIEditorListViewInteractionState& state,
const ::XCEngine::UI::Widgets::UISelectionModel& selectionModel) {
if (!selectionModel.HasSelection()) {
state.selectionAnchorId.clear();
return;
}
if (state.selectionAnchorId.empty() ||
!selectionModel.IsSelected(state.selectionAnchorId)) {
state.selectionAnchorId = selectionModel.GetSelectedId();
}
}
bool SelectItem(
UIEditorListViewInteractionState& state,
::XCEngine::UI::Widgets::UISelectionModel& selectionModel,
const std::vector<Widgets::UIEditorListViewItem>& items,
std::size_t itemIndex,
UIEditorListViewInteractionResult& result,
bool markKeyboardNavigation) {
if (itemIndex >= items.size()) {
return false;
}
const Widgets::UIEditorListViewItem& item = items[itemIndex];
result.selectionChanged = selectionModel.SetSelection(item.itemId);
result.selectedItemId = item.itemId;
result.selectedIndex = itemIndex;
result.keyboardNavigated = markKeyboardNavigation;
result.consumed = true;
state.keyboardNavigation.SetCurrentIndex(itemIndex);
state.selectionAnchorId = item.itemId;
return true;
}
bool ToggleItemSelection(
UIEditorListViewInteractionState& state,
::XCEngine::UI::Widgets::UISelectionModel& selectionModel,
const std::vector<Widgets::UIEditorListViewItem>& items,
std::size_t itemIndex,
UIEditorListViewInteractionResult& result) {
if (itemIndex >= items.size()) {
return false;
}
const Widgets::UIEditorListViewItem& item = items[itemIndex];
result.selectionChanged = selectionModel.ToggleSelectionMembership(item.itemId, true);
result.selectedItemId = item.itemId;
result.selectedIndex = itemIndex;
result.consumed = true;
state.keyboardNavigation.SetCurrentIndex(itemIndex);
state.selectionAnchorId = item.itemId;
return true;
}
bool SelectItemRange(
UIEditorListViewInteractionState& state,
::XCEngine::UI::Widgets::UISelectionModel& selectionModel,
const std::vector<Widgets::UIEditorListViewItem>& items,
std::size_t itemIndex,
UIEditorListViewInteractionResult& result,
bool markKeyboardNavigation) {
if (itemIndex >= items.size()) {
return false;
}
std::size_t anchorIndex = UIEditorListViewInvalidIndex;
if (!state.selectionAnchorId.empty()) {
anchorIndex = FindUIEditorListViewItemIndex(items, state.selectionAnchorId);
}
if (anchorIndex == UIEditorListViewInvalidIndex) {
return SelectItem(
state,
selectionModel,
items,
itemIndex,
result,
markKeyboardNavigation);
}
const std::size_t rangeBegin = (std::min)(anchorIndex, itemIndex);
const std::size_t rangeEnd = (std::max)(anchorIndex, itemIndex);
std::vector<std::string> selectedIds = {};
selectedIds.reserve(rangeEnd - rangeBegin + 1u);
for (std::size_t index = rangeBegin; index <= rangeEnd; ++index) {
selectedIds.push_back(items[index].itemId);
}
const Widgets::UIEditorListViewItem& item = items[itemIndex];
result.selectionChanged = selectionModel.SetSelections(std::move(selectedIds), item.itemId);
result.selectedItemId = item.itemId;
result.selectedIndex = itemIndex;
result.keyboardNavigated = markKeyboardNavigation;
result.consumed = true;
state.keyboardNavigation.SetCurrentIndex(itemIndex, false);
return true;
}
bool ApplyKeyboardNavigation(
UIEditorListViewInteractionState& state,
std::int32_t keyCode) {
::XCEngine::UI::Widgets::UISelectionModel& selectionModel,
const std::vector<Widgets::UIEditorListViewItem>& items,
std::int32_t keyCode,
UIEditorListViewInteractionResult& result,
bool extendSelectionRange) {
switch (static_cast<KeyCode>(keyCode)) {
case KeyCode::Up:
return state.keyboardNavigation.MovePrevious();
if (!state.keyboardNavigation.MovePrevious()) {
return false;
}
break;
case KeyCode::Down:
return state.keyboardNavigation.MoveNext();
if (!state.keyboardNavigation.MoveNext()) {
return false;
}
break;
case KeyCode::Home:
return state.keyboardNavigation.MoveHome();
if (!state.keyboardNavigation.MoveHome()) {
return false;
}
break;
case KeyCode::End:
return state.keyboardNavigation.MoveEnd();
if (!state.keyboardNavigation.MoveEnd()) {
return false;
}
break;
default:
return false;
}
if (!state.keyboardNavigation.HasCurrentIndex()) {
return false;
}
return extendSelectionRange
? SelectItemRange(
state,
selectionModel,
items,
state.keyboardNavigation.GetCurrentIndex(),
result,
true)
: SelectItem(
state,
selectionModel,
items,
state.keyboardNavigation.GetCurrentIndex(),
result,
true);
}
} // namespace
@@ -105,6 +237,7 @@ UIEditorListViewInteractionFrame UpdateUIEditorListViewInteraction(
Widgets::UIEditorListViewLayout layout =
BuildUIEditorListViewLayout(bounds, items, metrics);
SyncKeyboardNavigation(state, selectionModel, items);
SyncSelectionAnchor(state, selectionModel);
SyncHoverTarget(state, layout, items);
UIEditorListViewInteractionResult interactionResult = {};
@@ -180,38 +313,79 @@ UIEditorListViewInteractionFrame UpdateUIEditorListViewInteraction(
const Widgets::UIEditorListViewItem& item = items[hitTarget.itemIndex];
if (event.pointerButton == UIPointerButton::Left &&
hitTarget.kind == UIEditorListViewHitTargetKind::Row) {
eventResult.selectionChanged = selectionModel.SetSelection(item.itemId);
eventResult.selectedItemId = item.itemId;
eventResult.selectedIndex = hitTarget.itemIndex;
eventResult.consumed = true;
if (event.modifiers.shift) {
SelectItemRange(
state,
selectionModel,
items,
hitTarget.itemIndex,
eventResult,
false);
} else if (event.modifiers.control) {
ToggleItemSelection(
state,
selectionModel,
items,
hitTarget.itemIndex,
eventResult);
} else {
SelectItem(
state,
selectionModel,
items,
hitTarget.itemIndex,
eventResult,
false);
}
state.listViewState.focused = true;
state.keyboardNavigation.SetCurrentIndex(hitTarget.itemIndex);
} else if (event.pointerButton == UIPointerButton::Right &&
hitTarget.kind == UIEditorListViewHitTargetKind::Row) {
eventResult.selectionChanged = selectionModel.SetSelection(item.itemId);
eventResult.selectedItemId = item.itemId;
eventResult.selectedIndex = hitTarget.itemIndex;
if (selectionModel.IsSelected(item.itemId)) {
selectionModel.SetPrimarySelection(item.itemId);
eventResult.selectionChanged = false;
eventResult.selectedItemId = item.itemId;
eventResult.selectedIndex = hitTarget.itemIndex;
eventResult.consumed = true;
state.keyboardNavigation.SetCurrentIndex(hitTarget.itemIndex);
state.selectionAnchorId = item.itemId;
} else {
SelectItem(
state,
selectionModel,
items,
hitTarget.itemIndex,
eventResult,
false);
}
eventResult.secondaryClicked = true;
eventResult.consumed = true;
state.listViewState.focused = true;
state.keyboardNavigation.SetCurrentIndex(hitTarget.itemIndex);
}
break;
}
case UIInputEventType::KeyDown:
if (state.listViewState.focused && !HasNavigationModifiers(event.modifiers)) {
if (ApplyKeyboardNavigation(state, event.keyCode) &&
state.keyboardNavigation.HasCurrentIndex()) {
const std::size_t currentIndex = state.keyboardNavigation.GetCurrentIndex();
if (currentIndex < items.size()) {
eventResult.selectionChanged =
selectionModel.SetSelection(items[currentIndex].itemId);
eventResult.keyboardNavigated = true;
eventResult.selectedItemId = items[currentIndex].itemId;
eventResult.selectedIndex = currentIndex;
eventResult.consumed = true;
}
if (state.listViewState.focused &&
!event.modifiers.control &&
!event.modifiers.alt &&
!event.modifiers.super) {
if (event.keyCode == static_cast<std::int32_t>(KeyCode::F2) &&
selectionModel.HasSelection()) {
const std::string& selectedItemId = selectionModel.GetSelectedId();
eventResult.renameRequested = true;
eventResult.renameItemId = selectedItemId;
eventResult.selectedItemId = selectedItemId;
eventResult.selectedIndex =
FindUIEditorListViewItemIndex(items, selectedItemId);
eventResult.consumed = true;
} else if (ApplyKeyboardNavigation(
state,
selectionModel,
items,
event.keyCode,
eventResult,
event.modifiers.shift)) {
eventResult.consumed = true;
}
}
break;
@@ -222,6 +396,7 @@ UIEditorListViewInteractionFrame UpdateUIEditorListViewInteraction(
layout = BuildUIEditorListViewLayout(bounds, items, metrics);
SyncKeyboardNavigation(state, selectionModel, items);
SyncSelectionAnchor(state, selectionModel);
SyncHoverTarget(state, layout, items);
if (eventResult.hitTarget.kind == UIEditorListViewHitTargetKind::None &&
state.hasPointerPosition) {
@@ -232,14 +407,17 @@ UIEditorListViewInteractionFrame UpdateUIEditorListViewInteraction(
eventResult.selectionChanged ||
eventResult.keyboardNavigated ||
eventResult.secondaryClicked ||
eventResult.renameRequested ||
eventResult.hitTarget.kind != UIEditorListViewHitTargetKind::None ||
!eventResult.selectedItemId.empty()) {
!eventResult.selectedItemId.empty() ||
!eventResult.renameItemId.empty()) {
interactionResult = std::move(eventResult);
}
}
layout = BuildUIEditorListViewLayout(bounds, items, metrics);
SyncKeyboardNavigation(state, selectionModel, items);
SyncSelectionAnchor(state, selectionModel);
SyncHoverTarget(state, layout, items);
if (interactionResult.hitTarget.kind == UIEditorListViewHitTargetKind::None &&
state.hasPointerPosition) {

View File

@@ -1,4 +1,4 @@
#include <XCEditor/Widgets/UIEditorScrollView.h>
#include <XCEditor/Collections/UIEditorScrollView.h>
#include <algorithm>
@@ -14,6 +14,47 @@ float ClampRange(float value, float minValue, float maxValue) {
return (std::min)((std::max)(value, minValue), maxValue);
}
::XCEngine::UI::UIColor LerpColor(
const ::XCEngine::UI::UIColor& from,
const ::XCEngine::UI::UIColor& to,
float factor) {
const float t = ClampRange(factor, 0.0f, 1.0f);
return ::XCEngine::UI::UIColor(
from.r + (to.r - from.r) * t,
from.g + (to.g - from.g) * t,
from.b + (to.b - from.b) * t,
from.a + (to.a - from.a) * t);
}
::XCEngine::UI::UIColor ResolveBorderColor(
const UIEditorScrollViewState& state,
const UIEditorScrollViewPalette& palette) {
if (state.focused) {
return palette.focusedBorderColor;
}
if (state.hovered || state.scrollbarHovered) {
return LerpColor(palette.borderColor, palette.focusedBorderColor, 0.45f);
}
return palette.borderColor;
}
float ResolveBorderThickness(
const UIEditorScrollViewState& state,
const UIEditorScrollViewMetrics& metrics) {
if (state.focused) {
return metrics.focusedBorderThickness;
}
if (state.hovered || state.scrollbarHovered) {
return metrics.borderThickness +
(metrics.focusedBorderThickness - metrics.borderThickness) * 0.5f;
}
return metrics.borderThickness;
}
} // namespace
bool IsUIEditorScrollViewPointInside(
@@ -124,8 +165,8 @@ void AppendUIEditorScrollViewBackground(
drawList.AddFilledRect(layout.bounds, palette.surfaceColor, metrics.cornerRounding);
drawList.AddRectOutline(
layout.bounds,
state.focused ? palette.focusedBorderColor : palette.borderColor,
state.focused ? metrics.focusedBorderThickness : metrics.borderThickness,
ResolveBorderColor(state, palette),
ResolveBorderThickness(state, metrics),
metrics.cornerRounding);
if (!layout.hasScrollbar) {

View File

@@ -1,4 +1,4 @@
#include <XCEditor/Core/UIEditorScrollViewInteraction.h>
#include <XCEditor/Collections/UIEditorScrollViewInteraction.h>
#include <algorithm>
#include <utility>

View File

@@ -1,4 +1,4 @@
#include <XCEditor/Widgets/UIEditorTabStrip.h>
#include <XCEditor/Collections/UIEditorTabStrip.h>
#include <algorithm>
#include <string>
@@ -34,6 +34,20 @@ bool IsPointInsideRect(
point.y <= rect.y + rect.height;
}
float ResolveStripRounding(const UIEditorTabStripMetrics& metrics) {
return (std::max)(
(std::min)(ClampNonNegative(metrics.layoutMetrics.headerHeight) * 0.25f, kStripRounding),
0.0f);
}
float ResolveTabRounding(const UIEditorTabStripMetrics& metrics) {
return (std::max)(ResolveStripRounding(metrics) - 1.0f, 0.0f);
}
float ResolveCloseButtonRounding(const UIEditorTabStripMetrics& metrics) {
return (std::min)(ClampNonNegative(metrics.closeButtonExtent) * 0.35f, 5.0f);
}
std::size_t ResolveSelectedIndex(
std::size_t itemCount,
std::size_t selectedIndex) {
@@ -291,18 +305,20 @@ void AppendUIEditorTabStripBackground(
const UIEditorTabStripState& state,
const UIEditorTabStripPalette& palette,
const UIEditorTabStripMetrics& metrics) {
drawList.AddFilledRect(layout.bounds, palette.stripBackgroundColor, kStripRounding);
const float stripRounding = ResolveStripRounding(metrics);
const float tabRounding = ResolveTabRounding(metrics);
drawList.AddFilledRect(layout.bounds, palette.stripBackgroundColor, stripRounding);
if (layout.contentRect.height > 0.0f) {
drawList.AddFilledRect(layout.contentRect, palette.contentBackgroundColor, kStripRounding);
drawList.AddFilledRect(layout.contentRect, palette.contentBackgroundColor, stripRounding);
}
if (layout.headerRect.height > 0.0f) {
drawList.AddFilledRect(layout.headerRect, palette.headerBackgroundColor, kStripRounding);
drawList.AddFilledRect(layout.headerRect, palette.headerBackgroundColor, stripRounding);
}
drawList.AddRectOutline(
layout.bounds,
ResolveStripBorderColor(state, palette),
ResolveStripBorderThickness(state, metrics),
kStripRounding);
stripRounding);
for (std::size_t index = 0; index < layout.tabHeaderRects.size(); ++index) {
const bool selected = layout.selectedIndex == index;
@@ -310,12 +326,12 @@ void AppendUIEditorTabStripBackground(
drawList.AddFilledRect(
layout.tabHeaderRects[index],
ResolveTabFillColor(selected, hovered, palette),
kTabRounding);
tabRounding);
drawList.AddRectOutline(
layout.tabHeaderRects[index],
ResolveTabBorderColor(selected, hovered, state.focused, palette),
ResolveTabBorderThickness(selected, state.focused, metrics),
kTabRounding);
tabRounding);
}
}
@@ -361,15 +377,16 @@ void AppendUIEditorTabStripForeground(
const bool closeHovered = state.closeHoveredIndex == index;
const UIRect& closeRect = layout.closeButtonRects[index];
const float closeRounding = ResolveCloseButtonRounding(metrics);
drawList.AddFilledRect(
closeRect,
closeHovered ? palette.closeButtonHoveredColor : palette.closeButtonColor,
4.0f);
closeRounding);
drawList.AddRectOutline(
closeRect,
palette.closeButtonBorderColor,
1.0f,
4.0f);
closeRounding);
drawList.AddText(
UIPoint(
closeRect.x + (std::max)(0.0f, (closeRect.width - 7.0f) * 0.5f),

View File

@@ -0,0 +1,329 @@
#include <XCEditor/Collections/UIEditorTabStripInteraction.h>
#include <XCEngine/Input/InputTypes.h>
#include <utility>
namespace XCEngine::UI::Editor {
namespace {
using ::XCEngine::Input::KeyCode;
using ::XCEngine::UI::UIInputEvent;
using ::XCEngine::UI::UIInputEventType;
using ::XCEngine::UI::UIPointerButton;
using Widgets::BuildUIEditorTabStripLayout;
using Widgets::HitTestUIEditorTabStrip;
using Widgets::ResolveUIEditorTabStripSelectedIndex;
using Widgets::UIEditorTabStripHitTarget;
using Widgets::UIEditorTabStripHitTargetKind;
using Widgets::UIEditorTabStripInvalidIndex;
bool ShouldUsePointerPosition(const UIInputEvent& event) {
switch (event.type) {
case UIInputEventType::PointerMove:
case UIInputEventType::PointerEnter:
case UIInputEventType::PointerButtonDown:
case UIInputEventType::PointerButtonUp:
return true;
default:
return false;
}
}
bool HasNavigationModifiers(const ::XCEngine::UI::UIInputModifiers& modifiers) {
return modifiers.shift || modifiers.control || modifiers.alt || modifiers.super;
}
bool IsPointInside(const ::XCEngine::UI::UIRect& rect, const ::XCEngine::UI::UIPoint& point) {
return point.x >= rect.x &&
point.x <= rect.x + rect.width &&
point.y >= rect.y &&
point.y <= rect.y + rect.height;
}
bool AreEquivalentTargets(
const UIEditorTabStripHitTarget& lhs,
const UIEditorTabStripHitTarget& rhs) {
return lhs.kind == rhs.kind && lhs.index == rhs.index;
}
void ClearHoverState(UIEditorTabStripInteractionState& state) {
state.tabStripState.hoveredIndex = UIEditorTabStripInvalidIndex;
state.tabStripState.closeHoveredIndex = UIEditorTabStripInvalidIndex;
}
void SyncHoverTarget(
UIEditorTabStripInteractionState& state,
const Widgets::UIEditorTabStripLayout& layout) {
ClearHoverState(state);
if (!state.hasPointerPosition) {
return;
}
const UIEditorTabStripHitTarget hitTarget =
HitTestUIEditorTabStrip(layout, state.tabStripState, state.pointerPosition);
if (hitTarget.index == UIEditorTabStripInvalidIndex) {
return;
}
switch (hitTarget.kind) {
case UIEditorTabStripHitTargetKind::CloseButton:
state.tabStripState.hoveredIndex = hitTarget.index;
state.tabStripState.closeHoveredIndex = hitTarget.index;
break;
case UIEditorTabStripHitTargetKind::Tab:
state.tabStripState.hoveredIndex = hitTarget.index;
break;
default:
break;
}
}
void SyncSelectionState(
UIEditorTabStripInteractionState& state,
std::string_view selectedTabId,
const std::vector<Widgets::UIEditorTabStripItem>& items) {
state.navigationModel.SetItemCount(items.size());
const std::size_t fallbackIndex =
state.navigationModel.HasSelection()
? state.navigationModel.GetSelectedIndex()
: UIEditorTabStripInvalidIndex;
const std::size_t resolvedSelectedIndex =
ResolveUIEditorTabStripSelectedIndex(items, selectedTabId, fallbackIndex);
if (resolvedSelectedIndex != UIEditorTabStripInvalidIndex) {
state.navigationModel.SetSelectedIndex(resolvedSelectedIndex);
}
state.tabStripState.selectedIndex =
state.navigationModel.HasSelection()
? state.navigationModel.GetSelectedIndex()
: UIEditorTabStripInvalidIndex;
}
bool SelectTab(
UIEditorTabStripInteractionState& state,
std::string& selectedTabId,
const std::vector<Widgets::UIEditorTabStripItem>& items,
std::size_t selectedIndex,
UIEditorTabStripInteractionResult& result,
bool keyboardNavigated) {
if (selectedIndex >= items.size()) {
return false;
}
state.navigationModel.SetSelectedIndex(selectedIndex);
state.tabStripState.selectedIndex = selectedIndex;
const std::string& tabId = items[selectedIndex].tabId;
result.selectionChanged = selectedTabId != tabId;
selectedTabId = tabId;
result.keyboardNavigated = keyboardNavigated;
result.selectedTabId = tabId;
result.selectedIndex = selectedIndex;
result.consumed = true;
return true;
}
bool ApplyKeyboardNavigation(
UIEditorTabStripInteractionState& state,
std::int32_t keyCode) {
switch (static_cast<KeyCode>(keyCode)) {
case KeyCode::Left:
return state.navigationModel.SelectPrevious();
case KeyCode::Right:
return state.navigationModel.SelectNext();
case KeyCode::Home:
return state.navigationModel.SelectFirst();
case KeyCode::End:
return state.navigationModel.SelectLast();
default:
return false;
}
}
} // namespace
UIEditorTabStripInteractionFrame UpdateUIEditorTabStripInteraction(
UIEditorTabStripInteractionState& state,
std::string& selectedTabId,
const ::XCEngine::UI::UIRect& bounds,
const std::vector<Widgets::UIEditorTabStripItem>& items,
const std::vector<UIInputEvent>& inputEvents,
const Widgets::UIEditorTabStripMetrics& metrics) {
SyncSelectionState(state, selectedTabId, items);
Widgets::UIEditorTabStripLayout layout =
BuildUIEditorTabStripLayout(bounds, items, state.tabStripState, metrics);
SyncHoverTarget(state, layout);
UIEditorTabStripInteractionResult interactionResult = {};
for (const UIInputEvent& event : inputEvents) {
if (ShouldUsePointerPosition(event)) {
state.pointerPosition = event.position;
state.hasPointerPosition = true;
} else if (event.type == UIInputEventType::PointerLeave) {
state.hasPointerPosition = false;
}
UIEditorTabStripInteractionResult eventResult = {};
eventResult.hitTarget =
state.hasPointerPosition
? HitTestUIEditorTabStrip(layout, state.tabStripState, state.pointerPosition)
: UIEditorTabStripHitTarget {};
switch (event.type) {
case UIInputEventType::FocusGained:
state.tabStripState.focused = true;
break;
case UIInputEventType::FocusLost:
state.tabStripState.focused = false;
state.hasPointerPosition = false;
state.pressedTarget = {};
ClearHoverState(state);
break;
case UIInputEventType::PointerMove:
case UIInputEventType::PointerEnter:
case UIInputEventType::PointerLeave:
break;
case UIInputEventType::PointerButtonDown: {
if (event.pointerButton != UIPointerButton::Left) {
break;
}
state.pressedTarget = eventResult.hitTarget;
if (eventResult.hitTarget.kind != UIEditorTabStripHitTargetKind::None ||
(state.hasPointerPosition && IsPointInside(layout.bounds, state.pointerPosition))) {
state.tabStripState.focused = true;
eventResult.consumed = true;
} else {
state.tabStripState.focused = false;
state.pressedTarget = {};
}
break;
}
case UIInputEventType::PointerButtonUp: {
if (event.pointerButton != UIPointerButton::Left) {
break;
}
const bool insideStrip =
state.hasPointerPosition && IsPointInside(layout.bounds, state.pointerPosition);
const bool matchedPressedTarget =
AreEquivalentTargets(state.pressedTarget, eventResult.hitTarget);
if (matchedPressedTarget) {
switch (eventResult.hitTarget.kind) {
case UIEditorTabStripHitTargetKind::CloseButton:
if (eventResult.hitTarget.index < items.size() &&
items[eventResult.hitTarget.index].closable) {
eventResult.closeRequested = true;
eventResult.closedTabId = items[eventResult.hitTarget.index].tabId;
eventResult.closedIndex = eventResult.hitTarget.index;
eventResult.consumed = true;
state.tabStripState.focused = true;
} else if (insideStrip) {
state.tabStripState.focused = true;
eventResult.consumed = true;
}
break;
case UIEditorTabStripHitTargetKind::Tab:
SelectTab(
state,
selectedTabId,
items,
eventResult.hitTarget.index,
eventResult,
false);
state.tabStripState.focused = true;
break;
case UIEditorTabStripHitTargetKind::HeaderBackground:
case UIEditorTabStripHitTargetKind::Content:
state.tabStripState.focused = true;
eventResult.consumed = true;
break;
case UIEditorTabStripHitTargetKind::None:
default:
if (!insideStrip) {
state.tabStripState.focused = false;
}
break;
}
} else if (!insideStrip) {
state.tabStripState.focused = false;
} else if (eventResult.hitTarget.kind == UIEditorTabStripHitTargetKind::HeaderBackground ||
eventResult.hitTarget.kind == UIEditorTabStripHitTargetKind::Content) {
state.tabStripState.focused = true;
eventResult.consumed = true;
}
state.pressedTarget = {};
break;
}
case UIInputEventType::KeyDown:
if (state.tabStripState.focused &&
!HasNavigationModifiers(event.modifiers) &&
ApplyKeyboardNavigation(state, event.keyCode) &&
state.navigationModel.HasSelection()) {
const std::size_t selectedIndex = state.navigationModel.GetSelectedIndex();
SelectTab(
state,
selectedTabId,
items,
selectedIndex,
eventResult,
true);
}
break;
default:
break;
}
SyncSelectionState(state, selectedTabId, items);
layout = BuildUIEditorTabStripLayout(bounds, items, state.tabStripState, metrics);
SyncHoverTarget(state, layout);
if (eventResult.hitTarget.kind == UIEditorTabStripHitTargetKind::None &&
state.hasPointerPosition) {
eventResult.hitTarget =
HitTestUIEditorTabStrip(layout, state.tabStripState, state.pointerPosition);
}
if (eventResult.consumed ||
eventResult.selectionChanged ||
eventResult.closeRequested ||
eventResult.keyboardNavigated ||
eventResult.hitTarget.kind != UIEditorTabStripHitTargetKind::None ||
!eventResult.selectedTabId.empty() ||
!eventResult.closedTabId.empty()) {
interactionResult = std::move(eventResult);
}
}
SyncSelectionState(state, selectedTabId, items);
layout = BuildUIEditorTabStripLayout(bounds, items, state.tabStripState, metrics);
SyncHoverTarget(state, layout);
if (interactionResult.hitTarget.kind == UIEditorTabStripHitTargetKind::None &&
state.hasPointerPosition) {
interactionResult.hitTarget =
HitTestUIEditorTabStrip(layout, state.tabStripState, state.pointerPosition);
}
return {
std::move(layout),
std::move(interactionResult),
state.tabStripState.focused
};
}
} // namespace XCEngine::UI::Editor

View File

@@ -1,4 +1,4 @@
#include <XCEditor/Widgets/UIEditorTreeView.h>
#include <XCEditor/Collections/UIEditorTreeView.h>
#include <XCEngine/UI/Widgets/UIFlatHierarchyHelpers.h>

View File

@@ -0,0 +1,677 @@
#include <XCEditor/Collections/UIEditorTreeViewInteraction.h>
#include <XCEngine/Input/InputTypes.h>
#include <utility>
namespace XCEngine::UI::Editor {
namespace {
using ::XCEngine::Input::KeyCode;
using ::XCEngine::UI::UIInputEvent;
using ::XCEngine::UI::UIInputEventType;
using ::XCEngine::UI::UIPointerButton;
using Widgets::BuildUIEditorTreeViewLayout;
using Widgets::DoesUIEditorTreeViewItemHaveChildren;
using Widgets::FindUIEditorTreeViewFirstVisibleChildItemIndex;
using Widgets::FindUIEditorTreeViewItemIndex;
using Widgets::FindUIEditorTreeViewParentItemIndex;
using Widgets::HitTestUIEditorTreeView;
using Widgets::IsUIEditorTreeViewPointInside;
using Widgets::UIEditorTreeViewHitTarget;
using Widgets::UIEditorTreeViewHitTargetKind;
using Widgets::UIEditorTreeViewInvalidIndex;
bool ShouldUsePointerPosition(const UIInputEvent& event) {
switch (event.type) {
case UIInputEventType::PointerMove:
case UIInputEventType::PointerEnter:
case UIInputEventType::PointerButtonDown:
case UIInputEventType::PointerButtonUp:
return true;
default:
return false;
}
}
bool HasNavigationModifiers(const ::XCEngine::UI::UIInputModifiers& modifiers) {
return modifiers.shift || modifiers.control || modifiers.alt || modifiers.super;
}
void SyncHoverTarget(
UIEditorTreeViewInteractionState& state,
const Widgets::UIEditorTreeViewLayout& layout,
const std::vector<Widgets::UIEditorTreeViewItem>& items) {
state.treeViewState.hoveredItemId.clear();
if (!state.hasPointerPosition) {
return;
}
const UIEditorTreeViewHitTarget hitTarget =
HitTestUIEditorTreeView(layout, state.pointerPosition);
if (hitTarget.itemIndex < items.size()) {
state.treeViewState.hoveredItemId = items[hitTarget.itemIndex].itemId;
}
}
std::size_t FindVisibleIndexForItemIndex(
const Widgets::UIEditorTreeViewLayout& layout,
std::size_t itemIndex) {
for (std::size_t visibleIndex = 0u; visibleIndex < layout.visibleItemIndices.size(); ++visibleIndex) {
if (layout.visibleItemIndices[visibleIndex] == itemIndex) {
return visibleIndex;
}
}
return UIEditorTreeViewInvalidIndex;
}
std::size_t FindVisibleIndexForItemId(
const Widgets::UIEditorTreeViewLayout& layout,
const std::vector<Widgets::UIEditorTreeViewItem>& items,
std::string_view itemId) {
const std::size_t itemIndex = FindUIEditorTreeViewItemIndex(items, itemId);
if (itemIndex == UIEditorTreeViewInvalidIndex) {
return UIEditorTreeViewInvalidIndex;
}
return FindVisibleIndexForItemIndex(layout, itemIndex);
}
void SyncKeyboardNavigation(
UIEditorTreeViewInteractionState& state,
const ::XCEngine::UI::Widgets::UISelectionModel& selectionModel,
const Widgets::UIEditorTreeViewLayout& layout,
const std::vector<Widgets::UIEditorTreeViewItem>& items) {
state.keyboardNavigation.SetItemCount(layout.visibleItemIndices.size());
state.keyboardNavigation.ClampToItemCount();
if (!selectionModel.HasSelection()) {
return;
}
const std::size_t selectedVisibleIndex =
FindVisibleIndexForItemId(layout, items, selectionModel.GetSelectedId());
if (selectedVisibleIndex == UIEditorTreeViewInvalidIndex) {
return;
}
if (!state.keyboardNavigation.HasCurrentIndex() ||
state.keyboardNavigation.GetCurrentIndex() != selectedVisibleIndex) {
state.keyboardNavigation.SetCurrentIndex(selectedVisibleIndex);
}
}
void SyncSelectionAnchor(
UIEditorTreeViewInteractionState& state,
const ::XCEngine::UI::Widgets::UISelectionModel& selectionModel) {
if (!selectionModel.HasSelection()) {
state.selectionAnchorId.clear();
return;
}
if (state.selectionAnchorId.empty() ||
!selectionModel.IsSelected(state.selectionAnchorId)) {
state.selectionAnchorId = selectionModel.GetSelectedId();
}
}
bool IsDescendantItem(
const std::vector<Widgets::UIEditorTreeViewItem>& items,
std::size_t ancestorIndex,
std::size_t candidateIndex) {
if (ancestorIndex >= items.size() || candidateIndex >= items.size() || candidateIndex <= ancestorIndex) {
return false;
}
const std::uint32_t ancestorDepth = items[ancestorIndex].depth;
for (std::size_t index = ancestorIndex + 1u; index <= candidateIndex; ++index) {
if (items[index].depth <= ancestorDepth) {
return false;
}
}
return true;
}
bool SelectVisibleItem(
UIEditorTreeViewInteractionState& state,
::XCEngine::UI::Widgets::UISelectionModel& selectionModel,
const Widgets::UIEditorTreeViewLayout& layout,
const std::vector<Widgets::UIEditorTreeViewItem>& items,
std::size_t visibleIndex,
UIEditorTreeViewInteractionResult& result,
bool markKeyboardNavigation) {
if (visibleIndex >= layout.visibleItemIndices.size()) {
return false;
}
const std::size_t itemIndex = layout.visibleItemIndices[visibleIndex];
if (itemIndex >= items.size()) {
return false;
}
const Widgets::UIEditorTreeViewItem& item = items[itemIndex];
result.selectionChanged = selectionModel.SetSelection(item.itemId);
result.selectedItemId = item.itemId;
result.selectedVisibleIndex = visibleIndex;
result.keyboardNavigated = markKeyboardNavigation;
result.consumed = true;
state.keyboardNavigation.SetCurrentIndex(visibleIndex);
state.selectionAnchorId = item.itemId;
return true;
}
bool ToggleVisibleItemSelection(
UIEditorTreeViewInteractionState& state,
::XCEngine::UI::Widgets::UISelectionModel& selectionModel,
const Widgets::UIEditorTreeViewLayout& layout,
const std::vector<Widgets::UIEditorTreeViewItem>& items,
std::size_t visibleIndex,
UIEditorTreeViewInteractionResult& result) {
if (visibleIndex >= layout.visibleItemIndices.size()) {
return false;
}
const std::size_t itemIndex = layout.visibleItemIndices[visibleIndex];
if (itemIndex >= items.size()) {
return false;
}
const Widgets::UIEditorTreeViewItem& item = items[itemIndex];
result.selectionChanged = selectionModel.ToggleSelectionMembership(item.itemId, true);
result.selectedItemId = item.itemId;
result.selectedVisibleIndex = visibleIndex;
result.consumed = true;
state.keyboardNavigation.SetCurrentIndex(visibleIndex);
state.selectionAnchorId = item.itemId;
return true;
}
bool SelectVisibleItemRange(
UIEditorTreeViewInteractionState& state,
::XCEngine::UI::Widgets::UISelectionModel& selectionModel,
const Widgets::UIEditorTreeViewLayout& layout,
const std::vector<Widgets::UIEditorTreeViewItem>& items,
std::size_t visibleIndex,
UIEditorTreeViewInteractionResult& result,
bool markKeyboardNavigation) {
if (visibleIndex >= layout.visibleItemIndices.size()) {
return false;
}
std::size_t anchorVisibleIndex = UIEditorTreeViewInvalidIndex;
if (!state.selectionAnchorId.empty()) {
anchorVisibleIndex = FindVisibleIndexForItemId(layout, items, state.selectionAnchorId);
}
if (anchorVisibleIndex == UIEditorTreeViewInvalidIndex) {
return SelectVisibleItem(
state,
selectionModel,
layout,
items,
visibleIndex,
result,
markKeyboardNavigation);
}
const std::size_t rangeBegin = (std::min)(anchorVisibleIndex, visibleIndex);
const std::size_t rangeEnd = (std::max)(anchorVisibleIndex, visibleIndex);
std::vector<std::string> selectedIds = {};
selectedIds.reserve(rangeEnd - rangeBegin + 1u);
for (std::size_t index = rangeBegin; index <= rangeEnd; ++index) {
const std::size_t itemIndex = layout.visibleItemIndices[index];
if (itemIndex < items.size()) {
selectedIds.push_back(items[itemIndex].itemId);
}
}
const std::size_t itemIndex = layout.visibleItemIndices[visibleIndex];
if (itemIndex >= items.size()) {
return false;
}
const Widgets::UIEditorTreeViewItem& item = items[itemIndex];
result.selectionChanged = selectionModel.SetSelections(std::move(selectedIds), item.itemId);
result.selectedItemId = item.itemId;
result.selectedVisibleIndex = visibleIndex;
result.keyboardNavigated = markKeyboardNavigation;
result.consumed = true;
state.keyboardNavigation.SetCurrentIndex(visibleIndex);
return true;
}
void PopulateCurrentVisibleItemResult(
const Widgets::UIEditorTreeViewLayout& layout,
const std::vector<Widgets::UIEditorTreeViewItem>& items,
std::size_t visibleIndex,
UIEditorTreeViewInteractionResult& result) {
if (visibleIndex >= layout.visibleItemIndices.size()) {
return;
}
const std::size_t itemIndex = layout.visibleItemIndices[visibleIndex];
if (itemIndex >= items.size()) {
return;
}
result.selectedItemId = items[itemIndex].itemId;
result.selectedVisibleIndex = visibleIndex;
}
bool ApplyVerticalNavigation(
UIEditorTreeViewInteractionState& state,
::XCEngine::UI::Widgets::UISelectionModel& selectionModel,
const Widgets::UIEditorTreeViewLayout& layout,
const std::vector<Widgets::UIEditorTreeViewItem>& items,
std::int32_t keyCode,
UIEditorTreeViewInteractionResult& result,
bool extendSelectionRange) {
bool moved = false;
switch (static_cast<KeyCode>(keyCode)) {
case KeyCode::Up:
moved = state.keyboardNavigation.MovePrevious();
break;
case KeyCode::Down:
moved = state.keyboardNavigation.MoveNext();
break;
case KeyCode::Home:
moved = state.keyboardNavigation.MoveHome();
break;
case KeyCode::End:
moved = state.keyboardNavigation.MoveEnd();
break;
default:
break;
}
if (!moved || !state.keyboardNavigation.HasCurrentIndex()) {
return false;
}
return extendSelectionRange
? SelectVisibleItemRange(
state,
selectionModel,
layout,
items,
state.keyboardNavigation.GetCurrentIndex(),
result,
true)
: SelectVisibleItem(
state,
selectionModel,
layout,
items,
state.keyboardNavigation.GetCurrentIndex(),
result,
true);
}
bool ApplyHorizontalNavigation(
UIEditorTreeViewInteractionState& state,
::XCEngine::UI::Widgets::UISelectionModel& selectionModel,
::XCEngine::UI::Widgets::UIExpansionModel& expansionModel,
const Widgets::UIEditorTreeViewLayout& layout,
const std::vector<Widgets::UIEditorTreeViewItem>& items,
std::int32_t keyCode,
UIEditorTreeViewInteractionResult& result) {
if (!state.keyboardNavigation.HasCurrentIndex()) {
return false;
}
const std::size_t visibleIndex = state.keyboardNavigation.GetCurrentIndex();
if (visibleIndex >= layout.visibleItemIndices.size()) {
return false;
}
const std::size_t itemIndex = layout.visibleItemIndices[visibleIndex];
if (itemIndex >= items.size()) {
return false;
}
const Widgets::UIEditorTreeViewItem& item = items[itemIndex];
const bool hasChildren = DoesUIEditorTreeViewItemHaveChildren(items, itemIndex);
const bool expanded = expansionModel.IsExpanded(item.itemId);
switch (static_cast<KeyCode>(keyCode)) {
case KeyCode::Right:
if (hasChildren && !expanded) {
result.expansionChanged = expansionModel.Expand(item.itemId);
result.toggledItemId = item.itemId;
result.keyboardNavigated = true;
PopulateCurrentVisibleItemResult(layout, items, visibleIndex, result);
result.consumed = true;
return result.expansionChanged;
}
if (hasChildren && expanded) {
const std::size_t childItemIndex =
FindUIEditorTreeViewFirstVisibleChildItemIndex(items, expansionModel, itemIndex);
const std::size_t childVisibleIndex =
FindVisibleIndexForItemIndex(layout, childItemIndex);
if (childVisibleIndex != UIEditorTreeViewInvalidIndex) {
return SelectVisibleItem(
state,
selectionModel,
layout,
items,
childVisibleIndex,
result,
true);
}
}
return false;
case KeyCode::Left:
if (hasChildren && expanded) {
result.expansionChanged = expansionModel.Collapse(item.itemId);
result.toggledItemId = item.itemId;
result.keyboardNavigated = true;
PopulateCurrentVisibleItemResult(layout, items, visibleIndex, result);
result.consumed = true;
return result.expansionChanged;
}
{
const std::size_t parentItemIndex = FindUIEditorTreeViewParentItemIndex(items, itemIndex);
const std::size_t parentVisibleIndex =
FindVisibleIndexForItemIndex(layout, parentItemIndex);
if (parentVisibleIndex != UIEditorTreeViewInvalidIndex) {
return SelectVisibleItem(
state,
selectionModel,
layout,
items,
parentVisibleIndex,
result,
true);
}
}
return false;
default:
return false;
}
}
void NormalizeSelectionAfterCollapse(
UIEditorTreeViewInteractionState& state,
::XCEngine::UI::Widgets::UISelectionModel& selectionModel,
const Widgets::UIEditorTreeViewLayout& layout,
const std::vector<Widgets::UIEditorTreeViewItem>& items,
std::string_view collapsedItemId,
UIEditorTreeViewInteractionResult& result) {
if (!selectionModel.HasSelection()) {
return;
}
const std::size_t collapsedItemIndex = FindUIEditorTreeViewItemIndex(items, collapsedItemId);
const std::size_t selectedItemIndex = FindUIEditorTreeViewItemIndex(items, selectionModel.GetSelectedId());
if (collapsedItemIndex == UIEditorTreeViewInvalidIndex ||
selectedItemIndex == UIEditorTreeViewInvalidIndex ||
!IsDescendantItem(items, collapsedItemIndex, selectedItemIndex)) {
return;
}
const std::size_t collapsedVisibleIndex =
FindVisibleIndexForItemIndex(layout, collapsedItemIndex);
if (collapsedVisibleIndex == UIEditorTreeViewInvalidIndex) {
return;
}
SelectVisibleItem(
state,
selectionModel,
layout,
items,
collapsedVisibleIndex,
result,
false);
}
} // namespace
UIEditorTreeViewInteractionFrame UpdateUIEditorTreeViewInteraction(
UIEditorTreeViewInteractionState& state,
::XCEngine::UI::Widgets::UISelectionModel& selectionModel,
::XCEngine::UI::Widgets::UIExpansionModel& expansionModel,
const ::XCEngine::UI::UIRect& bounds,
const std::vector<Widgets::UIEditorTreeViewItem>& items,
const std::vector<UIInputEvent>& inputEvents,
const Widgets::UIEditorTreeViewMetrics& metrics) {
Widgets::UIEditorTreeViewLayout layout =
BuildUIEditorTreeViewLayout(bounds, items, expansionModel, metrics);
SyncKeyboardNavigation(state, selectionModel, layout, items);
SyncSelectionAnchor(state, selectionModel);
SyncHoverTarget(state, layout, items);
UIEditorTreeViewInteractionResult interactionResult = {};
for (const UIInputEvent& event : inputEvents) {
if (ShouldUsePointerPosition(event)) {
state.pointerPosition = event.position;
state.hasPointerPosition = true;
} else if (event.type == UIInputEventType::PointerLeave) {
state.hasPointerPosition = false;
}
UIEditorTreeViewInteractionResult eventResult = {};
switch (event.type) {
case UIInputEventType::FocusGained:
state.treeViewState.focused = true;
break;
case UIInputEventType::FocusLost:
state.treeViewState.focused = false;
state.hasPointerPosition = false;
state.treeViewState.hoveredItemId.clear();
break;
case UIInputEventType::PointerMove:
case UIInputEventType::PointerEnter:
break;
case UIInputEventType::PointerLeave:
state.treeViewState.hoveredItemId.clear();
break;
case UIInputEventType::PointerButtonDown: {
const UIEditorTreeViewHitTarget hitTarget =
state.hasPointerPosition
? HitTestUIEditorTreeView(layout, state.pointerPosition)
: UIEditorTreeViewHitTarget {};
eventResult.hitTarget = hitTarget;
if ((event.pointerButton == UIPointerButton::Left ||
event.pointerButton == UIPointerButton::Right) &&
hitTarget.kind != UIEditorTreeViewHitTargetKind::None) {
state.treeViewState.focused = true;
eventResult.consumed = true;
} else if (event.pointerButton == UIPointerButton::Left &&
(!state.hasPointerPosition ||
!IsUIEditorTreeViewPointInside(layout.bounds, state.pointerPosition))) {
state.treeViewState.focused = false;
}
break;
}
case UIInputEventType::PointerButtonUp: {
const UIEditorTreeViewHitTarget hitTarget =
state.hasPointerPosition
? HitTestUIEditorTreeView(layout, state.pointerPosition)
: UIEditorTreeViewHitTarget {};
eventResult.hitTarget = hitTarget;
const bool insideTree =
state.hasPointerPosition &&
IsUIEditorTreeViewPointInside(layout.bounds, state.pointerPosition);
if (hitTarget.itemIndex >= items.size()) {
if (event.pointerButton == UIPointerButton::Left && insideTree) {
eventResult.consumed = true;
state.treeViewState.focused = true;
} else if (event.pointerButton == UIPointerButton::Left) {
state.treeViewState.focused = false;
}
break;
}
const Widgets::UIEditorTreeViewItem& item = items[hitTarget.itemIndex];
if (event.pointerButton == UIPointerButton::Left) {
if (hitTarget.kind == UIEditorTreeViewHitTargetKind::Disclosure &&
DoesUIEditorTreeViewItemHaveChildren(items, hitTarget.itemIndex)) {
eventResult.expansionChanged =
expansionModel.ToggleExpanded(item.itemId);
eventResult.toggledItemId = item.itemId;
eventResult.consumed = true;
state.treeViewState.focused = true;
} else if (hitTarget.kind == UIEditorTreeViewHitTargetKind::Row) {
if (event.modifiers.shift) {
SelectVisibleItemRange(
state,
selectionModel,
layout,
items,
hitTarget.visibleIndex,
eventResult,
false);
} else if (event.modifiers.control) {
ToggleVisibleItemSelection(
state,
selectionModel,
layout,
items,
hitTarget.visibleIndex,
eventResult);
} else {
SelectVisibleItem(
state,
selectionModel,
layout,
items,
hitTarget.visibleIndex,
eventResult,
false);
}
state.treeViewState.focused = true;
}
} else if (event.pointerButton == UIPointerButton::Right &&
(hitTarget.kind == UIEditorTreeViewHitTargetKind::Row ||
hitTarget.kind == UIEditorTreeViewHitTargetKind::Disclosure)) {
if (hitTarget.visibleIndex != UIEditorTreeViewInvalidIndex &&
hitTarget.itemIndex < items.size()) {
const Widgets::UIEditorTreeViewItem& hitItem = items[hitTarget.itemIndex];
if (selectionModel.IsSelected(hitItem.itemId)) {
selectionModel.SetPrimarySelection(hitItem.itemId);
eventResult.selectedItemId = hitItem.itemId;
eventResult.selectedVisibleIndex = hitTarget.visibleIndex;
eventResult.consumed = true;
state.keyboardNavigation.SetCurrentIndex(hitTarget.visibleIndex);
state.selectionAnchorId = hitItem.itemId;
} else {
SelectVisibleItem(
state,
selectionModel,
layout,
items,
hitTarget.visibleIndex,
eventResult,
false);
}
}
eventResult.secondaryClicked = true;
eventResult.consumed = true;
state.treeViewState.focused = true;
}
break;
}
case UIInputEventType::KeyDown:
if (state.treeViewState.focused &&
!event.modifiers.control &&
!event.modifiers.alt &&
!event.modifiers.super) {
if (event.keyCode == static_cast<std::int32_t>(KeyCode::F2) &&
selectionModel.HasSelection()) {
eventResult.renameRequested = true;
eventResult.renameItemId = selectionModel.GetSelectedId();
eventResult.selectedItemId = selectionModel.GetSelectedId();
eventResult.selectedVisibleIndex =
FindVisibleIndexForItemId(layout, items, selectionModel.GetSelectedId());
eventResult.consumed = true;
break;
}
if (ApplyVerticalNavigation(
state,
selectionModel,
layout,
items,
event.keyCode,
eventResult,
event.modifiers.shift) ||
ApplyHorizontalNavigation(
state,
selectionModel,
expansionModel,
layout,
items,
event.keyCode,
eventResult)) {
eventResult.consumed = true;
}
}
break;
default:
break;
}
layout = BuildUIEditorTreeViewLayout(bounds, items, expansionModel, metrics);
if (eventResult.expansionChanged &&
!eventResult.toggledItemId.empty() &&
!expansionModel.IsExpanded(eventResult.toggledItemId)) {
NormalizeSelectionAfterCollapse(
state,
selectionModel,
layout,
items,
eventResult.toggledItemId,
eventResult);
}
SyncKeyboardNavigation(state, selectionModel, layout, items);
SyncSelectionAnchor(state, selectionModel);
SyncHoverTarget(state, layout, items);
if (eventResult.hitTarget.kind == UIEditorTreeViewHitTargetKind::None &&
state.hasPointerPosition) {
eventResult.hitTarget = HitTestUIEditorTreeView(layout, state.pointerPosition);
}
if (eventResult.consumed ||
eventResult.selectionChanged ||
eventResult.expansionChanged ||
eventResult.keyboardNavigated ||
eventResult.secondaryClicked ||
eventResult.renameRequested ||
eventResult.hitTarget.kind != UIEditorTreeViewHitTargetKind::None ||
!eventResult.selectedItemId.empty() ||
!eventResult.renameItemId.empty() ||
!eventResult.toggledItemId.empty()) {
interactionResult = std::move(eventResult);
}
}
layout = BuildUIEditorTreeViewLayout(bounds, items, expansionModel, metrics);
SyncKeyboardNavigation(state, selectionModel, layout, items);
SyncSelectionAnchor(state, selectionModel);
SyncHoverTarget(state, layout, items);
if (interactionResult.hitTarget.kind == UIEditorTreeViewHitTargetKind::None &&
state.hasPointerPosition) {
interactionResult.hitTarget = HitTestUIEditorTreeView(layout, state.pointerPosition);
}
return {
std::move(layout),
std::move(interactionResult)
};
}
} // namespace XCEngine::UI::Editor

View File

@@ -1,304 +0,0 @@
#include <XCEditor/Core/UIEditorDockHostInteraction.h>
#include <XCEngine/UI/Widgets/UISplitterInteraction.h>
#include <utility>
namespace XCEngine::UI::Editor {
namespace {
using ::XCEngine::UI::UIInputEvent;
using ::XCEngine::UI::UIInputEventType;
using ::XCEngine::UI::UIRect;
using ::XCEngine::UI::Widgets::BeginUISplitterDrag;
using ::XCEngine::UI::Widgets::EndUISplitterDrag;
using ::XCEngine::UI::Widgets::UpdateUISplitterDrag;
using Widgets::BuildUIEditorDockHostLayout;
using Widgets::FindUIEditorDockHostSplitterLayout;
using Widgets::HitTestUIEditorDockHost;
using Widgets::UIEditorDockHostHitTarget;
using Widgets::UIEditorDockHostHitTargetKind;
bool ShouldUsePointerPosition(const UIInputEvent& event) {
switch (event.type) {
case UIInputEventType::PointerMove:
case UIInputEventType::PointerEnter:
case UIInputEventType::PointerButtonDown:
case UIInputEventType::PointerButtonUp:
return true;
default:
return false;
}
}
UIEditorWorkspaceLayoutOperationResult ApplySplitRatio(
UIEditorWorkspaceController& controller,
std::string_view nodeId,
float splitRatio) {
return controller.SetSplitRatio(nodeId, splitRatio);
}
void SyncHoverTarget(
UIEditorDockHostInteractionState& state,
const Widgets::UIEditorDockHostLayout& layout) {
if (state.splitterDragState.active) {
state.dockHostState.hoveredTarget = {
UIEditorDockHostHitTargetKind::SplitterHandle,
state.dockHostState.activeSplitterNodeId,
{},
Widgets::UIEditorTabStripInvalidIndex
};
return;
}
if (!state.hasPointerPosition) {
state.dockHostState.hoveredTarget = {};
return;
}
state.dockHostState.hoveredTarget =
HitTestUIEditorDockHost(layout, state.pointerPosition);
}
UIEditorWorkspaceCommandResult DispatchPanelCommand(
UIEditorWorkspaceController& controller,
UIEditorWorkspaceCommandKind kind,
std::string panelId) {
UIEditorWorkspaceCommand command = {};
command.kind = kind;
command.panelId = std::move(panelId);
return controller.Dispatch(command);
}
} // namespace
UIEditorDockHostInteractionFrame UpdateUIEditorDockHostInteraction(
UIEditorDockHostInteractionState& state,
UIEditorWorkspaceController& controller,
const UIRect& bounds,
const std::vector<UIInputEvent>& inputEvents,
const Widgets::UIEditorDockHostMetrics& metrics) {
UIEditorDockHostInteractionResult interactionResult = {};
Widgets::UIEditorDockHostLayout layout = BuildUIEditorDockHostLayout(
bounds,
controller.GetPanelRegistry(),
controller.GetWorkspace(),
controller.GetSession(),
state.dockHostState,
metrics);
SyncHoverTarget(state, layout);
for (const UIInputEvent& event : inputEvents) {
if (ShouldUsePointerPosition(event)) {
state.pointerPosition = event.position;
state.hasPointerPosition = true;
} else if (event.type == UIInputEventType::PointerLeave) {
state.hasPointerPosition = false;
}
UIEditorDockHostInteractionResult eventResult = {};
switch (event.type) {
case UIInputEventType::FocusGained:
state.dockHostState.focused = true;
break;
case UIInputEventType::FocusLost:
state.dockHostState.focused = false;
state.dockHostState.hoveredTarget = {};
if (state.splitterDragState.active) {
EndUISplitterDrag(state.splitterDragState);
state.dockHostState.activeSplitterNodeId.clear();
eventResult.consumed = true;
eventResult.releasePointerCapture = true;
}
break;
case UIInputEventType::PointerMove:
case UIInputEventType::PointerEnter:
if (state.splitterDragState.active) {
const auto* splitter = FindUIEditorDockHostSplitterLayout(
layout,
state.dockHostState.activeSplitterNodeId);
if (splitter != nullptr) {
::XCEngine::UI::Layout::UISplitterLayoutResult draggedLayout = {};
if (UpdateUISplitterDrag(
state.splitterDragState,
state.pointerPosition,
draggedLayout)) {
eventResult.layoutResult = ApplySplitRatio(
controller,
state.dockHostState.activeSplitterNodeId,
draggedLayout.splitRatio);
eventResult.layoutChanged =
eventResult.layoutResult.status ==
UIEditorWorkspaceLayoutOperationStatus::Changed;
}
eventResult.consumed = true;
eventResult.hitTarget.kind = UIEditorDockHostHitTargetKind::SplitterHandle;
eventResult.hitTarget.nodeId = state.dockHostState.activeSplitterNodeId;
}
}
break;
case UIInputEventType::PointerLeave:
if (!state.splitterDragState.active) {
state.dockHostState.hoveredTarget = {};
}
break;
case UIInputEventType::PointerButtonDown:
if (event.pointerButton != ::XCEngine::UI::UIPointerButton::Left) {
break;
}
if (state.dockHostState.hoveredTarget.kind ==
UIEditorDockHostHitTargetKind::SplitterHandle) {
const auto* splitter = FindUIEditorDockHostSplitterLayout(
layout,
state.dockHostState.hoveredTarget.nodeId);
if (splitter != nullptr &&
BeginUISplitterDrag(
1u,
splitter->axis == UIEditorWorkspaceSplitAxis::Horizontal
? ::XCEngine::UI::Layout::UILayoutAxis::Horizontal
: ::XCEngine::UI::Layout::UILayoutAxis::Vertical,
splitter->bounds,
splitter->splitterLayout,
splitter->constraints,
splitter->metrics,
state.pointerPosition,
state.splitterDragState)) {
state.dockHostState.activeSplitterNodeId = splitter->nodeId;
state.dockHostState.focused = true;
eventResult.consumed = true;
eventResult.requestPointerCapture = true;
eventResult.hitTarget = state.dockHostState.hoveredTarget;
eventResult.activeSplitterNodeId = splitter->nodeId;
}
} else {
state.dockHostState.focused =
state.dockHostState.hoveredTarget.kind !=
UIEditorDockHostHitTargetKind::None;
eventResult.consumed = state.dockHostState.focused;
eventResult.hitTarget = state.dockHostState.hoveredTarget;
}
break;
case UIInputEventType::PointerButtonUp:
if (event.pointerButton != ::XCEngine::UI::UIPointerButton::Left) {
break;
}
if (state.splitterDragState.active) {
::XCEngine::UI::Layout::UISplitterLayoutResult draggedLayout = {};
if (UpdateUISplitterDrag(
state.splitterDragState,
state.pointerPosition,
draggedLayout)) {
eventResult.layoutResult = ApplySplitRatio(
controller,
state.dockHostState.activeSplitterNodeId,
draggedLayout.splitRatio);
eventResult.layoutChanged =
eventResult.layoutResult.status ==
UIEditorWorkspaceLayoutOperationStatus::Changed;
}
EndUISplitterDrag(state.splitterDragState);
eventResult.consumed = true;
eventResult.releasePointerCapture = true;
eventResult.activeSplitterNodeId = state.dockHostState.activeSplitterNodeId;
state.dockHostState.activeSplitterNodeId.clear();
break;
}
eventResult.hitTarget = state.dockHostState.hoveredTarget;
switch (state.dockHostState.hoveredTarget.kind) {
case UIEditorDockHostHitTargetKind::Tab:
case UIEditorDockHostHitTargetKind::PanelHeader:
case UIEditorDockHostHitTargetKind::PanelBody:
case UIEditorDockHostHitTargetKind::PanelFooter:
eventResult.commandResult = DispatchPanelCommand(
controller,
UIEditorWorkspaceCommandKind::ActivatePanel,
state.dockHostState.hoveredTarget.panelId);
eventResult.commandExecuted =
eventResult.commandResult.status !=
UIEditorWorkspaceCommandStatus::Rejected;
eventResult.consumed = true;
state.dockHostState.focused = true;
break;
case UIEditorDockHostHitTargetKind::TabCloseButton:
case UIEditorDockHostHitTargetKind::PanelCloseButton:
eventResult.commandResult = DispatchPanelCommand(
controller,
UIEditorWorkspaceCommandKind::ClosePanel,
state.dockHostState.hoveredTarget.panelId);
eventResult.commandExecuted =
eventResult.commandResult.status !=
UIEditorWorkspaceCommandStatus::Rejected;
eventResult.consumed = true;
state.dockHostState.focused = true;
break;
case UIEditorDockHostHitTargetKind::TabStripBackground:
state.dockHostState.focused = true;
eventResult.consumed = true;
break;
case UIEditorDockHostHitTargetKind::None:
default:
state.dockHostState.focused = false;
break;
}
break;
default:
break;
}
layout = BuildUIEditorDockHostLayout(
bounds,
controller.GetPanelRegistry(),
controller.GetWorkspace(),
controller.GetSession(),
state.dockHostState,
metrics);
SyncHoverTarget(state, layout);
if (eventResult.hitTarget.kind == UIEditorDockHostHitTargetKind::None) {
eventResult.hitTarget = state.dockHostState.hoveredTarget;
}
if (eventResult.consumed ||
eventResult.commandExecuted ||
eventResult.layoutChanged ||
eventResult.requestPointerCapture ||
eventResult.releasePointerCapture ||
eventResult.layoutResult.status != UIEditorWorkspaceLayoutOperationStatus::Rejected ||
eventResult.hitTarget.kind != UIEditorDockHostHitTargetKind::None ||
!eventResult.activeSplitterNodeId.empty()) {
interactionResult = std::move(eventResult);
}
}
layout = BuildUIEditorDockHostLayout(
bounds,
controller.GetPanelRegistry(),
controller.GetWorkspace(),
controller.GetSession(),
state.dockHostState,
metrics);
SyncHoverTarget(state, layout);
if (interactionResult.hitTarget.kind == UIEditorDockHostHitTargetKind::None) {
interactionResult.hitTarget = state.dockHostState.hoveredTarget;
}
return {
std::move(layout),
std::move(interactionResult),
state.dockHostState.focused
};
}
} // namespace XCEngine::UI::Editor

File diff suppressed because it is too large Load Diff

View File

@@ -1,192 +0,0 @@
#include <XCEditor/Core/UIEditorTreeViewInteraction.h>
namespace XCEngine::UI::Editor {
namespace {
using ::XCEngine::UI::UIInputEvent;
using ::XCEngine::UI::UIInputEventType;
using ::XCEngine::UI::UIPointerButton;
using Widgets::BuildUIEditorTreeViewLayout;
using Widgets::DoesUIEditorTreeViewItemHaveChildren;
using Widgets::HitTestUIEditorTreeView;
using Widgets::IsUIEditorTreeViewPointInside;
using Widgets::UIEditorTreeViewHitTarget;
using Widgets::UIEditorTreeViewHitTargetKind;
bool ShouldUsePointerPosition(const UIInputEvent& event) {
switch (event.type) {
case UIInputEventType::PointerMove:
case UIInputEventType::PointerEnter:
case UIInputEventType::PointerButtonDown:
case UIInputEventType::PointerButtonUp:
return true;
default:
return false;
}
}
void SyncHoverTarget(
UIEditorTreeViewInteractionState& state,
const Widgets::UIEditorTreeViewLayout& layout,
const std::vector<Widgets::UIEditorTreeViewItem>& items) {
state.treeViewState.hoveredItemId.clear();
if (!state.hasPointerPosition) {
return;
}
const UIEditorTreeViewHitTarget hitTarget =
HitTestUIEditorTreeView(layout, state.pointerPosition);
if (hitTarget.itemIndex < items.size()) {
state.treeViewState.hoveredItemId = items[hitTarget.itemIndex].itemId;
}
}
} // namespace
UIEditorTreeViewInteractionFrame UpdateUIEditorTreeViewInteraction(
UIEditorTreeViewInteractionState& state,
::XCEngine::UI::Widgets::UISelectionModel& selectionModel,
::XCEngine::UI::Widgets::UIExpansionModel& expansionModel,
const ::XCEngine::UI::UIRect& bounds,
const std::vector<Widgets::UIEditorTreeViewItem>& items,
const std::vector<UIInputEvent>& inputEvents,
const Widgets::UIEditorTreeViewMetrics& metrics) {
Widgets::UIEditorTreeViewLayout layout =
BuildUIEditorTreeViewLayout(bounds, items, expansionModel, metrics);
SyncHoverTarget(state, layout, items);
UIEditorTreeViewInteractionResult interactionResult = {};
for (const UIInputEvent& event : inputEvents) {
if (ShouldUsePointerPosition(event)) {
state.pointerPosition = event.position;
state.hasPointerPosition = true;
} else if (event.type == UIInputEventType::PointerLeave) {
state.hasPointerPosition = false;
}
UIEditorTreeViewInteractionResult eventResult = {};
switch (event.type) {
case UIInputEventType::FocusGained:
state.treeViewState.focused = true;
break;
case UIInputEventType::FocusLost:
state.treeViewState.focused = false;
state.hasPointerPosition = false;
state.treeViewState.hoveredItemId.clear();
break;
case UIInputEventType::PointerMove:
case UIInputEventType::PointerEnter:
break;
case UIInputEventType::PointerLeave:
state.treeViewState.hoveredItemId.clear();
break;
case UIInputEventType::PointerButtonDown: {
const UIEditorTreeViewHitTarget hitTarget =
state.hasPointerPosition
? HitTestUIEditorTreeView(layout, state.pointerPosition)
: UIEditorTreeViewHitTarget {};
eventResult.hitTarget = hitTarget;
if ((event.pointerButton == UIPointerButton::Left ||
event.pointerButton == UIPointerButton::Right) &&
hitTarget.kind != UIEditorTreeViewHitTargetKind::None) {
state.treeViewState.focused = true;
eventResult.consumed = true;
} else if (event.pointerButton == UIPointerButton::Left &&
(!state.hasPointerPosition ||
!IsUIEditorTreeViewPointInside(layout.bounds, state.pointerPosition))) {
state.treeViewState.focused = false;
}
break;
}
case UIInputEventType::PointerButtonUp: {
const UIEditorTreeViewHitTarget hitTarget =
state.hasPointerPosition
? HitTestUIEditorTreeView(layout, state.pointerPosition)
: UIEditorTreeViewHitTarget {};
eventResult.hitTarget = hitTarget;
const bool insideTree =
state.hasPointerPosition &&
IsUIEditorTreeViewPointInside(layout.bounds, state.pointerPosition);
if (hitTarget.itemIndex >= items.size()) {
if (event.pointerButton == UIPointerButton::Left && insideTree) {
eventResult.consumed = true;
state.treeViewState.focused = true;
} else if (event.pointerButton == UIPointerButton::Left) {
state.treeViewState.focused = false;
}
break;
}
const Widgets::UIEditorTreeViewItem& item = items[hitTarget.itemIndex];
if (event.pointerButton == UIPointerButton::Left) {
if (hitTarget.kind == UIEditorTreeViewHitTargetKind::Disclosure &&
DoesUIEditorTreeViewItemHaveChildren(items, hitTarget.itemIndex)) {
eventResult.expansionChanged =
expansionModel.ToggleExpanded(item.itemId);
eventResult.toggledItemId = item.itemId;
eventResult.consumed = true;
state.treeViewState.focused = true;
} else if (hitTarget.kind == UIEditorTreeViewHitTargetKind::Row) {
eventResult.selectionChanged =
selectionModel.SetSelection(item.itemId);
eventResult.selectedItemId = item.itemId;
eventResult.consumed = true;
state.treeViewState.focused = true;
}
} else if (event.pointerButton == UIPointerButton::Right &&
(hitTarget.kind == UIEditorTreeViewHitTargetKind::Row ||
hitTarget.kind == UIEditorTreeViewHitTargetKind::Disclosure)) {
eventResult.selectionChanged =
selectionModel.SetSelection(item.itemId);
eventResult.selectedItemId = item.itemId;
eventResult.secondaryClicked = true;
eventResult.consumed = true;
state.treeViewState.focused = true;
}
break;
}
default:
break;
}
layout = BuildUIEditorTreeViewLayout(bounds, items, expansionModel, metrics);
SyncHoverTarget(state, layout, items);
if (eventResult.hitTarget.kind == UIEditorTreeViewHitTargetKind::None &&
state.hasPointerPosition) {
eventResult.hitTarget = HitTestUIEditorTreeView(layout, state.pointerPosition);
}
if (eventResult.consumed ||
eventResult.selectionChanged ||
eventResult.expansionChanged ||
eventResult.secondaryClicked ||
eventResult.hitTarget.kind != UIEditorTreeViewHitTargetKind::None ||
!eventResult.selectedItemId.empty() ||
!eventResult.toggledItemId.empty()) {
interactionResult = std::move(eventResult);
}
}
layout = BuildUIEditorTreeViewLayout(bounds, items, expansionModel, metrics);
SyncHoverTarget(state, layout, items);
if (interactionResult.hitTarget.kind == UIEditorTreeViewHitTargetKind::None &&
state.hasPointerPosition) {
interactionResult.hitTarget = HitTestUIEditorTreeView(layout, state.pointerPosition);
}
return {
std::move(layout),
std::move(interactionResult)
};
}
} // namespace XCEngine::UI::Editor

View File

@@ -0,0 +1,394 @@
#include <XCEditor/Fields/UIEditorAssetField.h>
#include <XCEditor/Widgets/UIEditorFieldRowLayout.h>
#include <XCEditor/Widgets/UIEditorTextLayout.h>
#include <algorithm>
#include <cctype>
#include <string_view>
namespace XCEngine::UI::Editor::Widgets {
namespace {
using ::XCEngine::UI::UIDrawList;
using ::XCEngine::UI::UIPoint;
using ::XCEngine::UI::UIRect;
float ClampNonNegative(float value) {
return (std::max)(0.0f, value);
}
float EstimateTextWidth(std::string_view text, float fontSize) {
return static_cast<float>(text.size()) * fontSize * 0.58f;
}
bool ShowsPickerButton(const UIEditorAssetFieldSpec& spec) {
return spec.showPickerButton;
}
bool ShowsClearButton(const UIEditorAssetFieldSpec& spec) {
return spec.allowClear && HasUIEditorAssetFieldValue(spec);
}
bool ShowsStatusBadge(const UIEditorAssetFieldSpec& spec) {
return spec.showStatusBadge && !spec.statusText.empty();
}
::XCEngine::UI::UIColor ResolveRowFillColor(
const UIEditorAssetFieldState& state,
const UIEditorAssetFieldPalette& palette) {
if (state.activeTarget != UIEditorAssetFieldHitTargetKind::None) {
return palette.rowActiveColor;
}
if (state.hoveredTarget != UIEditorAssetFieldHitTargetKind::None) {
return palette.rowHoverColor;
}
return palette.surfaceColor;
}
::XCEngine::UI::UIColor ResolveValueFillColor(
const UIEditorAssetFieldSpec& spec,
const UIEditorAssetFieldState& state,
const UIEditorAssetFieldPalette& palette) {
if (spec.readOnly) {
return palette.readOnlyColor;
}
if (state.activeTarget == UIEditorAssetFieldHitTargetKind::ValueBox ||
state.activeTarget == UIEditorAssetFieldHitTargetKind::PickerButton ||
state.activeTarget == UIEditorAssetFieldHitTargetKind::ClearButton) {
return palette.valueBoxActiveColor;
}
if (state.hoveredTarget == UIEditorAssetFieldHitTargetKind::ValueBox ||
state.hoveredTarget == UIEditorAssetFieldHitTargetKind::PickerButton ||
state.hoveredTarget == UIEditorAssetFieldHitTargetKind::ClearButton) {
return palette.valueBoxHoverColor;
}
return palette.valueBoxColor;
}
::XCEngine::UI::UIColor ResolveActionFillColor(
UIEditorAssetFieldHitTargetKind targetKind,
const UIEditorAssetFieldState& state,
const UIEditorAssetFieldPalette& palette) {
if (state.activeTarget == targetKind) {
return palette.actionButtonActiveColor;
}
if (state.hoveredTarget == targetKind) {
return palette.actionButtonHoverColor;
}
return palette.actionButtonColor;
}
} // namespace
bool HasUIEditorAssetFieldValue(const UIEditorAssetFieldSpec& spec) {
return !spec.assetId.empty() || !spec.displayName.empty();
}
std::string ResolveUIEditorAssetFieldValueText(const UIEditorAssetFieldSpec& spec) {
if (!spec.displayName.empty()) {
return spec.displayName;
}
if (!spec.assetId.empty()) {
return spec.assetId;
}
return spec.emptyText.empty() ? std::string("None") : spec.emptyText;
}
std::string ResolveUIEditorAssetFieldPreviewGlyph(const UIEditorAssetFieldSpec& spec) {
const std::string source =
!spec.statusText.empty() ? spec.statusText : ResolveUIEditorAssetFieldValueText(spec);
for (char character : source) {
if (std::isalnum(static_cast<unsigned char>(character)) != 0) {
return std::string(
1u,
static_cast<char>(std::toupper(static_cast<unsigned char>(character))));
}
}
return "-";
}
bool IsUIEditorAssetFieldPointInside(const UIRect& rect, const UIPoint& point) {
return point.x >= rect.x &&
point.x <= rect.x + rect.width &&
point.y >= rect.y &&
point.y <= rect.y + rect.height;
}
UIEditorAssetFieldLayout BuildUIEditorAssetFieldLayout(
const UIRect& bounds,
const UIEditorAssetFieldSpec& spec,
const UIEditorAssetFieldMetrics& metrics) {
const UIEditorFieldRowLayout hostLayout = BuildUIEditorFieldRowLayout(
bounds,
metrics.valueBoxMinWidth,
UIEditorFieldRowLayoutMetrics {
metrics.rowHeight,
metrics.horizontalPadding,
metrics.labelControlGap,
metrics.controlColumnStart,
metrics.controlTrailingInset,
metrics.controlInsetY,
});
UIEditorAssetFieldLayout layout = {};
layout.bounds = hostLayout.bounds;
layout.labelRect = hostLayout.labelRect;
layout.controlRect = hostLayout.controlRect;
layout.valueRect = hostLayout.controlRect;
float trailingX = layout.valueRect.x + layout.valueRect.width;
if (ShowsPickerButton(spec)) {
trailingX -= metrics.actionButtonWidth;
layout.pickerRect = UIRect(
trailingX,
layout.valueRect.y,
metrics.actionButtonWidth,
layout.valueRect.height);
trailingX -= metrics.actionButtonGap;
}
if (ShowsClearButton(spec)) {
trailingX -= metrics.actionButtonWidth;
layout.clearRect = UIRect(
trailingX,
layout.valueRect.y,
metrics.actionButtonWidth,
layout.valueRect.height);
trailingX -= metrics.actionButtonGap;
}
const float previewSize =
(std::min)(metrics.previewSize, ClampNonNegative(layout.valueRect.height - 4.0f));
layout.previewRect = UIRect(
layout.valueRect.x + metrics.previewInsetX,
layout.valueRect.y + ClampNonNegative((layout.valueRect.height - previewSize) * 0.5f),
previewSize,
previewSize);
const float contentLeft = layout.previewRect.x + layout.previewRect.width + metrics.previewGap;
if (ShowsStatusBadge(spec)) {
const float estimatedBadgeWidth =
EstimateTextWidth(spec.statusText, metrics.statusBadgeFontSize) +
metrics.statusBadgePaddingX * 2.0f;
const float badgeWidth = (std::min)(
(std::max)(metrics.statusBadgeMinWidth, estimatedBadgeWidth),
ClampNonNegative((trailingX - contentLeft) * 0.45f));
if (badgeWidth > 20.0f) {
trailingX -= badgeWidth;
layout.statusBadgeRect = UIRect(
trailingX,
layout.valueRect.y + ClampNonNegative((layout.valueRect.height - metrics.statusBadgeHeight) * 0.5f),
badgeWidth,
(std::min)(metrics.statusBadgeHeight, layout.valueRect.height));
trailingX -= metrics.statusBadgeGap;
}
}
layout.textRect = UIRect(
contentLeft + metrics.valueTextInsetX,
layout.valueRect.y,
ClampNonNegative(trailingX - contentLeft - metrics.valueTextInsetX),
layout.valueRect.height);
return layout;
}
UIEditorAssetFieldHitTarget HitTestUIEditorAssetField(
const UIEditorAssetFieldLayout& layout,
const UIPoint& point) {
if (layout.clearRect.width > 0.0f &&
IsUIEditorAssetFieldPointInside(layout.clearRect, point)) {
return { UIEditorAssetFieldHitTargetKind::ClearButton };
}
if (layout.pickerRect.width > 0.0f &&
IsUIEditorAssetFieldPointInside(layout.pickerRect, point)) {
return { UIEditorAssetFieldHitTargetKind::PickerButton };
}
if (IsUIEditorAssetFieldPointInside(layout.valueRect, point)) {
return { UIEditorAssetFieldHitTargetKind::ValueBox };
}
if (IsUIEditorAssetFieldPointInside(layout.bounds, point)) {
return { UIEditorAssetFieldHitTargetKind::Row };
}
return {};
}
void AppendUIEditorAssetFieldBackground(
UIDrawList& drawList,
const UIEditorAssetFieldLayout& layout,
const UIEditorAssetFieldSpec& spec,
const UIEditorAssetFieldState& state,
const UIEditorAssetFieldPalette& palette,
const UIEditorAssetFieldMetrics& metrics) {
const auto rowFillColor = ResolveRowFillColor(state, palette);
if (rowFillColor.a > 0.0f) {
drawList.AddFilledRect(layout.bounds, rowFillColor, metrics.cornerRounding);
}
const auto rowBorderColor = state.focused ? palette.focusedBorderColor : palette.borderColor;
if (rowBorderColor.a > 0.0f) {
drawList.AddRectOutline(
layout.bounds,
rowBorderColor,
state.focused ? metrics.focusedBorderThickness : metrics.borderThickness,
metrics.cornerRounding);
}
drawList.AddFilledRect(
layout.valueRect,
ResolveValueFillColor(spec, state, palette),
metrics.valueBoxRounding);
drawList.AddRectOutline(
layout.valueRect,
state.focused ? palette.controlFocusedBorderColor : palette.controlBorderColor,
state.focused ? metrics.focusedBorderThickness : metrics.borderThickness,
metrics.valueBoxRounding);
if (HasUIEditorAssetFieldValue(spec)) {
drawList.AddFilledRectLinearGradient(
layout.previewRect,
palette.previewBaseColor,
spec.tint,
::XCEngine::UI::UILinearGradientDirection::Vertical,
metrics.previewRounding);
} else {
drawList.AddFilledRect(layout.previewRect, palette.previewEmptyColor, metrics.previewRounding);
}
drawList.AddRectOutline(
layout.previewRect,
palette.previewBorderColor,
metrics.borderThickness,
metrics.previewRounding);
if (layout.statusBadgeRect.width > 0.0f && layout.statusBadgeRect.height > 0.0f) {
drawList.AddFilledRect(layout.statusBadgeRect, palette.statusBadgeColor, metrics.badgeRounding);
drawList.AddRectOutline(
layout.statusBadgeRect,
palette.statusBadgeBorderColor,
metrics.borderThickness,
metrics.badgeRounding);
}
if (layout.pickerRect.width > 0.0f) {
const auto fill =
ResolveActionFillColor(UIEditorAssetFieldHitTargetKind::PickerButton, state, palette);
drawList.AddFilledRect(layout.pickerRect, fill, metrics.valueBoxRounding);
drawList.AddLine(
UIPoint(layout.pickerRect.x, layout.pickerRect.y + 1.0f),
UIPoint(layout.pickerRect.x, layout.pickerRect.y + layout.pickerRect.height - 1.0f),
palette.separatorColor,
1.0f);
}
if (layout.clearRect.width > 0.0f) {
const auto fill =
ResolveActionFillColor(UIEditorAssetFieldHitTargetKind::ClearButton, state, palette);
drawList.AddFilledRect(layout.clearRect, fill, metrics.valueBoxRounding);
drawList.AddLine(
UIPoint(layout.clearRect.x, layout.clearRect.y + 1.0f),
UIPoint(layout.clearRect.x, layout.clearRect.y + layout.clearRect.height - 1.0f),
palette.separatorColor,
1.0f);
}
}
void AppendUIEditorAssetFieldForeground(
UIDrawList& drawList,
const UIEditorAssetFieldLayout& layout,
const UIEditorAssetFieldSpec& spec,
const UIEditorAssetFieldState&,
const UIEditorAssetFieldPalette& palette,
const UIEditorAssetFieldMetrics& metrics) {
drawList.PushClipRect(ResolveUIEditorTextClipRect(layout.labelRect, metrics.labelFontSize));
drawList.AddText(
UIPoint(
layout.labelRect.x,
ResolveUIEditorTextTop(layout.labelRect, metrics.labelFontSize, metrics.labelTextInsetY)),
spec.label,
palette.labelColor,
metrics.labelFontSize);
drawList.PopClipRect();
drawList.PushClipRect(ResolveUIEditorTextClipRect(layout.previewRect, metrics.previewGlyphFontSize));
drawList.AddText(
UIPoint(
layout.previewRect.x +
ClampNonNegative((layout.previewRect.width - metrics.previewGlyphFontSize) * 0.5f),
ResolveUIEditorTextTop(layout.previewRect, metrics.previewGlyphFontSize, -1.0f)),
ResolveUIEditorAssetFieldPreviewGlyph(spec),
palette.previewGlyphColor,
metrics.previewGlyphFontSize);
drawList.PopClipRect();
drawList.PushClipRect(ResolveUIEditorTextClipRect(layout.textRect, metrics.valueFontSize));
drawList.AddText(
UIPoint(
layout.textRect.x,
ResolveUIEditorTextTop(layout.textRect, metrics.valueFontSize, metrics.valueTextInsetY)),
ResolveUIEditorAssetFieldValueText(spec),
HasUIEditorAssetFieldValue(spec) ? palette.valueColor : palette.emptyValueColor,
metrics.valueFontSize);
drawList.PopClipRect();
if (layout.statusBadgeRect.width > 0.0f && layout.statusBadgeRect.height > 0.0f) {
const UIRect badgeTextRect(
layout.statusBadgeRect.x + metrics.statusBadgePaddingX,
layout.statusBadgeRect.y,
ClampNonNegative(layout.statusBadgeRect.width - metrics.statusBadgePaddingX * 2.0f),
layout.statusBadgeRect.height);
drawList.PushClipRect(ResolveUIEditorTextClipRect(badgeTextRect, metrics.statusBadgeFontSize));
drawList.AddText(
UIPoint(
badgeTextRect.x,
ResolveUIEditorTextTop(badgeTextRect, metrics.statusBadgeFontSize, -1.0f)),
spec.statusText,
palette.statusBadgeTextColor,
metrics.statusBadgeFontSize);
drawList.PopClipRect();
}
if (layout.pickerRect.width > 0.0f) {
drawList.AddText(
UIPoint(
layout.pickerRect.x +
ClampNonNegative((layout.pickerRect.width - metrics.actionGlyphFontSize) * 0.5f),
ResolveUIEditorTextTop(
layout.pickerRect,
metrics.actionGlyphFontSize,
metrics.actionGlyphInsetY)),
"o",
palette.pickerGlyphColor,
metrics.actionGlyphFontSize);
}
if (layout.clearRect.width > 0.0f) {
drawList.AddText(
UIPoint(
layout.clearRect.x +
ClampNonNegative((layout.clearRect.width - metrics.actionGlyphFontSize) * 0.5f),
ResolveUIEditorTextTop(
layout.clearRect,
metrics.actionGlyphFontSize,
metrics.actionGlyphInsetY)),
"X",
palette.clearGlyphColor,
metrics.actionGlyphFontSize);
}
}
void AppendUIEditorAssetField(
UIDrawList& drawList,
const UIRect& bounds,
const UIEditorAssetFieldSpec& spec,
const UIEditorAssetFieldState& state,
const UIEditorAssetFieldPalette& palette,
const UIEditorAssetFieldMetrics& metrics) {
const UIEditorAssetFieldLayout layout = BuildUIEditorAssetFieldLayout(bounds, spec, metrics);
AppendUIEditorAssetFieldBackground(drawList, layout, spec, state, palette, metrics);
AppendUIEditorAssetFieldForeground(drawList, layout, spec, state, palette, metrics);
}
} // namespace XCEngine::UI::Editor::Widgets

View File

@@ -0,0 +1,260 @@
#include <XCEditor/Fields/UIEditorAssetFieldInteraction.h>
#include <XCEngine/Input/InputTypes.h>
#include <utility>
namespace XCEngine::UI::Editor {
namespace {
using ::XCEngine::Input::KeyCode;
using ::XCEngine::UI::UIInputEvent;
using ::XCEngine::UI::UIInputEventType;
using ::XCEngine::UI::UIPointerButton;
using Widgets::BuildUIEditorAssetFieldLayout;
using Widgets::HasUIEditorAssetFieldValue;
using Widgets::HitTestUIEditorAssetField;
using Widgets::IsUIEditorAssetFieldPointInside;
using Widgets::UIEditorAssetFieldHitTarget;
using Widgets::UIEditorAssetFieldHitTargetKind;
using Widgets::UIEditorAssetFieldLayout;
using Widgets::UIEditorAssetFieldMetrics;
using Widgets::UIEditorAssetFieldSpec;
bool ShouldUsePointerPosition(const UIInputEvent& event) {
switch (event.type) {
case UIInputEventType::PointerMove:
case UIInputEventType::PointerEnter:
case UIInputEventType::PointerButtonDown:
case UIInputEventType::PointerButtonUp:
case UIInputEventType::PointerWheel:
return true;
default:
return false;
}
}
void SyncHoverTarget(
UIEditorAssetFieldInteractionState& state,
const UIEditorAssetFieldLayout& layout) {
if (!state.hasPointerPosition) {
state.fieldState.hoveredTarget = UIEditorAssetFieldHitTargetKind::None;
return;
}
state.fieldState.hoveredTarget = HitTestUIEditorAssetField(layout, state.pointerPosition).kind;
}
bool CanRequestPicker(const UIEditorAssetFieldSpec& spec) {
return !spec.readOnly && spec.showPickerButton;
}
bool CanClearValue(const UIEditorAssetFieldSpec& spec) {
return !spec.readOnly && spec.allowClear && HasUIEditorAssetFieldValue(spec);
}
void ClearValue(
UIEditorAssetFieldSpec& spec,
UIEditorAssetFieldInteractionResult& result) {
result.assetIdBefore = spec.assetId;
result.displayNameBefore = spec.displayName;
spec.assetId.clear();
spec.displayName.clear();
spec.statusText.clear();
result.assetIdAfter = spec.assetId;
result.displayNameAfter = spec.displayName;
result.clearRequested = true;
result.valueChanged = true;
result.consumed = true;
}
void RequestPicker(UIEditorAssetFieldInteractionResult& result) {
result.pickerRequested = true;
result.consumed = true;
}
void RequestActivate(UIEditorAssetFieldInteractionResult& result) {
result.activateRequested = true;
result.consumed = true;
}
} // namespace
UIEditorAssetFieldInteractionFrame UpdateUIEditorAssetFieldInteraction(
UIEditorAssetFieldInteractionState& state,
UIEditorAssetFieldSpec& spec,
const ::XCEngine::UI::UIRect& bounds,
const std::vector<UIInputEvent>& inputEvents,
const UIEditorAssetFieldMetrics& metrics) {
UIEditorAssetFieldLayout layout = BuildUIEditorAssetFieldLayout(bounds, spec, metrics);
SyncHoverTarget(state, layout);
UIEditorAssetFieldInteractionResult interactionResult = {};
for (const UIInputEvent& event : inputEvents) {
if (ShouldUsePointerPosition(event)) {
state.pointerPosition = event.position;
state.hasPointerPosition = true;
} else if (event.type == UIInputEventType::PointerLeave) {
state.hasPointerPosition = false;
}
UIEditorAssetFieldInteractionResult eventResult = {};
switch (event.type) {
case UIInputEventType::FocusGained:
eventResult.focusChanged = !state.fieldState.focused;
state.fieldState.focused = true;
break;
case UIInputEventType::FocusLost:
eventResult.focusChanged = state.fieldState.focused;
state.fieldState.focused = false;
state.fieldState.activeTarget = UIEditorAssetFieldHitTargetKind::None;
state.hasPointerPosition = false;
break;
case UIInputEventType::PointerMove:
case UIInputEventType::PointerEnter:
case UIInputEventType::PointerLeave:
break;
case UIInputEventType::PointerButtonDown: {
const UIEditorAssetFieldHitTarget hitTarget =
state.hasPointerPosition
? HitTestUIEditorAssetField(layout, state.pointerPosition)
: UIEditorAssetFieldHitTarget {};
eventResult.hitTarget = hitTarget;
if (event.pointerButton != UIPointerButton::Left) {
break;
}
const bool insideField =
state.hasPointerPosition &&
IsUIEditorAssetFieldPointInside(layout.bounds, state.pointerPosition);
if (insideField) {
eventResult.focusChanged = !state.fieldState.focused;
state.fieldState.focused = true;
state.fieldState.activeTarget =
hitTarget.kind == UIEditorAssetFieldHitTargetKind::None
? UIEditorAssetFieldHitTargetKind::Row
: hitTarget.kind;
eventResult.consumed = true;
} else {
if (state.fieldState.focused) {
eventResult.focusChanged = true;
state.fieldState.focused = false;
}
state.fieldState.activeTarget = UIEditorAssetFieldHitTargetKind::None;
}
break;
}
case UIInputEventType::PointerButtonUp: {
const UIEditorAssetFieldHitTarget hitTarget =
state.hasPointerPosition
? HitTestUIEditorAssetField(layout, state.pointerPosition)
: UIEditorAssetFieldHitTarget {};
eventResult.hitTarget = hitTarget;
if (event.pointerButton == UIPointerButton::Left) {
const UIEditorAssetFieldHitTargetKind activeTarget = state.fieldState.activeTarget;
state.fieldState.activeTarget = UIEditorAssetFieldHitTargetKind::None;
if (activeTarget == hitTarget.kind) {
switch (activeTarget) {
case UIEditorAssetFieldHitTargetKind::PickerButton:
if (CanRequestPicker(spec)) {
RequestPicker(eventResult);
}
break;
case UIEditorAssetFieldHitTargetKind::ClearButton:
if (CanClearValue(spec)) {
ClearValue(spec, eventResult);
}
break;
case UIEditorAssetFieldHitTargetKind::ValueBox:
RequestActivate(eventResult);
break;
case UIEditorAssetFieldHitTargetKind::Row:
eventResult.consumed = true;
break;
case UIEditorAssetFieldHitTargetKind::None:
default:
break;
}
}
}
break;
}
case UIInputEventType::KeyDown:
if (!state.fieldState.focused) {
break;
}
switch (static_cast<KeyCode>(event.keyCode)) {
case KeyCode::Enter:
case KeyCode::Space:
if (CanRequestPicker(spec)) {
RequestPicker(eventResult);
eventResult.hitTarget.kind = UIEditorAssetFieldHitTargetKind::PickerButton;
} else {
RequestActivate(eventResult);
eventResult.hitTarget.kind = UIEditorAssetFieldHitTargetKind::ValueBox;
}
break;
case KeyCode::Delete:
case KeyCode::Backspace:
if (CanClearValue(spec)) {
ClearValue(spec, eventResult);
eventResult.hitTarget.kind = UIEditorAssetFieldHitTargetKind::ClearButton;
}
break;
default:
break;
}
break;
default:
break;
}
layout = BuildUIEditorAssetFieldLayout(bounds, spec, metrics);
SyncHoverTarget(state, layout);
if (eventResult.hitTarget.kind == UIEditorAssetFieldHitTargetKind::None &&
state.hasPointerPosition) {
eventResult.hitTarget = HitTestUIEditorAssetField(layout, state.pointerPosition);
}
if (eventResult.consumed ||
eventResult.focusChanged ||
eventResult.valueChanged ||
eventResult.activateRequested ||
eventResult.pickerRequested ||
eventResult.clearRequested ||
eventResult.hitTarget.kind != UIEditorAssetFieldHitTargetKind::None) {
interactionResult = std::move(eventResult);
}
}
layout = BuildUIEditorAssetFieldLayout(bounds, spec, metrics);
SyncHoverTarget(state, layout);
if (interactionResult.hitTarget.kind == UIEditorAssetFieldHitTargetKind::None &&
state.hasPointerPosition) {
interactionResult.hitTarget = HitTestUIEditorAssetField(layout, state.pointerPosition);
}
return {
std::move(layout),
std::move(interactionResult)
};
}
} // namespace XCEngine::UI::Editor

View File

@@ -1,4 +1,4 @@
#include <XCEditor/Widgets/UIEditorBoolField.h>
#include <XCEditor/Fields/UIEditorBoolField.h>
#include <XCEditor/Widgets/UIEditorFieldRowLayout.h>
#include <XCEditor/Widgets/UIEditorTextLayout.h>

View File

@@ -1,4 +1,4 @@
#include <XCEditor/Core/UIEditorBoolFieldInteraction.h>
#include <XCEditor/Fields/UIEditorBoolFieldInteraction.h>
#include <XCEngine/Input/InputTypes.h>

View File

@@ -0,0 +1,873 @@
#include <XCEditor/Fields/UIEditorColorField.h>
#include <XCEditor/Widgets/UIEditorColorUtils.h>
#include <XCEditor/Widgets/UIEditorFieldRowLayout.h>
#include <XCEditor/Widgets/UIEditorTextLayout.h>
#include <XCEngine/UI/Widgets/UIPopupOverlayModel.h>
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <string>
namespace XCEngine::UI::Editor::Widgets {
namespace {
using ::XCEngine::UI::UIColor;
using ::XCEngine::UI::UIDrawList;
using ::XCEngine::UI::UILinearGradientDirection;
using ::XCEngine::UI::UIPoint;
using ::XCEngine::UI::UIRect;
using ::XCEngine::UI::UISize;
using ::XCEngine::UI::Widgets::ResolvePopupPlacementRect;
using ::XCEngine::UI::Widgets::UIPopupPlacement;
constexpr float kPi = 3.14159265358979323846f;
float ClampNonNegative(float value) {
return (std::max)(0.0f, value);
}
UIEditorColorFieldMetrics ResolveMetrics(const UIEditorColorFieldMetrics& metrics) {
const auto& tokens = GetUIEditorInspectorFieldStyleTokens();
UIEditorColorFieldMetrics resolved = metrics;
if (AreUIEditorFieldMetricsEqual(metrics.controlTrailingInset, 8.0f)) {
resolved.controlTrailingInset = tokens.controlTrailingInset;
}
return resolved;
}
UIEditorColorFieldPalette ResolvePalette(const UIEditorColorFieldPalette& palette) {
const auto& tokens = GetUIEditorInspectorFieldStyleTokens();
UIEditorColorFieldPalette resolved = palette;
if (AreUIEditorFieldColorsEqual(palette.rowHoverColor, UIColor(0.0f, 0.0f, 0.0f, 0.0f))) {
resolved.rowHoverColor = tokens.rowHoverColor;
}
if (AreUIEditorFieldColorsEqual(palette.rowActiveColor, UIColor(0.0f, 0.0f, 0.0f, 0.0f))) {
resolved.rowActiveColor = tokens.rowActiveColor;
}
if (AreUIEditorFieldColorsEqual(palette.swatchBorderColor, UIColor(0.14f, 0.14f, 0.14f, 1.0f))) {
resolved.swatchBorderColor = tokens.swatchBorderColor;
}
if (AreUIEditorFieldColorsEqual(palette.swatchHoverBorderColor, UIColor(0.34f, 0.34f, 0.34f, 1.0f))) {
resolved.swatchHoverBorderColor = tokens.swatchHoverBorderColor;
}
if (AreUIEditorFieldColorsEqual(
palette.swatchReadOnlyOverlayColor,
UIColor(0.08f, 0.08f, 0.08f, 0.18f))) {
resolved.swatchReadOnlyOverlayColor = tokens.swatchReadOnlyOverlayColor;
}
if (AreUIEditorFieldColorsEqual(palette.popupColor, UIColor(0.24f, 0.24f, 0.24f, 1.0f))) {
resolved.popupColor = tokens.popupColor;
}
if (AreUIEditorFieldColorsEqual(palette.popupBorderColor, UIColor(0.15f, 0.15f, 0.15f, 1.0f))) {
resolved.popupBorderColor = tokens.popupBorderColor;
}
if (AreUIEditorFieldColorsEqual(palette.popupHeaderColor, UIColor(0.43f, 0.24f, 0.05f, 1.0f))) {
resolved.popupHeaderColor = tokens.popupHeaderColor;
}
if (AreUIEditorFieldColorsEqual(palette.popupTitleColor, UIColor(0.95f, 0.95f, 0.95f, 1.0f))) {
resolved.popupTitleColor = tokens.popupTitleColor;
}
if (AreUIEditorFieldColorsEqual(palette.closeButtonColor, UIColor(0.76f, 0.35f, 0.34f, 1.0f))) {
resolved.closeButtonColor = tokens.closeButtonColor;
}
if (AreUIEditorFieldColorsEqual(palette.closeButtonHoverColor, UIColor(0.82f, 0.40f, 0.39f, 1.0f))) {
resolved.closeButtonHoverColor = tokens.closeButtonHoverColor;
}
if (AreUIEditorFieldColorsEqual(palette.closeGlyphColor, UIColor(0.95f, 0.95f, 0.95f, 1.0f))) {
resolved.closeGlyphColor = tokens.closeGlyphColor;
}
if (AreUIEditorFieldColorsEqual(palette.labelColor, UIColor(0.88f, 0.88f, 0.88f, 1.0f))) {
resolved.labelColor = tokens.labelColor;
}
if (AreUIEditorFieldColorsEqual(palette.popupTextColor, UIColor(0.86f, 0.86f, 0.86f, 1.0f))) {
resolved.popupTextColor = tokens.popupTextColor;
}
if (AreUIEditorFieldColorsEqual(palette.popupTextMutedColor, UIColor(0.72f, 0.72f, 0.72f, 1.0f))) {
resolved.popupTextMutedColor = tokens.popupTextMutedColor;
}
if (AreUIEditorFieldColorsEqual(palette.previewBorderColor, UIColor(0.12f, 0.12f, 0.12f, 1.0f))) {
resolved.previewBorderColor = tokens.previewBorderColor;
}
if (AreUIEditorFieldColorsEqual(palette.previewBaseColor, UIColor(0.19f, 0.19f, 0.19f, 1.0f))) {
resolved.previewBaseColor = tokens.previewBaseColor;
}
if (AreUIEditorFieldColorsEqual(palette.checkerLightColor, UIColor(0.84f, 0.84f, 0.84f, 1.0f))) {
resolved.checkerLightColor = tokens.checkerLightColor;
}
if (AreUIEditorFieldColorsEqual(palette.checkerDarkColor, UIColor(0.55f, 0.55f, 0.55f, 1.0f))) {
resolved.checkerDarkColor = tokens.checkerDarkColor;
}
if (AreUIEditorFieldColorsEqual(palette.sliderBorderColor, UIColor(0.12f, 0.12f, 0.12f, 1.0f))) {
resolved.sliderBorderColor = tokens.sliderBorderColor;
}
if (AreUIEditorFieldColorsEqual(palette.numericBoxColor, UIColor(0.19f, 0.19f, 0.19f, 1.0f))) {
resolved.numericBoxColor = tokens.numericBoxColor;
}
if (AreUIEditorFieldColorsEqual(palette.numericBoxBorderColor, UIColor(0.12f, 0.12f, 0.12f, 1.0f))) {
resolved.numericBoxBorderColor = tokens.numericBoxBorderColor;
}
if (AreUIEditorFieldColorsEqual(palette.numericBoxTextColor, UIColor(0.90f, 0.90f, 0.90f, 1.0f))) {
resolved.numericBoxTextColor = tokens.numericBoxTextColor;
}
if (AreUIEditorFieldColorsEqual(palette.handleFillColor, UIColor(1.0f, 1.0f, 1.0f, 1.0f))) {
resolved.handleFillColor = tokens.handleFillColor;
}
if (AreUIEditorFieldColorsEqual(palette.handleStrokeColor, UIColor(0.0f, 0.0f, 0.0f, 0.4f))) {
resolved.handleStrokeColor = tokens.handleStrokeColor;
}
return resolved;
}
bool ContainsPoint(const UIRect& rect, const UIPoint& point) {
return point.x >= rect.x &&
point.x <= rect.x + rect.width &&
point.y >= rect.y &&
point.y <= rect.y + rect.height;
}
UIColor ClampColor(const UIColor& color) {
return UIColor(
ClampUIEditorColorUnit(color.r),
ClampUIEditorColorUnit(color.g),
ClampUIEditorColorUnit(color.b),
ClampUIEditorColorUnit(color.a));
}
UIRect ResolveViewportRect(
const UIRect& anchorRect,
const UIRect& viewportRect) {
if (viewportRect.width > 0.0f && viewportRect.height > 0.0f) {
return viewportRect;
}
return UIRect(anchorRect.x - 4096.0f, anchorRect.y - 4096.0f, 8192.0f, 8192.0f);
}
UIEditorHsvColor ResolveDisplayHsv(
const UIEditorColorFieldSpec& spec,
const UIEditorColorFieldState& state) {
return ResolveUIEditorDisplayHsv(spec.value, state.hue, state.hueValid);
}
float ResolvePopupHeight(
const UIEditorColorFieldMetrics& metrics,
bool showAlpha) {
const float channelCount = showAlpha ? 4.0f : 3.0f;
const float channelsHeight =
channelCount * metrics.channelRowHeight +
(channelCount - 1.0f) * metrics.controlRowSpacing;
return metrics.popupHeaderHeight +
metrics.popupPadding * 2.0f +
metrics.popupTopRowHeight +
metrics.popupGapY +
metrics.wheelRegionHeight +
metrics.popupGapY +
channelsHeight +
metrics.controlRowSpacing +
metrics.channelRowHeight;
}
void AppendCheckerboard(
UIDrawList& drawList,
const UIRect& rect,
float checkerSize,
const UIColor& darkColor,
const UIColor& lightColor) {
const float tileSize = (std::max)(1.0f, checkerSize);
int row = 0;
for (float y = rect.y; y < rect.y + rect.height; y += tileSize, ++row) {
int column = 0;
for (float x = rect.x; x < rect.x + rect.width; x += tileSize, ++column) {
drawList.AddFilledRect(
UIRect(
x,
y,
(std::min)(tileSize, rect.x + rect.width - x),
(std::min)(tileSize, rect.y + rect.height - y)),
((row + column) & 1) == 0 ? lightColor : darkColor);
}
}
}
void AppendColorSample(
UIDrawList& drawList,
const UIRect& rect,
const UIColor& color,
bool showAlpha,
const UIEditorColorFieldPalette& palette,
const UIEditorColorFieldMetrics& metrics,
const UIColor& borderColor,
float borderThickness,
float rounding) {
const UIColor clamped = ClampColor(color);
drawList.AddFilledRect(rect, palette.previewBaseColor, rounding);
if (showAlpha) {
AppendCheckerboard(
drawList,
rect,
metrics.checkerSize,
palette.checkerDarkColor,
palette.checkerLightColor);
}
drawList.AddFilledRect(
rect,
UIColor(clamped.r, clamped.g, clamped.b, showAlpha ? clamped.a : 1.0f),
rounding);
drawList.AddRectOutline(rect, borderColor, borderThickness, rounding);
}
void AppendPopupText(
UIDrawList& drawList,
const UIRect& rect,
const std::string& text,
const UIColor& color,
float fontSize,
float insetX,
float insetY) {
drawList.PushClipRect(ResolveUIEditorTextClipRect(rect, fontSize));
drawList.AddText(
UIPoint(rect.x + insetX, ResolveUIEditorTextTop(rect, fontSize, insetY)),
text,
color,
fontSize);
drawList.PopClipRect();
}
void AppendCloseGlyph(
UIDrawList& drawList,
const UIRect& rect,
const UIColor& color) {
constexpr float kGlyphPad = 5.0f;
drawList.AddLine(
UIPoint(rect.x + kGlyphPad, rect.y + kGlyphPad),
UIPoint(rect.x + rect.width - kGlyphPad, rect.y + rect.height - kGlyphPad),
color,
1.2f);
drawList.AddLine(
UIPoint(rect.x + rect.width - kGlyphPad, rect.y + kGlyphPad),
UIPoint(rect.x + kGlyphPad, rect.y + rect.height - kGlyphPad),
color,
1.2f);
}
std::string FormatChannelValue(float value) {
char buffer[32] = {};
std::snprintf(buffer, sizeof(buffer), "%.4f", ClampUIEditorColorUnit(value));
return std::string(buffer);
}
std::string FormatPopupHexText(const UIEditorColorFieldSpec& spec) {
std::string hex = FormatUIEditorColorFieldHexText(spec);
if (!hex.empty() && hex.front() == '#') {
hex.erase(hex.begin());
}
return hex;
}
UIColor ResolvePopupSliderBorderColor(const UIEditorColorFieldPalette& palette) {
return palette.sliderBorderColor.a > 0.0f ? palette.sliderBorderColor : palette.popupBorderColor;
}
void AppendNumericValueBox(
UIDrawList& drawList,
const UIRect& rect,
const std::string& text,
const UIEditorColorFieldPalette& palette,
const UIEditorColorFieldMetrics& metrics) {
drawList.AddFilledRect(rect, palette.numericBoxColor, metrics.swatchRounding);
drawList.AddRectOutline(rect, palette.numericBoxBorderColor, metrics.borderThickness, metrics.swatchRounding);
AppendPopupText(
drawList,
rect,
text,
palette.numericBoxTextColor,
metrics.valueFontSize,
metrics.valueTextInsetX,
metrics.valueTextInsetY);
}
void AppendSliderHandle(
UIDrawList& drawList,
const UIRect& rect,
float unit,
const UIEditorColorFieldPalette& palette) {
const float x = rect.x + ClampUIEditorColorUnit(unit) * rect.width;
drawList.AddLine(
UIPoint(x, rect.y),
UIPoint(x, rect.y + rect.height),
palette.handleFillColor,
2.0f);
drawList.AddLine(
UIPoint(x + 1.5f, rect.y),
UIPoint(x + 1.5f, rect.y + rect.height),
palette.handleStrokeColor,
1.0f);
}
void AppendChannelSlider(
UIDrawList& drawList,
const UIRect& rect,
const UIColor& startColor,
const UIColor& endColor,
float unit,
const UIEditorColorFieldPalette& palette,
const UIEditorColorFieldMetrics& metrics,
bool alphaBackground) {
if (alphaBackground) {
AppendCheckerboard(
drawList,
rect,
metrics.checkerSize - 1.0f,
palette.checkerDarkColor,
palette.checkerLightColor);
}
drawList.AddFilledRectLinearGradient(
rect,
startColor,
endColor,
UILinearGradientDirection::Horizontal);
drawList.AddRectOutline(
rect,
ResolvePopupSliderBorderColor(palette),
metrics.borderThickness);
AppendSliderHandle(drawList, rect, unit, palette);
}
void AppendPopupChannelRow(
UIDrawList& drawList,
const UIRect& labelRect,
const UIRect& sliderRect,
const UIRect& valueRect,
const char* label,
const std::string& valueText,
const UIColor& startColor,
const UIColor& endColor,
float unit,
bool alphaBackground,
const UIEditorColorFieldPalette& palette,
const UIEditorColorFieldMetrics& metrics) {
AppendPopupText(
drawList,
labelRect,
label,
palette.popupTextColor,
metrics.valueFontSize,
0.0f,
metrics.valueTextInsetY);
AppendChannelSlider(
drawList,
sliderRect,
startColor,
endColor,
unit,
palette,
metrics,
alphaBackground);
AppendNumericValueBox(drawList, valueRect, valueText, palette, metrics);
}
void AppendSaturationValueSquare(
UIDrawList& drawList,
const UIRect& rect,
const UIEditorHsvColor& hsv,
const UIEditorColorFieldPalette& palette,
const UIEditorColorFieldMetrics& metrics) {
const UIColor hueColor = ConvertUIEditorHsvToColor(UIEditorHsvColor { hsv.hue, 1.0f, 1.0f, 1.0f });
drawList.AddFilledRectLinearGradient(
rect,
UIColor(1.0f, 1.0f, 1.0f, 1.0f),
hueColor,
UILinearGradientDirection::Horizontal);
drawList.AddFilledRectLinearGradient(
rect,
UIColor(0.0f, 0.0f, 0.0f, 0.0f),
UIColor(0.0f, 0.0f, 0.0f, 1.0f),
UILinearGradientDirection::Vertical);
drawList.AddRectOutline(
rect,
ResolvePopupSliderBorderColor(palette),
metrics.borderThickness);
}
void AppendWheelHandle(
UIDrawList& drawList,
const UIPoint& center,
float radius,
const UIEditorColorFieldPalette& palette) {
drawList.AddFilledCircle(center, (std::max)(1.0f, radius - 1.0f), palette.handleFillColor);
drawList.AddCircleOutline(center, radius, palette.handleStrokeColor, 1.4f);
}
void AppendHueWheel(
UIDrawList& drawList,
const UIEditorColorFieldLayout& layout,
const UIEditorColorFieldPalette& palette,
const UIEditorColorFieldMetrics& metrics) {
const float outerRadius = layout.hueWheelOuterRadius;
const float innerRadius = layout.hueWheelInnerRadius;
const float ringRadius = (outerRadius + innerRadius) * 0.5f;
const float ringThickness = ClampNonNegative(outerRadius - innerRadius);
const int segmentCount = (std::max)(192, static_cast<int>(outerRadius * 2.4f));
for (int segment = 0; segment < segmentCount; ++segment) {
const float t0 = static_cast<float>(segment) / static_cast<float>(segmentCount);
const float t1 = static_cast<float>(segment + 1) / static_cast<float>(segmentCount);
const float angle0 = t0 * 2.0f * kPi;
const float angle1 = t1 * 2.0f * kPi;
drawList.AddLine(
UIPoint(
layout.hueWheelCenter.x + std::cos(angle0) * ringRadius,
layout.hueWheelCenter.y + std::sin(angle0) * ringRadius),
UIPoint(
layout.hueWheelCenter.x + std::cos(angle1) * ringRadius,
layout.hueWheelCenter.y + std::sin(angle1) * ringRadius),
ConvertUIEditorHsvToColor(UIEditorHsvColor { t0, 1.0f, 1.0f, 1.0f }),
ringThickness + 1.0f);
}
drawList.AddCircleOutline(
layout.hueWheelCenter,
outerRadius,
ResolvePopupSliderBorderColor(palette),
metrics.borderThickness);
drawList.AddCircleOutline(
layout.hueWheelCenter,
innerRadius,
ResolvePopupSliderBorderColor(palette),
metrics.borderThickness);
}
UIPoint ResolveHueWheelHandleCenter(
const UIEditorColorFieldLayout& layout,
float hue) {
const float angle = ClampUIEditorColorUnit(hue) * 2.0f * kPi;
const float ringRadius = (layout.hueWheelOuterRadius + layout.hueWheelInnerRadius) * 0.5f;
return UIPoint(
layout.hueWheelCenter.x + std::cos(angle) * ringRadius,
layout.hueWheelCenter.y + std::sin(angle) * ringRadius);
}
UIPoint ResolveSaturationValueHandleCenter(
const UIEditorColorFieldLayout& layout,
const UIEditorHsvColor& hsv) {
return UIPoint(
layout.saturationValueRect.x + ClampUIEditorColorUnit(hsv.saturation) * layout.saturationValueRect.width,
layout.saturationValueRect.y + (1.0f - ClampUIEditorColorUnit(hsv.value)) * layout.saturationValueRect.height);
}
} // namespace
std::string FormatUIEditorColorFieldRgbaText(const UIEditorColorFieldSpec& spec) {
char buffer[48] = {};
if (spec.showAlpha) {
std::snprintf(
buffer,
sizeof(buffer),
"RGBA %d, %d, %d, %d",
ToUIEditorColorByte(spec.value.r),
ToUIEditorColorByte(spec.value.g),
ToUIEditorColorByte(spec.value.b),
ToUIEditorColorByte(spec.value.a));
} else {
std::snprintf(
buffer,
sizeof(buffer),
"RGB %d, %d, %d",
ToUIEditorColorByte(spec.value.r),
ToUIEditorColorByte(spec.value.g),
ToUIEditorColorByte(spec.value.b));
}
return std::string(buffer);
}
std::string FormatUIEditorColorFieldHexText(const UIEditorColorFieldSpec& spec) {
return FormatUIEditorColorHex(spec.value, spec.showAlpha);
}
UIEditorColorFieldLayout BuildUIEditorColorFieldLayout(
const UIRect& bounds,
const UIEditorColorFieldSpec& spec,
const UIEditorColorFieldMetrics& metrics,
const UIRect& viewportRect) {
const UIEditorColorFieldMetrics resolvedMetrics = ResolveMetrics(metrics);
const UIEditorFieldRowLayout hostLayout = BuildUIEditorFieldRowLayout(
bounds,
resolvedMetrics.swatchWidth,
UIEditorFieldRowLayoutMetrics {
resolvedMetrics.rowHeight,
resolvedMetrics.horizontalPadding,
resolvedMetrics.labelControlGap,
resolvedMetrics.controlColumnStart,
resolvedMetrics.controlTrailingInset,
resolvedMetrics.swatchInsetY,
});
UIEditorColorFieldLayout layout = {};
layout.bounds = hostLayout.bounds;
layout.labelRect = hostLayout.labelRect;
layout.controlRect = hostLayout.controlRect;
layout.swatchRect = UIRect(
hostLayout.controlRect.x,
hostLayout.controlRect.y + resolvedMetrics.swatchInsetY,
(std::min)(resolvedMetrics.swatchWidth, hostLayout.controlRect.width),
ClampNonNegative(hostLayout.controlRect.height - resolvedMetrics.swatchInsetY * 2.0f));
const auto placement = ResolvePopupPlacementRect(
layout.swatchRect,
UISize(resolvedMetrics.popupWidth, ResolvePopupHeight(resolvedMetrics, spec.showAlpha)),
ResolveViewportRect(layout.swatchRect, viewportRect),
UIPopupPlacement::BottomStart);
layout.popupRect = placement.rect;
layout.popupHeaderRect =
UIRect(layout.popupRect.x, layout.popupRect.y, layout.popupRect.width, resolvedMetrics.popupHeaderHeight);
layout.popupTitleRect = UIRect(
layout.popupRect.x + resolvedMetrics.popupPadding,
layout.popupRect.y,
ClampNonNegative(
layout.popupRect.width - resolvedMetrics.popupPadding * 3.0f - resolvedMetrics.popupCloseButtonSize),
resolvedMetrics.popupHeaderHeight);
layout.popupCloseButtonRect = UIRect(
layout.popupRect.x + layout.popupRect.width - resolvedMetrics.popupPadding - resolvedMetrics.popupCloseButtonSize,
layout.popupRect.y + (resolvedMetrics.popupHeaderHeight - resolvedMetrics.popupCloseButtonSize) * 0.5f,
resolvedMetrics.popupCloseButtonSize,
resolvedMetrics.popupCloseButtonSize);
layout.popupBodyRect = UIRect(
layout.popupRect.x + resolvedMetrics.popupPadding,
layout.popupRect.y + resolvedMetrics.popupHeaderHeight + resolvedMetrics.popupPadding,
ClampNonNegative(layout.popupRect.width - resolvedMetrics.popupPadding * 2.0f),
ClampNonNegative(
layout.popupRect.height - resolvedMetrics.popupHeaderHeight - resolvedMetrics.popupPadding * 2.0f));
layout.popupTopRowRect = UIRect(
layout.popupBodyRect.x,
layout.popupBodyRect.y,
layout.popupBodyRect.width,
resolvedMetrics.popupTopRowHeight);
layout.popupPreviewRect = UIRect(
layout.popupTopRowRect.x + layout.popupTopRowRect.width - resolvedMetrics.popupPreviewWidth,
layout.popupTopRowRect.y + (layout.popupTopRowRect.height - resolvedMetrics.popupPreviewHeight) * 0.5f,
resolvedMetrics.popupPreviewWidth,
resolvedMetrics.popupPreviewHeight);
layout.popupWheelRect = UIRect(
layout.popupBodyRect.x,
layout.popupTopRowRect.y + layout.popupTopRowRect.height + resolvedMetrics.popupGapY,
layout.popupBodyRect.width,
resolvedMetrics.wheelRegionHeight);
layout.hueWheelCenter = UIPoint(
layout.popupWheelRect.x + layout.popupWheelRect.width * 0.5f,
layout.popupWheelRect.y + resolvedMetrics.wheelOuterRadius);
layout.hueWheelOuterRadius = resolvedMetrics.wheelOuterRadius;
layout.hueWheelInnerRadius =
(std::max)(1.0f, resolvedMetrics.wheelOuterRadius - resolvedMetrics.wheelRingThickness);
layout.saturationValueRect = UIRect(
layout.hueWheelCenter.x - resolvedMetrics.saturationValueSize * 0.5f,
layout.hueWheelCenter.y - resolvedMetrics.saturationValueSize * 0.5f,
resolvedMetrics.saturationValueSize,
resolvedMetrics.saturationValueSize);
const float controlsX = layout.popupBodyRect.x + resolvedMetrics.popupFieldInset;
const float controlsWidth =
ClampNonNegative(layout.popupBodyRect.width - resolvedMetrics.popupFieldInset * 2.0f);
const float sliderWidth = ClampNonNegative(
controlsWidth -
resolvedMetrics.channelLabelWidth -
resolvedMetrics.controlRowSpacing -
resolvedMetrics.numericBoxWidth -
resolvedMetrics.controlRowSpacing);
float rowY = layout.popupWheelRect.y + layout.popupWheelRect.height + resolvedMetrics.popupGapY;
auto assignChannelRow = [&](UIRect& labelRect, UIRect& sliderRect, UIRect& valueRect) {
labelRect = UIRect(controlsX, rowY, resolvedMetrics.channelLabelWidth, resolvedMetrics.channelRowHeight);
sliderRect = UIRect(
labelRect.x + labelRect.width + resolvedMetrics.controlRowSpacing,
rowY,
sliderWidth,
resolvedMetrics.channelRowHeight);
valueRect = UIRect(
sliderRect.x + sliderRect.width + resolvedMetrics.controlRowSpacing,
rowY,
resolvedMetrics.numericBoxWidth,
resolvedMetrics.channelRowHeight);
rowY += resolvedMetrics.channelRowHeight + resolvedMetrics.controlRowSpacing;
};
assignChannelRow(layout.redLabelRect, layout.redSliderRect, layout.redValueRect);
assignChannelRow(layout.greenLabelRect, layout.greenSliderRect, layout.greenValueRect);
assignChannelRow(layout.blueLabelRect, layout.blueSliderRect, layout.blueValueRect);
if (spec.showAlpha) {
assignChannelRow(layout.alphaLabelRect, layout.alphaSliderRect, layout.alphaValueRect);
}
layout.hexLabelRect = UIRect(controlsX, rowY, resolvedMetrics.hexLabelWidth, resolvedMetrics.channelRowHeight);
layout.hexValueRect = UIRect(
layout.hexLabelRect.x + layout.hexLabelRect.width + resolvedMetrics.controlRowSpacing,
rowY,
ClampNonNegative(controlsWidth - resolvedMetrics.hexLabelWidth - resolvedMetrics.controlRowSpacing),
resolvedMetrics.channelRowHeight);
return layout;
}
UIEditorColorFieldHitTarget HitTestUIEditorColorField(
const UIEditorColorFieldLayout& layout,
bool popupOpen,
const UIPoint& point) {
if (popupOpen && ContainsPoint(layout.popupRect, point)) {
if (ContainsPoint(layout.popupCloseButtonRect, point)) {
return { UIEditorColorFieldHitTargetKind::PopupCloseButton };
}
const float dx = point.x - layout.hueWheelCenter.x;
const float dy = point.y - layout.hueWheelCenter.y;
const float distance = std::sqrt(dx * dx + dy * dy);
if (distance >= layout.hueWheelInnerRadius && distance <= layout.hueWheelOuterRadius) {
return { UIEditorColorFieldHitTargetKind::HueWheel };
}
if (ContainsPoint(layout.saturationValueRect, point)) {
return { UIEditorColorFieldHitTargetKind::SaturationValue };
}
if (ContainsPoint(layout.redSliderRect, point)) {
return { UIEditorColorFieldHitTargetKind::RedChannel };
}
if (ContainsPoint(layout.greenSliderRect, point)) {
return { UIEditorColorFieldHitTargetKind::GreenChannel };
}
if (ContainsPoint(layout.blueSliderRect, point)) {
return { UIEditorColorFieldHitTargetKind::BlueChannel };
}
if (layout.alphaSliderRect.width > 0.0f && ContainsPoint(layout.alphaSliderRect, point)) {
return { UIEditorColorFieldHitTargetKind::AlphaChannel };
}
return { UIEditorColorFieldHitTargetKind::PopupSurface };
}
if (ContainsPoint(layout.swatchRect, point)) {
return { UIEditorColorFieldHitTargetKind::Swatch };
}
if (ContainsPoint(layout.bounds, point)) {
return { UIEditorColorFieldHitTargetKind::Row };
}
return {};
}
void AppendUIEditorColorFieldBackground(
UIDrawList& drawList,
const UIEditorColorFieldLayout& layout,
const UIEditorColorFieldSpec& spec,
const UIEditorColorFieldState& state,
const UIEditorColorFieldPalette& palette,
const UIEditorColorFieldMetrics& metrics) {
const UIEditorColorFieldPalette resolvedPalette = ResolvePalette(palette);
const UIEditorColorFieldMetrics resolvedMetrics = ResolveMetrics(metrics);
if (state.activeTarget != UIEditorColorFieldHitTargetKind::None && resolvedPalette.rowActiveColor.a > 0.0f) {
drawList.AddFilledRect(layout.bounds, resolvedPalette.rowActiveColor);
} else if (
state.hoveredTarget != UIEditorColorFieldHitTargetKind::None &&
resolvedPalette.rowHoverColor.a > 0.0f) {
drawList.AddFilledRect(layout.bounds, resolvedPalette.rowHoverColor);
} else if (resolvedPalette.surfaceColor.a > 0.0f) {
drawList.AddFilledRect(layout.bounds, resolvedPalette.surfaceColor);
}
const bool swatchHighlighted =
state.popupOpen ||
state.focused ||
state.hoveredTarget == UIEditorColorFieldHitTargetKind::Swatch;
AppendColorSample(
drawList,
layout.swatchRect,
spec.value,
spec.showAlpha,
resolvedPalette,
resolvedMetrics,
swatchHighlighted ? resolvedPalette.swatchHoverBorderColor : resolvedPalette.swatchBorderColor,
swatchHighlighted ? resolvedMetrics.focusedBorderThickness : resolvedMetrics.borderThickness,
resolvedMetrics.swatchRounding);
if (spec.readOnly) {
drawList.AddFilledRect(
layout.swatchRect,
resolvedPalette.swatchReadOnlyOverlayColor,
resolvedMetrics.swatchRounding);
}
if (!state.popupOpen) {
return;
}
drawList.AddFilledRect(layout.popupRect, resolvedPalette.popupColor, resolvedMetrics.popupBorderRounding);
drawList.AddRectOutline(
layout.popupRect,
resolvedPalette.popupBorderColor,
resolvedMetrics.borderThickness,
resolvedMetrics.popupBorderRounding);
drawList.AddFilledRect(
layout.popupHeaderRect,
resolvedPalette.popupHeaderColor,
resolvedMetrics.popupBorderRounding);
drawList.AddFilledRect(
UIRect(
layout.popupHeaderRect.x,
layout.popupHeaderRect.y + layout.popupHeaderRect.height - resolvedMetrics.borderThickness,
layout.popupHeaderRect.width,
resolvedMetrics.borderThickness),
resolvedPalette.popupBorderColor);
drawList.AddFilledRect(
layout.popupCloseButtonRect,
state.hoveredTarget == UIEditorColorFieldHitTargetKind::PopupCloseButton
? resolvedPalette.closeButtonHoverColor
: resolvedPalette.closeButtonColor,
0.0f);
AppendColorSample(
drawList,
layout.popupPreviewRect,
spec.value,
spec.showAlpha,
resolvedPalette,
resolvedMetrics,
resolvedPalette.previewBorderColor,
resolvedMetrics.borderThickness,
resolvedMetrics.swatchRounding);
const UIEditorHsvColor hsv = ResolveDisplayHsv(spec, state);
AppendHueWheel(drawList, layout, resolvedPalette, resolvedMetrics);
AppendSaturationValueSquare(drawList, layout.saturationValueRect, hsv, resolvedPalette, resolvedMetrics);
const UIColor rgbColor = ClampColor(spec.value);
AppendPopupChannelRow(
drawList,
layout.redLabelRect,
layout.redSliderRect,
layout.redValueRect,
"R",
FormatChannelValue(spec.value.r),
UIColor(0.0f, rgbColor.g, rgbColor.b, 1.0f),
UIColor(1.0f, rgbColor.g, rgbColor.b, 1.0f),
spec.value.r,
false,
resolvedPalette,
resolvedMetrics);
AppendPopupChannelRow(
drawList,
layout.greenLabelRect,
layout.greenSliderRect,
layout.greenValueRect,
"G",
FormatChannelValue(spec.value.g),
UIColor(rgbColor.r, 0.0f, rgbColor.b, 1.0f),
UIColor(rgbColor.r, 1.0f, rgbColor.b, 1.0f),
spec.value.g,
false,
resolvedPalette,
resolvedMetrics);
AppendPopupChannelRow(
drawList,
layout.blueLabelRect,
layout.blueSliderRect,
layout.blueValueRect,
"B",
FormatChannelValue(spec.value.b),
UIColor(rgbColor.r, rgbColor.g, 0.0f, 1.0f),
UIColor(rgbColor.r, rgbColor.g, 1.0f, 1.0f),
spec.value.b,
false,
resolvedPalette,
resolvedMetrics);
if (spec.showAlpha && layout.alphaSliderRect.width > 0.0f) {
AppendPopupChannelRow(
drawList,
layout.alphaLabelRect,
layout.alphaSliderRect,
layout.alphaValueRect,
"A",
FormatChannelValue(spec.value.a),
UIColor(rgbColor.r, rgbColor.g, rgbColor.b, 0.0f),
UIColor(rgbColor.r, rgbColor.g, rgbColor.b, 1.0f),
spec.value.a,
true,
resolvedPalette,
resolvedMetrics);
}
AppendNumericValueBox(drawList, layout.hexValueRect, FormatPopupHexText(spec), resolvedPalette, resolvedMetrics);
}
void AppendUIEditorColorFieldForeground(
UIDrawList& drawList,
const UIEditorColorFieldLayout& layout,
const UIEditorColorFieldSpec& spec,
const UIEditorColorFieldState& state,
const UIEditorColorFieldPalette& palette,
const UIEditorColorFieldMetrics& metrics) {
const UIEditorColorFieldPalette resolvedPalette = ResolvePalette(palette);
const UIEditorColorFieldMetrics resolvedMetrics = ResolveMetrics(metrics);
AppendPopupText(
drawList,
layout.labelRect,
spec.label,
resolvedPalette.labelColor,
resolvedMetrics.labelFontSize,
0.0f,
resolvedMetrics.labelTextInsetY);
if (!state.popupOpen) {
return;
}
AppendPopupText(
drawList,
layout.popupTitleRect,
"Color",
resolvedPalette.popupTitleColor,
resolvedMetrics.titleFontSize,
0.0f,
0.0f);
AppendCloseGlyph(drawList, layout.popupCloseButtonRect, resolvedPalette.closeGlyphColor);
const UIEditorHsvColor hsv = ResolveDisplayHsv(spec, state);
AppendWheelHandle(
drawList,
ResolveHueWheelHandleCenter(layout, hsv.hue),
resolvedMetrics.ringHandleRadius,
resolvedPalette);
AppendWheelHandle(
drawList,
ResolveSaturationValueHandleCenter(layout, hsv),
resolvedMetrics.handleRadius,
resolvedPalette);
AppendPopupText(
drawList,
layout.hexLabelRect,
"Hexadecimal",
resolvedPalette.popupTextMutedColor,
resolvedMetrics.valueFontSize,
0.0f,
resolvedMetrics.valueTextInsetY);
}
void AppendUIEditorColorField(
UIDrawList& drawList,
const UIRect& bounds,
const UIEditorColorFieldSpec& spec,
const UIEditorColorFieldState& state,
const UIEditorColorFieldPalette& palette,
const UIEditorColorFieldMetrics& metrics,
const UIRect& viewportRect) {
const UIEditorColorFieldPalette resolvedPalette = ResolvePalette(palette);
const UIEditorColorFieldMetrics resolvedMetrics = ResolveMetrics(metrics);
const UIEditorColorFieldLayout layout =
BuildUIEditorColorFieldLayout(bounds, spec, resolvedMetrics, viewportRect);
AppendUIEditorColorFieldBackground(drawList, layout, spec, state, resolvedPalette, resolvedMetrics);
AppendUIEditorColorFieldForeground(drawList, layout, spec, state, resolvedPalette, resolvedMetrics);
}
} // namespace XCEngine::UI::Editor::Widgets

View File

@@ -0,0 +1,453 @@
#include <XCEditor/Fields/UIEditorColorFieldInteraction.h>
#include <XCEditor/Widgets/UIEditorColorUtils.h>
#include <XCEngine/Input/InputTypes.h>
#include <algorithm>
#include <cmath>
#include <utility>
namespace XCEngine::UI::Editor {
namespace {
using ::XCEngine::Input::KeyCode;
using ::XCEngine::UI::UIColor;
using ::XCEngine::UI::UIInputEvent;
using ::XCEngine::UI::UIInputEventType;
using ::XCEngine::UI::UIPointerButton;
using Widgets::BuildUIEditorColorFieldLayout;
using Widgets::ConvertUIEditorHsvToColor;
using Widgets::HitTestUIEditorColorField;
using Widgets::ResolveUIEditorDisplayHsv;
using Widgets::UIEditorColorFieldHitTarget;
using Widgets::UIEditorColorFieldHitTargetKind;
using Widgets::UIEditorColorFieldLayout;
using Widgets::UIEditorColorFieldMetrics;
using Widgets::UIEditorColorFieldSpec;
using Widgets::UIEditorHsvColor;
constexpr float kPi = 3.14159265358979323846f;
float Clamp01(float value) {
return (std::clamp)(value, 0.0f, 1.0f);
}
bool AreColorsEqual(
const UIColor& lhs,
const UIColor& rhs) {
return lhs.r == rhs.r &&
lhs.g == rhs.g &&
lhs.b == rhs.b &&
lhs.a == rhs.a;
}
bool ShouldUsePointerPosition(const UIInputEvent& event) {
switch (event.type) {
case UIInputEventType::PointerMove:
case UIInputEventType::PointerEnter:
case UIInputEventType::PointerButtonDown:
case UIInputEventType::PointerButtonUp:
return true;
default:
return false;
}
}
void SyncRememberedHue(
UIEditorColorFieldInteractionState& state,
const UIEditorColorFieldSpec& spec) {
const UIEditorHsvColor hsv = ResolveUIEditorDisplayHsv(
spec.value,
state.colorFieldState.hue,
state.colorFieldState.hueValid);
state.colorFieldState.hue = hsv.hue;
state.colorFieldState.hueValid = true;
}
void SyncHoverTarget(
UIEditorColorFieldInteractionState& state,
const UIEditorColorFieldLayout& layout) {
if (!state.hasPointerPosition) {
state.colorFieldState.hoveredTarget = UIEditorColorFieldHitTargetKind::None;
return;
}
state.colorFieldState.hoveredTarget =
HitTestUIEditorColorField(layout, state.colorFieldState.popupOpen, state.pointerPosition).kind;
}
void ClosePopup(
UIEditorColorFieldInteractionState& state,
UIEditorColorFieldInteractionResult& result) {
if (!state.colorFieldState.popupOpen) {
return;
}
state.colorFieldState.popupOpen = false;
state.colorFieldState.activeTarget = UIEditorColorFieldHitTargetKind::None;
result.popupClosed = true;
result.consumed = true;
}
void OpenPopup(
UIEditorColorFieldInteractionState& state,
const UIEditorColorFieldSpec& spec,
UIEditorColorFieldInteractionResult& result) {
if (spec.readOnly || state.colorFieldState.popupOpen) {
return;
}
state.colorFieldState.popupOpen = true;
SyncRememberedHue(state, spec);
result.popupOpened = true;
result.consumed = true;
}
void AssignRawColor(
UIEditorColorFieldInteractionState& state,
UIEditorColorFieldSpec& spec,
const UIColor& color,
UIEditorColorFieldInteractionResult& result) {
result.valueBefore = spec.value;
spec.value = UIColor(
Clamp01(color.r),
Clamp01(color.g),
Clamp01(color.b),
Clamp01(color.a));
SyncRememberedHue(state, spec);
result.valueAfter = spec.value;
result.colorChanged = !AreColorsEqual(result.valueBefore, result.valueAfter);
result.consumed = true;
}
void AssignHsvColor(
UIEditorColorFieldInteractionState& state,
UIEditorColorFieldSpec& spec,
const UIEditorHsvColor& hsv,
UIEditorColorFieldInteractionResult& result) {
AssignRawColor(state, spec, ConvertUIEditorHsvToColor(hsv), result);
state.colorFieldState.hue = Clamp01(hsv.hue);
state.colorFieldState.hueValid = true;
}
bool ApplySaturationValueDrag(
UIEditorColorFieldInteractionState& state,
UIEditorColorFieldSpec& spec,
const UIEditorColorFieldLayout& layout,
UIEditorColorFieldInteractionResult& result,
const ::XCEngine::UI::UIPoint& point) {
if (layout.saturationValueRect.width <= 0.0f || layout.saturationValueRect.height <= 0.0f) {
return false;
}
UIEditorHsvColor hsv = ResolveUIEditorDisplayHsv(
spec.value,
state.colorFieldState.hue,
state.colorFieldState.hueValid);
hsv.saturation = Clamp01((point.x - layout.saturationValueRect.x) / layout.saturationValueRect.width);
hsv.value = 1.0f - Clamp01((point.y - layout.saturationValueRect.y) / layout.saturationValueRect.height);
hsv.alpha = Clamp01(spec.value.a);
AssignHsvColor(state, spec, hsv, result);
return true;
}
bool ApplyHueWheelDrag(
UIEditorColorFieldInteractionState& state,
UIEditorColorFieldSpec& spec,
const UIEditorColorFieldLayout& layout,
UIEditorColorFieldInteractionResult& result,
const ::XCEngine::UI::UIPoint& point) {
const float dx = point.x - layout.hueWheelCenter.x;
const float dy = point.y - layout.hueWheelCenter.y;
float angle = std::atan2(dy, dx);
if (angle < 0.0f) {
angle += 2.0f * kPi;
}
UIEditorHsvColor hsv = ResolveUIEditorDisplayHsv(
spec.value,
state.colorFieldState.hue,
state.colorFieldState.hueValid);
hsv.hue = Clamp01(angle / (2.0f * kPi));
hsv.alpha = Clamp01(spec.value.a);
AssignHsvColor(state, spec, hsv, result);
return true;
}
bool ApplyRgbChannelDrag(
UIEditorColorFieldInteractionState& state,
UIEditorColorFieldSpec& spec,
const ::XCEngine::UI::UIRect& sliderRect,
int componentIndex,
UIEditorColorFieldInteractionResult& result,
const ::XCEngine::UI::UIPoint& point) {
if (sliderRect.width <= 0.0f) {
return false;
}
UIColor color = spec.value;
const float unit = Clamp01((point.x - sliderRect.x) / sliderRect.width);
switch (componentIndex) {
case 0:
color.r = unit;
break;
case 1:
color.g = unit;
break;
case 2:
color.b = unit;
break;
default:
return false;
}
AssignRawColor(state, spec, color, result);
return true;
}
bool ApplyAlphaChannelDrag(
UIEditorColorFieldInteractionState& state,
UIEditorColorFieldSpec& spec,
const UIEditorColorFieldLayout& layout,
UIEditorColorFieldInteractionResult& result,
const ::XCEngine::UI::UIPoint& point) {
if (!spec.showAlpha || layout.alphaSliderRect.width <= 0.0f) {
return false;
}
UIColor color = spec.value;
color.a = Clamp01((point.x - layout.alphaSliderRect.x) / layout.alphaSliderRect.width);
AssignRawColor(state, spec, color, result);
return true;
}
void ApplyActiveDrag(
UIEditorColorFieldInteractionState& state,
UIEditorColorFieldSpec& spec,
const UIEditorColorFieldLayout& layout,
UIEditorColorFieldInteractionResult& result) {
switch (state.colorFieldState.activeTarget) {
case UIEditorColorFieldHitTargetKind::HueWheel:
ApplyHueWheelDrag(state, spec, layout, result, state.pointerPosition);
break;
case UIEditorColorFieldHitTargetKind::SaturationValue:
ApplySaturationValueDrag(state, spec, layout, result, state.pointerPosition);
break;
case UIEditorColorFieldHitTargetKind::RedChannel:
ApplyRgbChannelDrag(state, spec, layout.redSliderRect, 0, result, state.pointerPosition);
break;
case UIEditorColorFieldHitTargetKind::GreenChannel:
ApplyRgbChannelDrag(state, spec, layout.greenSliderRect, 1, result, state.pointerPosition);
break;
case UIEditorColorFieldHitTargetKind::BlueChannel:
ApplyRgbChannelDrag(state, spec, layout.blueSliderRect, 2, result, state.pointerPosition);
break;
case UIEditorColorFieldHitTargetKind::AlphaChannel:
ApplyAlphaChannelDrag(state, spec, layout, result, state.pointerPosition);
break;
default:
break;
}
}
void AccumulateResult(
UIEditorColorFieldInteractionResult& accumulated,
const UIEditorColorFieldInteractionResult& current) {
accumulated.consumed = accumulated.consumed || current.consumed;
accumulated.focusChanged = accumulated.focusChanged || current.focusChanged;
accumulated.popupOpened = accumulated.popupOpened || current.popupOpened;
accumulated.popupClosed = accumulated.popupClosed || current.popupClosed;
if (current.colorChanged) {
if (!accumulated.colorChanged) {
accumulated.valueBefore = current.valueBefore;
}
accumulated.valueAfter = current.valueAfter;
}
accumulated.colorChanged = accumulated.colorChanged || current.colorChanged;
if (current.hitTarget.kind != UIEditorColorFieldHitTargetKind::None) {
accumulated.hitTarget = current.hitTarget;
}
}
bool IsDragTarget(UIEditorColorFieldHitTargetKind kind) {
switch (kind) {
case UIEditorColorFieldHitTargetKind::HueWheel:
case UIEditorColorFieldHitTargetKind::SaturationValue:
case UIEditorColorFieldHitTargetKind::RedChannel:
case UIEditorColorFieldHitTargetKind::GreenChannel:
case UIEditorColorFieldHitTargetKind::BlueChannel:
case UIEditorColorFieldHitTargetKind::AlphaChannel:
return true;
default:
return false;
}
}
} // namespace
UIEditorColorFieldInteractionFrame UpdateUIEditorColorFieldInteraction(
UIEditorColorFieldInteractionState& state,
UIEditorColorFieldSpec& spec,
const ::XCEngine::UI::UIRect& bounds,
const std::vector<UIInputEvent>& inputEvents,
const UIEditorColorFieldMetrics& metrics,
const ::XCEngine::UI::UIRect& viewportRect) {
spec.value.r = Clamp01(spec.value.r);
spec.value.g = Clamp01(spec.value.g);
spec.value.b = Clamp01(spec.value.b);
spec.value.a = Clamp01(spec.value.a);
SyncRememberedHue(state, spec);
UIEditorColorFieldLayout layout =
BuildUIEditorColorFieldLayout(bounds, spec, metrics, viewportRect);
SyncHoverTarget(state, layout);
UIEditorColorFieldInteractionResult interactionResult = {};
for (const UIInputEvent& event : inputEvents) {
if (ShouldUsePointerPosition(event)) {
state.pointerPosition = event.position;
state.hasPointerPosition = true;
} else if (event.type == UIInputEventType::PointerLeave) {
state.hasPointerPosition = false;
}
UIEditorColorFieldInteractionResult eventResult = {};
switch (event.type) {
case UIInputEventType::FocusGained:
eventResult.focusChanged = !state.colorFieldState.focused;
state.colorFieldState.focused = true;
break;
case UIInputEventType::FocusLost:
eventResult.focusChanged = state.colorFieldState.focused;
state.colorFieldState.focused = false;
ClosePopup(state, eventResult);
break;
case UIInputEventType::PointerMove:
case UIInputEventType::PointerEnter:
case UIInputEventType::PointerLeave:
ApplyActiveDrag(state, spec, layout, eventResult);
break;
case UIInputEventType::PointerButtonDown: {
if (event.pointerButton != UIPointerButton::Left) {
break;
}
const UIEditorColorFieldHitTarget hitTarget =
state.hasPointerPosition
? HitTestUIEditorColorField(layout, state.colorFieldState.popupOpen, state.pointerPosition)
: UIEditorColorFieldHitTarget {};
eventResult.hitTarget = hitTarget;
if (hitTarget.kind == UIEditorColorFieldHitTargetKind::None) {
if (state.colorFieldState.popupOpen) {
ClosePopup(state, eventResult);
}
if (state.colorFieldState.focused) {
eventResult.focusChanged = true;
state.colorFieldState.focused = false;
}
state.colorFieldState.activeTarget = UIEditorColorFieldHitTargetKind::None;
break;
}
eventResult.focusChanged = !state.colorFieldState.focused;
state.colorFieldState.focused = true;
state.colorFieldState.activeTarget = hitTarget.kind;
if (IsDragTarget(hitTarget.kind)) {
ApplyActiveDrag(state, spec, layout, eventResult);
} else {
eventResult.consumed = true;
}
break;
}
case UIInputEventType::PointerButtonUp: {
if (event.pointerButton != UIPointerButton::Left) {
break;
}
const UIEditorColorFieldHitTarget hitTarget =
state.hasPointerPosition
? HitTestUIEditorColorField(layout, state.colorFieldState.popupOpen, state.pointerPosition)
: UIEditorColorFieldHitTarget {};
eventResult.hitTarget = hitTarget;
const UIEditorColorFieldHitTargetKind activeTarget = state.colorFieldState.activeTarget;
state.colorFieldState.activeTarget = UIEditorColorFieldHitTargetKind::None;
if (activeTarget == UIEditorColorFieldHitTargetKind::Swatch &&
hitTarget.kind == UIEditorColorFieldHitTargetKind::Swatch &&
!spec.readOnly) {
if (state.colorFieldState.popupOpen) {
ClosePopup(state, eventResult);
} else {
OpenPopup(state, spec, eventResult);
}
} else if (
activeTarget == UIEditorColorFieldHitTargetKind::PopupCloseButton &&
hitTarget.kind == UIEditorColorFieldHitTargetKind::PopupCloseButton) {
ClosePopup(state, eventResult);
} else if (IsDragTarget(activeTarget)) {
ApplyActiveDrag(state, spec, layout, eventResult);
} else if (
hitTarget.kind == UIEditorColorFieldHitTargetKind::PopupSurface ||
hitTarget.kind == UIEditorColorFieldHitTargetKind::Row) {
eventResult.consumed = true;
}
break;
}
case UIInputEventType::KeyDown:
if (!state.colorFieldState.focused) {
break;
}
if (event.keyCode == static_cast<std::int32_t>(KeyCode::Escape)) {
ClosePopup(state, eventResult);
break;
}
if ((event.keyCode == static_cast<std::int32_t>(KeyCode::Enter) ||
event.keyCode == static_cast<std::int32_t>(KeyCode::Space)) &&
!spec.readOnly) {
if (state.colorFieldState.popupOpen) {
ClosePopup(state, eventResult);
} else {
OpenPopup(state, spec, eventResult);
}
}
break;
default:
break;
}
SyncRememberedHue(state, spec);
layout = BuildUIEditorColorFieldLayout(bounds, spec, metrics, viewportRect);
SyncHoverTarget(state, layout);
if (eventResult.hitTarget.kind == UIEditorColorFieldHitTargetKind::None &&
state.hasPointerPosition) {
eventResult.hitTarget =
HitTestUIEditorColorField(layout, state.colorFieldState.popupOpen, state.pointerPosition);
}
AccumulateResult(interactionResult, eventResult);
}
SyncRememberedHue(state, spec);
layout = BuildUIEditorColorFieldLayout(bounds, spec, metrics, viewportRect);
SyncHoverTarget(state, layout);
if (interactionResult.hitTarget.kind == UIEditorColorFieldHitTargetKind::None &&
state.hasPointerPosition) {
interactionResult.hitTarget =
HitTestUIEditorColorField(layout, state.colorFieldState.popupOpen, state.pointerPosition);
}
return { std::move(layout), std::move(interactionResult) };
}
} // namespace XCEngine::UI::Editor

View File

@@ -0,0 +1,250 @@
#include <XCEditor/Fields/UIEditorEnumField.h>
#include <XCEditor/Widgets/UIEditorFieldRowLayout.h>
#include <XCEditor/Widgets/UIEditorTextLayout.h>
#include <algorithm>
namespace XCEngine::UI::Editor::Widgets {
namespace {
float ClampNonNegative(float value) {
return (std::max)(0.0f, value);
}
UIEditorEnumFieldMetrics ResolveMetrics(const UIEditorEnumFieldMetrics& metrics) {
const auto& tokens = GetUIEditorInspectorFieldStyleTokens();
UIEditorEnumFieldMetrics resolved = metrics;
if (AreUIEditorFieldMetricsEqual(metrics.controlTrailingInset, 8.0f)) {
resolved.controlTrailingInset = tokens.controlTrailingInset;
}
if (AreUIEditorFieldMetricsEqual(metrics.valueBoxMinWidth, 96.0f)) {
resolved.valueBoxMinWidth = tokens.controlMinWidth;
}
if (AreUIEditorFieldMetricsEqual(metrics.dropdownArrowWidth, 20.0f)) {
resolved.dropdownArrowWidth = tokens.dropdownArrowWidth;
}
return resolved;
}
UIEditorEnumFieldPalette ResolvePalette(const UIEditorEnumFieldPalette& palette) {
const auto& tokens = GetUIEditorInspectorFieldStyleTokens();
UIEditorEnumFieldPalette resolved = palette;
if (AreUIEditorFieldColorsEqual(palette.rowHoverColor, ::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.0f))) {
resolved.rowHoverColor = tokens.rowHoverColor;
}
if (AreUIEditorFieldColorsEqual(palette.rowActiveColor, ::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.0f))) {
resolved.rowActiveColor = tokens.rowActiveColor;
}
if (AreUIEditorFieldColorsEqual(palette.focusedBorderColor, ::XCEngine::UI::UIColor(0.14f, 0.14f, 0.14f, 1.0f))) {
resolved.focusedBorderColor = tokens.controlFocusedBorderColor;
}
if (AreUIEditorFieldColorsEqual(palette.valueBoxColor, ::XCEngine::UI::UIColor(0.18f, 0.18f, 0.18f, 1.0f))) {
resolved.valueBoxColor = tokens.controlColor;
}
if (AreUIEditorFieldColorsEqual(palette.valueBoxHoverColor, ::XCEngine::UI::UIColor(0.21f, 0.21f, 0.21f, 1.0f))) {
resolved.valueBoxHoverColor = tokens.controlHoverColor;
}
if (AreUIEditorFieldColorsEqual(palette.readOnlyColor, ::XCEngine::UI::UIColor(0.17f, 0.17f, 0.17f, 1.0f))) {
resolved.readOnlyColor = tokens.controlReadOnlyColor;
}
if (AreUIEditorFieldColorsEqual(palette.controlBorderColor, ::XCEngine::UI::UIColor(0.14f, 0.14f, 0.14f, 1.0f))) {
resolved.controlBorderColor = tokens.controlBorderColor;
}
if (AreUIEditorFieldColorsEqual(palette.labelColor, ::XCEngine::UI::UIColor(0.88f, 0.88f, 0.88f, 1.0f))) {
resolved.labelColor = tokens.labelColor;
}
if (AreUIEditorFieldColorsEqual(palette.valueColor, ::XCEngine::UI::UIColor(0.88f, 0.88f, 0.88f, 1.0f))) {
resolved.valueColor = tokens.valueColor;
}
if (AreUIEditorFieldColorsEqual(palette.arrowColor, ::XCEngine::UI::UIColor(0.88f, 0.88f, 0.88f, 1.0f))) {
resolved.arrowColor = tokens.arrowColor;
}
return resolved;
}
bool ContainsPoint(const ::XCEngine::UI::UIRect& rect, const ::XCEngine::UI::UIPoint& point) {
return point.x >= rect.x &&
point.x <= rect.x + rect.width &&
point.y >= rect.y &&
point.y <= rect.y + rect.height;
}
std::size_t ClampSelectedIndex(const UIEditorEnumFieldSpec& spec) {
if (spec.options.empty()) {
return 0u;
}
return (std::min)(spec.selectedIndex, spec.options.size() - 1u);
}
void AppendDropdownChevron(
::XCEngine::UI::UIDrawList& drawList,
const ::XCEngine::UI::UIRect& rect,
const ::XCEngine::UI::UIColor& color) {
const float centerX = rect.x + rect.width * 0.5f;
const float centerY = rect.y + rect.height * 0.5f;
const float halfWidth = ClampNonNegative(rect.width * 0.18f);
const float halfHeight = ClampNonNegative(rect.height * 0.10f);
drawList.AddLine(
::XCEngine::UI::UIPoint(centerX - halfWidth, centerY - halfHeight),
::XCEngine::UI::UIPoint(centerX, centerY + halfHeight),
color,
1.0f);
drawList.AddLine(
::XCEngine::UI::UIPoint(centerX, centerY + halfHeight),
::XCEngine::UI::UIPoint(centerX + halfWidth, centerY - halfHeight),
color,
1.0f);
}
} // namespace
std::string ResolveUIEditorEnumFieldValueText(const UIEditorEnumFieldSpec& spec) {
if (spec.options.empty()) {
return "(none)";
}
return spec.options[ClampSelectedIndex(spec)];
}
UIEditorEnumFieldLayout BuildUIEditorEnumFieldLayout(
const ::XCEngine::UI::UIRect& bounds,
const UIEditorEnumFieldSpec&,
const UIEditorEnumFieldMetrics& metrics) {
const UIEditorEnumFieldMetrics resolvedMetrics = ResolveMetrics(metrics);
const UIEditorFieldRowLayout hostLayout = BuildUIEditorFieldRowLayout(
bounds,
resolvedMetrics.valueBoxMinWidth,
UIEditorFieldRowLayoutMetrics {
resolvedMetrics.rowHeight,
resolvedMetrics.horizontalPadding,
resolvedMetrics.labelControlGap,
resolvedMetrics.controlColumnStart,
resolvedMetrics.controlTrailingInset,
resolvedMetrics.controlInsetY,
});
UIEditorEnumFieldLayout layout = {};
layout.bounds = hostLayout.bounds;
layout.labelRect = hostLayout.labelRect;
layout.controlRect = hostLayout.controlRect;
layout.valueRect = layout.controlRect;
const float arrowWidth = (std::min)(
(std::max)(resolvedMetrics.dropdownArrowWidth, 0.0f),
layout.valueRect.width);
layout.arrowRect = ::XCEngine::UI::UIRect(
layout.valueRect.x + (std::max)(0.0f, layout.valueRect.width - arrowWidth),
layout.valueRect.y,
arrowWidth,
layout.valueRect.height);
return layout;
}
UIEditorEnumFieldHitTarget HitTestUIEditorEnumField(
const UIEditorEnumFieldLayout& layout,
const ::XCEngine::UI::UIPoint& point) {
if (!ContainsPoint(layout.bounds, point)) {
return {};
}
if (ContainsPoint(layout.arrowRect, point)) {
return { UIEditorEnumFieldHitTargetKind::DropdownArrow };
}
if (ContainsPoint(layout.valueRect, point)) {
return { UIEditorEnumFieldHitTargetKind::ValueBox };
}
return { UIEditorEnumFieldHitTargetKind::Row };
}
void AppendUIEditorEnumFieldBackground(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorEnumFieldLayout& layout,
const UIEditorEnumFieldSpec& spec,
const UIEditorEnumFieldState& state,
const UIEditorEnumFieldPalette& palette,
const UIEditorEnumFieldMetrics& metrics) {
const UIEditorEnumFieldPalette resolvedPalette = ResolvePalette(palette);
const UIEditorEnumFieldMetrics resolvedMetrics = ResolveMetrics(metrics);
const auto rowFillColor =
state.active ? resolvedPalette.rowActiveColor
: (state.hoveredTarget != UIEditorEnumFieldHitTargetKind::None
? resolvedPalette.rowHoverColor
: resolvedPalette.surfaceColor);
if (rowFillColor.a > 0.0f) {
drawList.AddFilledRect(layout.bounds, rowFillColor, resolvedMetrics.cornerRounding);
}
const bool controlHovered =
state.hoveredTarget == UIEditorEnumFieldHitTargetKind::ValueBox ||
state.hoveredTarget == UIEditorEnumFieldHitTargetKind::DropdownArrow;
const ::XCEngine::UI::UIColor controlColor =
spec.readOnly
? resolvedPalette.readOnlyColor
: (controlHovered || state.popupOpen ? resolvedPalette.valueBoxHoverColor : resolvedPalette.valueBoxColor);
drawList.AddFilledRect(layout.valueRect, controlColor, resolvedMetrics.valueBoxRounding);
drawList.AddRectOutline(
layout.valueRect,
state.popupOpen ? resolvedPalette.focusedBorderColor : resolvedPalette.controlBorderColor,
state.popupOpen ? resolvedMetrics.focusedBorderThickness : resolvedMetrics.borderThickness,
resolvedMetrics.valueBoxRounding);
drawList.AddLine(
::XCEngine::UI::UIPoint(layout.arrowRect.x, layout.arrowRect.y + 1.0f),
::XCEngine::UI::UIPoint(layout.arrowRect.x, layout.arrowRect.y + layout.arrowRect.height - 1.0f),
resolvedPalette.controlBorderColor,
1.0f);
}
void AppendUIEditorEnumFieldForeground(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorEnumFieldLayout& layout,
const UIEditorEnumFieldSpec& spec,
const UIEditorEnumFieldPalette& palette,
const UIEditorEnumFieldMetrics& metrics) {
const UIEditorEnumFieldPalette resolvedPalette = ResolvePalette(palette);
const UIEditorEnumFieldMetrics resolvedMetrics = ResolveMetrics(metrics);
drawList.PushClipRect(ResolveUIEditorTextClipRect(layout.labelRect, resolvedMetrics.labelFontSize));
drawList.AddText(
::XCEngine::UI::UIPoint(
layout.labelRect.x,
ResolveUIEditorTextTop(layout.labelRect, resolvedMetrics.labelFontSize, resolvedMetrics.labelTextInsetY)),
spec.label,
resolvedPalette.labelColor,
resolvedMetrics.labelFontSize);
drawList.PopClipRect();
const ::XCEngine::UI::UIRect valueTextClipRect(
layout.valueRect.x + resolvedMetrics.valueTextInsetX,
layout.valueRect.y,
ClampNonNegative(layout.arrowRect.x - layout.valueRect.x - resolvedMetrics.valueTextInsetX),
layout.valueRect.height);
drawList.PushClipRect(ResolveUIEditorTextClipRect(valueTextClipRect, resolvedMetrics.valueFontSize));
drawList.AddText(
::XCEngine::UI::UIPoint(
layout.valueRect.x + resolvedMetrics.valueTextInsetX,
ResolveUIEditorTextTop(layout.valueRect, resolvedMetrics.valueFontSize, resolvedMetrics.valueTextInsetY)),
ResolveUIEditorEnumFieldValueText(spec),
resolvedPalette.valueColor,
resolvedMetrics.valueFontSize);
drawList.PopClipRect();
AppendDropdownChevron(drawList, layout.arrowRect, resolvedPalette.arrowColor);
}
void AppendUIEditorEnumField(
::XCEngine::UI::UIDrawList& drawList,
const ::XCEngine::UI::UIRect& bounds,
const UIEditorEnumFieldSpec& spec,
const UIEditorEnumFieldState& state,
const UIEditorEnumFieldPalette& palette,
const UIEditorEnumFieldMetrics& metrics) {
const UIEditorEnumFieldPalette resolvedPalette = ResolvePalette(palette);
const UIEditorEnumFieldMetrics resolvedMetrics = ResolveMetrics(metrics);
const UIEditorEnumFieldLayout layout = BuildUIEditorEnumFieldLayout(bounds, spec, resolvedMetrics);
AppendUIEditorEnumFieldBackground(drawList, layout, spec, state, resolvedPalette, resolvedMetrics);
AppendUIEditorEnumFieldForeground(drawList, layout, spec, resolvedPalette, resolvedMetrics);
}
} // namespace XCEngine::UI::Editor::Widgets

View File

@@ -1,4 +1,4 @@
#include <XCEditor/Core/UIEditorEnumFieldInteraction.h>
#include <XCEditor/Fields/UIEditorEnumFieldInteraction.h>
#include <XCEngine/Input/InputTypes.h>
#include <XCEngine/UI/Widgets/UIPopupOverlayModel.h>

View File

@@ -0,0 +1,477 @@
#include <XCEditor/Fields/UIEditorFieldStyle.h>
namespace XCEngine::UI::Editor {
namespace {
using ::XCEngine::UI::UIColor;
constexpr UIColor kTransparent(0.0f, 0.0f, 0.0f, 0.0f);
} // namespace
const Widgets::UIEditorPropertyGridMetrics& GetUIEditorFixedPropertyGridMetrics() {
static const Widgets::UIEditorPropertyGridMetrics kMetrics = {};
return kMetrics;
}
const Widgets::UIEditorPropertyGridPalette& GetUIEditorFixedPropertyGridPalette() {
static const Widgets::UIEditorPropertyGridPalette kPalette = {};
return kPalette;
}
Widgets::UIEditorBoolFieldMetrics BuildUIEditorPropertyGridBoolFieldMetrics(
const Widgets::UIEditorPropertyGridMetrics& propertyGridMetrics,
const Widgets::UIEditorBoolFieldMetrics& fallback) {
Widgets::UIEditorBoolFieldMetrics hosted = fallback;
hosted.rowHeight = propertyGridMetrics.fieldRowHeight;
hosted.horizontalPadding = propertyGridMetrics.horizontalPadding;
hosted.labelControlGap = propertyGridMetrics.labelControlGap;
hosted.controlColumnStart = propertyGridMetrics.controlColumnStart;
hosted.controlTrailingInset = propertyGridMetrics.valueBoxInsetX;
hosted.labelTextInsetY = propertyGridMetrics.labelTextInsetY;
hosted.labelFontSize = propertyGridMetrics.labelFontSize;
hosted.checkboxGlyphFontSize = propertyGridMetrics.valueFontSize;
hosted.cornerRounding = propertyGridMetrics.cornerRounding;
hosted.checkboxRounding = propertyGridMetrics.valueBoxRounding;
hosted.borderThickness = propertyGridMetrics.borderThickness;
hosted.focusedBorderThickness = propertyGridMetrics.focusedBorderThickness;
return hosted;
}
Widgets::UIEditorBoolFieldPalette BuildUIEditorPropertyGridBoolFieldPalette(
const Widgets::UIEditorPropertyGridPalette& propertyGridPalette,
const Widgets::UIEditorBoolFieldPalette& fallback) {
Widgets::UIEditorBoolFieldPalette hosted = fallback;
hosted.surfaceColor = kTransparent;
hosted.borderColor = kTransparent;
hosted.focusedBorderColor = kTransparent;
hosted.rowHoverColor = kTransparent;
hosted.rowActiveColor = kTransparent;
hosted.checkboxColor = propertyGridPalette.valueBoxColor;
hosted.checkboxHoverColor = propertyGridPalette.valueBoxHoverColor;
hosted.checkboxReadOnlyColor = propertyGridPalette.valueBoxReadOnlyColor;
hosted.checkboxBorderColor = propertyGridPalette.valueBoxBorderColor;
hosted.checkboxMarkColor = propertyGridPalette.valueTextColor;
hosted.labelColor = propertyGridPalette.labelTextColor;
return hosted;
}
Widgets::UIEditorNumberFieldMetrics BuildUIEditorPropertyGridNumberFieldMetrics(
const Widgets::UIEditorPropertyGridMetrics& propertyGridMetrics,
const Widgets::UIEditorNumberFieldMetrics& fallback) {
Widgets::UIEditorNumberFieldMetrics hosted = fallback;
hosted.rowHeight = propertyGridMetrics.fieldRowHeight;
hosted.horizontalPadding = propertyGridMetrics.horizontalPadding;
hosted.labelControlGap = propertyGridMetrics.labelControlGap;
hosted.controlColumnStart = propertyGridMetrics.controlColumnStart;
hosted.controlTrailingInset = propertyGridMetrics.valueBoxInsetX;
hosted.controlInsetY = propertyGridMetrics.valueBoxInsetY;
hosted.labelTextInsetY = propertyGridMetrics.labelTextInsetY;
hosted.labelFontSize = propertyGridMetrics.labelFontSize;
hosted.valueTextInsetX = propertyGridMetrics.valueBoxInsetX;
hosted.valueTextInsetY = propertyGridMetrics.valueTextInsetY;
hosted.valueFontSize = propertyGridMetrics.valueFontSize;
hosted.cornerRounding = propertyGridMetrics.cornerRounding;
hosted.valueBoxRounding = propertyGridMetrics.valueBoxRounding;
hosted.borderThickness = propertyGridMetrics.borderThickness;
hosted.focusedBorderThickness = propertyGridMetrics.focusedBorderThickness;
return hosted;
}
Widgets::UIEditorNumberFieldPalette BuildUIEditorPropertyGridNumberFieldPalette(
const Widgets::UIEditorPropertyGridPalette& propertyGridPalette,
const Widgets::UIEditorNumberFieldPalette& fallback) {
Widgets::UIEditorNumberFieldPalette hosted = fallback;
hosted.surfaceColor = kTransparent;
hosted.borderColor = kTransparent;
hosted.focusedBorderColor = kTransparent;
hosted.rowHoverColor = kTransparent;
hosted.rowActiveColor = kTransparent;
hosted.valueBoxColor = propertyGridPalette.valueBoxColor;
hosted.valueBoxHoverColor = propertyGridPalette.valueBoxHoverColor;
hosted.valueBoxEditingColor = propertyGridPalette.valueBoxEditingColor;
hosted.readOnlyColor = propertyGridPalette.valueBoxReadOnlyColor;
hosted.controlBorderColor = propertyGridPalette.valueBoxBorderColor;
hosted.controlFocusedBorderColor = propertyGridPalette.valueBoxEditingBorderColor;
hosted.labelColor = propertyGridPalette.labelTextColor;
hosted.valueColor = propertyGridPalette.valueTextColor;
hosted.readOnlyValueColor = propertyGridPalette.readOnlyValueTextColor;
return hosted;
}
Widgets::UIEditorTextFieldMetrics BuildUIEditorPropertyGridTextFieldMetrics(
const Widgets::UIEditorPropertyGridMetrics& propertyGridMetrics,
const Widgets::UIEditorTextFieldMetrics& fallback) {
Widgets::UIEditorTextFieldMetrics hosted = fallback;
hosted.rowHeight = propertyGridMetrics.fieldRowHeight;
hosted.horizontalPadding = propertyGridMetrics.horizontalPadding;
hosted.labelControlGap = propertyGridMetrics.labelControlGap;
hosted.controlColumnStart = propertyGridMetrics.controlColumnStart;
hosted.controlTrailingInset = propertyGridMetrics.valueBoxInsetX;
hosted.controlInsetY = propertyGridMetrics.valueBoxInsetY;
hosted.labelTextInsetY = propertyGridMetrics.labelTextInsetY;
hosted.labelFontSize = propertyGridMetrics.labelFontSize;
hosted.valueTextInsetX = propertyGridMetrics.valueBoxInsetX;
hosted.valueTextInsetY = propertyGridMetrics.valueTextInsetY;
hosted.valueFontSize = propertyGridMetrics.valueFontSize;
hosted.cornerRounding = propertyGridMetrics.cornerRounding;
hosted.valueBoxRounding = propertyGridMetrics.valueBoxRounding;
hosted.borderThickness = propertyGridMetrics.borderThickness;
hosted.focusedBorderThickness = propertyGridMetrics.focusedBorderThickness;
return hosted;
}
Widgets::UIEditorTextFieldPalette BuildUIEditorPropertyGridTextFieldPalette(
const Widgets::UIEditorPropertyGridPalette& propertyGridPalette,
const Widgets::UIEditorTextFieldPalette& fallback) {
Widgets::UIEditorTextFieldPalette hosted = fallback;
hosted.surfaceColor = kTransparent;
hosted.borderColor = kTransparent;
hosted.focusedBorderColor = kTransparent;
hosted.rowHoverColor = kTransparent;
hosted.rowActiveColor = kTransparent;
hosted.valueBoxColor = propertyGridPalette.valueBoxColor;
hosted.valueBoxHoverColor = propertyGridPalette.valueBoxHoverColor;
hosted.valueBoxEditingColor = propertyGridPalette.valueBoxEditingColor;
hosted.readOnlyColor = propertyGridPalette.valueBoxReadOnlyColor;
hosted.controlBorderColor = propertyGridPalette.valueBoxBorderColor;
hosted.controlFocusedBorderColor = propertyGridPalette.valueBoxEditingBorderColor;
hosted.labelColor = propertyGridPalette.labelTextColor;
hosted.valueColor = propertyGridPalette.valueTextColor;
hosted.readOnlyValueColor = propertyGridPalette.readOnlyValueTextColor;
return hosted;
}
Widgets::UIEditorVector2FieldMetrics BuildUIEditorPropertyGridVector2FieldMetrics(
const Widgets::UIEditorPropertyGridMetrics& propertyGridMetrics,
const Widgets::UIEditorVector2FieldMetrics& fallback) {
Widgets::UIEditorVector2FieldMetrics hosted = fallback;
hosted.rowHeight = propertyGridMetrics.fieldRowHeight;
hosted.horizontalPadding = propertyGridMetrics.horizontalPadding;
hosted.labelControlGap = propertyGridMetrics.labelControlGap;
hosted.controlColumnStart = propertyGridMetrics.controlColumnStart;
hosted.controlTrailingInset = propertyGridMetrics.valueBoxInsetX;
hosted.controlInsetY = propertyGridMetrics.valueBoxInsetY;
hosted.labelTextInsetY = propertyGridMetrics.labelTextInsetY;
hosted.labelFontSize = propertyGridMetrics.labelFontSize;
hosted.valueTextInsetX = propertyGridMetrics.valueBoxInsetX;
hosted.valueTextInsetY = propertyGridMetrics.valueTextInsetY;
hosted.valueFontSize = propertyGridMetrics.valueFontSize;
hosted.cornerRounding = propertyGridMetrics.cornerRounding;
hosted.componentRounding = propertyGridMetrics.valueBoxRounding;
hosted.borderThickness = propertyGridMetrics.borderThickness;
hosted.focusedBorderThickness = propertyGridMetrics.focusedBorderThickness;
return hosted;
}
Widgets::UIEditorVector2FieldPalette BuildUIEditorPropertyGridVector2FieldPalette(
const Widgets::UIEditorPropertyGridPalette& propertyGridPalette,
const Widgets::UIEditorVector2FieldPalette& fallback) {
Widgets::UIEditorVector2FieldPalette hosted = fallback;
hosted.surfaceColor = kTransparent;
hosted.borderColor = kTransparent;
hosted.focusedBorderColor = kTransparent;
hosted.rowHoverColor = kTransparent;
hosted.rowActiveColor = kTransparent;
hosted.componentColor = propertyGridPalette.valueBoxColor;
hosted.componentHoverColor = propertyGridPalette.valueBoxHoverColor;
hosted.componentEditingColor = propertyGridPalette.valueBoxEditingColor;
hosted.readOnlyColor = propertyGridPalette.valueBoxReadOnlyColor;
hosted.componentBorderColor = propertyGridPalette.valueBoxBorderColor;
hosted.componentFocusedBorderColor = propertyGridPalette.valueBoxEditingBorderColor;
hosted.prefixColor = propertyGridPalette.valueBoxHoverColor;
hosted.prefixBorderColor = propertyGridPalette.valueBoxBorderColor;
hosted.labelColor = propertyGridPalette.labelTextColor;
hosted.valueColor = propertyGridPalette.valueTextColor;
hosted.readOnlyValueColor = propertyGridPalette.readOnlyValueTextColor;
return hosted;
}
Widgets::UIEditorVector3FieldMetrics BuildUIEditorPropertyGridVector3FieldMetrics(
const Widgets::UIEditorPropertyGridMetrics& propertyGridMetrics,
const Widgets::UIEditorVector3FieldMetrics& fallback) {
Widgets::UIEditorVector3FieldMetrics hosted = fallback;
hosted.rowHeight = propertyGridMetrics.fieldRowHeight;
hosted.horizontalPadding = propertyGridMetrics.horizontalPadding;
hosted.labelControlGap = propertyGridMetrics.labelControlGap;
hosted.controlColumnStart = propertyGridMetrics.controlColumnStart;
hosted.controlTrailingInset = propertyGridMetrics.valueBoxInsetX;
hosted.controlInsetY = propertyGridMetrics.valueBoxInsetY;
hosted.labelTextInsetY = propertyGridMetrics.labelTextInsetY;
hosted.labelFontSize = propertyGridMetrics.labelFontSize;
hosted.valueTextInsetX = propertyGridMetrics.valueBoxInsetX;
hosted.valueTextInsetY = propertyGridMetrics.valueTextInsetY;
hosted.valueFontSize = propertyGridMetrics.valueFontSize;
hosted.cornerRounding = propertyGridMetrics.cornerRounding;
hosted.componentRounding = propertyGridMetrics.valueBoxRounding;
hosted.borderThickness = propertyGridMetrics.borderThickness;
hosted.focusedBorderThickness = propertyGridMetrics.focusedBorderThickness;
return hosted;
}
Widgets::UIEditorVector3FieldPalette BuildUIEditorPropertyGridVector3FieldPalette(
const Widgets::UIEditorPropertyGridPalette& propertyGridPalette,
const Widgets::UIEditorVector3FieldPalette& fallback) {
Widgets::UIEditorVector3FieldPalette hosted = fallback;
hosted.surfaceColor = kTransparent;
hosted.borderColor = kTransparent;
hosted.focusedBorderColor = kTransparent;
hosted.rowHoverColor = kTransparent;
hosted.rowActiveColor = kTransparent;
hosted.componentColor = propertyGridPalette.valueBoxColor;
hosted.componentHoverColor = propertyGridPalette.valueBoxHoverColor;
hosted.componentEditingColor = propertyGridPalette.valueBoxEditingColor;
hosted.readOnlyColor = propertyGridPalette.valueBoxReadOnlyColor;
hosted.componentBorderColor = propertyGridPalette.valueBoxBorderColor;
hosted.componentFocusedBorderColor = propertyGridPalette.valueBoxEditingBorderColor;
hosted.prefixColor = propertyGridPalette.valueBoxHoverColor;
hosted.prefixBorderColor = propertyGridPalette.valueBoxBorderColor;
hosted.labelColor = propertyGridPalette.labelTextColor;
hosted.valueColor = propertyGridPalette.valueTextColor;
hosted.readOnlyValueColor = propertyGridPalette.readOnlyValueTextColor;
return hosted;
}
Widgets::UIEditorVector4FieldMetrics BuildUIEditorPropertyGridVector4FieldMetrics(
const Widgets::UIEditorPropertyGridMetrics& propertyGridMetrics,
const Widgets::UIEditorVector4FieldMetrics& fallback) {
Widgets::UIEditorVector4FieldMetrics hosted = fallback;
hosted.rowHeight = propertyGridMetrics.fieldRowHeight;
hosted.horizontalPadding = propertyGridMetrics.horizontalPadding;
hosted.labelControlGap = propertyGridMetrics.labelControlGap;
hosted.controlColumnStart = propertyGridMetrics.controlColumnStart;
hosted.controlTrailingInset = propertyGridMetrics.valueBoxInsetX;
hosted.controlInsetY = propertyGridMetrics.valueBoxInsetY;
hosted.labelTextInsetY = propertyGridMetrics.labelTextInsetY;
hosted.labelFontSize = propertyGridMetrics.labelFontSize;
hosted.valueTextInsetX = propertyGridMetrics.valueBoxInsetX;
hosted.valueTextInsetY = propertyGridMetrics.valueTextInsetY;
hosted.valueFontSize = propertyGridMetrics.valueFontSize;
hosted.cornerRounding = propertyGridMetrics.cornerRounding;
hosted.componentRounding = propertyGridMetrics.valueBoxRounding;
hosted.borderThickness = propertyGridMetrics.borderThickness;
hosted.focusedBorderThickness = propertyGridMetrics.focusedBorderThickness;
return hosted;
}
Widgets::UIEditorVector4FieldPalette BuildUIEditorPropertyGridVector4FieldPalette(
const Widgets::UIEditorPropertyGridPalette& propertyGridPalette,
const Widgets::UIEditorVector4FieldPalette& fallback) {
Widgets::UIEditorVector4FieldPalette hosted = fallback;
hosted.surfaceColor = kTransparent;
hosted.borderColor = kTransparent;
hosted.focusedBorderColor = kTransparent;
hosted.rowHoverColor = kTransparent;
hosted.rowActiveColor = kTransparent;
hosted.componentColor = propertyGridPalette.valueBoxColor;
hosted.componentHoverColor = propertyGridPalette.valueBoxHoverColor;
hosted.componentEditingColor = propertyGridPalette.valueBoxEditingColor;
hosted.readOnlyColor = propertyGridPalette.valueBoxReadOnlyColor;
hosted.componentBorderColor = propertyGridPalette.valueBoxBorderColor;
hosted.componentFocusedBorderColor = propertyGridPalette.valueBoxEditingBorderColor;
hosted.prefixColor = propertyGridPalette.valueBoxHoverColor;
hosted.prefixBorderColor = propertyGridPalette.valueBoxBorderColor;
hosted.labelColor = propertyGridPalette.labelTextColor;
hosted.valueColor = propertyGridPalette.valueTextColor;
hosted.readOnlyValueColor = propertyGridPalette.readOnlyValueTextColor;
return hosted;
}
Widgets::UIEditorEnumFieldMetrics BuildUIEditorPropertyGridEnumFieldMetrics(
const Widgets::UIEditorPropertyGridMetrics& propertyGridMetrics,
const Widgets::UIEditorEnumFieldMetrics& fallback) {
Widgets::UIEditorEnumFieldMetrics hosted = fallback;
hosted.rowHeight = propertyGridMetrics.fieldRowHeight;
hosted.horizontalPadding = propertyGridMetrics.horizontalPadding;
hosted.labelControlGap = propertyGridMetrics.labelControlGap;
hosted.controlColumnStart = propertyGridMetrics.controlColumnStart;
hosted.controlTrailingInset = propertyGridMetrics.valueBoxInsetX;
hosted.controlInsetY = propertyGridMetrics.valueBoxInsetY;
hosted.labelTextInsetY = propertyGridMetrics.labelTextInsetY;
hosted.valueTextInsetX = propertyGridMetrics.valueBoxInsetX;
hosted.valueTextInsetY = propertyGridMetrics.valueTextInsetY;
hosted.cornerRounding = propertyGridMetrics.cornerRounding;
hosted.valueBoxRounding = propertyGridMetrics.valueBoxRounding;
hosted.borderThickness = propertyGridMetrics.borderThickness;
hosted.focusedBorderThickness = propertyGridMetrics.focusedBorderThickness;
hosted.labelFontSize = propertyGridMetrics.labelFontSize;
hosted.valueFontSize = propertyGridMetrics.valueFontSize;
hosted.dropdownArrowFontSize = propertyGridMetrics.valueFontSize;
return hosted;
}
Widgets::UIEditorEnumFieldPalette BuildUIEditorPropertyGridEnumFieldPalette(
const Widgets::UIEditorPropertyGridPalette& propertyGridPalette,
const Widgets::UIEditorEnumFieldPalette& fallback) {
Widgets::UIEditorEnumFieldPalette hosted = fallback;
hosted.surfaceColor = kTransparent;
hosted.borderColor = kTransparent;
hosted.focusedBorderColor = kTransparent;
hosted.rowHoverColor = kTransparent;
hosted.rowActiveColor = kTransparent;
hosted.valueBoxColor = propertyGridPalette.valueBoxColor;
hosted.valueBoxHoverColor = propertyGridPalette.valueBoxHoverColor;
hosted.readOnlyColor = propertyGridPalette.valueBoxReadOnlyColor;
hosted.controlBorderColor = propertyGridPalette.valueBoxBorderColor;
hosted.labelColor = propertyGridPalette.labelTextColor;
hosted.valueColor = propertyGridPalette.valueTextColor;
hosted.arrowColor = propertyGridPalette.valueTextColor;
return hosted;
}
Widgets::UIEditorColorFieldMetrics BuildUIEditorPropertyGridColorFieldMetrics(
const Widgets::UIEditorPropertyGridMetrics& propertyGridMetrics,
const Widgets::UIEditorColorFieldMetrics& fallback) {
Widgets::UIEditorColorFieldMetrics hosted = fallback;
hosted.rowHeight = propertyGridMetrics.fieldRowHeight;
hosted.horizontalPadding = propertyGridMetrics.horizontalPadding;
hosted.labelControlGap = propertyGridMetrics.labelControlGap;
hosted.controlColumnStart = propertyGridMetrics.controlColumnStart;
hosted.controlTrailingInset = propertyGridMetrics.valueBoxInsetX;
hosted.swatchInsetY = propertyGridMetrics.valueBoxInsetY;
hosted.labelTextInsetY = propertyGridMetrics.labelTextInsetY;
hosted.labelFontSize = propertyGridMetrics.labelFontSize;
hosted.swatchRounding = propertyGridMetrics.valueBoxRounding;
hosted.valueFontSize = propertyGridMetrics.valueFontSize;
hosted.valueTextInsetX = propertyGridMetrics.valueBoxInsetX;
hosted.valueTextInsetY = propertyGridMetrics.valueTextInsetY;
hosted.borderThickness = propertyGridMetrics.borderThickness;
hosted.focusedBorderThickness = propertyGridMetrics.focusedBorderThickness;
return hosted;
}
Widgets::UIEditorColorFieldPalette BuildUIEditorPropertyGridColorFieldPalette(
const Widgets::UIEditorPropertyGridPalette& propertyGridPalette,
const Widgets::UIEditorColorFieldPalette& fallback) {
Widgets::UIEditorColorFieldPalette hosted = fallback;
hosted.surfaceColor = kTransparent;
hosted.borderColor = kTransparent;
hosted.focusedBorderColor = kTransparent;
hosted.rowHoverColor = kTransparent;
hosted.rowActiveColor = kTransparent;
hosted.labelColor = propertyGridPalette.labelTextColor;
hosted.swatchBorderColor = propertyGridPalette.valueBoxBorderColor;
hosted.swatchHoverBorderColor = propertyGridPalette.valueBoxEditingBorderColor;
hosted.swatchReadOnlyOverlayColor = propertyGridPalette.valueBoxReadOnlyColor;
hosted.checkerLightColor = propertyGridPalette.valueBoxHoverColor;
hosted.checkerDarkColor = propertyGridPalette.valueBoxColor;
hosted.handleFillColor = propertyGridPalette.valueTextColor;
hosted.handleStrokeColor = propertyGridPalette.borderColor;
return hosted;
}
Widgets::UIEditorObjectFieldMetrics BuildUIEditorPropertyGridObjectFieldMetrics(
const Widgets::UIEditorPropertyGridMetrics& propertyGridMetrics,
const Widgets::UIEditorObjectFieldMetrics& fallback) {
Widgets::UIEditorObjectFieldMetrics hosted = fallback;
hosted.rowHeight = propertyGridMetrics.fieldRowHeight;
hosted.horizontalPadding = propertyGridMetrics.horizontalPadding;
hosted.labelControlGap = propertyGridMetrics.labelControlGap;
hosted.controlColumnStart = propertyGridMetrics.controlColumnStart;
hosted.controlTrailingInset = propertyGridMetrics.valueBoxInsetX;
hosted.controlInsetY = propertyGridMetrics.valueBoxInsetY;
hosted.labelTextInsetY = propertyGridMetrics.labelTextInsetY;
hosted.labelFontSize = propertyGridMetrics.labelFontSize;
hosted.valueTextInsetX = propertyGridMetrics.valueBoxInsetX;
hosted.valueTextInsetY = propertyGridMetrics.valueTextInsetY;
hosted.valueFontSize = propertyGridMetrics.valueFontSize;
hosted.typeTextInsetX = propertyGridMetrics.valueBoxInsetX;
hosted.typeTextInsetY = propertyGridMetrics.valueTextInsetY;
hosted.typeFontSize = propertyGridMetrics.tagFontSize;
hosted.cornerRounding = propertyGridMetrics.cornerRounding;
hosted.valueBoxRounding = propertyGridMetrics.valueBoxRounding;
hosted.borderThickness = propertyGridMetrics.borderThickness;
hosted.focusedBorderThickness = propertyGridMetrics.focusedBorderThickness;
hosted.buttonGlyphFontSize = propertyGridMetrics.valueFontSize;
return hosted;
}
Widgets::UIEditorObjectFieldPalette BuildUIEditorPropertyGridObjectFieldPalette(
const Widgets::UIEditorPropertyGridPalette& propertyGridPalette,
const Widgets::UIEditorObjectFieldPalette& fallback) {
Widgets::UIEditorObjectFieldPalette hosted = fallback;
hosted.surfaceColor = kTransparent;
hosted.borderColor = kTransparent;
hosted.focusedBorderColor = kTransparent;
hosted.rowHoverColor = kTransparent;
hosted.rowActiveColor = kTransparent;
hosted.valueBoxColor = propertyGridPalette.valueBoxColor;
hosted.valueBoxHoverColor = propertyGridPalette.valueBoxHoverColor;
hosted.readOnlyColor = propertyGridPalette.valueBoxReadOnlyColor;
hosted.controlBorderColor = propertyGridPalette.valueBoxBorderColor;
hosted.controlFocusedBorderColor = propertyGridPalette.valueBoxEditingBorderColor;
hosted.buttonColor = propertyGridPalette.valueBoxReadOnlyColor;
hosted.buttonHoverColor = propertyGridPalette.valueBoxHoverColor;
hosted.buttonActiveColor = propertyGridPalette.valueBoxEditingColor;
hosted.buttonGlyphColor = propertyGridPalette.valueTextColor;
hosted.separatorColor = propertyGridPalette.valueBoxBorderColor;
hosted.labelColor = propertyGridPalette.labelTextColor;
hosted.valueColor = propertyGridPalette.valueTextColor;
hosted.emptyValueColor = propertyGridPalette.readOnlyValueTextColor;
hosted.typeColor = propertyGridPalette.readOnlyValueTextColor;
return hosted;
}
Widgets::UIEditorAssetFieldMetrics BuildUIEditorPropertyGridAssetFieldMetrics(
const Widgets::UIEditorPropertyGridMetrics& propertyGridMetrics,
const Widgets::UIEditorAssetFieldMetrics& fallback) {
Widgets::UIEditorAssetFieldMetrics hosted = fallback;
hosted.rowHeight = propertyGridMetrics.fieldRowHeight;
hosted.horizontalPadding = propertyGridMetrics.horizontalPadding;
hosted.labelControlGap = propertyGridMetrics.labelControlGap;
hosted.controlColumnStart = propertyGridMetrics.controlColumnStart;
hosted.controlTrailingInset = propertyGridMetrics.valueBoxInsetX;
hosted.controlInsetY = propertyGridMetrics.valueBoxInsetY;
hosted.labelTextInsetY = propertyGridMetrics.labelTextInsetY;
hosted.labelFontSize = propertyGridMetrics.labelFontSize;
hosted.valueTextInsetX = propertyGridMetrics.valueBoxInsetX;
hosted.valueTextInsetY = propertyGridMetrics.valueTextInsetY;
hosted.valueFontSize = propertyGridMetrics.valueFontSize;
hosted.previewGlyphFontSize = propertyGridMetrics.valueFontSize;
hosted.statusBadgeFontSize = propertyGridMetrics.tagFontSize;
hosted.actionGlyphFontSize = propertyGridMetrics.valueFontSize;
hosted.cornerRounding = propertyGridMetrics.cornerRounding;
hosted.valueBoxRounding = propertyGridMetrics.valueBoxRounding;
hosted.previewRounding = propertyGridMetrics.valueBoxRounding;
hosted.badgeRounding = propertyGridMetrics.valueBoxRounding;
hosted.borderThickness = propertyGridMetrics.borderThickness;
hosted.focusedBorderThickness = propertyGridMetrics.focusedBorderThickness;
return hosted;
}
Widgets::UIEditorAssetFieldPalette BuildUIEditorPropertyGridAssetFieldPalette(
const Widgets::UIEditorPropertyGridPalette& propertyGridPalette,
const Widgets::UIEditorAssetFieldPalette& fallback) {
Widgets::UIEditorAssetFieldPalette hosted = fallback;
hosted.surfaceColor = kTransparent;
hosted.borderColor = kTransparent;
hosted.focusedBorderColor = kTransparent;
hosted.rowHoverColor = kTransparent;
hosted.rowActiveColor = kTransparent;
hosted.valueBoxColor = propertyGridPalette.valueBoxColor;
hosted.valueBoxHoverColor = propertyGridPalette.valueBoxHoverColor;
hosted.valueBoxActiveColor = propertyGridPalette.valueBoxEditingColor;
hosted.readOnlyColor = propertyGridPalette.valueBoxReadOnlyColor;
hosted.controlBorderColor = propertyGridPalette.valueBoxBorderColor;
hosted.controlFocusedBorderColor = propertyGridPalette.valueBoxEditingBorderColor;
hosted.labelColor = propertyGridPalette.labelTextColor;
hosted.valueColor = propertyGridPalette.valueTextColor;
hosted.emptyValueColor = propertyGridPalette.readOnlyValueTextColor;
hosted.previewBaseColor = propertyGridPalette.valueBoxColor;
hosted.previewEmptyColor = propertyGridPalette.valueBoxReadOnlyColor;
hosted.previewBorderColor = propertyGridPalette.valueBoxBorderColor;
hosted.previewGlyphColor = propertyGridPalette.valueTextColor;
hosted.statusBadgeColor = propertyGridPalette.editTagColor;
hosted.statusBadgeBorderColor = propertyGridPalette.valueBoxBorderColor;
hosted.statusBadgeTextColor = propertyGridPalette.valueTextColor;
hosted.actionButtonHoverColor = propertyGridPalette.valueBoxHoverColor;
hosted.actionButtonActiveColor = propertyGridPalette.valueBoxEditingColor;
hosted.actionButtonColor = propertyGridPalette.valueBoxReadOnlyColor;
hosted.separatorColor = propertyGridPalette.valueBoxBorderColor;
hosted.pickerGlyphColor = propertyGridPalette.valueTextColor;
hosted.clearGlyphColor = propertyGridPalette.readOnlyValueTextColor;
return hosted;
}
} // namespace XCEngine::UI::Editor

View File

@@ -1,4 +1,4 @@
#include <XCEditor/Widgets/UIEditorNumberField.h>
#include <XCEditor/Fields/UIEditorNumberField.h>
#include <XCEditor/Widgets/UIEditorFieldRowLayout.h>
#include <XCEditor/Widgets/UIEditorTextLayout.h>
@@ -16,8 +16,63 @@ using ::XCEngine::UI::UIDrawList;
using ::XCEngine::UI::UIPoint;
using ::XCEngine::UI::UIRect;
float ClampNonNegative(float value) {
return (std::max)(0.0f, value);
UIEditorNumberFieldMetrics ResolveMetrics(const UIEditorNumberFieldMetrics& metrics) {
const auto& tokens = GetUIEditorInspectorFieldStyleTokens();
UIEditorNumberFieldMetrics resolved = metrics;
if (AreUIEditorFieldMetricsEqual(metrics.controlTrailingInset, 8.0f)) {
resolved.controlTrailingInset = tokens.controlTrailingInset;
}
if (AreUIEditorFieldMetricsEqual(metrics.valueBoxMinWidth, 96.0f)) {
resolved.valueBoxMinWidth = tokens.controlMinWidth;
}
return resolved;
}
UIEditorNumberFieldPalette ResolvePalette(const UIEditorNumberFieldPalette& palette) {
const auto& tokens = GetUIEditorInspectorFieldStyleTokens();
UIEditorNumberFieldPalette resolved = palette;
if (AreUIEditorFieldColorsEqual(palette.rowHoverColor, ::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.0f))) {
resolved.rowHoverColor = tokens.rowHoverColor;
}
if (AreUIEditorFieldColorsEqual(palette.rowActiveColor, ::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.0f))) {
resolved.rowActiveColor = tokens.rowActiveColor;
}
if (AreUIEditorFieldColorsEqual(palette.valueBoxColor, ::XCEngine::UI::UIColor(0.18f, 0.18f, 0.18f, 1.0f))) {
resolved.valueBoxColor = tokens.controlColor;
}
if (AreUIEditorFieldColorsEqual(palette.valueBoxHoverColor, ::XCEngine::UI::UIColor(0.21f, 0.21f, 0.21f, 1.0f))) {
resolved.valueBoxHoverColor = tokens.controlHoverColor;
}
if (AreUIEditorFieldColorsEqual(palette.valueBoxEditingColor, ::XCEngine::UI::UIColor(0.24f, 0.24f, 0.24f, 1.0f))) {
resolved.valueBoxEditingColor = tokens.controlEditingColor;
}
if (AreUIEditorFieldColorsEqual(palette.readOnlyColor, ::XCEngine::UI::UIColor(0.17f, 0.17f, 0.17f, 1.0f))) {
resolved.readOnlyColor = tokens.controlReadOnlyColor;
}
if (AreUIEditorFieldColorsEqual(palette.controlBorderColor, ::XCEngine::UI::UIColor(0.14f, 0.14f, 0.14f, 1.0f))) {
resolved.controlBorderColor = tokens.controlBorderColor;
}
if (AreUIEditorFieldColorsEqual(
palette.controlFocusedBorderColor,
::XCEngine::UI::UIColor(0.46f, 0.46f, 0.46f, 1.0f))) {
resolved.controlFocusedBorderColor = tokens.controlFocusedBorderColor;
}
if (AreUIEditorFieldColorsEqual(palette.labelColor, ::XCEngine::UI::UIColor(0.80f, 0.80f, 0.80f, 1.0f))) {
resolved.labelColor = tokens.labelColor;
}
if (AreUIEditorFieldColorsEqual(palette.valueColor, ::XCEngine::UI::UIColor(0.92f, 0.92f, 0.92f, 1.0f))) {
resolved.valueColor = tokens.valueColor;
}
if (AreUIEditorFieldColorsEqual(
palette.readOnlyValueColor,
::XCEngine::UI::UIColor(0.62f, 0.62f, 0.62f, 1.0f))) {
resolved.readOnlyValueColor = tokens.readOnlyValueColor;
}
return resolved;
}
double NormalizeRangeValue(const UIEditorNumberFieldSpec& spec, double value) {
@@ -76,14 +131,16 @@ void AppendLabelText(
std::string_view text,
const UIEditorNumberFieldPalette& palette,
const UIEditorNumberFieldMetrics& metrics) {
drawList.PushClipRect(ResolveUIEditorTextClipRect(layout.labelRect, metrics.labelFontSize));
const UIEditorNumberFieldPalette resolvedPalette = ResolvePalette(palette);
const UIEditorNumberFieldMetrics resolvedMetrics = ResolveMetrics(metrics);
drawList.PushClipRect(ResolveUIEditorTextClipRect(layout.labelRect, resolvedMetrics.labelFontSize));
drawList.AddText(
UIPoint(
layout.labelRect.x,
ResolveUIEditorTextTop(layout.labelRect, metrics.labelFontSize, metrics.labelTextInsetY)),
ResolveUIEditorTextTop(layout.labelRect, resolvedMetrics.labelFontSize, resolvedMetrics.labelTextInsetY)),
std::string(text),
palette.labelColor,
metrics.labelFontSize);
resolvedPalette.labelColor,
resolvedMetrics.labelFontSize);
drawList.PopClipRect();
}
@@ -94,14 +151,16 @@ void AppendValueText(
const UIEditorNumberFieldSpec& spec,
const UIEditorNumberFieldPalette& palette,
const UIEditorNumberFieldMetrics& metrics) {
drawList.PushClipRect(ResolveUIEditorTextClipRect(layout.valueRect, metrics.valueFontSize));
const UIEditorNumberFieldPalette resolvedPalette = ResolvePalette(palette);
const UIEditorNumberFieldMetrics resolvedMetrics = ResolveMetrics(metrics);
drawList.PushClipRect(ResolveUIEditorTextClipRect(layout.valueRect, resolvedMetrics.valueFontSize));
drawList.AddText(
UIPoint(
layout.valueRect.x + metrics.valueTextInsetX,
ResolveUIEditorTextTop(layout.valueRect, metrics.valueFontSize, metrics.valueTextInsetY)),
layout.valueRect.x + resolvedMetrics.valueTextInsetX,
ResolveUIEditorTextTop(layout.valueRect, resolvedMetrics.valueFontSize, resolvedMetrics.valueTextInsetY)),
std::string(text),
spec.readOnly ? palette.readOnlyValueColor : palette.valueColor,
metrics.valueFontSize);
spec.readOnly ? resolvedPalette.readOnlyValueColor : resolvedPalette.valueColor,
resolvedMetrics.valueFontSize);
drawList.PopClipRect();
}
@@ -158,16 +217,17 @@ UIEditorNumberFieldLayout BuildUIEditorNumberFieldLayout(
const UIRect& bounds,
const UIEditorNumberFieldSpec&,
const UIEditorNumberFieldMetrics& metrics) {
const UIEditorNumberFieldMetrics resolvedMetrics = ResolveMetrics(metrics);
const UIEditorFieldRowLayout hostLayout = BuildUIEditorFieldRowLayout(
bounds,
metrics.valueBoxMinWidth,
resolvedMetrics.valueBoxMinWidth,
UIEditorFieldRowLayoutMetrics {
metrics.rowHeight,
metrics.horizontalPadding,
metrics.labelControlGap,
metrics.controlColumnStart,
metrics.controlTrailingInset,
metrics.controlInsetY,
resolvedMetrics.rowHeight,
resolvedMetrics.horizontalPadding,
resolvedMetrics.labelControlGap,
resolvedMetrics.controlColumnStart,
resolvedMetrics.controlTrailingInset,
resolvedMetrics.controlInsetY,
});
UIEditorNumberFieldLayout layout = {};
@@ -197,28 +257,30 @@ void AppendUIEditorNumberFieldBackground(
const UIEditorNumberFieldState& state,
const UIEditorNumberFieldPalette& palette,
const UIEditorNumberFieldMetrics& metrics) {
const auto rowFillColor = ResolveRowFillColor(state, palette);
const UIEditorNumberFieldPalette resolvedPalette = ResolvePalette(palette);
const UIEditorNumberFieldMetrics resolvedMetrics = ResolveMetrics(metrics);
const auto rowFillColor = ResolveRowFillColor(state, resolvedPalette);
if (rowFillColor.a > 0.0f) {
drawList.AddFilledRect(layout.bounds, rowFillColor, metrics.cornerRounding);
drawList.AddFilledRect(layout.bounds, rowFillColor, resolvedMetrics.cornerRounding);
}
const auto rowBorderColor = state.focused ? palette.focusedBorderColor : palette.borderColor;
const auto rowBorderColor = state.focused ? resolvedPalette.focusedBorderColor : resolvedPalette.borderColor;
if (rowBorderColor.a > 0.0f) {
drawList.AddRectOutline(
layout.bounds,
rowBorderColor,
state.focused ? metrics.focusedBorderThickness : metrics.borderThickness,
metrics.cornerRounding);
state.focused ? resolvedMetrics.focusedBorderThickness : resolvedMetrics.borderThickness,
resolvedMetrics.cornerRounding);
}
drawList.AddFilledRect(
layout.valueRect,
ResolveValueFillColor(spec, state, palette),
metrics.valueBoxRounding);
ResolveValueFillColor(spec, state, resolvedPalette),
resolvedMetrics.valueBoxRounding);
drawList.AddRectOutline(
layout.valueRect,
state.editing ? palette.controlFocusedBorderColor : palette.controlBorderColor,
state.editing ? metrics.focusedBorderThickness : metrics.borderThickness,
metrics.valueBoxRounding);
state.editing ? resolvedPalette.controlFocusedBorderColor : resolvedPalette.controlBorderColor,
state.editing ? resolvedMetrics.focusedBorderThickness : resolvedMetrics.borderThickness,
resolvedMetrics.valueBoxRounding);
}
void AppendUIEditorNumberFieldForeground(
@@ -228,14 +290,16 @@ void AppendUIEditorNumberFieldForeground(
const UIEditorNumberFieldState& state,
const UIEditorNumberFieldPalette& palette,
const UIEditorNumberFieldMetrics& metrics) {
AppendLabelText(drawList, layout, spec.label, palette, metrics);
const UIEditorNumberFieldPalette resolvedPalette = ResolvePalette(palette);
const UIEditorNumberFieldMetrics resolvedMetrics = ResolveMetrics(metrics);
AppendLabelText(drawList, layout, spec.label, resolvedPalette, resolvedMetrics);
AppendValueText(
drawList,
layout,
state.editing ? std::string_view(state.displayText) : std::string_view(FormatUIEditorNumberFieldValue(spec)),
spec,
palette,
metrics);
resolvedPalette,
resolvedMetrics);
}
void AppendUIEditorNumberField(
@@ -245,19 +309,21 @@ void AppendUIEditorNumberField(
const UIEditorNumberFieldState& state,
const UIEditorNumberFieldPalette& palette,
const UIEditorNumberFieldMetrics& metrics) {
const UIEditorNumberFieldLayout layout = BuildUIEditorNumberFieldLayout(bounds, spec, metrics);
AppendUIEditorNumberFieldBackground(drawList, layout, spec, state, palette, metrics);
const UIEditorNumberFieldPalette resolvedPalette = ResolvePalette(palette);
const UIEditorNumberFieldMetrics resolvedMetrics = ResolveMetrics(metrics);
const UIEditorNumberFieldLayout layout = BuildUIEditorNumberFieldLayout(bounds, spec, resolvedMetrics);
AppendUIEditorNumberFieldBackground(drawList, layout, spec, state, resolvedPalette, resolvedMetrics);
if (state.editing) {
UIEditorNumberFieldSpec editingSpec = spec;
if (double parsedValue = 0.0; TryParseUIEditorNumberFieldValue(spec, state.displayText, parsedValue)) {
editingSpec.value = parsedValue;
}
AppendUIEditorNumberFieldForeground(drawList, layout, editingSpec, state, palette, metrics);
AppendUIEditorNumberFieldForeground(drawList, layout, editingSpec, state, resolvedPalette, resolvedMetrics);
return;
}
AppendUIEditorNumberFieldForeground(drawList, layout, spec, state, palette, metrics);
AppendUIEditorNumberFieldForeground(drawList, layout, spec, state, resolvedPalette, resolvedMetrics);
}
} // namespace XCEngine::UI::Editor::Widgets

View File

@@ -1,4 +1,4 @@
#include <XCEditor/Core/UIEditorNumberFieldInteraction.h>
#include <XCEditor/Fields/UIEditorNumberFieldInteraction.h>
#include <XCEngine/Input/InputTypes.h>

View File

@@ -0,0 +1,319 @@
#include <XCEditor/Fields/UIEditorObjectField.h>
#include <XCEditor/Widgets/UIEditorFieldRowLayout.h>
#include <XCEditor/Widgets/UIEditorTextLayout.h>
#include <algorithm>
namespace XCEngine::UI::Editor::Widgets {
namespace {
using ::XCEngine::UI::UIDrawList;
using ::XCEngine::UI::UIPoint;
using ::XCEngine::UI::UIRect;
float ClampNonNegative(float value) {
return (std::max)(0.0f, value);
}
bool ContainsPoint(const UIRect& rect, const UIPoint& point) {
return point.x >= rect.x &&
point.x <= rect.x + rect.width &&
point.y >= rect.y &&
point.y <= rect.y + rect.height;
}
bool HasClearButton(const UIEditorObjectFieldSpec& spec) {
return spec.showClearButton && spec.hasValue;
}
bool HasPickerButton(const UIEditorObjectFieldSpec& spec) {
return spec.showPickerButton;
}
bool HasTypeText(const UIEditorObjectFieldSpec& spec) {
return spec.hasValue && !spec.objectTypeName.empty();
}
::XCEngine::UI::UIColor ResolveRowFillColor(
const UIEditorObjectFieldState& state,
const UIEditorObjectFieldPalette& palette) {
if (state.activeTarget != UIEditorObjectFieldHitTargetKind::None) {
return palette.rowActiveColor;
}
if (state.hoveredTarget != UIEditorObjectFieldHitTargetKind::None) {
return palette.rowHoverColor;
}
return palette.surfaceColor;
}
::XCEngine::UI::UIColor ResolveControlFillColor(
const UIEditorObjectFieldSpec& spec,
const UIEditorObjectFieldState& state,
const UIEditorObjectFieldPalette& palette) {
if (spec.readOnly) {
return palette.readOnlyColor;
}
if (state.hoveredTarget == UIEditorObjectFieldHitTargetKind::ValueBox ||
state.activeTarget == UIEditorObjectFieldHitTargetKind::ValueBox) {
return palette.valueBoxHoverColor;
}
return palette.valueBoxColor;
}
::XCEngine::UI::UIColor ResolveButtonFillColor(
const UIEditorObjectFieldSpec& spec,
const UIEditorObjectFieldState& state,
UIEditorObjectFieldHitTargetKind target,
const UIEditorObjectFieldPalette& palette) {
if (spec.readOnly) {
return palette.readOnlyColor;
}
if (state.activeTarget == target) {
return palette.buttonActiveColor;
}
if (state.hoveredTarget == target) {
return palette.buttonHoverColor;
}
return palette.buttonColor;
}
void AppendButtonGlyph(
UIDrawList& drawList,
const UIRect& rect,
const char* glyph,
const UIEditorObjectFieldPalette& palette,
const UIEditorObjectFieldMetrics& metrics) {
if (rect.width <= 0.0f || rect.height <= 0.0f) {
return;
}
drawList.AddText(
UIPoint(
rect.x + ClampNonNegative((rect.width - metrics.buttonGlyphFontSize) * 0.5f),
ResolveUIEditorTextTop(rect, metrics.buttonGlyphFontSize, metrics.buttonGlyphInsetY)),
glyph,
palette.buttonGlyphColor,
metrics.buttonGlyphFontSize);
}
} // namespace
std::string ResolveUIEditorObjectFieldDisplayText(const UIEditorObjectFieldSpec& spec) {
if (!spec.hasValue) {
return spec.emptyText.empty() ? "(none)" : spec.emptyText;
}
return spec.objectName.empty() ? "(unnamed)" : spec.objectName;
}
UIEditorObjectFieldLayout BuildUIEditorObjectFieldLayout(
const UIRect& bounds,
const UIEditorObjectFieldSpec& spec,
const UIEditorObjectFieldMetrics& metrics) {
const UIEditorFieldRowLayout hostLayout = BuildUIEditorFieldRowLayout(
bounds,
metrics.valueBoxMinWidth,
UIEditorFieldRowLayoutMetrics {
metrics.rowHeight,
metrics.horizontalPadding,
metrics.labelControlGap,
metrics.controlColumnStart,
metrics.controlTrailingInset,
metrics.controlInsetY,
});
UIEditorObjectFieldLayout layout = {};
layout.bounds = hostLayout.bounds;
layout.labelRect = hostLayout.labelRect;
layout.controlRect = hostLayout.controlRect;
const float pickerWidth =
HasPickerButton(spec) ? (std::min)(metrics.buttonWidth, layout.controlRect.width) : 0.0f;
const float clearWidth =
HasClearButton(spec)
? (std::min)(metrics.buttonWidth, ClampNonNegative(layout.controlRect.width - pickerWidth))
: 0.0f;
const float buttonWidth = pickerWidth + clearWidth;
layout.valueRect = UIRect(
layout.controlRect.x,
layout.controlRect.y,
ClampNonNegative(layout.controlRect.width - buttonWidth),
layout.controlRect.height);
float nextButtonX = layout.valueRect.x + layout.valueRect.width;
if (clearWidth > 0.0f) {
layout.clearButtonRect = UIRect(
nextButtonX,
layout.controlRect.y,
clearWidth,
layout.controlRect.height);
nextButtonX += clearWidth;
}
if (pickerWidth > 0.0f) {
layout.pickerButtonRect = UIRect(
nextButtonX,
layout.controlRect.y,
pickerWidth,
layout.controlRect.height);
}
if (HasTypeText(spec) && layout.valueRect.width > metrics.typeMinWidth + metrics.valueTextInsetX) {
const float reservedWidth = ClampNonNegative(
layout.valueRect.width * 0.35f + metrics.typeTextInsetX);
const float typeWidth = (std::min)(
metrics.typeMaxWidth,
(std::max)(metrics.typeMinWidth, reservedWidth));
if (layout.valueRect.width > typeWidth + metrics.valueTextInsetX + metrics.valueTypeGap) {
layout.typeRect = UIRect(
layout.valueRect.x + layout.valueRect.width - typeWidth,
layout.valueRect.y,
typeWidth,
layout.valueRect.height);
}
}
return layout;
}
UIEditorObjectFieldHitTarget HitTestUIEditorObjectField(
const UIEditorObjectFieldLayout& layout,
const UIPoint& point) {
if (!ContainsPoint(layout.bounds, point)) {
return {};
}
if (layout.pickerButtonRect.width > 0.0f && ContainsPoint(layout.pickerButtonRect, point)) {
return { UIEditorObjectFieldHitTargetKind::PickerButton };
}
if (layout.clearButtonRect.width > 0.0f && ContainsPoint(layout.clearButtonRect, point)) {
return { UIEditorObjectFieldHitTargetKind::ClearButton };
}
if (ContainsPoint(layout.valueRect, point)) {
return { UIEditorObjectFieldHitTargetKind::ValueBox };
}
return { UIEditorObjectFieldHitTargetKind::Row };
}
void AppendUIEditorObjectFieldBackground(
UIDrawList& drawList,
const UIEditorObjectFieldLayout& layout,
const UIEditorObjectFieldSpec& spec,
const UIEditorObjectFieldState& state,
const UIEditorObjectFieldPalette& palette,
const UIEditorObjectFieldMetrics& metrics) {
const auto rowFillColor = ResolveRowFillColor(state, palette);
if (rowFillColor.a > 0.0f) {
drawList.AddFilledRect(layout.bounds, rowFillColor, metrics.cornerRounding);
}
const auto rowBorderColor = state.focused ? palette.focusedBorderColor : palette.borderColor;
if (rowBorderColor.a > 0.0f) {
drawList.AddRectOutline(
layout.bounds,
rowBorderColor,
state.focused ? metrics.focusedBorderThickness : metrics.borderThickness,
metrics.cornerRounding);
}
drawList.AddFilledRect(
layout.controlRect,
ResolveControlFillColor(spec, state, palette),
metrics.valueBoxRounding);
drawList.AddRectOutline(
layout.controlRect,
state.focused ? palette.controlFocusedBorderColor : palette.controlBorderColor,
state.focused ? metrics.focusedBorderThickness : metrics.borderThickness,
metrics.valueBoxRounding);
if (layout.clearButtonRect.width > 0.0f) {
drawList.AddFilledRect(
layout.clearButtonRect,
ResolveButtonFillColor(spec, state, UIEditorObjectFieldHitTargetKind::ClearButton, palette));
drawList.AddLine(
UIPoint(layout.clearButtonRect.x, layout.clearButtonRect.y + 1.0f),
UIPoint(layout.clearButtonRect.x, layout.clearButtonRect.y + layout.clearButtonRect.height - 1.0f),
palette.separatorColor,
1.0f);
}
if (layout.pickerButtonRect.width > 0.0f) {
drawList.AddFilledRect(
layout.pickerButtonRect,
ResolveButtonFillColor(spec, state, UIEditorObjectFieldHitTargetKind::PickerButton, palette));
drawList.AddLine(
UIPoint(layout.pickerButtonRect.x, layout.pickerButtonRect.y + 1.0f),
UIPoint(layout.pickerButtonRect.x, layout.pickerButtonRect.y + layout.pickerButtonRect.height - 1.0f),
palette.separatorColor,
1.0f);
}
}
void AppendUIEditorObjectFieldForeground(
UIDrawList& drawList,
const UIEditorObjectFieldLayout& layout,
const UIEditorObjectFieldSpec& spec,
const UIEditorObjectFieldPalette& palette,
const UIEditorObjectFieldMetrics& metrics) {
drawList.PushClipRect(ResolveUIEditorTextClipRect(layout.labelRect, metrics.labelFontSize));
drawList.AddText(
UIPoint(
layout.labelRect.x,
ResolveUIEditorTextTop(layout.labelRect, metrics.labelFontSize, metrics.labelTextInsetY)),
spec.label,
palette.labelColor,
metrics.labelFontSize);
drawList.PopClipRect();
float primaryClipWidth = layout.valueRect.width;
if (layout.typeRect.width > 0.0f) {
primaryClipWidth =
ClampNonNegative(layout.typeRect.x - layout.valueRect.x - metrics.valueTypeGap);
}
const UIRect primaryClipRect(
layout.valueRect.x + metrics.valueTextInsetX,
layout.valueRect.y,
ClampNonNegative(primaryClipWidth - metrics.valueTextInsetX),
layout.valueRect.height);
drawList.PushClipRect(ResolveUIEditorTextClipRect(primaryClipRect, metrics.valueFontSize));
drawList.AddText(
UIPoint(
layout.valueRect.x + metrics.valueTextInsetX,
ResolveUIEditorTextTop(layout.valueRect, metrics.valueFontSize, metrics.valueTextInsetY)),
ResolveUIEditorObjectFieldDisplayText(spec),
spec.hasValue ? palette.valueColor : palette.emptyValueColor,
metrics.valueFontSize);
drawList.PopClipRect();
if (layout.typeRect.width > 0.0f) {
const UIRect typeClipRect(
layout.typeRect.x + metrics.typeTextInsetX,
layout.typeRect.y,
ClampNonNegative(layout.typeRect.width - metrics.typeTextInsetX),
layout.typeRect.height);
drawList.PushClipRect(ResolveUIEditorTextClipRect(typeClipRect, metrics.typeFontSize));
drawList.AddText(
UIPoint(
layout.typeRect.x + metrics.typeTextInsetX,
ResolveUIEditorTextTop(layout.typeRect, metrics.typeFontSize, metrics.typeTextInsetY)),
spec.objectTypeName,
palette.typeColor,
metrics.typeFontSize);
drawList.PopClipRect();
}
AppendButtonGlyph(drawList, layout.clearButtonRect, "X", palette, metrics);
AppendButtonGlyph(drawList, layout.pickerButtonRect, "o", palette, metrics);
}
void AppendUIEditorObjectField(
UIDrawList& drawList,
const UIRect& bounds,
const UIEditorObjectFieldSpec& spec,
const UIEditorObjectFieldState& state,
const UIEditorObjectFieldPalette& palette,
const UIEditorObjectFieldMetrics& metrics) {
const UIEditorObjectFieldLayout layout = BuildUIEditorObjectFieldLayout(bounds, spec, metrics);
AppendUIEditorObjectFieldBackground(drawList, layout, spec, state, palette, metrics);
AppendUIEditorObjectFieldForeground(drawList, layout, spec, palette, metrics);
}
} // namespace XCEngine::UI::Editor::Widgets

View File

@@ -0,0 +1,174 @@
#include <XCEditor/Fields/UIEditorObjectFieldInteraction.h>
#include <XCEngine/Input/InputTypes.h>
namespace XCEngine::UI::Editor {
namespace {
using ::XCEngine::Input::KeyCode;
using ::XCEngine::UI::UIInputEvent;
using ::XCEngine::UI::UIInputEventType;
using ::XCEngine::UI::UIPointerButton;
using Widgets::BuildUIEditorObjectFieldLayout;
using Widgets::HitTestUIEditorObjectField;
using Widgets::UIEditorObjectFieldHitTargetKind;
bool ShouldUsePointerPosition(const UIInputEvent& event) {
switch (event.type) {
case UIInputEventType::PointerMove:
case UIInputEventType::PointerEnter:
case UIInputEventType::PointerButtonDown:
case UIInputEventType::PointerButtonUp:
return true;
default:
return false;
}
}
bool CanActivate(const Widgets::UIEditorObjectFieldSpec& spec) {
return !spec.readOnly;
}
bool CanClear(const Widgets::UIEditorObjectFieldSpec& spec) {
return !spec.readOnly && spec.hasValue && spec.showClearButton;
}
} // namespace
UIEditorObjectFieldInteractionFrame UpdateUIEditorObjectFieldInteraction(
UIEditorObjectFieldInteractionState& state,
const Widgets::UIEditorObjectFieldSpec& spec,
const ::XCEngine::UI::UIRect& bounds,
const std::vector<UIInputEvent>& inputEvents,
const Widgets::UIEditorObjectFieldMetrics& metrics) {
Widgets::UIEditorObjectFieldLayout layout =
BuildUIEditorObjectFieldLayout(bounds, spec, metrics);
if (state.hasPointerPosition) {
state.fieldState.hoveredTarget = HitTestUIEditorObjectField(layout, state.pointerPosition).kind;
} else {
state.fieldState.hoveredTarget = UIEditorObjectFieldHitTargetKind::None;
}
UIEditorObjectFieldInteractionResult interactionResult = {};
for (const UIInputEvent& event : inputEvents) {
if (ShouldUsePointerPosition(event)) {
state.pointerPosition = event.position;
state.hasPointerPosition = true;
} else if (event.type == UIInputEventType::PointerLeave) {
state.hasPointerPosition = false;
}
UIEditorObjectFieldInteractionResult eventResult = {};
switch (event.type) {
case UIInputEventType::FocusGained:
eventResult.focusChanged = !state.fieldState.focused;
state.fieldState.focused = true;
break;
case UIInputEventType::FocusLost:
eventResult.focusChanged = state.fieldState.focused;
state.fieldState.focused = false;
state.fieldState.activeTarget = UIEditorObjectFieldHitTargetKind::None;
state.hasPointerPosition = false;
state.fieldState.hoveredTarget = UIEditorObjectFieldHitTargetKind::None;
break;
case UIInputEventType::PointerMove:
case UIInputEventType::PointerEnter:
case UIInputEventType::PointerLeave:
break;
case UIInputEventType::PointerButtonDown:
eventResult.hitTarget = state.hasPointerPosition
? HitTestUIEditorObjectField(layout, state.pointerPosition)
: Widgets::UIEditorObjectFieldHitTarget {};
if (event.pointerButton == UIPointerButton::Left &&
eventResult.hitTarget.kind != UIEditorObjectFieldHitTargetKind::None) {
eventResult.focusChanged = !state.fieldState.focused;
state.fieldState.focused = true;
state.fieldState.activeTarget = eventResult.hitTarget.kind;
eventResult.consumed = true;
} else if (event.pointerButton == UIPointerButton::Left) {
eventResult.focusChanged = state.fieldState.focused;
state.fieldState.focused = false;
state.fieldState.activeTarget = UIEditorObjectFieldHitTargetKind::None;
}
break;
case UIInputEventType::PointerButtonUp:
eventResult.hitTarget = state.hasPointerPosition
? HitTestUIEditorObjectField(layout, state.pointerPosition)
: Widgets::UIEditorObjectFieldHitTarget {};
if (event.pointerButton == UIPointerButton::Left) {
const UIEditorObjectFieldHitTargetKind activeTarget = state.fieldState.activeTarget;
state.fieldState.activeTarget = UIEditorObjectFieldHitTargetKind::None;
if (eventResult.hitTarget.kind == activeTarget) {
if ((activeTarget == UIEditorObjectFieldHitTargetKind::ValueBox ||
activeTarget == UIEditorObjectFieldHitTargetKind::PickerButton) &&
CanActivate(spec)) {
eventResult.activateRequested = true;
eventResult.consumed = true;
} else if (activeTarget == UIEditorObjectFieldHitTargetKind::ClearButton &&
CanClear(spec)) {
eventResult.clearRequested = true;
eventResult.consumed = true;
} else if (activeTarget == UIEditorObjectFieldHitTargetKind::Row) {
eventResult.consumed = true;
}
}
}
break;
case UIInputEventType::KeyDown:
if (!state.fieldState.focused) {
break;
}
if ((static_cast<KeyCode>(event.keyCode) == KeyCode::Enter ||
static_cast<KeyCode>(event.keyCode) == KeyCode::Space) &&
CanActivate(spec)) {
eventResult.activateRequested = true;
eventResult.consumed = true;
} else if ((static_cast<KeyCode>(event.keyCode) == KeyCode::Delete ||
static_cast<KeyCode>(event.keyCode) == KeyCode::Backspace) &&
CanClear(spec)) {
eventResult.clearRequested = true;
eventResult.consumed = true;
}
break;
default:
break;
}
layout = BuildUIEditorObjectFieldLayout(bounds, spec, metrics);
if (state.hasPointerPosition) {
state.fieldState.hoveredTarget = HitTestUIEditorObjectField(layout, state.pointerPosition).kind;
} else {
state.fieldState.hoveredTarget = UIEditorObjectFieldHitTargetKind::None;
}
if (eventResult.consumed ||
eventResult.focusChanged ||
eventResult.activateRequested ||
eventResult.clearRequested ||
eventResult.hitTarget.kind != UIEditorObjectFieldHitTargetKind::None) {
interactionResult = eventResult;
}
}
layout = BuildUIEditorObjectFieldLayout(bounds, spec, metrics);
if (state.hasPointerPosition) {
state.fieldState.hoveredTarget = HitTestUIEditorObjectField(layout, state.pointerPosition).kind;
if (interactionResult.hitTarget.kind == UIEditorObjectFieldHitTargetKind::None) {
interactionResult.hitTarget = HitTestUIEditorObjectField(layout, state.pointerPosition);
}
} else {
state.fieldState.hoveredTarget = UIEditorObjectFieldHitTargetKind::None;
}
return { layout, interactionResult };
}
} // namespace XCEngine::UI::Editor

View File

@@ -1,15 +1,16 @@
#include <XCEditor/Widgets/UIEditorPropertyGrid.h>
#include <XCEditor/Fields/UIEditorPropertyGrid.h>
#include <XCEditor/Core/UIEditorTheme.h>
#include <XCEditor/Widgets/UIEditorBoolField.h>
#include <XCEditor/Widgets/UIEditorEnumField.h>
#include <XCEditor/Widgets/UIEditorMenuPopup.h>
#include <XCEditor/Widgets/UIEditorNumberField.h>
#include <XCEditor/Widgets/UIEditorTextField.h>
#include <XCEditor/Fields/UIEditorFieldStyle.h>
#include <XCEditor/Fields/UIEditorBoolField.h>
#include <XCEditor/Fields/UIEditorColorField.h>
#include <XCEditor/Fields/UIEditorEnumField.h>
#include <XCEditor/Shell/UIEditorMenuPopup.h>
#include <XCEditor/Fields/UIEditorNumberField.h>
#include <XCEditor/Fields/UIEditorTextField.h>
#include <XCEditor/Widgets/UIEditorTextLayout.h>
#include <XCEditor/Widgets/UIEditorVector2Field.h>
#include <XCEditor/Widgets/UIEditorVector3Field.h>
#include <XCEditor/Widgets/UIEditorVector4Field.h>
#include <XCEditor/Fields/UIEditorVector2Field.h>
#include <XCEditor/Fields/UIEditorVector3Field.h>
#include <XCEditor/Fields/UIEditorVector4Field.h>
#include <XCEngine/UI/Widgets/UIPopupOverlayModel.h>
@@ -88,6 +89,16 @@ UIEditorEnumFieldSpec BuildEnumFieldSpec(const UIEditorPropertyGridField& field)
return spec;
}
UIEditorColorFieldSpec BuildColorFieldSpec(const UIEditorPropertyGridField& field) {
UIEditorColorFieldSpec spec = {};
spec.fieldId = field.fieldId;
spec.label = field.label;
spec.value = field.colorValue.value;
spec.showAlpha = field.colorValue.showAlpha;
spec.readOnly = field.readOnly;
return spec;
}
UIEditorTextFieldSpec BuildTextFieldSpec(const UIEditorPropertyGridField& field) {
UIEditorTextFieldSpec spec = {};
spec.fieldId = field.fieldId;
@@ -153,7 +164,7 @@ UIEditorPropertyGridFieldRects ResolveFieldRects(
const UIEditorBoolFieldLayout fieldLayout = BuildUIEditorBoolFieldLayout(
rowRect,
BuildBoolFieldSpec(field),
::XCEngine::UI::Editor::BuildUIEditorHostedBoolFieldMetrics(metrics));
::XCEngine::UI::Editor::BuildUIEditorPropertyGridBoolFieldMetrics(metrics));
return { fieldLayout.labelRect, fieldLayout.controlRect };
}
@@ -161,7 +172,7 @@ UIEditorPropertyGridFieldRects ResolveFieldRects(
const UIEditorNumberFieldLayout fieldLayout = BuildUIEditorNumberFieldLayout(
rowRect,
BuildNumberFieldSpec(field),
::XCEngine::UI::Editor::BuildUIEditorHostedNumberFieldMetrics(metrics));
::XCEngine::UI::Editor::BuildUIEditorPropertyGridNumberFieldMetrics(metrics));
return { fieldLayout.labelRect, fieldLayout.valueRect };
}
@@ -169,15 +180,23 @@ UIEditorPropertyGridFieldRects ResolveFieldRects(
const UIEditorEnumFieldLayout fieldLayout = BuildUIEditorEnumFieldLayout(
rowRect,
BuildEnumFieldSpec(field),
::XCEngine::UI::Editor::BuildUIEditorHostedEnumFieldMetrics(metrics));
::XCEngine::UI::Editor::BuildUIEditorPropertyGridEnumFieldMetrics(metrics));
return { fieldLayout.labelRect, fieldLayout.valueRect };
}
case UIEditorPropertyGridFieldKind::Color: {
const UIEditorColorFieldLayout fieldLayout = BuildUIEditorColorFieldLayout(
rowRect,
BuildColorFieldSpec(field),
::XCEngine::UI::Editor::BuildUIEditorPropertyGridColorFieldMetrics(metrics));
return { fieldLayout.labelRect, fieldLayout.controlRect };
}
case UIEditorPropertyGridFieldKind::Vector2: {
const UIEditorVector2FieldLayout fieldLayout = BuildUIEditorVector2FieldLayout(
rowRect,
BuildVector2FieldSpec(field),
::XCEngine::UI::Editor::BuildUIEditorHostedVector2FieldMetrics(metrics));
::XCEngine::UI::Editor::BuildUIEditorPropertyGridVector2FieldMetrics(metrics));
return { fieldLayout.labelRect, fieldLayout.controlRect };
}
@@ -185,7 +204,7 @@ UIEditorPropertyGridFieldRects ResolveFieldRects(
const UIEditorVector3FieldLayout fieldLayout = BuildUIEditorVector3FieldLayout(
rowRect,
BuildVector3FieldSpec(field),
::XCEngine::UI::Editor::BuildUIEditorHostedVector3FieldMetrics(metrics));
::XCEngine::UI::Editor::BuildUIEditorPropertyGridVector3FieldMetrics(metrics));
return { fieldLayout.labelRect, fieldLayout.controlRect };
}
@@ -193,7 +212,7 @@ UIEditorPropertyGridFieldRects ResolveFieldRects(
const UIEditorVector4FieldLayout fieldLayout = BuildUIEditorVector4FieldLayout(
rowRect,
BuildVector4FieldSpec(field),
::XCEngine::UI::Editor::BuildUIEditorHostedVector4FieldMetrics(metrics));
::XCEngine::UI::Editor::BuildUIEditorPropertyGridVector4FieldMetrics(metrics));
return { fieldLayout.labelRect, fieldLayout.controlRect };
}
@@ -202,7 +221,7 @@ UIEditorPropertyGridFieldRects ResolveFieldRects(
const UIEditorTextFieldLayout fieldLayout = BuildUIEditorTextFieldLayout(
rowRect,
BuildTextFieldSpec(field),
::XCEngine::UI::Editor::BuildUIEditorHostedTextFieldMetrics(metrics));
::XCEngine::UI::Editor::BuildUIEditorPropertyGridTextFieldMetrics(metrics));
return { fieldLayout.labelRect, fieldLayout.valueRect };
}
}
@@ -256,6 +275,18 @@ UIEditorEnumFieldHitTargetKind ResolveEnumHoveredTarget(
: UIEditorEnumFieldHitTargetKind::Row;
}
UIEditorColorFieldHitTargetKind ResolveColorHoveredTarget(
const UIEditorPropertyGridState& state,
const UIEditorPropertyGridField& field) {
if (state.hoveredFieldId != field.fieldId) {
return UIEditorColorFieldHitTargetKind::None;
}
return state.hoveredHitTarget == UIEditorPropertyGridHitTargetKind::ValueBox
? UIEditorColorFieldHitTargetKind::Swatch
: UIEditorColorFieldHitTargetKind::Row;
}
UIEditorTextFieldHitTargetKind ResolveTextHoveredTarget(
const UIEditorPropertyGridState& state,
const UIEditorPropertyGridField& field) {
@@ -323,6 +354,18 @@ std::vector<UIEditorMenuPopupItem> BuildEnumPopupItems(
return items;
}
const UIEditorPropertyGridColorFieldVisualState* FindColorFieldVisualState(
const UIEditorPropertyGridState& state,
std::string_view fieldId) {
for (const UIEditorPropertyGridColorFieldVisualState& entry : state.colorFieldStates) {
if (entry.fieldId == fieldId) {
return &entry;
}
}
return nullptr;
}
UIRect ResolvePopupViewportRect(const UIRect& bounds) {
return UIRect(bounds.x - 4096.0f, bounds.y - 4096.0f, 8192.0f, 8192.0f);
}
@@ -385,6 +428,10 @@ std::string FormatVector2ValueText(const UIEditorPropertyGridField& field) {
FormatUIEditorVector2FieldComponentValue(spec, 1u);
}
std::string FormatColorValueText(const UIEditorPropertyGridField& field) {
return FormatUIEditorColorFieldHexText(BuildColorFieldSpec(field));
}
std::string FormatVector3ValueText(const UIEditorPropertyGridField& field) {
const UIEditorVector3FieldSpec spec = BuildVector3FieldSpec(field);
return FormatUIEditorVector3FieldComponentValue(spec, 0u) + ", " +
@@ -450,6 +497,9 @@ std::string ResolveUIEditorPropertyGridFieldValueText(
case UIEditorPropertyGridFieldKind::Enum:
return ResolveUIEditorEnumFieldValueText(BuildEnumFieldSpec(field));
case UIEditorPropertyGridFieldKind::Color:
return FormatColorValueText(field);
case UIEditorPropertyGridFieldKind::Vector2:
return FormatVector2ValueText(field);
@@ -689,33 +739,38 @@ void AppendUIEditorPropertyGridForeground(
}
const UIEditorBoolFieldMetrics boolMetrics =
::XCEngine::UI::Editor::BuildUIEditorHostedBoolFieldMetrics(metrics);
::XCEngine::UI::Editor::BuildUIEditorPropertyGridBoolFieldMetrics(metrics);
const UIEditorBoolFieldPalette boolPalette =
::XCEngine::UI::Editor::BuildUIEditorHostedBoolFieldPalette(palette);
::XCEngine::UI::Editor::BuildUIEditorPropertyGridBoolFieldPalette(palette);
const UIEditorNumberFieldMetrics numberMetrics =
::XCEngine::UI::Editor::BuildUIEditorHostedNumberFieldMetrics(metrics);
::XCEngine::UI::Editor::BuildUIEditorPropertyGridNumberFieldMetrics(metrics);
const UIEditorNumberFieldPalette numberPalette =
::XCEngine::UI::Editor::BuildUIEditorHostedNumberFieldPalette(palette);
::XCEngine::UI::Editor::BuildUIEditorPropertyGridNumberFieldPalette(palette);
const UIEditorTextFieldMetrics textMetrics =
::XCEngine::UI::Editor::BuildUIEditorHostedTextFieldMetrics(metrics);
::XCEngine::UI::Editor::BuildUIEditorPropertyGridTextFieldMetrics(metrics);
const UIEditorTextFieldPalette textPalette =
::XCEngine::UI::Editor::BuildUIEditorHostedTextFieldPalette(palette);
::XCEngine::UI::Editor::BuildUIEditorPropertyGridTextFieldPalette(palette);
const UIEditorVector2FieldMetrics vector2Metrics =
::XCEngine::UI::Editor::BuildUIEditorHostedVector2FieldMetrics(metrics);
::XCEngine::UI::Editor::BuildUIEditorPropertyGridVector2FieldMetrics(metrics);
const UIEditorVector2FieldPalette vector2Palette =
::XCEngine::UI::Editor::BuildUIEditorHostedVector2FieldPalette(palette);
::XCEngine::UI::Editor::BuildUIEditorPropertyGridVector2FieldPalette(palette);
const UIEditorVector3FieldMetrics vector3Metrics =
::XCEngine::UI::Editor::BuildUIEditorHostedVector3FieldMetrics(metrics);
::XCEngine::UI::Editor::BuildUIEditorPropertyGridVector3FieldMetrics(metrics);
const UIEditorVector3FieldPalette vector3Palette =
::XCEngine::UI::Editor::BuildUIEditorHostedVector3FieldPalette(palette);
::XCEngine::UI::Editor::BuildUIEditorPropertyGridVector3FieldPalette(palette);
const UIEditorVector4FieldMetrics vector4Metrics =
::XCEngine::UI::Editor::BuildUIEditorHostedVector4FieldMetrics(metrics);
::XCEngine::UI::Editor::BuildUIEditorPropertyGridVector4FieldMetrics(metrics);
const UIEditorVector4FieldPalette vector4Palette =
::XCEngine::UI::Editor::BuildUIEditorHostedVector4FieldPalette(palette);
::XCEngine::UI::Editor::BuildUIEditorPropertyGridVector4FieldPalette(palette);
const UIEditorEnumFieldMetrics enumMetrics =
::XCEngine::UI::Editor::BuildUIEditorHostedEnumFieldMetrics(metrics);
::XCEngine::UI::Editor::BuildUIEditorPropertyGridEnumFieldMetrics(metrics);
const UIEditorEnumFieldPalette enumPalette =
::XCEngine::UI::Editor::BuildUIEditorHostedEnumFieldPalette(palette);
::XCEngine::UI::Editor::BuildUIEditorPropertyGridEnumFieldPalette(palette);
const UIEditorColorFieldMetrics colorMetrics =
::XCEngine::UI::Editor::BuildUIEditorPropertyGridColorFieldMetrics(metrics);
const UIEditorColorFieldPalette colorPalette =
::XCEngine::UI::Editor::BuildUIEditorPropertyGridColorFieldPalette(palette);
const UIRect popupViewportRect = ResolvePopupViewportRect(layout.bounds);
for (std::size_t visibleFieldIndex = 0u;
visibleFieldIndex < layout.fieldRowRects.size();
@@ -781,6 +836,28 @@ void AppendUIEditorPropertyGridForeground(
break;
}
case UIEditorPropertyGridFieldKind::Color: {
UIEditorColorFieldState fieldState = {};
if (const UIEditorPropertyGridColorFieldVisualState* visualState =
FindColorFieldVisualState(state, field.fieldId);
visualState != nullptr) {
fieldState = visualState->state;
} else {
fieldState.hoveredTarget = ResolveColorHoveredTarget(state, field);
fieldState.focused = state.focused;
}
AppendUIEditorColorField(
drawList,
layout.fieldRowRects[visibleFieldIndex],
BuildColorFieldSpec(field),
fieldState,
colorPalette,
colorMetrics,
popupViewportRect);
break;
}
case UIEditorPropertyGridFieldKind::Vector2: {
UIEditorVector2FieldState fieldState = {};
fieldState.hoveredTarget = ResolveVector2HoveredTarget(state, field);

View File

@@ -1,7 +1,9 @@
#include <XCEditor/Core/UIEditorPropertyGridInteraction.h>
#include <XCEditor/Fields/UIEditorPropertyGridInteraction.h>
#include <XCEditor/Widgets/UIEditorMenuPopup.h>
#include <XCEditor/Widgets/UIEditorNumberField.h>
#include <XCEditor/Fields/UIEditorFieldStyle.h>
#include <XCEditor/Fields/UIEditorColorFieldInteraction.h>
#include <XCEditor/Shell/UIEditorMenuPopup.h>
#include <XCEditor/Fields/UIEditorNumberField.h>
#include <XCEngine/Input/InputTypes.h>
#include <XCEngine/UI/Widgets/UIPopupOverlayModel.h>
@@ -20,6 +22,10 @@ using ::XCEngine::UI::UIInputEvent;
using ::XCEngine::UI::UIInputEventType;
using ::XCEngine::UI::UIPointerButton;
using ::XCEngine::UI::UISize;
using ::XCEngine::UI::Editor::BuildUIEditorPropertyGridColorFieldMetrics;
using ::XCEngine::UI::Editor::UIEditorColorFieldInteractionFrame;
using ::XCEngine::UI::Editor::UIEditorColorFieldInteractionState;
using ::XCEngine::UI::Editor::UpdateUIEditorColorFieldInteraction;
using ::XCEngine::UI::Widgets::ResolvePopupPlacementRect;
using ::XCEngine::UI::Widgets::UIPopupPlacement;
using Widgets::BuildUIEditorPropertyGridLayout;
@@ -35,11 +41,14 @@ using Widgets::UIEditorPropertyGridHitTargetKind;
using Widgets::UIEditorPropertyGridInvalidIndex;
using Widgets::UIEditorPropertyGridLayout;
using Widgets::UIEditorPropertyGridSection;
using Widgets::UIEditorPropertyGridColorFieldVisualState;
using Widgets::UIEditorMenuPopupHitTarget;
using Widgets::UIEditorMenuPopupHitTargetKind;
using Widgets::UIEditorMenuPopupInvalidIndex;
using Widgets::UIEditorMenuPopupItem;
using Widgets::UIEditorMenuPopupLayout;
using Widgets::UIEditorColorFieldHitTargetKind;
using Widgets::UIEditorColorFieldSpec;
using ::XCEngine::UI::Editor::UIEditorMenuItemKind;
bool ShouldUsePointerPosition(const UIInputEvent& event) {
@@ -110,6 +119,16 @@ bool IsNumberEditCharacter(const UIEditorPropertyGridField& field, std::uint32_t
return !field.numberValue.integerMode && character == static_cast<std::uint32_t>('.');
}
UIEditorColorFieldSpec BuildColorFieldSpec(const UIEditorPropertyGridField& field) {
UIEditorColorFieldSpec spec = {};
spec.fieldId = field.fieldId;
spec.label = field.label;
spec.value = field.colorValue.value;
spec.showAlpha = field.colorValue.showAlpha;
spec.readOnly = field.readOnly;
return spec;
}
UIEditorPropertyGridField* FindMutableField(
std::vector<UIEditorPropertyGridSection>& sections,
std::string_view fieldId) {
@@ -144,6 +163,148 @@ void SetChangedValueResult(
result.changedValue = ResolveUIEditorPropertyGridFieldValueText(field);
}
void MergeInteractionResult(
UIEditorPropertyGridInteractionResult& accumulated,
const UIEditorPropertyGridInteractionResult& current) {
accumulated.consumed = accumulated.consumed || current.consumed;
accumulated.sectionToggled = accumulated.sectionToggled || current.sectionToggled;
accumulated.selectionChanged = accumulated.selectionChanged || current.selectionChanged;
accumulated.keyboardNavigated = accumulated.keyboardNavigated || current.keyboardNavigated;
accumulated.editStarted = accumulated.editStarted || current.editStarted;
accumulated.editValueChanged = accumulated.editValueChanged || current.editValueChanged;
accumulated.editCommitted = accumulated.editCommitted || current.editCommitted;
accumulated.editCommitRejected = accumulated.editCommitRejected || current.editCommitRejected;
accumulated.editCanceled = accumulated.editCanceled || current.editCanceled;
accumulated.popupOpened = accumulated.popupOpened || current.popupOpened;
accumulated.popupClosed = accumulated.popupClosed || current.popupClosed;
accumulated.fieldValueChanged = accumulated.fieldValueChanged || current.fieldValueChanged;
accumulated.secondaryClicked = accumulated.secondaryClicked || current.secondaryClicked;
if (current.hitTarget.kind != UIEditorPropertyGridHitTargetKind::None) {
accumulated.hitTarget = current.hitTarget;
}
if (!current.toggledSectionId.empty()) {
accumulated.toggledSectionId = current.toggledSectionId;
}
if (!current.selectedFieldId.empty()) {
accumulated.selectedFieldId = current.selectedFieldId;
}
if (!current.activeFieldId.empty()) {
accumulated.activeFieldId = current.activeFieldId;
}
if (!current.committedFieldId.empty()) {
accumulated.committedFieldId = current.committedFieldId;
}
if (!current.committedValue.empty()) {
accumulated.committedValue = current.committedValue;
}
if (!current.changedFieldId.empty()) {
accumulated.changedFieldId = current.changedFieldId;
}
if (!current.changedValue.empty()) {
accumulated.changedValue = current.changedValue;
}
}
UIEditorPropertyGridColorFieldVisualState* FindMutableColorFieldVisualState(
UIEditorPropertyGridInteractionState& state,
std::string_view fieldId) {
for (UIEditorPropertyGridColorFieldVisualState& entry : state.propertyGridState.colorFieldStates) {
if (entry.fieldId == fieldId) {
return &entry;
}
}
return nullptr;
}
const UIEditorPropertyGridColorFieldVisualState* FindColorFieldVisualState(
const UIEditorPropertyGridInteractionState& state,
std::string_view fieldId) {
for (const UIEditorPropertyGridColorFieldVisualState& entry : state.propertyGridState.colorFieldStates) {
if (entry.fieldId == fieldId) {
return &entry;
}
}
return nullptr;
}
UIEditorColorFieldInteractionState BuildColorFieldInteractionState(
const UIEditorPropertyGridInteractionState& state,
std::string_view fieldId) {
UIEditorColorFieldInteractionState interactionState = {};
if (const UIEditorPropertyGridColorFieldVisualState* entry =
FindColorFieldVisualState(state, fieldId);
entry != nullptr) {
interactionState.colorFieldState = entry->state;
}
interactionState.pointerPosition = state.pointerPosition;
interactionState.hasPointerPosition = state.hasPointerPosition;
return interactionState;
}
void StoreColorFieldVisualState(
UIEditorPropertyGridInteractionState& state,
std::string_view fieldId,
const UIEditorColorFieldInteractionState& interactionState) {
if (UIEditorPropertyGridColorFieldVisualState* entry =
FindMutableColorFieldVisualState(state, fieldId);
entry != nullptr) {
entry->state = interactionState.colorFieldState;
return;
}
UIEditorPropertyGridColorFieldVisualState entry = {};
entry.fieldId = std::string(fieldId);
entry.state = interactionState.colorFieldState;
state.propertyGridState.colorFieldStates.push_back(std::move(entry));
}
void PruneColorFieldVisualStates(
UIEditorPropertyGridInteractionState& state,
const UIEditorPropertyGridLayout& layout,
const std::vector<UIEditorPropertyGridSection>& sections) {
const auto isVisibleColorFieldId = [&layout, &sections](std::string_view fieldId) {
const std::size_t visibleFieldIndex =
FindUIEditorPropertyGridVisibleFieldIndex(layout, fieldId, sections);
if (visibleFieldIndex == UIEditorPropertyGridInvalidIndex ||
visibleFieldIndex >= layout.visibleFieldSectionIndices.size() ||
visibleFieldIndex >= layout.visibleFieldIndices.size()) {
return false;
}
const std::size_t sectionIndex = layout.visibleFieldSectionIndices[visibleFieldIndex];
const std::size_t fieldIndex = layout.visibleFieldIndices[visibleFieldIndex];
return sectionIndex < sections.size() &&
fieldIndex < sections[sectionIndex].fields.size() &&
sections[sectionIndex].fields[fieldIndex].kind == UIEditorPropertyGridFieldKind::Color;
};
state.propertyGridState.colorFieldStates.erase(
std::remove_if(
state.propertyGridState.colorFieldStates.begin(),
state.propertyGridState.colorFieldStates.end(),
[&isVisibleColorFieldId](const UIEditorPropertyGridColorFieldVisualState& entry) {
return !isVisibleColorFieldId(entry.fieldId);
}),
state.propertyGridState.colorFieldStates.end());
}
void CloseOtherColorFieldPopups(
UIEditorPropertyGridInteractionState& state,
std::string_view keepFieldId) {
for (UIEditorPropertyGridColorFieldVisualState& entry : state.propertyGridState.colorFieldStates) {
if (entry.fieldId == keepFieldId) {
continue;
}
entry.state.popupOpen = false;
entry.state.activeTarget = UIEditorColorFieldHitTargetKind::None;
}
}
std::vector<UIEditorMenuPopupItem> BuildPopupItems(
const UIEditorPropertyGridField& field) {
std::vector<UIEditorMenuPopupItem> items = {};
@@ -544,6 +705,110 @@ bool ToggleBoolField(
return true;
}
bool ProcessColorFieldEvent(
UIEditorPropertyGridInteractionState& state,
::XCEngine::UI::Widgets::UISelectionModel& selectionModel,
::XCEngine::UI::Widgets::UIPropertyEditModel& propertyEditModel,
const UIEditorPropertyGridLayout& layout,
std::vector<UIEditorPropertyGridSection>& sections,
const Widgets::UIEditorPropertyGridMetrics& metrics,
const UIInputEvent& event,
UIEditorPropertyGridInteractionResult& result) {
const Widgets::UIEditorColorFieldMetrics colorMetrics =
BuildUIEditorPropertyGridColorFieldMetrics(metrics);
const ::XCEngine::UI::UIRect popupViewportRect = ResolvePopupViewportRect(layout.bounds);
bool handled = false;
for (std::size_t visibleFieldIndex = 0u;
visibleFieldIndex < layout.visibleFieldIndices.size();
++visibleFieldIndex) {
const std::size_t sectionIndex = layout.visibleFieldSectionIndices[visibleFieldIndex];
const std::size_t fieldIndex = layout.visibleFieldIndices[visibleFieldIndex];
if (sectionIndex >= sections.size() ||
fieldIndex >= sections[sectionIndex].fields.size()) {
continue;
}
UIEditorPropertyGridField& field = sections[sectionIndex].fields[fieldIndex];
if (field.kind != UIEditorPropertyGridFieldKind::Color) {
continue;
}
UIEditorColorFieldInteractionState colorState =
BuildColorFieldInteractionState(state, field.fieldId);
const bool popupWasOpen = colorState.colorFieldState.popupOpen;
UIEditorColorFieldSpec spec = BuildColorFieldSpec(field);
const UIEditorColorFieldInteractionFrame frame =
UpdateUIEditorColorFieldInteraction(
colorState,
spec,
layout.fieldRowRects[visibleFieldIndex],
{ event },
colorMetrics,
popupViewportRect);
if (!frame.result.consumed &&
frame.result.hitTarget.kind == UIEditorColorFieldHitTargetKind::None &&
!popupWasOpen &&
!colorState.colorFieldState.popupOpen) {
continue;
}
handled = true;
field.colorValue.value = spec.value;
field.colorValue.showAlpha = spec.showAlpha;
StoreColorFieldVisualState(state, field.fieldId, colorState);
if (frame.result.popupOpened) {
ClosePopup(state, result);
CloseOtherColorFieldPopups(state, field.fieldId);
}
state.propertyGridState.focused = colorState.colorFieldState.focused;
state.propertyGridState.pressedFieldId.clear();
if (frame.result.hitTarget.kind != UIEditorColorFieldHitTargetKind::None ||
frame.result.popupOpened ||
frame.result.colorChanged ||
popupWasOpen ||
colorState.colorFieldState.popupOpen) {
result.selectionChanged =
selectionModel.SetSelection(field.fieldId) || result.selectionChanged;
result.selectedFieldId = field.fieldId;
result.activeFieldId = field.fieldId;
state.keyboardNavigation.SetCurrentIndex(visibleFieldIndex);
}
if (propertyEditModel.HasActiveEdit() &&
propertyEditModel.GetActiveFieldId() != field.fieldId &&
(frame.result.hitTarget.kind != UIEditorColorFieldHitTargetKind::None ||
frame.result.popupOpened)) {
CommitActiveEdit(state, propertyEditModel, sections, result);
}
result.popupOpened = result.popupOpened || frame.result.popupOpened;
result.popupClosed = result.popupClosed || frame.result.popupClosed;
result.consumed = result.consumed || frame.result.consumed;
if (frame.result.colorChanged) {
SetChangedValueResult(field, result);
}
if (frame.result.hitTarget.kind == UIEditorColorFieldHitTargetKind::Row) {
result.hitTarget.kind = UIEditorPropertyGridHitTargetKind::FieldRow;
result.hitTarget.sectionIndex = sectionIndex;
result.hitTarget.fieldIndex = fieldIndex;
result.hitTarget.visibleFieldIndex = visibleFieldIndex;
} else if (frame.result.hitTarget.kind != UIEditorColorFieldHitTargetKind::None) {
result.hitTarget.kind = UIEditorPropertyGridHitTargetKind::ValueBox;
result.hitTarget.sectionIndex = sectionIndex;
result.hitTarget.fieldIndex = fieldIndex;
result.hitTarget.visibleFieldIndex = visibleFieldIndex;
}
}
return handled;
}
} // namespace
UIEditorPropertyGridInteractionFrame UpdateUIEditorPropertyGridInteraction(
@@ -558,6 +823,7 @@ UIEditorPropertyGridInteractionFrame UpdateUIEditorPropertyGridInteraction(
const Widgets::UIEditorMenuPopupMetrics& popupMetrics) {
UIEditorPropertyGridLayout layout =
BuildUIEditorPropertyGridLayout(bounds, sections, expansionModel, metrics);
PruneColorFieldVisualStates(state, layout, sections);
SyncKeyboardNavigation(state, selectionModel, layout, sections);
SyncHoverTarget(state, layout, sections, popupMetrics);
@@ -578,6 +844,23 @@ UIEditorPropertyGridInteractionFrame UpdateUIEditorPropertyGridInteraction(
: UIEditorPropertyGridHitTarget {};
eventResult.hitTarget = hitTarget;
if (ProcessColorFieldEvent(
state,
selectionModel,
propertyEditModel,
layout,
sections,
metrics,
event,
eventResult)) {
MergeInteractionResult(interactionResult, eventResult);
layout = BuildUIEditorPropertyGridLayout(bounds, sections, expansionModel, metrics);
PruneColorFieldVisualStates(state, layout, sections);
SyncKeyboardNavigation(state, selectionModel, layout, sections);
SyncHoverTarget(state, layout, sections, popupMetrics);
continue;
}
switch (event.type) {
case UIInputEventType::FocusGained:
state.propertyGridState.focused = true;
@@ -864,6 +1147,7 @@ UIEditorPropertyGridInteractionFrame UpdateUIEditorPropertyGridInteraction(
}
layout = BuildUIEditorPropertyGridLayout(bounds, sections, expansionModel, metrics);
PruneColorFieldVisualStates(state, layout, sections);
SyncKeyboardNavigation(state, selectionModel, layout, sections);
SyncHoverTarget(state, layout, sections, popupMetrics);
if (eventResult.hitTarget.kind == UIEditorPropertyGridHitTargetKind::None &&
@@ -895,6 +1179,7 @@ UIEditorPropertyGridInteractionFrame UpdateUIEditorPropertyGridInteraction(
}
layout = BuildUIEditorPropertyGridLayout(bounds, sections, expansionModel, metrics);
PruneColorFieldVisualStates(state, layout, sections);
SyncKeyboardNavigation(state, selectionModel, layout, sections);
SyncHoverTarget(state, layout, sections, popupMetrics);
if (interactionResult.hitTarget.kind == UIEditorPropertyGridHitTargetKind::None &&

View File

@@ -0,0 +1,227 @@
#include <XCEditor/Fields/UIEditorTextField.h>
#include <XCEditor/Widgets/UIEditorFieldRowLayout.h>
#include <XCEditor/Widgets/UIEditorTextLayout.h>
#include <algorithm>
namespace XCEngine::UI::Editor::Widgets {
namespace {
using ::XCEngine::UI::UIDrawList;
using ::XCEngine::UI::UIPoint;
using ::XCEngine::UI::UIRect;
UIEditorTextFieldMetrics ResolveMetrics(const UIEditorTextFieldMetrics& metrics) {
const auto& tokens = GetUIEditorInspectorFieldStyleTokens();
UIEditorTextFieldMetrics resolved = metrics;
if (AreUIEditorFieldMetricsEqual(metrics.controlTrailingInset, 8.0f)) {
resolved.controlTrailingInset = tokens.controlTrailingInset;
}
if (AreUIEditorFieldMetricsEqual(metrics.valueBoxMinWidth, 96.0f)) {
resolved.valueBoxMinWidth = tokens.controlMinWidth;
}
return resolved;
}
UIEditorTextFieldPalette ResolvePalette(const UIEditorTextFieldPalette& palette) {
const auto& tokens = GetUIEditorInspectorFieldStyleTokens();
UIEditorTextFieldPalette resolved = palette;
if (AreUIEditorFieldColorsEqual(palette.rowHoverColor, ::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.0f))) {
resolved.rowHoverColor = tokens.rowHoverColor;
}
if (AreUIEditorFieldColorsEqual(palette.rowActiveColor, ::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.0f))) {
resolved.rowActiveColor = tokens.rowActiveColor;
}
if (AreUIEditorFieldColorsEqual(palette.valueBoxColor, ::XCEngine::UI::UIColor(0.18f, 0.18f, 0.18f, 1.0f))) {
resolved.valueBoxColor = tokens.controlColor;
}
if (AreUIEditorFieldColorsEqual(palette.valueBoxHoverColor, ::XCEngine::UI::UIColor(0.21f, 0.21f, 0.21f, 1.0f))) {
resolved.valueBoxHoverColor = tokens.controlHoverColor;
}
if (AreUIEditorFieldColorsEqual(palette.valueBoxEditingColor, ::XCEngine::UI::UIColor(0.24f, 0.24f, 0.24f, 1.0f))) {
resolved.valueBoxEditingColor = tokens.controlEditingColor;
}
if (AreUIEditorFieldColorsEqual(palette.readOnlyColor, ::XCEngine::UI::UIColor(0.17f, 0.17f, 0.17f, 1.0f))) {
resolved.readOnlyColor = tokens.controlReadOnlyColor;
}
if (AreUIEditorFieldColorsEqual(palette.controlBorderColor, ::XCEngine::UI::UIColor(0.14f, 0.14f, 0.14f, 1.0f))) {
resolved.controlBorderColor = tokens.controlBorderColor;
}
if (AreUIEditorFieldColorsEqual(
palette.controlFocusedBorderColor,
::XCEngine::UI::UIColor(0.46f, 0.46f, 0.46f, 1.0f))) {
resolved.controlFocusedBorderColor = tokens.controlFocusedBorderColor;
}
if (AreUIEditorFieldColorsEqual(palette.labelColor, ::XCEngine::UI::UIColor(0.80f, 0.80f, 0.80f, 1.0f))) {
resolved.labelColor = tokens.labelColor;
}
if (AreUIEditorFieldColorsEqual(palette.valueColor, ::XCEngine::UI::UIColor(0.92f, 0.92f, 0.92f, 1.0f))) {
resolved.valueColor = tokens.valueColor;
}
if (AreUIEditorFieldColorsEqual(
palette.readOnlyValueColor,
::XCEngine::UI::UIColor(0.62f, 0.62f, 0.62f, 1.0f))) {
resolved.readOnlyValueColor = tokens.readOnlyValueColor;
}
return resolved;
}
::XCEngine::UI::UIColor ResolveRowFillColor(
const UIEditorTextFieldState& state,
const UIEditorTextFieldPalette& palette) {
if (state.activeTarget != UIEditorTextFieldHitTargetKind::None) {
return palette.rowActiveColor;
}
if (state.hoveredTarget != UIEditorTextFieldHitTargetKind::None) {
return palette.rowHoverColor;
}
return palette.surfaceColor;
}
::XCEngine::UI::UIColor ResolveValueFillColor(
const UIEditorTextFieldSpec& spec,
const UIEditorTextFieldState& state,
const UIEditorTextFieldPalette& palette) {
if (spec.readOnly) {
return palette.readOnlyColor;
}
if (state.editing) {
return palette.valueBoxEditingColor;
}
if (state.hoveredTarget == UIEditorTextFieldHitTargetKind::ValueBox) {
return palette.valueBoxHoverColor;
}
return palette.valueBoxColor;
}
} // namespace
bool IsUIEditorTextFieldPointInside(
const UIRect& rect,
const UIPoint& point) {
return point.x >= rect.x &&
point.x <= rect.x + rect.width &&
point.y >= rect.y &&
point.y <= rect.y + rect.height;
}
UIEditorTextFieldLayout BuildUIEditorTextFieldLayout(
const UIRect& bounds,
const UIEditorTextFieldSpec&,
const UIEditorTextFieldMetrics& metrics) {
const UIEditorTextFieldMetrics resolvedMetrics = ResolveMetrics(metrics);
const UIEditorFieldRowLayout hostLayout = BuildUIEditorFieldRowLayout(
bounds,
resolvedMetrics.valueBoxMinWidth,
UIEditorFieldRowLayoutMetrics {
resolvedMetrics.rowHeight,
resolvedMetrics.horizontalPadding,
resolvedMetrics.labelControlGap,
resolvedMetrics.controlColumnStart,
resolvedMetrics.controlTrailingInset,
resolvedMetrics.controlInsetY,
});
UIEditorTextFieldLayout layout = {};
layout.bounds = hostLayout.bounds;
layout.labelRect = hostLayout.labelRect;
layout.controlRect = hostLayout.controlRect;
layout.valueRect = layout.controlRect;
return layout;
}
UIEditorTextFieldHitTarget HitTestUIEditorTextField(
const UIEditorTextFieldLayout& layout,
const UIPoint& point) {
if (IsUIEditorTextFieldPointInside(layout.valueRect, point)) {
return { UIEditorTextFieldHitTargetKind::ValueBox };
}
if (IsUIEditorTextFieldPointInside(layout.bounds, point)) {
return { UIEditorTextFieldHitTargetKind::Row };
}
return {};
}
void AppendUIEditorTextFieldBackground(
UIDrawList& drawList,
const UIEditorTextFieldLayout& layout,
const UIEditorTextFieldSpec& spec,
const UIEditorTextFieldState& state,
const UIEditorTextFieldPalette& palette,
const UIEditorTextFieldMetrics& metrics) {
const UIEditorTextFieldPalette resolvedPalette = ResolvePalette(palette);
const UIEditorTextFieldMetrics resolvedMetrics = ResolveMetrics(metrics);
const auto rowFillColor = ResolveRowFillColor(state, resolvedPalette);
if (rowFillColor.a > 0.0f) {
drawList.AddFilledRect(layout.bounds, rowFillColor, resolvedMetrics.cornerRounding);
}
const auto rowBorderColor = state.focused ? resolvedPalette.focusedBorderColor : resolvedPalette.borderColor;
if (rowBorderColor.a > 0.0f) {
drawList.AddRectOutline(
layout.bounds,
rowBorderColor,
state.focused ? resolvedMetrics.focusedBorderThickness : resolvedMetrics.borderThickness,
resolvedMetrics.cornerRounding);
}
drawList.AddFilledRect(
layout.valueRect,
ResolveValueFillColor(spec, state, resolvedPalette),
resolvedMetrics.valueBoxRounding);
drawList.AddRectOutline(
layout.valueRect,
state.editing ? resolvedPalette.controlFocusedBorderColor : resolvedPalette.controlBorderColor,
state.editing ? resolvedMetrics.focusedBorderThickness : resolvedMetrics.borderThickness,
resolvedMetrics.valueBoxRounding);
}
void AppendUIEditorTextFieldForeground(
UIDrawList& drawList,
const UIEditorTextFieldLayout& layout,
const UIEditorTextFieldSpec& spec,
const UIEditorTextFieldState& state,
const UIEditorTextFieldPalette& palette,
const UIEditorTextFieldMetrics& metrics) {
const UIEditorTextFieldPalette resolvedPalette = ResolvePalette(palette);
const UIEditorTextFieldMetrics resolvedMetrics = ResolveMetrics(metrics);
drawList.PushClipRect(ResolveUIEditorTextClipRect(layout.labelRect, resolvedMetrics.labelFontSize));
drawList.AddText(
UIPoint(
layout.labelRect.x,
ResolveUIEditorTextTop(layout.labelRect, resolvedMetrics.labelFontSize, resolvedMetrics.labelTextInsetY)),
spec.label,
resolvedPalette.labelColor,
resolvedMetrics.labelFontSize);
drawList.PopClipRect();
drawList.PushClipRect(ResolveUIEditorTextClipRect(layout.valueRect, resolvedMetrics.valueFontSize));
drawList.AddText(
UIPoint(
layout.valueRect.x + resolvedMetrics.valueTextInsetX,
ResolveUIEditorTextTop(layout.valueRect, resolvedMetrics.valueFontSize, resolvedMetrics.valueTextInsetY)),
state.editing ? state.displayText : spec.value,
spec.readOnly ? resolvedPalette.readOnlyValueColor : resolvedPalette.valueColor,
resolvedMetrics.valueFontSize);
drawList.PopClipRect();
}
void AppendUIEditorTextField(
UIDrawList& drawList,
const UIRect& bounds,
const UIEditorTextFieldSpec& spec,
const UIEditorTextFieldState& state,
const UIEditorTextFieldPalette& palette,
const UIEditorTextFieldMetrics& metrics) {
const UIEditorTextFieldMetrics resolvedMetrics = ResolveMetrics(metrics);
const UIEditorTextFieldPalette resolvedPalette = ResolvePalette(palette);
const UIEditorTextFieldLayout layout = BuildUIEditorTextFieldLayout(bounds, spec, resolvedMetrics);
AppendUIEditorTextFieldBackground(drawList, layout, spec, state, resolvedPalette, resolvedMetrics);
AppendUIEditorTextFieldForeground(drawList, layout, spec, state, resolvedPalette, resolvedMetrics);
}
} // namespace XCEngine::UI::Editor::Widgets

View File

@@ -1,4 +1,4 @@
#include <XCEditor/Core/UIEditorTextFieldInteraction.h>
#include <XCEditor/Fields/UIEditorTextFieldInteraction.h>
#include <XCEngine/Input/InputTypes.h>

View File

@@ -1,7 +1,7 @@
#include <XCEditor/Widgets/UIEditorVector2Field.h>
#include <XCEditor/Fields/UIEditorVector2Field.h>
#include <XCEditor/Widgets/UIEditorFieldRowLayout.h>
#include <XCEditor/Widgets/UIEditorNumberField.h>
#include <XCEditor/Fields/UIEditorNumberField.h>
#include <XCEditor/Widgets/UIEditorTextLayout.h>
#include <algorithm>
@@ -18,6 +18,91 @@ float ClampNonNegative(float value) {
return (std::max)(0.0f, value);
}
UIEditorVector2FieldMetrics ResolveMetrics(const UIEditorVector2FieldMetrics& metrics) {
const auto& tokens = GetUIEditorInspectorFieldStyleTokens();
UIEditorVector2FieldMetrics resolved = metrics;
if (AreUIEditorFieldMetricsEqual(metrics.controlTrailingInset, 8.0f)) {
resolved.controlTrailingInset = tokens.controlTrailingInset;
}
if (AreUIEditorFieldMetricsEqual(metrics.componentMinWidth, 72.0f)) {
resolved.componentMinWidth = tokens.vectorComponentMinWidth;
}
if (AreUIEditorFieldMetricsEqual(metrics.componentPrefixWidth, 9.0f)) {
resolved.componentPrefixWidth = tokens.vectorPrefixWidth;
}
if (AreUIEditorFieldMetricsEqual(metrics.componentLabelGap, 4.0f)) {
resolved.componentLabelGap = tokens.vectorPrefixGap;
}
return resolved;
}
UIEditorVector2FieldPalette ResolvePalette(const UIEditorVector2FieldPalette& palette) {
const auto& tokens = GetUIEditorInspectorFieldStyleTokens();
UIEditorVector2FieldPalette resolved = palette;
if (AreUIEditorFieldColorsEqual(palette.rowHoverColor, ::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.0f))) {
resolved.rowHoverColor = tokens.rowHoverColor;
}
if (AreUIEditorFieldColorsEqual(palette.rowActiveColor, ::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.0f))) {
resolved.rowActiveColor = tokens.rowActiveColor;
}
if (AreUIEditorFieldColorsEqual(palette.componentColor, ::XCEngine::UI::UIColor(0.18f, 0.18f, 0.18f, 1.0f))) {
resolved.componentColor = tokens.controlColor;
}
if (AreUIEditorFieldColorsEqual(
palette.componentHoverColor,
::XCEngine::UI::UIColor(0.21f, 0.21f, 0.21f, 1.0f))) {
resolved.componentHoverColor = tokens.controlHoverColor;
}
if (AreUIEditorFieldColorsEqual(
palette.componentEditingColor,
::XCEngine::UI::UIColor(0.24f, 0.24f, 0.24f, 1.0f))) {
resolved.componentEditingColor = tokens.controlEditingColor;
}
if (AreUIEditorFieldColorsEqual(palette.readOnlyColor, ::XCEngine::UI::UIColor(0.17f, 0.17f, 0.17f, 1.0f))) {
resolved.readOnlyColor = tokens.controlReadOnlyColor;
}
if (AreUIEditorFieldColorsEqual(
palette.componentBorderColor,
::XCEngine::UI::UIColor(0.14f, 0.14f, 0.14f, 1.0f))) {
resolved.componentBorderColor = tokens.controlBorderColor;
}
if (AreUIEditorFieldColorsEqual(
palette.componentFocusedBorderColor,
::XCEngine::UI::UIColor(0.20f, 0.20f, 0.20f, 1.0f))) {
resolved.componentFocusedBorderColor = tokens.controlFocusedBorderColor;
}
if (AreUIEditorFieldColorsEqual(palette.prefixColor, ::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.0f))) {
resolved.prefixColor = tokens.prefixColor;
}
if (AreUIEditorFieldColorsEqual(
palette.prefixBorderColor,
::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.0f))) {
resolved.prefixBorderColor = tokens.prefixBorderColor;
}
if (AreUIEditorFieldColorsEqual(palette.labelColor, ::XCEngine::UI::UIColor(0.80f, 0.80f, 0.80f, 1.0f))) {
resolved.labelColor = tokens.labelColor;
}
if (AreUIEditorFieldColorsEqual(palette.valueColor, ::XCEngine::UI::UIColor(0.92f, 0.92f, 0.92f, 1.0f))) {
resolved.valueColor = tokens.valueColor;
}
if (AreUIEditorFieldColorsEqual(
palette.readOnlyValueColor,
::XCEngine::UI::UIColor(0.62f, 0.62f, 0.62f, 1.0f))) {
resolved.readOnlyValueColor = tokens.readOnlyValueColor;
}
if (AreUIEditorFieldColorsEqual(palette.axisXColor, ::XCEngine::UI::UIColor(0.67f, 0.67f, 0.67f, 1.0f))) {
resolved.axisXColor = tokens.axisXColor;
}
if (AreUIEditorFieldColorsEqual(palette.axisYColor, ::XCEngine::UI::UIColor(0.67f, 0.67f, 0.67f, 1.0f))) {
resolved.axisYColor = tokens.axisYColor;
}
return resolved;
}
float ApproximateTextWidth(float fontSize, std::size_t characterCount) {
return fontSize * 0.55f * static_cast<float>(characterCount);
}
@@ -116,20 +201,22 @@ UIEditorVector2FieldLayout BuildUIEditorVector2FieldLayout(
const UIRect& bounds,
const UIEditorVector2FieldSpec&,
const UIEditorVector2FieldMetrics& metrics) {
const float requiredControlWidth = metrics.componentMinWidth * 2.0f + metrics.componentGap;
const UIEditorVector2FieldMetrics resolvedMetrics = ResolveMetrics(metrics);
const float requiredControlWidth =
resolvedMetrics.componentMinWidth * 2.0f + resolvedMetrics.componentGap;
const UIEditorFieldRowLayout hostLayout = BuildUIEditorFieldRowLayout(
bounds,
requiredControlWidth,
UIEditorFieldRowLayoutMetrics {
metrics.rowHeight,
metrics.horizontalPadding,
metrics.labelControlGap,
metrics.controlColumnStart,
metrics.controlTrailingInset,
metrics.controlInsetY,
resolvedMetrics.rowHeight,
resolvedMetrics.horizontalPadding,
resolvedMetrics.labelControlGap,
resolvedMetrics.controlColumnStart,
resolvedMetrics.controlTrailingInset,
resolvedMetrics.controlInsetY,
});
const float componentWidth =
ClampNonNegative((hostLayout.controlRect.width - metrics.componentGap) / 2.0f);
ClampNonNegative((hostLayout.controlRect.width - resolvedMetrics.componentGap) / 2.0f);
UIEditorVector2FieldLayout layout = {};
layout.bounds = hostLayout.bounds;
@@ -138,10 +225,11 @@ UIEditorVector2FieldLayout BuildUIEditorVector2FieldLayout(
for (std::size_t componentIndex = 0u; componentIndex < layout.componentRects.size(); ++componentIndex) {
const float componentX =
layout.controlRect.x + (componentWidth + metrics.componentGap) * static_cast<float>(componentIndex);
layout.controlRect.x +
(componentWidth + resolvedMetrics.componentGap) * static_cast<float>(componentIndex);
const UIRect componentRect(componentX, layout.controlRect.y, componentWidth, layout.controlRect.height);
const float prefixWidth = (std::min)(metrics.componentPrefixWidth, componentRect.width);
const float labelGap = ClampNonNegative(metrics.componentLabelGap);
const float prefixWidth = (std::min)(resolvedMetrics.componentPrefixWidth, componentRect.width);
const float labelGap = ClampNonNegative(resolvedMetrics.componentLabelGap);
const float valueX = componentRect.x + prefixWidth + labelGap;
layout.componentRects[componentIndex] = componentRect;
layout.componentPrefixRects[componentIndex] =
@@ -178,27 +266,45 @@ void AppendUIEditorVector2FieldBackground(
const UIEditorVector2FieldState& state,
const UIEditorVector2FieldPalette& palette,
const UIEditorVector2FieldMetrics& metrics) {
if (ResolveRowFillColor(state, palette).a > 0.0f) {
drawList.AddFilledRect(layout.bounds, ResolveRowFillColor(state, palette), metrics.cornerRounding);
const UIEditorVector2FieldPalette resolvedPalette = ResolvePalette(palette);
const UIEditorVector2FieldMetrics resolvedMetrics = ResolveMetrics(metrics);
if (ResolveRowFillColor(state, resolvedPalette).a > 0.0f) {
drawList.AddFilledRect(
layout.bounds,
ResolveRowFillColor(state, resolvedPalette),
resolvedMetrics.cornerRounding);
}
if ((state.focused ? palette.focusedBorderColor.a : palette.borderColor.a) > 0.0f) {
if ((state.focused ? resolvedPalette.focusedBorderColor.a : resolvedPalette.borderColor.a) > 0.0f) {
drawList.AddRectOutline(
layout.bounds,
state.focused ? palette.focusedBorderColor : palette.borderColor,
state.focused ? metrics.focusedBorderThickness : metrics.borderThickness,
metrics.cornerRounding);
state.focused ? resolvedPalette.focusedBorderColor : resolvedPalette.borderColor,
state.focused ? resolvedMetrics.focusedBorderThickness : resolvedMetrics.borderThickness,
resolvedMetrics.cornerRounding);
}
for (std::size_t componentIndex = 0u; componentIndex < layout.componentRects.size(); ++componentIndex) {
if (resolvedPalette.prefixColor.a > 0.0f) {
drawList.AddFilledRect(
layout.componentPrefixRects[componentIndex],
resolvedPalette.prefixColor,
resolvedMetrics.componentRounding);
}
if (resolvedPalette.prefixBorderColor.a > 0.0f) {
drawList.AddRectOutline(
layout.componentPrefixRects[componentIndex],
resolvedPalette.prefixBorderColor,
resolvedMetrics.borderThickness,
resolvedMetrics.componentRounding);
}
drawList.AddFilledRect(
layout.componentValueRects[componentIndex],
ResolveComponentFillColor(spec, state, palette, componentIndex),
metrics.componentRounding);
ResolveComponentFillColor(spec, state, resolvedPalette, componentIndex),
resolvedMetrics.componentRounding);
drawList.AddRectOutline(
layout.componentValueRects[componentIndex],
ResolveComponentBorderColor(state, palette, componentIndex),
metrics.borderThickness,
metrics.componentRounding);
ResolveComponentBorderColor(state, resolvedPalette, componentIndex),
resolvedMetrics.borderThickness,
resolvedMetrics.componentRounding);
}
}
@@ -209,53 +315,55 @@ void AppendUIEditorVector2FieldForeground(
const UIEditorVector2FieldState& state,
const UIEditorVector2FieldPalette& palette,
const UIEditorVector2FieldMetrics& metrics) {
drawList.PushClipRect(ResolveUIEditorTextClipRect(layout.labelRect, metrics.labelFontSize));
const UIEditorVector2FieldPalette resolvedPalette = ResolvePalette(palette);
const UIEditorVector2FieldMetrics resolvedMetrics = ResolveMetrics(metrics);
drawList.PushClipRect(ResolveUIEditorTextClipRect(layout.labelRect, resolvedMetrics.labelFontSize));
drawList.AddText(
UIPoint(
layout.labelRect.x,
ResolveUIEditorTextTop(layout.labelRect, metrics.labelFontSize, metrics.labelTextInsetY)),
ResolveUIEditorTextTop(layout.labelRect, resolvedMetrics.labelFontSize, resolvedMetrics.labelTextInsetY)),
spec.label,
palette.labelColor,
metrics.labelFontSize);
resolvedPalette.labelColor,
resolvedMetrics.labelFontSize);
drawList.PopClipRect();
for (std::size_t componentIndex = 0u; componentIndex < layout.componentRects.size(); ++componentIndex) {
drawList.PushClipRect(
ResolveUIEditorTextClipRect(layout.componentPrefixRects[componentIndex], metrics.prefixFontSize));
ResolveUIEditorTextClipRect(layout.componentPrefixRects[componentIndex], resolvedMetrics.prefixFontSize));
const std::string& componentLabel = spec.componentLabels[componentIndex];
const float prefixTextX =
layout.componentPrefixRects[componentIndex].x +
ClampNonNegative(
(layout.componentPrefixRects[componentIndex].width -
ApproximateTextWidth(metrics.prefixFontSize, componentLabel.size())) *
ApproximateTextWidth(resolvedMetrics.prefixFontSize, componentLabel.size())) *
0.5f) +
metrics.prefixTextInsetX;
resolvedMetrics.prefixTextInsetX;
drawList.AddText(
UIPoint(
prefixTextX,
ResolveUIEditorTextTop(
layout.componentPrefixRects[componentIndex],
metrics.prefixFontSize,
metrics.prefixTextInsetY)),
resolvedMetrics.prefixFontSize,
resolvedMetrics.prefixTextInsetY)),
componentLabel,
ResolveAxisColor(palette, componentIndex),
metrics.prefixFontSize);
ResolveAxisColor(resolvedPalette, componentIndex),
resolvedMetrics.prefixFontSize);
drawList.PopClipRect();
drawList.PushClipRect(
ResolveUIEditorTextClipRect(layout.componentValueRects[componentIndex], metrics.valueFontSize));
ResolveUIEditorTextClipRect(layout.componentValueRects[componentIndex], resolvedMetrics.valueFontSize));
drawList.AddText(
UIPoint(
layout.componentValueRects[componentIndex].x + metrics.valueTextInsetX,
layout.componentValueRects[componentIndex].x + resolvedMetrics.valueTextInsetX,
ResolveUIEditorTextTop(
layout.componentValueRects[componentIndex],
metrics.valueFontSize,
metrics.valueTextInsetY)),
resolvedMetrics.valueFontSize,
resolvedMetrics.valueTextInsetY)),
state.editing && state.selectedComponentIndex == componentIndex
? state.displayTexts[componentIndex]
: FormatUIEditorVector2FieldComponentValue(spec, componentIndex),
spec.readOnly ? palette.readOnlyValueColor : palette.valueColor,
metrics.valueFontSize);
spec.readOnly ? resolvedPalette.readOnlyValueColor : resolvedPalette.valueColor,
resolvedMetrics.valueFontSize);
drawList.PopClipRect();
}
}
@@ -267,9 +375,11 @@ void AppendUIEditorVector2Field(
const UIEditorVector2FieldState& state,
const UIEditorVector2FieldPalette& palette,
const UIEditorVector2FieldMetrics& metrics) {
const UIEditorVector2FieldLayout layout = BuildUIEditorVector2FieldLayout(bounds, spec, metrics);
AppendUIEditorVector2FieldBackground(drawList, layout, spec, state, palette, metrics);
AppendUIEditorVector2FieldForeground(drawList, layout, spec, state, palette, metrics);
const UIEditorVector2FieldPalette resolvedPalette = ResolvePalette(palette);
const UIEditorVector2FieldMetrics resolvedMetrics = ResolveMetrics(metrics);
const UIEditorVector2FieldLayout layout = BuildUIEditorVector2FieldLayout(bounds, spec, resolvedMetrics);
AppendUIEditorVector2FieldBackground(drawList, layout, spec, state, resolvedPalette, resolvedMetrics);
AppendUIEditorVector2FieldForeground(drawList, layout, spec, state, resolvedPalette, resolvedMetrics);
}
} // namespace XCEngine::UI::Editor::Widgets

View File

@@ -1,4 +1,4 @@
#include <XCEditor/Core/UIEditorVector2FieldInteraction.h>
#include <XCEditor/Fields/UIEditorVector2FieldInteraction.h>
#include <XCEngine/Input/InputTypes.h>

View File

@@ -1,7 +1,7 @@
#include <XCEditor/Widgets/UIEditorVector3Field.h>
#include <XCEditor/Fields/UIEditorVector3Field.h>
#include <XCEditor/Widgets/UIEditorFieldRowLayout.h>
#include <XCEditor/Widgets/UIEditorNumberField.h>
#include <XCEditor/Fields/UIEditorNumberField.h>
#include <XCEditor/Widgets/UIEditorTextLayout.h>
#include <algorithm>
@@ -18,6 +18,94 @@ float ClampNonNegative(float value) {
return (std::max)(0.0f, value);
}
UIEditorVector3FieldMetrics ResolveMetrics(const UIEditorVector3FieldMetrics& metrics) {
const auto& tokens = GetUIEditorInspectorFieldStyleTokens();
UIEditorVector3FieldMetrics resolved = metrics;
if (AreUIEditorFieldMetricsEqual(metrics.controlTrailingInset, 8.0f)) {
resolved.controlTrailingInset = tokens.controlTrailingInset;
}
if (AreUIEditorFieldMetricsEqual(metrics.componentMinWidth, 72.0f)) {
resolved.componentMinWidth = tokens.vectorComponentMinWidth;
}
if (AreUIEditorFieldMetricsEqual(metrics.componentPrefixWidth, 9.0f)) {
resolved.componentPrefixWidth = tokens.vectorPrefixWidth;
}
if (AreUIEditorFieldMetricsEqual(metrics.componentLabelGap, 4.0f)) {
resolved.componentLabelGap = tokens.vectorPrefixGap;
}
return resolved;
}
UIEditorVector3FieldPalette ResolvePalette(const UIEditorVector3FieldPalette& palette) {
const auto& tokens = GetUIEditorInspectorFieldStyleTokens();
UIEditorVector3FieldPalette resolved = palette;
if (AreUIEditorFieldColorsEqual(palette.rowHoverColor, ::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.0f))) {
resolved.rowHoverColor = tokens.rowHoverColor;
}
if (AreUIEditorFieldColorsEqual(palette.rowActiveColor, ::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.0f))) {
resolved.rowActiveColor = tokens.rowActiveColor;
}
if (AreUIEditorFieldColorsEqual(palette.componentColor, ::XCEngine::UI::UIColor(0.18f, 0.18f, 0.18f, 1.0f))) {
resolved.componentColor = tokens.controlColor;
}
if (AreUIEditorFieldColorsEqual(
palette.componentHoverColor,
::XCEngine::UI::UIColor(0.21f, 0.21f, 0.21f, 1.0f))) {
resolved.componentHoverColor = tokens.controlHoverColor;
}
if (AreUIEditorFieldColorsEqual(
palette.componentEditingColor,
::XCEngine::UI::UIColor(0.24f, 0.24f, 0.24f, 1.0f))) {
resolved.componentEditingColor = tokens.controlEditingColor;
}
if (AreUIEditorFieldColorsEqual(palette.readOnlyColor, ::XCEngine::UI::UIColor(0.17f, 0.17f, 0.17f, 1.0f))) {
resolved.readOnlyColor = tokens.controlReadOnlyColor;
}
if (AreUIEditorFieldColorsEqual(
palette.componentBorderColor,
::XCEngine::UI::UIColor(0.14f, 0.14f, 0.14f, 1.0f))) {
resolved.componentBorderColor = tokens.controlBorderColor;
}
if (AreUIEditorFieldColorsEqual(
palette.componentFocusedBorderColor,
::XCEngine::UI::UIColor(0.20f, 0.20f, 0.20f, 1.0f))) {
resolved.componentFocusedBorderColor = tokens.controlFocusedBorderColor;
}
if (AreUIEditorFieldColorsEqual(palette.prefixColor, ::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.0f))) {
resolved.prefixColor = tokens.prefixColor;
}
if (AreUIEditorFieldColorsEqual(
palette.prefixBorderColor,
::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.0f))) {
resolved.prefixBorderColor = tokens.prefixBorderColor;
}
if (AreUIEditorFieldColorsEqual(palette.labelColor, ::XCEngine::UI::UIColor(0.80f, 0.80f, 0.80f, 1.0f))) {
resolved.labelColor = tokens.labelColor;
}
if (AreUIEditorFieldColorsEqual(palette.valueColor, ::XCEngine::UI::UIColor(0.92f, 0.92f, 0.92f, 1.0f))) {
resolved.valueColor = tokens.valueColor;
}
if (AreUIEditorFieldColorsEqual(
palette.readOnlyValueColor,
::XCEngine::UI::UIColor(0.62f, 0.62f, 0.62f, 1.0f))) {
resolved.readOnlyValueColor = tokens.readOnlyValueColor;
}
if (AreUIEditorFieldColorsEqual(palette.axisXColor, ::XCEngine::UI::UIColor(0.67f, 0.67f, 0.67f, 1.0f))) {
resolved.axisXColor = tokens.axisXColor;
}
if (AreUIEditorFieldColorsEqual(palette.axisYColor, ::XCEngine::UI::UIColor(0.67f, 0.67f, 0.67f, 1.0f))) {
resolved.axisYColor = tokens.axisYColor;
}
if (AreUIEditorFieldColorsEqual(palette.axisZColor, ::XCEngine::UI::UIColor(0.67f, 0.67f, 0.67f, 1.0f))) {
resolved.axisZColor = tokens.axisZColor;
}
return resolved;
}
float ApproximateTextWidth(float fontSize, std::size_t characterCount) {
return fontSize * 0.55f * static_cast<float>(characterCount);
}
@@ -124,20 +212,22 @@ UIEditorVector3FieldLayout BuildUIEditorVector3FieldLayout(
const UIRect& bounds,
const UIEditorVector3FieldSpec&,
const UIEditorVector3FieldMetrics& metrics) {
const float requiredControlWidth = metrics.componentMinWidth * 3.0f + metrics.componentGap * 2.0f;
const UIEditorVector3FieldMetrics resolvedMetrics = ResolveMetrics(metrics);
const float requiredControlWidth =
resolvedMetrics.componentMinWidth * 3.0f + resolvedMetrics.componentGap * 2.0f;
const UIEditorFieldRowLayout hostLayout = BuildUIEditorFieldRowLayout(
bounds,
requiredControlWidth,
UIEditorFieldRowLayoutMetrics {
metrics.rowHeight,
metrics.horizontalPadding,
metrics.labelControlGap,
metrics.controlColumnStart,
metrics.controlTrailingInset,
metrics.controlInsetY,
resolvedMetrics.rowHeight,
resolvedMetrics.horizontalPadding,
resolvedMetrics.labelControlGap,
resolvedMetrics.controlColumnStart,
resolvedMetrics.controlTrailingInset,
resolvedMetrics.controlInsetY,
});
const float componentWidth =
ClampNonNegative((hostLayout.controlRect.width - metrics.componentGap * 2.0f) / 3.0f);
ClampNonNegative((hostLayout.controlRect.width - resolvedMetrics.componentGap * 2.0f) / 3.0f);
UIEditorVector3FieldLayout layout = {};
layout.bounds = hostLayout.bounds;
@@ -146,10 +236,11 @@ UIEditorVector3FieldLayout BuildUIEditorVector3FieldLayout(
for (std::size_t componentIndex = 0u; componentIndex < layout.componentRects.size(); ++componentIndex) {
const float componentX =
layout.controlRect.x + (componentWidth + metrics.componentGap) * static_cast<float>(componentIndex);
layout.controlRect.x +
(componentWidth + resolvedMetrics.componentGap) * static_cast<float>(componentIndex);
const UIRect componentRect(componentX, layout.controlRect.y, componentWidth, layout.controlRect.height);
const float prefixWidth = (std::min)(metrics.componentPrefixWidth, componentRect.width);
const float labelGap = ClampNonNegative(metrics.componentLabelGap);
const float prefixWidth = (std::min)(resolvedMetrics.componentPrefixWidth, componentRect.width);
const float labelGap = ClampNonNegative(resolvedMetrics.componentLabelGap);
const float valueX = componentRect.x + prefixWidth + labelGap;
layout.componentRects[componentIndex] = componentRect;
layout.componentPrefixRects[componentIndex] =
@@ -186,27 +277,45 @@ void AppendUIEditorVector3FieldBackground(
const UIEditorVector3FieldState& state,
const UIEditorVector3FieldPalette& palette,
const UIEditorVector3FieldMetrics& metrics) {
if (ResolveRowFillColor(state, palette).a > 0.0f) {
drawList.AddFilledRect(layout.bounds, ResolveRowFillColor(state, palette), metrics.cornerRounding);
const UIEditorVector3FieldPalette resolvedPalette = ResolvePalette(palette);
const UIEditorVector3FieldMetrics resolvedMetrics = ResolveMetrics(metrics);
if (ResolveRowFillColor(state, resolvedPalette).a > 0.0f) {
drawList.AddFilledRect(
layout.bounds,
ResolveRowFillColor(state, resolvedPalette),
resolvedMetrics.cornerRounding);
}
if ((state.focused ? palette.focusedBorderColor.a : palette.borderColor.a) > 0.0f) {
if ((state.focused ? resolvedPalette.focusedBorderColor.a : resolvedPalette.borderColor.a) > 0.0f) {
drawList.AddRectOutline(
layout.bounds,
state.focused ? palette.focusedBorderColor : palette.borderColor,
state.focused ? metrics.focusedBorderThickness : metrics.borderThickness,
metrics.cornerRounding);
state.focused ? resolvedPalette.focusedBorderColor : resolvedPalette.borderColor,
state.focused ? resolvedMetrics.focusedBorderThickness : resolvedMetrics.borderThickness,
resolvedMetrics.cornerRounding);
}
for (std::size_t componentIndex = 0u; componentIndex < layout.componentRects.size(); ++componentIndex) {
if (resolvedPalette.prefixColor.a > 0.0f) {
drawList.AddFilledRect(
layout.componentPrefixRects[componentIndex],
resolvedPalette.prefixColor,
resolvedMetrics.componentRounding);
}
if (resolvedPalette.prefixBorderColor.a > 0.0f) {
drawList.AddRectOutline(
layout.componentPrefixRects[componentIndex],
resolvedPalette.prefixBorderColor,
resolvedMetrics.borderThickness,
resolvedMetrics.componentRounding);
}
drawList.AddFilledRect(
layout.componentValueRects[componentIndex],
ResolveComponentFillColor(spec, state, palette, componentIndex),
metrics.componentRounding);
ResolveComponentFillColor(spec, state, resolvedPalette, componentIndex),
resolvedMetrics.componentRounding);
drawList.AddRectOutline(
layout.componentValueRects[componentIndex],
ResolveComponentBorderColor(state, palette, componentIndex),
metrics.borderThickness,
metrics.componentRounding);
ResolveComponentBorderColor(state, resolvedPalette, componentIndex),
resolvedMetrics.borderThickness,
resolvedMetrics.componentRounding);
}
}
@@ -217,53 +326,55 @@ void AppendUIEditorVector3FieldForeground(
const UIEditorVector3FieldState& state,
const UIEditorVector3FieldPalette& palette,
const UIEditorVector3FieldMetrics& metrics) {
drawList.PushClipRect(ResolveUIEditorTextClipRect(layout.labelRect, metrics.labelFontSize));
const UIEditorVector3FieldPalette resolvedPalette = ResolvePalette(palette);
const UIEditorVector3FieldMetrics resolvedMetrics = ResolveMetrics(metrics);
drawList.PushClipRect(ResolveUIEditorTextClipRect(layout.labelRect, resolvedMetrics.labelFontSize));
drawList.AddText(
UIPoint(
layout.labelRect.x,
ResolveUIEditorTextTop(layout.labelRect, metrics.labelFontSize, metrics.labelTextInsetY)),
ResolveUIEditorTextTop(layout.labelRect, resolvedMetrics.labelFontSize, resolvedMetrics.labelTextInsetY)),
spec.label,
palette.labelColor,
metrics.labelFontSize);
resolvedPalette.labelColor,
resolvedMetrics.labelFontSize);
drawList.PopClipRect();
for (std::size_t componentIndex = 0u; componentIndex < layout.componentRects.size(); ++componentIndex) {
drawList.PushClipRect(
ResolveUIEditorTextClipRect(layout.componentPrefixRects[componentIndex], metrics.prefixFontSize));
ResolveUIEditorTextClipRect(layout.componentPrefixRects[componentIndex], resolvedMetrics.prefixFontSize));
const std::string& componentLabel = spec.componentLabels[componentIndex];
const float prefixTextX =
layout.componentPrefixRects[componentIndex].x +
ClampNonNegative(
(layout.componentPrefixRects[componentIndex].width -
ApproximateTextWidth(metrics.prefixFontSize, componentLabel.size())) *
ApproximateTextWidth(resolvedMetrics.prefixFontSize, componentLabel.size())) *
0.5f) +
metrics.prefixTextInsetX;
resolvedMetrics.prefixTextInsetX;
drawList.AddText(
UIPoint(
prefixTextX,
ResolveUIEditorTextTop(
layout.componentPrefixRects[componentIndex],
metrics.prefixFontSize,
metrics.prefixTextInsetY)),
resolvedMetrics.prefixFontSize,
resolvedMetrics.prefixTextInsetY)),
componentLabel,
ResolveAxisColor(palette, componentIndex),
metrics.prefixFontSize);
ResolveAxisColor(resolvedPalette, componentIndex),
resolvedMetrics.prefixFontSize);
drawList.PopClipRect();
drawList.PushClipRect(
ResolveUIEditorTextClipRect(layout.componentValueRects[componentIndex], metrics.valueFontSize));
ResolveUIEditorTextClipRect(layout.componentValueRects[componentIndex], resolvedMetrics.valueFontSize));
drawList.AddText(
UIPoint(
layout.componentValueRects[componentIndex].x + metrics.valueTextInsetX,
layout.componentValueRects[componentIndex].x + resolvedMetrics.valueTextInsetX,
ResolveUIEditorTextTop(
layout.componentValueRects[componentIndex],
metrics.valueFontSize,
metrics.valueTextInsetY)),
resolvedMetrics.valueFontSize,
resolvedMetrics.valueTextInsetY)),
state.editing && state.selectedComponentIndex == componentIndex
? state.displayTexts[componentIndex]
: FormatUIEditorVector3FieldComponentValue(spec, componentIndex),
spec.readOnly ? palette.readOnlyValueColor : palette.valueColor,
metrics.valueFontSize);
spec.readOnly ? resolvedPalette.readOnlyValueColor : resolvedPalette.valueColor,
resolvedMetrics.valueFontSize);
drawList.PopClipRect();
}
}
@@ -275,9 +386,11 @@ void AppendUIEditorVector3Field(
const UIEditorVector3FieldState& state,
const UIEditorVector3FieldPalette& palette,
const UIEditorVector3FieldMetrics& metrics) {
const UIEditorVector3FieldLayout layout = BuildUIEditorVector3FieldLayout(bounds, spec, metrics);
AppendUIEditorVector3FieldBackground(drawList, layout, spec, state, palette, metrics);
AppendUIEditorVector3FieldForeground(drawList, layout, spec, state, palette, metrics);
const UIEditorVector3FieldPalette resolvedPalette = ResolvePalette(palette);
const UIEditorVector3FieldMetrics resolvedMetrics = ResolveMetrics(metrics);
const UIEditorVector3FieldLayout layout = BuildUIEditorVector3FieldLayout(bounds, spec, resolvedMetrics);
AppendUIEditorVector3FieldBackground(drawList, layout, spec, state, resolvedPalette, resolvedMetrics);
AppendUIEditorVector3FieldForeground(drawList, layout, spec, state, resolvedPalette, resolvedMetrics);
}
} // namespace XCEngine::UI::Editor::Widgets

View File

@@ -1,4 +1,4 @@
#include <XCEditor/Core/UIEditorVector3FieldInteraction.h>
#include <XCEditor/Fields/UIEditorVector3FieldInteraction.h>
#include <XCEngine/Input/InputTypes.h>

Some files were not shown because too many files have changed in this diff Show More