Add XCUI command routing and widget state models
This commit is contained in:
@@ -522,9 +522,13 @@ add_library(XCEngine STATIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/UI/Text/UITextInputController.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/UI/Widgets/UIEditorCollectionPrimitives.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/UI/Widgets/UIExpansionModel.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/UI/Widgets/UIKeyboardNavigationModel.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/UI/Widgets/UIPropertyEditModel.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/UI/Widgets/UISelectionModel.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/UI/Widgets/UIEditorCollectionPrimitives.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/UI/Widgets/UIExpansionModel.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/UI/Widgets/UIKeyboardNavigationModel.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/UI/Widgets/UIPropertyEditModel.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/UI/Widgets/UISelectionModel.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/UI/Runtime/UIScreenTypes.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/UI/Runtime/UIScreenDocumentHost.h
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace UI {
|
||||
namespace Widgets {
|
||||
|
||||
class UIKeyboardNavigationModel {
|
||||
public:
|
||||
static constexpr std::size_t InvalidIndex = static_cast<std::size_t>(-1);
|
||||
|
||||
std::size_t GetItemCount() const;
|
||||
bool SetItemCount(std::size_t itemCount);
|
||||
bool ClampToItemCount();
|
||||
|
||||
bool HasCurrentIndex() const;
|
||||
std::size_t GetCurrentIndex() const;
|
||||
bool SetCurrentIndex(std::size_t index, bool updateAnchor = true);
|
||||
bool ClearCurrentIndex();
|
||||
|
||||
bool HasSelectionAnchor() const;
|
||||
std::size_t GetSelectionAnchorIndex() const;
|
||||
bool SetSelectionAnchorIndex(std::size_t index);
|
||||
bool ClearSelectionAnchor();
|
||||
|
||||
bool MoveNext();
|
||||
bool MovePrevious();
|
||||
bool MoveHome();
|
||||
bool MoveEnd();
|
||||
|
||||
private:
|
||||
std::size_t m_itemCount = 0;
|
||||
std::size_t m_currentIndex = InvalidIndex;
|
||||
std::size_t m_selectionAnchorIndex = InvalidIndex;
|
||||
};
|
||||
|
||||
} // namespace Widgets
|
||||
} // namespace UI
|
||||
} // namespace XCEngine
|
||||
32
engine/include/XCEngine/UI/Widgets/UIPropertyEditModel.h
Normal file
32
engine/include/XCEngine/UI/Widgets/UIPropertyEditModel.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace UI {
|
||||
namespace Widgets {
|
||||
|
||||
class UIPropertyEditModel {
|
||||
public:
|
||||
bool HasActiveEdit() const;
|
||||
const std::string& GetActiveFieldId() const;
|
||||
const std::string& GetStagedValue() const;
|
||||
bool IsDirty() const;
|
||||
|
||||
bool BeginEdit(std::string fieldId, std::string initialValue);
|
||||
bool UpdateStagedValue(std::string stagedValue);
|
||||
bool CommitEdit(
|
||||
std::string* outFieldId = nullptr,
|
||||
std::string* outCommittedValue = nullptr);
|
||||
bool CancelEdit();
|
||||
|
||||
private:
|
||||
std::string m_activeFieldId = {};
|
||||
std::string m_baselineValue = {};
|
||||
std::string m_stagedValue = {};
|
||||
bool m_dirty = false;
|
||||
};
|
||||
|
||||
} // namespace Widgets
|
||||
} // namespace UI
|
||||
} // namespace XCEngine
|
||||
162
engine/src/UI/Widgets/UIKeyboardNavigationModel.cpp
Normal file
162
engine/src/UI/Widgets/UIKeyboardNavigationModel.cpp
Normal file
@@ -0,0 +1,162 @@
|
||||
#include <XCEngine/UI/Widgets/UIKeyboardNavigationModel.h>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace UI {
|
||||
namespace Widgets {
|
||||
|
||||
namespace {
|
||||
|
||||
bool ClampIndexToCount(std::size_t itemCount, std::size_t& index) {
|
||||
if (index == UIKeyboardNavigationModel::InvalidIndex) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (itemCount == 0u) {
|
||||
index = UIKeyboardNavigationModel::InvalidIndex;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (index < itemCount) {
|
||||
return false;
|
||||
}
|
||||
|
||||
index = itemCount - 1u;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::size_t UIKeyboardNavigationModel::GetItemCount() const {
|
||||
return m_itemCount;
|
||||
}
|
||||
|
||||
bool UIKeyboardNavigationModel::SetItemCount(std::size_t itemCount) {
|
||||
const bool countChanged = m_itemCount != itemCount;
|
||||
m_itemCount = itemCount;
|
||||
return ClampToItemCount() || countChanged;
|
||||
}
|
||||
|
||||
bool UIKeyboardNavigationModel::ClampToItemCount() {
|
||||
bool changed = false;
|
||||
changed = ClampIndexToCount(m_itemCount, m_currentIndex) || changed;
|
||||
changed = ClampIndexToCount(m_itemCount, m_selectionAnchorIndex) || changed;
|
||||
return changed;
|
||||
}
|
||||
|
||||
bool UIKeyboardNavigationModel::HasCurrentIndex() const {
|
||||
return m_currentIndex != InvalidIndex;
|
||||
}
|
||||
|
||||
std::size_t UIKeyboardNavigationModel::GetCurrentIndex() const {
|
||||
return m_currentIndex;
|
||||
}
|
||||
|
||||
bool UIKeyboardNavigationModel::SetCurrentIndex(std::size_t index, bool updateAnchor) {
|
||||
if (index >= m_itemCount) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool changed = false;
|
||||
if (m_currentIndex != index) {
|
||||
m_currentIndex = index;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (updateAnchor && m_selectionAnchorIndex != index) {
|
||||
m_selectionAnchorIndex = index;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
bool UIKeyboardNavigationModel::ClearCurrentIndex() {
|
||||
if (m_currentIndex == InvalidIndex) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_currentIndex = InvalidIndex;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UIKeyboardNavigationModel::HasSelectionAnchor() const {
|
||||
return m_selectionAnchorIndex != InvalidIndex;
|
||||
}
|
||||
|
||||
std::size_t UIKeyboardNavigationModel::GetSelectionAnchorIndex() const {
|
||||
return m_selectionAnchorIndex;
|
||||
}
|
||||
|
||||
bool UIKeyboardNavigationModel::SetSelectionAnchorIndex(std::size_t index) {
|
||||
if (index >= m_itemCount) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_selectionAnchorIndex == index) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_selectionAnchorIndex = index;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UIKeyboardNavigationModel::ClearSelectionAnchor() {
|
||||
if (m_selectionAnchorIndex == InvalidIndex) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_selectionAnchorIndex = InvalidIndex;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UIKeyboardNavigationModel::MoveNext() {
|
||||
if (m_itemCount == 0u) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_currentIndex == InvalidIndex) {
|
||||
return SetCurrentIndex(0u);
|
||||
}
|
||||
|
||||
if ((m_currentIndex + 1u) >= m_itemCount) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return SetCurrentIndex(m_currentIndex + 1u);
|
||||
}
|
||||
|
||||
bool UIKeyboardNavigationModel::MovePrevious() {
|
||||
if (m_itemCount == 0u) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_currentIndex == InvalidIndex) {
|
||||
return SetCurrentIndex(m_itemCount - 1u);
|
||||
}
|
||||
|
||||
if (m_currentIndex == 0u) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return SetCurrentIndex(m_currentIndex - 1u);
|
||||
}
|
||||
|
||||
bool UIKeyboardNavigationModel::MoveHome() {
|
||||
if (m_itemCount == 0u) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return SetCurrentIndex(0u);
|
||||
}
|
||||
|
||||
bool UIKeyboardNavigationModel::MoveEnd() {
|
||||
if (m_itemCount == 0u) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return SetCurrentIndex(m_itemCount - 1u);
|
||||
}
|
||||
|
||||
} // namespace Widgets
|
||||
} // namespace UI
|
||||
} // namespace XCEngine
|
||||
95
engine/src/UI/Widgets/UIPropertyEditModel.cpp
Normal file
95
engine/src/UI/Widgets/UIPropertyEditModel.cpp
Normal file
@@ -0,0 +1,95 @@
|
||||
#include <XCEngine/UI/Widgets/UIPropertyEditModel.h>
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace UI {
|
||||
namespace Widgets {
|
||||
|
||||
bool UIPropertyEditModel::HasActiveEdit() const {
|
||||
return !m_activeFieldId.empty();
|
||||
}
|
||||
|
||||
const std::string& UIPropertyEditModel::GetActiveFieldId() const {
|
||||
return m_activeFieldId;
|
||||
}
|
||||
|
||||
const std::string& UIPropertyEditModel::GetStagedValue() const {
|
||||
return m_stagedValue;
|
||||
}
|
||||
|
||||
bool UIPropertyEditModel::IsDirty() const {
|
||||
return m_dirty;
|
||||
}
|
||||
|
||||
bool UIPropertyEditModel::BeginEdit(std::string fieldId, std::string initialValue) {
|
||||
if (fieldId.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool stateChanged =
|
||||
m_activeFieldId != fieldId ||
|
||||
m_baselineValue != initialValue ||
|
||||
m_stagedValue != initialValue ||
|
||||
m_dirty;
|
||||
if (!stateChanged) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_activeFieldId = std::move(fieldId);
|
||||
m_baselineValue = std::move(initialValue);
|
||||
m_stagedValue = m_baselineValue;
|
||||
m_dirty = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UIPropertyEditModel::UpdateStagedValue(std::string stagedValue) {
|
||||
if (!HasActiveEdit()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_stagedValue == stagedValue) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_stagedValue = std::move(stagedValue);
|
||||
m_dirty = (m_stagedValue != m_baselineValue);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UIPropertyEditModel::CommitEdit(
|
||||
std::string* outFieldId,
|
||||
std::string* outCommittedValue) {
|
||||
if (!HasActiveEdit()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (outFieldId != nullptr) {
|
||||
*outFieldId = m_activeFieldId;
|
||||
}
|
||||
if (outCommittedValue != nullptr) {
|
||||
*outCommittedValue = m_stagedValue;
|
||||
}
|
||||
|
||||
m_activeFieldId.clear();
|
||||
m_baselineValue.clear();
|
||||
m_stagedValue.clear();
|
||||
m_dirty = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UIPropertyEditModel::CancelEdit() {
|
||||
if (!HasActiveEdit()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_activeFieldId.clear();
|
||||
m_baselineValue.clear();
|
||||
m_stagedValue.clear();
|
||||
m_dirty = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Widgets
|
||||
} // namespace UI
|
||||
} // namespace XCEngine
|
||||
Reference in New Issue
Block a user