Build XCUI splitter foundation and test harness
This commit is contained in:
@@ -1,8 +1,19 @@
|
||||
#include <XCEngine/UI/Input/UIInputDispatcher.h>
|
||||
|
||||
#include <XCEngine/Input/InputTypes.h>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace UI {
|
||||
|
||||
namespace {
|
||||
|
||||
bool IsKeyboardActivationKey(std::int32_t keyCode) {
|
||||
return keyCode == static_cast<std::int32_t>(Input::KeyCode::Enter) ||
|
||||
keyCode == static_cast<std::int32_t>(Input::KeyCode::Space);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool UIInputDispatcher::ShouldTransferFocusOnPointerDown(
|
||||
const UIInputEvent& event,
|
||||
const UIInputPath& hoveredPath) const {
|
||||
@@ -20,10 +31,52 @@ bool UIInputDispatcher::ShouldStartActivePathOnPointerDown(
|
||||
!hoveredPath.Empty();
|
||||
}
|
||||
|
||||
bool UIInputDispatcher::ShouldStartActivePathOnKeyDown(
|
||||
const UIInputEvent& event) const {
|
||||
return event.type == UIInputEventType::KeyDown &&
|
||||
!m_focusController.GetFocusedPath().Empty() &&
|
||||
IsKeyboardActivationKey(event.keyCode);
|
||||
}
|
||||
|
||||
bool UIInputDispatcher::ShouldClearActivePathOnKeyUp(
|
||||
const UIInputEvent& event) const {
|
||||
return event.type == UIInputEventType::KeyUp &&
|
||||
IsKeyboardActivationKey(event.keyCode);
|
||||
}
|
||||
|
||||
UIShortcutContext UIInputDispatcher::BuildEffectiveShortcutContext(
|
||||
const UIInputPath& hoveredPath) const {
|
||||
UIShortcutContext context = m_shortcutContext;
|
||||
context.focusedPath = m_focusController.GetFocusedPath();
|
||||
context.activePath = m_focusController.GetActivePath();
|
||||
context.hoveredPath = hoveredPath;
|
||||
|
||||
if (context.commandScope.path.Empty()) {
|
||||
context.commandScope.path = !context.focusedPath.Empty()
|
||||
? context.focusedPath
|
||||
: context.activePath;
|
||||
}
|
||||
|
||||
if (context.commandScope.widgetId == 0 &&
|
||||
!context.commandScope.path.Empty()) {
|
||||
context.commandScope.widgetId = context.commandScope.path.Target();
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
bool UIInputDispatcher::ShouldSuppressShortcutMatch(
|
||||
const UIInputEvent&,
|
||||
const UIShortcutMatch& shortcutMatch,
|
||||
const UIShortcutContext& shortcutContext) const {
|
||||
return shortcutMatch.matched && shortcutContext.textInputActive;
|
||||
}
|
||||
|
||||
UIInputDispatchSummary UIInputDispatcher::FinalizeDispatch(
|
||||
const UIInputEvent& event,
|
||||
UIInputDispatchSummary&& summary) {
|
||||
if (event.type == UIInputEventType::PointerButtonUp) {
|
||||
if (event.type == UIInputEventType::PointerButtonUp ||
|
||||
ShouldClearActivePathOnKeyUp(event)) {
|
||||
m_focusController.ClearActivePath();
|
||||
}
|
||||
|
||||
|
||||
@@ -6,14 +6,18 @@ namespace UI {
|
||||
namespace {
|
||||
|
||||
const UIInputPath& ResolvePrimaryShortcutPath(const UIShortcutContext& context) {
|
||||
if (!context.activePath.Empty()) {
|
||||
return context.activePath;
|
||||
if (!context.commandScope.path.Empty()) {
|
||||
return context.commandScope.path;
|
||||
}
|
||||
|
||||
if (!context.focusedPath.Empty()) {
|
||||
return context.focusedPath;
|
||||
}
|
||||
|
||||
if (!context.activePath.Empty()) {
|
||||
return context.activePath;
|
||||
}
|
||||
|
||||
return context.hoveredPath;
|
||||
}
|
||||
|
||||
@@ -26,6 +30,28 @@ bool ModifiersEqual(
|
||||
lhs.super == rhs.super;
|
||||
}
|
||||
|
||||
UIElementId ResolveScopedOwnerId(
|
||||
UIShortcutScope scope,
|
||||
const UIShortcutContext& context) {
|
||||
switch (scope) {
|
||||
case UIShortcutScope::Window:
|
||||
return context.commandScope.windowId;
|
||||
case UIShortcutScope::Panel:
|
||||
return context.commandScope.panelId;
|
||||
case UIShortcutScope::Widget:
|
||||
if (context.commandScope.widgetId != 0) {
|
||||
return context.commandScope.widgetId;
|
||||
}
|
||||
|
||||
return context.commandScope.path.Empty()
|
||||
? 0
|
||||
: context.commandScope.path.Target();
|
||||
case UIShortcutScope::Global:
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool IsBindingActive(
|
||||
const UIShortcutBinding& binding,
|
||||
const UIShortcutContext& context) {
|
||||
@@ -37,6 +63,11 @@ bool IsBindingActive(
|
||||
return false;
|
||||
}
|
||||
|
||||
const UIElementId resolvedOwnerId = ResolveScopedOwnerId(binding.scope, context);
|
||||
if (resolvedOwnerId != 0) {
|
||||
return binding.ownerId == resolvedOwnerId;
|
||||
}
|
||||
|
||||
return ResolvePrimaryShortcutPath(context).Contains(binding.ownerId);
|
||||
}
|
||||
|
||||
@@ -91,6 +122,7 @@ bool UIShortcutRegistry::UnregisterBinding(std::uint64_t bindingId) {
|
||||
|
||||
void UIShortcutRegistry::Clear() {
|
||||
m_bindings.clear();
|
||||
m_nextBindingId = 1;
|
||||
}
|
||||
|
||||
UIShortcutMatch UIShortcutRegistry::Match(
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,115 +0,0 @@
|
||||
#include <XCEngine/UI/Widgets/UIEditorCollectionPrimitives.h>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace UI {
|
||||
namespace Widgets {
|
||||
|
||||
namespace {
|
||||
|
||||
float ResolveFloatToken(
|
||||
const Style::UITheme& theme,
|
||||
const char* tokenName,
|
||||
float fallbackValue) {
|
||||
const Style::UITokenResolveResult result =
|
||||
theme.ResolveToken(tokenName, Style::UIStyleValueType::Float);
|
||||
if (result.status != Style::UITokenResolveStatus::Resolved) {
|
||||
return fallbackValue;
|
||||
}
|
||||
|
||||
const float* value = result.value.TryGetFloat();
|
||||
return value != nullptr ? *value : fallbackValue;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
UIEditorCollectionPrimitiveKind ClassifyUIEditorCollectionPrimitive(std::string_view tagName) {
|
||||
if (tagName == "ScrollView") {
|
||||
return UIEditorCollectionPrimitiveKind::ScrollView;
|
||||
}
|
||||
if (tagName == "TreeView") {
|
||||
return UIEditorCollectionPrimitiveKind::TreeView;
|
||||
}
|
||||
if (tagName == "TreeItem") {
|
||||
return UIEditorCollectionPrimitiveKind::TreeItem;
|
||||
}
|
||||
if (tagName == "ListView") {
|
||||
return UIEditorCollectionPrimitiveKind::ListView;
|
||||
}
|
||||
if (tagName == "ListItem") {
|
||||
return UIEditorCollectionPrimitiveKind::ListItem;
|
||||
}
|
||||
if (tagName == "PropertySection") {
|
||||
return UIEditorCollectionPrimitiveKind::PropertySection;
|
||||
}
|
||||
if (tagName == "FieldRow") {
|
||||
return UIEditorCollectionPrimitiveKind::FieldRow;
|
||||
}
|
||||
|
||||
return UIEditorCollectionPrimitiveKind::None;
|
||||
}
|
||||
|
||||
bool IsUIEditorCollectionPrimitiveContainer(UIEditorCollectionPrimitiveKind kind) {
|
||||
return kind == UIEditorCollectionPrimitiveKind::ScrollView ||
|
||||
kind == UIEditorCollectionPrimitiveKind::TreeView ||
|
||||
kind == UIEditorCollectionPrimitiveKind::ListView ||
|
||||
kind == UIEditorCollectionPrimitiveKind::PropertySection;
|
||||
}
|
||||
|
||||
bool UsesUIEditorCollectionPrimitiveColumnLayout(UIEditorCollectionPrimitiveKind kind) {
|
||||
return kind == UIEditorCollectionPrimitiveKind::TreeView ||
|
||||
kind == UIEditorCollectionPrimitiveKind::ListView ||
|
||||
kind == UIEditorCollectionPrimitiveKind::PropertySection;
|
||||
}
|
||||
|
||||
bool IsUIEditorCollectionPrimitiveHoverable(UIEditorCollectionPrimitiveKind kind) {
|
||||
return kind == UIEditorCollectionPrimitiveKind::TreeItem ||
|
||||
kind == UIEditorCollectionPrimitiveKind::ListItem ||
|
||||
kind == UIEditorCollectionPrimitiveKind::PropertySection ||
|
||||
kind == UIEditorCollectionPrimitiveKind::FieldRow;
|
||||
}
|
||||
|
||||
bool DoesUIEditorCollectionPrimitiveClipChildren(UIEditorCollectionPrimitiveKind kind) {
|
||||
return kind == UIEditorCollectionPrimitiveKind::ScrollView ||
|
||||
kind == UIEditorCollectionPrimitiveKind::TreeView ||
|
||||
kind == UIEditorCollectionPrimitiveKind::ListView;
|
||||
}
|
||||
|
||||
float ResolveUIEditorCollectionPrimitivePadding(
|
||||
UIEditorCollectionPrimitiveKind kind,
|
||||
const Style::UITheme& theme) {
|
||||
return kind == UIEditorCollectionPrimitiveKind::TreeView ||
|
||||
kind == UIEditorCollectionPrimitiveKind::ListView ||
|
||||
kind == UIEditorCollectionPrimitiveKind::PropertySection
|
||||
? ResolveFloatToken(theme, "space.cardInset", 12.0f)
|
||||
: 0.0f;
|
||||
}
|
||||
|
||||
float ResolveUIEditorCollectionPrimitiveDefaultHeight(
|
||||
UIEditorCollectionPrimitiveKind kind,
|
||||
const Style::UITheme& theme) {
|
||||
switch (kind) {
|
||||
case UIEditorCollectionPrimitiveKind::TreeItem:
|
||||
return ResolveFloatToken(theme, "size.treeItemHeight", 28.0f);
|
||||
case UIEditorCollectionPrimitiveKind::ListItem:
|
||||
return ResolveFloatToken(theme, "size.listItemHeight", 60.0f);
|
||||
case UIEditorCollectionPrimitiveKind::FieldRow:
|
||||
return ResolveFloatToken(theme, "size.fieldRowHeight", 32.0f);
|
||||
case UIEditorCollectionPrimitiveKind::PropertySection:
|
||||
return ResolveFloatToken(theme, "size.propertySectionHeight", 148.0f);
|
||||
default:
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
float ResolveUIEditorCollectionPrimitiveIndent(
|
||||
UIEditorCollectionPrimitiveKind kind,
|
||||
const Style::UITheme& theme,
|
||||
float indentLevel) {
|
||||
return kind == UIEditorCollectionPrimitiveKind::TreeItem
|
||||
? indentLevel * ResolveFloatToken(theme, "size.treeIndent", 18.0f)
|
||||
: 0.0f;
|
||||
}
|
||||
|
||||
} // namespace Widgets
|
||||
} // namespace UI
|
||||
} // namespace XCEngine
|
||||
Reference in New Issue
Block a user