Build XCEditor viewport input bridge foundation

This commit is contained in:
2026-04-07 04:53:24 +08:00
parent 93ceb61483
commit a53f47e561
11 changed files with 1400 additions and 0 deletions

View File

@@ -19,6 +19,7 @@ add_library(XCUIEditorLib STATIC
src/Core/UIEditorMenuSession.cpp
src/Core/UIEditorPanelRegistry.cpp
src/Core/UIEditorShortcutManager.cpp
src/Core/UIEditorViewportInputBridge.cpp
src/Core/UIEditorWorkspaceLayoutPersistence.cpp
src/Core/UIEditorWorkspaceController.cpp
src/Core/UIEditorWorkspaceModel.cpp

View File

@@ -0,0 +1,67 @@
#pragma once
#include <XCEngine/UI/Types.h>
#include <cstdint>
#include <unordered_set>
#include <vector>
namespace XCEngine::UI::Editor {
struct UIEditorViewportInputBridgeConfig {
bool focusOnPointerDownInside = true;
bool clearFocusOnPointerDownOutside = true;
bool capturePointerOnPointerDownInside = true;
};
struct UIEditorViewportInputBridgeState {
bool hovered = false;
bool focused = false;
bool captured = false;
::XCEngine::UI::UIPointerButton captureButton = ::XCEngine::UI::UIPointerButton::None;
::XCEngine::UI::UIPoint lastScreenPointerPosition = {};
::XCEngine::UI::UIPoint lastLocalPointerPosition = {};
bool hasPointerPosition = false;
::XCEngine::UI::UIInputModifiers modifiers = {};
std::unordered_set<std::int32_t> pressedKeys = {};
std::uint8_t pointerButtonsDownMask = 0;
};
struct UIEditorViewportInputBridgeFrame {
bool hovered = false;
bool focused = false;
bool captured = false;
bool pointerInside = false;
bool pointerMoved = false;
bool pointerPressedInside = false;
bool pointerReleasedInside = false;
bool focusGained = false;
bool focusLost = false;
bool captureStarted = false;
bool captureEnded = false;
::XCEngine::UI::UIPointerButton changedPointerButton = ::XCEngine::UI::UIPointerButton::None;
::XCEngine::UI::UIPoint screenPointerPosition = {};
::XCEngine::UI::UIPoint localPointerPosition = {};
::XCEngine::UI::UIPoint pointerDelta = {};
float wheelDelta = 0.0f;
::XCEngine::UI::UIInputModifiers modifiers = {};
std::vector<std::int32_t> pressedKeyCodes = {};
std::vector<std::int32_t> releasedKeyCodes = {};
std::vector<std::uint32_t> characters = {};
};
bool IsUIEditorViewportInputBridgeKeyDown(
const UIEditorViewportInputBridgeState& state,
std::int32_t keyCode);
bool IsUIEditorViewportInputBridgePointerButtonDown(
const UIEditorViewportInputBridgeState& state,
::XCEngine::UI::UIPointerButton button);
UIEditorViewportInputBridgeFrame UpdateUIEditorViewportInputBridge(
UIEditorViewportInputBridgeState& state,
const ::XCEngine::UI::UIRect& inputRect,
const std::vector<::XCEngine::UI::UIInputEvent>& events,
const UIEditorViewportInputBridgeConfig& config = {});
} // namespace XCEngine::UI::Editor

View File

@@ -0,0 +1,221 @@
#include <XCEditor/Core/UIEditorViewportInputBridge.h>
#include <algorithm>
namespace XCEngine::UI::Editor {
namespace {
using ::XCEngine::UI::UIInputEvent;
using ::XCEngine::UI::UIInputEventType;
using ::XCEngine::UI::UIPoint;
using ::XCEngine::UI::UIPointerButton;
using ::XCEngine::UI::UIRect;
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;
}
UIPoint ToLocalPoint(const UIRect& rect, const UIPoint& point) {
return UIPoint(point.x - rect.x, point.y - rect.y);
}
std::uint8_t ButtonMask(UIPointerButton button) {
switch (button) {
case UIPointerButton::Left: return 1u << 0u;
case UIPointerButton::Right: return 1u << 1u;
case UIPointerButton::Middle: return 1u << 2u;
case UIPointerButton::X1: return 1u << 3u;
case UIPointerButton::X2: return 1u << 4u;
case UIPointerButton::None:
default:
return 0u;
}
}
bool AnyPointerButtonsDown(const UIEditorViewportInputBridgeState& state) {
return state.pointerButtonsDownMask != 0u;
}
void ClearCapture(UIEditorViewportInputBridgeState& state) {
state.captured = false;
state.captureButton = UIPointerButton::None;
}
void ClearInputState(UIEditorViewportInputBridgeState& state) {
state.pressedKeys.clear();
state.pointerButtonsDownMask = 0u;
ClearCapture(state);
state.hovered = false;
}
void UpdatePointerPosition(
UIEditorViewportInputBridgeState& state,
UIEditorViewportInputBridgeFrame& frame,
const UIRect& inputRect,
const UIPoint& screenPosition,
const ::XCEngine::UI::UIInputModifiers& modifiers) {
if (state.hasPointerPosition) {
frame.pointerDelta.x += screenPosition.x - state.lastScreenPointerPosition.x;
frame.pointerDelta.y += screenPosition.y - state.lastScreenPointerPosition.y;
frame.pointerMoved =
frame.pointerMoved ||
screenPosition.x != state.lastScreenPointerPosition.x ||
screenPosition.y != state.lastScreenPointerPosition.y;
}
state.lastScreenPointerPosition = screenPosition;
state.lastLocalPointerPosition = ToLocalPoint(inputRect, screenPosition);
state.hasPointerPosition = true;
state.modifiers = modifiers;
frame.screenPointerPosition = state.lastScreenPointerPosition;
frame.localPointerPosition = state.lastLocalPointerPosition;
frame.modifiers = state.modifiers;
}
} // namespace
bool IsUIEditorViewportInputBridgeKeyDown(
const UIEditorViewportInputBridgeState& state,
std::int32_t keyCode) {
return state.pressedKeys.contains(keyCode);
}
bool IsUIEditorViewportInputBridgePointerButtonDown(
const UIEditorViewportInputBridgeState& state,
UIPointerButton button) {
const std::uint8_t mask = ButtonMask(button);
return mask != 0u && (state.pointerButtonsDownMask & mask) != 0u;
}
UIEditorViewportInputBridgeFrame UpdateUIEditorViewportInputBridge(
UIEditorViewportInputBridgeState& state,
const UIRect& inputRect,
const std::vector<UIInputEvent>& events,
const UIEditorViewportInputBridgeConfig& config) {
UIEditorViewportInputBridgeFrame frame = {};
frame.screenPointerPosition = state.lastScreenPointerPosition;
frame.localPointerPosition = state.lastLocalPointerPosition;
frame.modifiers = state.modifiers;
for (const UIInputEvent& event : events) {
const bool inside = ContainsPoint(inputRect, event.position);
switch (event.type) {
case UIInputEventType::PointerEnter:
case UIInputEventType::PointerMove:
UpdatePointerPosition(state, frame, inputRect, event.position, event.modifiers);
state.hovered = inside;
break;
case UIInputEventType::PointerLeave:
state.hovered = false;
state.lastScreenPointerPosition = event.position;
state.lastLocalPointerPosition = ToLocalPoint(inputRect, event.position);
frame.screenPointerPosition = state.lastScreenPointerPosition;
frame.localPointerPosition = state.lastLocalPointerPosition;
frame.modifiers = state.modifiers;
break;
case UIInputEventType::PointerButtonDown:
UpdatePointerPosition(state, frame, inputRect, event.position, event.modifiers);
state.hovered = inside;
state.pointerButtonsDownMask |= ButtonMask(event.pointerButton);
frame.changedPointerButton = event.pointerButton;
if (inside) {
frame.pointerPressedInside = true;
if (config.focusOnPointerDownInside && !state.focused) {
state.focused = true;
frame.focusGained = true;
}
if (config.capturePointerOnPointerDownInside && !state.captured) {
state.captured = true;
state.captureButton = event.pointerButton;
frame.captureStarted = true;
}
} else if (config.clearFocusOnPointerDownOutside) {
if (state.focused) {
state.focused = false;
frame.focusLost = true;
}
if (state.captured) {
ClearCapture(state);
frame.captureEnded = true;
}
}
break;
case UIInputEventType::PointerButtonUp:
UpdatePointerPosition(state, frame, inputRect, event.position, event.modifiers);
state.hovered = inside;
state.pointerButtonsDownMask &= static_cast<std::uint8_t>(~ButtonMask(event.pointerButton));
frame.changedPointerButton = event.pointerButton;
if (inside) {
frame.pointerReleasedInside = true;
}
if (state.captured &&
state.captureButton == event.pointerButton &&
!AnyPointerButtonsDown(state)) {
ClearCapture(state);
frame.captureEnded = true;
}
break;
case UIInputEventType::PointerWheel:
UpdatePointerPosition(state, frame, inputRect, event.position, event.modifiers);
state.hovered = inside;
if (inside || state.captured) {
frame.wheelDelta += event.wheelDelta;
}
break;
case UIInputEventType::KeyDown:
state.modifiers = event.modifiers;
frame.modifiers = state.modifiers;
if (state.focused) {
state.pressedKeys.insert(event.keyCode);
frame.pressedKeyCodes.push_back(event.keyCode);
}
break;
case UIInputEventType::KeyUp:
state.modifiers = event.modifiers;
frame.modifiers = state.modifiers;
if (state.focused) {
state.pressedKeys.erase(event.keyCode);
frame.releasedKeyCodes.push_back(event.keyCode);
}
break;
case UIInputEventType::Character:
if (state.focused) {
frame.characters.push_back(event.character);
}
break;
case UIInputEventType::FocusLost:
if (state.focused) {
frame.focusLost = true;
}
if (state.captured) {
frame.captureEnded = true;
}
state.focused = false;
ClearInputState(state);
break;
case UIInputEventType::FocusGained:
state.modifiers = event.modifiers;
frame.modifiers = state.modifiers;
break;
case UIInputEventType::None:
default:
break;
}
}
frame.pointerInside = ContainsPoint(inputRect, state.lastScreenPointerPosition);
frame.hovered = state.hovered;
frame.focused = state.focused;
frame.captured = state.captured;
frame.screenPointerPosition = state.lastScreenPointerPosition;
frame.localPointerPosition = state.lastLocalPointerPosition;
frame.modifiers = state.modifiers;
return frame;
}
} // namespace XCEngine::UI::Editor