Build XCEditor viewport input bridge foundation
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
221
new_editor/src/Core/UIEditorViewportInputBridge.cpp
Normal file
221
new_editor/src/Core/UIEditorViewportInputBridge.cpp
Normal 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
|
||||
Reference in New Issue
Block a user