Add XCUI command routing and widget state models

This commit is contained in:
2026-04-05 12:10:55 +08:00
parent 511e94fd30
commit 68c4c80b06
18 changed files with 1329 additions and 9 deletions

View 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

View 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