Refactor XCUI editor module layout
This commit is contained in:
@@ -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})
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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 {
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEditor/Widgets/UIEditorScrollView.h>
|
||||
#include <XCEditor/Collections/UIEditorScrollView.h>
|
||||
|
||||
#include <XCEngine/UI/Types.h>
|
||||
|
||||
@@ -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 {
|
||||
@@ -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
|
||||
@@ -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 {
|
||||
@@ -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
|
||||
188
new_editor/include/XCEditor/Fields/UIEditorAssetField.h
Normal file
188
new_editor/include/XCEditor/Fields/UIEditorAssetField.h
Normal 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
|
||||
@@ -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
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEditor/Widgets/UIEditorBoolField.h>
|
||||
#include <XCEditor/Fields/UIEditorBoolField.h>
|
||||
|
||||
#include <XCEngine/UI/Types.h>
|
||||
|
||||
220
new_editor/include/XCEditor/Fields/UIEditorColorField.h
Normal file
220
new_editor/include/XCEditor/Fields/UIEditorColorField.h
Normal 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
|
||||
@@ -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
|
||||
@@ -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>
|
||||
|
||||
101
new_editor/include/XCEditor/Fields/UIEditorFieldStyle.h
Normal file
101
new_editor/include/XCEditor/Fields/UIEditorFieldStyle.h
Normal 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
|
||||
@@ -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>
|
||||
153
new_editor/include/XCEditor/Fields/UIEditorObjectField.h
Normal file
153
new_editor/include/XCEditor/Fields/UIEditorObjectField.h
Normal 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
|
||||
@@ -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
|
||||
@@ -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 {
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEditor/Core/UIEditorCommandRegistry.h>
|
||||
#include <XCEditor/Foundation/UIEditorCommandRegistry.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEditor/Core/UIEditorWorkspaceController.h>
|
||||
#include <XCEditor/Shell/UIEditorWorkspaceController.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEditor/Core/UIEditorCommandDispatcher.h>
|
||||
#include <XCEditor/Foundation/UIEditorCommandDispatcher.h>
|
||||
|
||||
#include <XCEngine/UI/Input/UIShortcutRegistry.h>
|
||||
|
||||
76
new_editor/include/XCEditor/Foundation/UIEditorTheme.h
Normal file
76
new_editor/include/XCEditor/Foundation/UIEditorTheme.h
Normal 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
|
||||
@@ -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 {
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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 {
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEditor/Core/UIEditorCommandDispatcher.h>
|
||||
#include <XCEditor/Foundation/UIEditorCommandDispatcher.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEditor/Core/UIEditorMenuModel.h>
|
||||
#include <XCEditor/Shell/UIEditorMenuModel.h>
|
||||
#include <XCEngine/UI/DrawData.h>
|
||||
|
||||
#include <cstddef>
|
||||
@@ -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 = {};
|
||||
};
|
||||
@@ -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>
|
||||
@@ -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 {
|
||||
@@ -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>
|
||||
@@ -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 = {};
|
||||
};
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 {
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
@@ -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
|
||||
@@ -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>
|
||||
@@ -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
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEditor/Core/UIEditorWorkspaceSession.h>
|
||||
#include <XCEditor/Shell/UIEditorWorkspaceSession.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
@@ -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>
|
||||
@@ -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
|
||||
|
||||
178
new_editor/include/XCEditor/Widgets/UIEditorColorUtils.h
Normal file
178
new_editor/include/XCEditor/Widgets/UIEditorColorUtils.h
Normal 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
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
114
new_editor/src/Collections/UIEditorInlineRenameSession.cpp
Normal file
114
new_editor/src/Collections/UIEditorInlineRenameSession.cpp
Normal 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
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <XCEditor/Widgets/UIEditorListView.h>
|
||||
#include <XCEditor/Collections/UIEditorListView.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
@@ -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) {
|
||||
@@ -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) {
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <XCEditor/Core/UIEditorScrollViewInteraction.h>
|
||||
#include <XCEditor/Collections/UIEditorScrollViewInteraction.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
@@ -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),
|
||||
329
new_editor/src/Collections/UIEditorTabStripInteraction.cpp
Normal file
329
new_editor/src/Collections/UIEditorTabStripInteraction.cpp
Normal 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
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <XCEditor/Widgets/UIEditorTreeView.h>
|
||||
#include <XCEditor/Collections/UIEditorTreeView.h>
|
||||
|
||||
#include <XCEngine/UI/Widgets/UIFlatHierarchyHelpers.h>
|
||||
|
||||
677
new_editor/src/Collections/UIEditorTreeViewInteraction.cpp
Normal file
677
new_editor/src/Collections/UIEditorTreeViewInteraction.cpp
Normal 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
|
||||
@@ -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
@@ -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
|
||||
394
new_editor/src/Fields/UIEditorAssetField.cpp
Normal file
394
new_editor/src/Fields/UIEditorAssetField.cpp
Normal 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
|
||||
260
new_editor/src/Fields/UIEditorAssetFieldInteraction.cpp
Normal file
260
new_editor/src/Fields/UIEditorAssetFieldInteraction.cpp
Normal 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
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <XCEditor/Widgets/UIEditorBoolField.h>
|
||||
#include <XCEditor/Fields/UIEditorBoolField.h>
|
||||
#include <XCEditor/Widgets/UIEditorFieldRowLayout.h>
|
||||
#include <XCEditor/Widgets/UIEditorTextLayout.h>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <XCEditor/Core/UIEditorBoolFieldInteraction.h>
|
||||
#include <XCEditor/Fields/UIEditorBoolFieldInteraction.h>
|
||||
|
||||
#include <XCEngine/Input/InputTypes.h>
|
||||
|
||||
873
new_editor/src/Fields/UIEditorColorField.cpp
Normal file
873
new_editor/src/Fields/UIEditorColorField.cpp
Normal 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
|
||||
453
new_editor/src/Fields/UIEditorColorFieldInteraction.cpp
Normal file
453
new_editor/src/Fields/UIEditorColorFieldInteraction.cpp
Normal 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
|
||||
250
new_editor/src/Fields/UIEditorEnumField.cpp
Normal file
250
new_editor/src/Fields/UIEditorEnumField.cpp
Normal 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
|
||||
@@ -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>
|
||||
477
new_editor/src/Fields/UIEditorFieldStyle.cpp
Normal file
477
new_editor/src/Fields/UIEditorFieldStyle.cpp
Normal 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
|
||||
@@ -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
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <XCEditor/Core/UIEditorNumberFieldInteraction.h>
|
||||
#include <XCEditor/Fields/UIEditorNumberFieldInteraction.h>
|
||||
|
||||
#include <XCEngine/Input/InputTypes.h>
|
||||
|
||||
319
new_editor/src/Fields/UIEditorObjectField.cpp
Normal file
319
new_editor/src/Fields/UIEditorObjectField.cpp
Normal 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
|
||||
174
new_editor/src/Fields/UIEditorObjectFieldInteraction.cpp
Normal file
174
new_editor/src/Fields/UIEditorObjectFieldInteraction.cpp
Normal 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
|
||||
@@ -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);
|
||||
@@ -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, §ions](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 &&
|
||||
227
new_editor/src/Fields/UIEditorTextField.cpp
Normal file
227
new_editor/src/Fields/UIEditorTextField.cpp
Normal 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
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <XCEditor/Core/UIEditorTextFieldInteraction.h>
|
||||
#include <XCEditor/Fields/UIEditorTextFieldInteraction.h>
|
||||
|
||||
#include <XCEngine/Input/InputTypes.h>
|
||||
|
||||
@@ -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
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <XCEditor/Core/UIEditorVector2FieldInteraction.h>
|
||||
#include <XCEditor/Fields/UIEditorVector2FieldInteraction.h>
|
||||
|
||||
#include <XCEngine/Input/InputTypes.h>
|
||||
|
||||
@@ -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
|
||||
@@ -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
Reference in New Issue
Block a user