Add editor list, scroll, and property grid widgets

This commit is contained in:
2026-04-07 16:57:04 +08:00
parent 0308be1483
commit e22ce763c2
32 changed files with 6057 additions and 0 deletions

View File

@@ -0,0 +1,44 @@
#pragma once
#include <XCEditor/Widgets/UIEditorListView.h>
#include <XCEngine/UI/Types.h>
#include <XCEngine/UI/Widgets/UIKeyboardNavigationModel.h>
#include <XCEngine/UI/Widgets/UISelectionModel.h>
#include <string>
#include <vector>
namespace XCEngine::UI::Editor {
struct UIEditorListViewInteractionState {
Widgets::UIEditorListViewState listViewState = {};
::XCEngine::UI::Widgets::UIKeyboardNavigationModel keyboardNavigation = {};
::XCEngine::UI::UIPoint pointerPosition = {};
bool hasPointerPosition = false;
};
struct UIEditorListViewInteractionResult {
bool consumed = false;
bool selectionChanged = false;
bool keyboardNavigated = false;
bool secondaryClicked = false;
Widgets::UIEditorListViewHitTarget hitTarget = {};
std::string selectedItemId = {};
std::size_t selectedIndex = Widgets::UIEditorListViewInvalidIndex;
};
struct UIEditorListViewInteractionFrame {
Widgets::UIEditorListViewLayout layout = {};
UIEditorListViewInteractionResult result = {};
};
UIEditorListViewInteractionFrame UpdateUIEditorListViewInteraction(
UIEditorListViewInteractionState& state,
::XCEngine::UI::Widgets::UISelectionModel& selectionModel,
const ::XCEngine::UI::UIRect& bounds,
const std::vector<Widgets::UIEditorListViewItem>& items,
const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents,
const Widgets::UIEditorListViewMetrics& metrics = {});
} // namespace XCEngine::UI::Editor

View File

@@ -0,0 +1,58 @@
#pragma once
#include <XCEditor/Widgets/UIEditorPropertyGrid.h>
#include <XCEngine/UI/Text/UITextInputController.h>
#include <XCEngine/UI/Types.h>
#include <XCEngine/UI/Widgets/UIExpansionModel.h>
#include <XCEngine/UI/Widgets/UIKeyboardNavigationModel.h>
#include <XCEngine/UI/Widgets/UIPropertyEditModel.h>
#include <XCEngine/UI/Widgets/UISelectionModel.h>
#include <string>
#include <vector>
namespace XCEngine::UI::Editor {
struct UIEditorPropertyGridInteractionState {
Widgets::UIEditorPropertyGridState propertyGridState = {};
::XCEngine::UI::Widgets::UIKeyboardNavigationModel keyboardNavigation = {};
::XCEngine::UI::Text::UITextInputState textInputState = {};
::XCEngine::UI::UIPoint pointerPosition = {};
bool hasPointerPosition = false;
};
struct UIEditorPropertyGridInteractionResult {
bool consumed = false;
bool sectionToggled = false;
bool selectionChanged = false;
bool keyboardNavigated = false;
bool editStarted = false;
bool editValueChanged = false;
bool editCommitted = false;
bool editCanceled = false;
bool secondaryClicked = false;
Widgets::UIEditorPropertyGridHitTarget hitTarget = {};
std::string toggledSectionId = {};
std::string selectedFieldId = {};
std::string activeFieldId = {};
std::string committedFieldId = {};
std::string committedValue = {};
};
struct UIEditorPropertyGridInteractionFrame {
Widgets::UIEditorPropertyGridLayout layout = {};
UIEditorPropertyGridInteractionResult result = {};
};
UIEditorPropertyGridInteractionFrame UpdateUIEditorPropertyGridInteraction(
UIEditorPropertyGridInteractionState& state,
::XCEngine::UI::Widgets::UISelectionModel& selectionModel,
::XCEngine::UI::Widgets::UIExpansionModel& expansionModel,
::XCEngine::UI::Widgets::UIPropertyEditModel& propertyEditModel,
const ::XCEngine::UI::UIRect& bounds,
const std::vector<Widgets::UIEditorPropertyGridSection>& sections,
const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents,
const Widgets::UIEditorPropertyGridMetrics& metrics = {});
} // namespace XCEngine::UI::Editor

View File

@@ -0,0 +1,42 @@
#pragma once
#include <XCEditor/Widgets/UIEditorScrollView.h>
#include <XCEngine/UI/Types.h>
#include <vector>
namespace XCEngine::UI::Editor {
struct UIEditorScrollViewInteractionState {
Widgets::UIEditorScrollViewState scrollViewState = {};
::XCEngine::UI::UIPoint pointerPosition = {};
float thumbDragStartPointerY = 0.0f;
float thumbDragStartOffset = 0.0f;
bool hasPointerPosition = false;
};
struct UIEditorScrollViewInteractionResult {
bool consumed = false;
bool offsetChanged = false;
bool focusChanged = false;
bool startedThumbDrag = false;
bool endedThumbDrag = false;
Widgets::UIEditorScrollViewHitTarget hitTarget = {};
float verticalOffset = 0.0f;
};
struct UIEditorScrollViewInteractionFrame {
Widgets::UIEditorScrollViewLayout layout = {};
UIEditorScrollViewInteractionResult result = {};
};
UIEditorScrollViewInteractionFrame UpdateUIEditorScrollViewInteraction(
UIEditorScrollViewInteractionState& state,
float& verticalOffset,
const ::XCEngine::UI::UIRect& bounds,
float contentHeight,
const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents,
const Widgets::UIEditorScrollViewMetrics& metrics = {});
} // namespace XCEngine::UI::Editor

View File

@@ -0,0 +1,121 @@
#pragma once
#include <XCEngine/UI/DrawData.h>
#include <XCEngine/UI/Widgets/UISelectionModel.h>
#include <cstddef>
#include <cstdint>
#include <string>
#include <string_view>
#include <vector>
namespace XCEngine::UI::Editor::Widgets {
inline constexpr std::size_t UIEditorListViewInvalidIndex = static_cast<std::size_t>(-1);
enum class UIEditorListViewHitTargetKind : std::uint8_t {
None = 0,
Row
};
struct UIEditorListViewItem {
std::string itemId = {};
std::string primaryText = {};
std::string secondaryText = {};
float desiredHeight = 0.0f;
};
struct UIEditorListViewState {
std::string hoveredItemId = {};
bool focused = false;
};
struct UIEditorListViewMetrics {
float rowHeight = 44.0f;
float rowGap = 2.0f;
float horizontalPadding = 10.0f;
float primaryTextInsetY = 7.0f;
float secondaryTextInsetY = 23.0f;
float singleLineTextInsetY = 13.0f;
float cornerRounding = 6.0f;
float borderThickness = 1.0f;
float focusedBorderThickness = 2.0f;
};
struct UIEditorListViewPalette {
::XCEngine::UI::UIColor surfaceColor =
::XCEngine::UI::UIColor(0.14f, 0.14f, 0.14f, 1.0f);
::XCEngine::UI::UIColor borderColor =
::XCEngine::UI::UIColor(0.29f, 0.29f, 0.29f, 1.0f);
::XCEngine::UI::UIColor focusedBorderColor =
::XCEngine::UI::UIColor(0.84f, 0.84f, 0.84f, 1.0f);
::XCEngine::UI::UIColor rowHoverColor =
::XCEngine::UI::UIColor(0.24f, 0.24f, 0.24f, 1.0f);
::XCEngine::UI::UIColor rowSelectedColor =
::XCEngine::UI::UIColor(0.31f, 0.31f, 0.31f, 1.0f);
::XCEngine::UI::UIColor rowSelectedFocusedColor =
::XCEngine::UI::UIColor(0.40f, 0.40f, 0.40f, 1.0f);
::XCEngine::UI::UIColor primaryTextColor =
::XCEngine::UI::UIColor(0.94f, 0.94f, 0.94f, 1.0f);
::XCEngine::UI::UIColor secondaryTextColor =
::XCEngine::UI::UIColor(0.70f, 0.70f, 0.70f, 1.0f);
};
struct UIEditorListViewLayout {
::XCEngine::UI::UIRect bounds = {};
std::vector<std::size_t> itemIndices = {};
std::vector<::XCEngine::UI::UIRect> rowRects = {};
std::vector<::XCEngine::UI::UIRect> primaryTextRects = {};
std::vector<::XCEngine::UI::UIRect> secondaryTextRects = {};
std::vector<bool> hasSecondaryText = {};
};
struct UIEditorListViewHitTarget {
UIEditorListViewHitTargetKind kind = UIEditorListViewHitTargetKind::None;
std::size_t visibleIndex = UIEditorListViewInvalidIndex;
std::size_t itemIndex = UIEditorListViewInvalidIndex;
};
bool IsUIEditorListViewPointInside(
const ::XCEngine::UI::UIRect& rect,
const ::XCEngine::UI::UIPoint& point);
std::size_t FindUIEditorListViewItemIndex(
const std::vector<UIEditorListViewItem>& items,
std::string_view itemId);
UIEditorListViewLayout BuildUIEditorListViewLayout(
const ::XCEngine::UI::UIRect& bounds,
const std::vector<UIEditorListViewItem>& items,
const UIEditorListViewMetrics& metrics = {});
UIEditorListViewHitTarget HitTestUIEditorListView(
const UIEditorListViewLayout& layout,
const ::XCEngine::UI::UIPoint& point);
void AppendUIEditorListViewBackground(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorListViewLayout& layout,
const std::vector<UIEditorListViewItem>& items,
const ::XCEngine::UI::Widgets::UISelectionModel& selectionModel,
const UIEditorListViewState& state,
const UIEditorListViewPalette& palette = {},
const UIEditorListViewMetrics& metrics = {});
void AppendUIEditorListViewForeground(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorListViewLayout& layout,
const std::vector<UIEditorListViewItem>& items,
const UIEditorListViewPalette& palette = {},
const UIEditorListViewMetrics& metrics = {});
void AppendUIEditorListView(
::XCEngine::UI::UIDrawList& drawList,
const ::XCEngine::UI::UIRect& bounds,
const std::vector<UIEditorListViewItem>& items,
const ::XCEngine::UI::Widgets::UISelectionModel& selectionModel,
const UIEditorListViewState& state,
const UIEditorListViewPalette& palette = {},
const UIEditorListViewMetrics& metrics = {});
} // namespace XCEngine::UI::Editor::Widgets

View File

@@ -0,0 +1,200 @@
#pragma once
#include <XCEngine/UI/DrawData.h>
#include <XCEngine/UI/Widgets/UIExpansionModel.h>
#include <XCEngine/UI/Widgets/UIPropertyEditModel.h>
#include <XCEngine/UI/Widgets/UISelectionModel.h>
#include <cstddef>
#include <cstdint>
#include <string>
#include <string_view>
#include <vector>
namespace XCEngine::UI::Editor::Widgets {
inline constexpr std::size_t UIEditorPropertyGridInvalidIndex = static_cast<std::size_t>(-1);
enum class UIEditorPropertyGridHitTargetKind : std::uint8_t {
None = 0,
SectionHeader,
FieldRow,
ValueBox
};
struct UIEditorPropertyGridFieldLocation {
std::size_t sectionIndex = UIEditorPropertyGridInvalidIndex;
std::size_t fieldIndex = UIEditorPropertyGridInvalidIndex;
constexpr bool IsValid() const {
return sectionIndex != UIEditorPropertyGridInvalidIndex &&
fieldIndex != UIEditorPropertyGridInvalidIndex;
}
};
struct UIEditorPropertyGridField {
std::string fieldId = {};
std::string label = {};
std::string valueText = {};
bool readOnly = false;
float desiredHeight = 0.0f;
};
struct UIEditorPropertyGridSection {
std::string sectionId = {};
std::string title = {};
std::vector<UIEditorPropertyGridField> fields = {};
float desiredHeaderHeight = 0.0f;
};
struct UIEditorPropertyGridState {
std::string hoveredSectionId = {};
std::string hoveredFieldId = {};
bool focused = false;
};
struct UIEditorPropertyGridMetrics {
float contentInset = 8.0f;
float sectionGap = 8.0f;
float sectionHeaderHeight = 32.0f;
float fieldRowHeight = 32.0f;
float rowGap = 2.0f;
float horizontalPadding = 12.0f;
float controlColumnStart = 236.0f;
float labelControlGap = 20.0f;
float disclosureExtent = 12.0f;
float disclosureLabelGap = 8.0f;
float sectionTextInsetY = 8.0f;
float labelTextInsetY = 8.0f;
float valueTextInsetY = 8.0f;
float valueBoxInsetY = 4.0f;
float valueBoxInsetX = 8.0f;
float cornerRounding = 6.0f;
float valueBoxRounding = 5.0f;
float borderThickness = 1.0f;
float focusedBorderThickness = 2.0f;
float editOutlineThickness = 1.0f;
};
struct UIEditorPropertyGridPalette {
::XCEngine::UI::UIColor surfaceColor =
::XCEngine::UI::UIColor(0.14f, 0.14f, 0.14f, 1.0f);
::XCEngine::UI::UIColor borderColor =
::XCEngine::UI::UIColor(0.29f, 0.29f, 0.29f, 1.0f);
::XCEngine::UI::UIColor focusedBorderColor =
::XCEngine::UI::UIColor(0.84f, 0.84f, 0.84f, 1.0f);
::XCEngine::UI::UIColor sectionHeaderColor =
::XCEngine::UI::UIColor(0.19f, 0.19f, 0.19f, 1.0f);
::XCEngine::UI::UIColor sectionHeaderHoverColor =
::XCEngine::UI::UIColor(0.24f, 0.24f, 0.24f, 1.0f);
::XCEngine::UI::UIColor fieldHoverColor =
::XCEngine::UI::UIColor(0.24f, 0.24f, 0.24f, 1.0f);
::XCEngine::UI::UIColor fieldSelectedColor =
::XCEngine::UI::UIColor(0.31f, 0.31f, 0.31f, 1.0f);
::XCEngine::UI::UIColor fieldSelectedFocusedColor =
::XCEngine::UI::UIColor(0.40f, 0.40f, 0.40f, 1.0f);
::XCEngine::UI::UIColor valueBoxColor =
::XCEngine::UI::UIColor(0.17f, 0.17f, 0.17f, 1.0f);
::XCEngine::UI::UIColor valueBoxHoverColor =
::XCEngine::UI::UIColor(0.22f, 0.22f, 0.22f, 1.0f);
::XCEngine::UI::UIColor valueBoxEditingColor =
::XCEngine::UI::UIColor(0.24f, 0.24f, 0.24f, 1.0f);
::XCEngine::UI::UIColor valueBoxReadOnlyColor =
::XCEngine::UI::UIColor(0.15f, 0.15f, 0.15f, 1.0f);
::XCEngine::UI::UIColor valueBoxBorderColor =
::XCEngine::UI::UIColor(0.32f, 0.32f, 0.32f, 1.0f);
::XCEngine::UI::UIColor valueBoxEditingBorderColor =
::XCEngine::UI::UIColor(0.75f, 0.75f, 0.75f, 1.0f);
::XCEngine::UI::UIColor disclosureColor =
::XCEngine::UI::UIColor(0.74f, 0.74f, 0.74f, 1.0f);
::XCEngine::UI::UIColor sectionTextColor =
::XCEngine::UI::UIColor(0.94f, 0.94f, 0.94f, 1.0f);
::XCEngine::UI::UIColor labelTextColor =
::XCEngine::UI::UIColor(0.84f, 0.84f, 0.84f, 1.0f);
::XCEngine::UI::UIColor valueTextColor =
::XCEngine::UI::UIColor(0.94f, 0.94f, 0.94f, 1.0f);
::XCEngine::UI::UIColor readOnlyValueTextColor =
::XCEngine::UI::UIColor(0.60f, 0.60f, 0.60f, 1.0f);
::XCEngine::UI::UIColor editTagColor =
::XCEngine::UI::UIColor(0.62f, 0.78f, 0.96f, 1.0f);
};
struct UIEditorPropertyGridLayout {
::XCEngine::UI::UIRect bounds = {};
std::vector<std::size_t> sectionIndices = {};
std::vector<::XCEngine::UI::UIRect> sectionHeaderRects = {};
std::vector<::XCEngine::UI::UIRect> sectionDisclosureRects = {};
std::vector<::XCEngine::UI::UIRect> sectionTitleRects = {};
std::vector<bool> sectionExpanded = {};
std::vector<std::size_t> visibleFieldSectionIndices = {};
std::vector<std::size_t> visibleFieldIndices = {};
std::vector<::XCEngine::UI::UIRect> fieldRowRects = {};
std::vector<::XCEngine::UI::UIRect> fieldLabelRects = {};
std::vector<::XCEngine::UI::UIRect> fieldValueRects = {};
std::vector<bool> fieldReadOnly = {};
};
struct UIEditorPropertyGridHitTarget {
UIEditorPropertyGridHitTargetKind kind = UIEditorPropertyGridHitTargetKind::None;
std::size_t sectionIndex = UIEditorPropertyGridInvalidIndex;
std::size_t fieldIndex = UIEditorPropertyGridInvalidIndex;
std::size_t visibleFieldIndex = UIEditorPropertyGridInvalidIndex;
};
bool IsUIEditorPropertyGridPointInside(
const ::XCEngine::UI::UIRect& rect,
const ::XCEngine::UI::UIPoint& point);
std::size_t FindUIEditorPropertyGridSectionIndex(
const std::vector<UIEditorPropertyGridSection>& sections,
std::string_view sectionId);
UIEditorPropertyGridFieldLocation FindUIEditorPropertyGridFieldLocation(
const std::vector<UIEditorPropertyGridSection>& sections,
std::string_view fieldId);
std::size_t FindUIEditorPropertyGridVisibleFieldIndex(
const UIEditorPropertyGridLayout& layout,
std::string_view fieldId,
const std::vector<UIEditorPropertyGridSection>& sections);
UIEditorPropertyGridLayout BuildUIEditorPropertyGridLayout(
const ::XCEngine::UI::UIRect& bounds,
const std::vector<UIEditorPropertyGridSection>& sections,
const ::XCEngine::UI::Widgets::UIExpansionModel& expansionModel,
const UIEditorPropertyGridMetrics& metrics = {});
UIEditorPropertyGridHitTarget HitTestUIEditorPropertyGrid(
const UIEditorPropertyGridLayout& layout,
const ::XCEngine::UI::UIPoint& point);
void AppendUIEditorPropertyGridBackground(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorPropertyGridLayout& layout,
const std::vector<UIEditorPropertyGridSection>& sections,
const ::XCEngine::UI::Widgets::UISelectionModel& selectionModel,
const ::XCEngine::UI::Widgets::UIPropertyEditModel& propertyEditModel,
const UIEditorPropertyGridState& state,
const UIEditorPropertyGridPalette& palette = {},
const UIEditorPropertyGridMetrics& metrics = {});
void AppendUIEditorPropertyGridForeground(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorPropertyGridLayout& layout,
const std::vector<UIEditorPropertyGridSection>& sections,
const ::XCEngine::UI::Widgets::UIPropertyEditModel& propertyEditModel,
const UIEditorPropertyGridPalette& palette = {},
const UIEditorPropertyGridMetrics& metrics = {});
void AppendUIEditorPropertyGrid(
::XCEngine::UI::UIDrawList& drawList,
const ::XCEngine::UI::UIRect& bounds,
const std::vector<UIEditorPropertyGridSection>& sections,
const ::XCEngine::UI::Widgets::UISelectionModel& selectionModel,
const ::XCEngine::UI::Widgets::UIExpansionModel& expansionModel,
const ::XCEngine::UI::Widgets::UIPropertyEditModel& propertyEditModel,
const UIEditorPropertyGridState& state,
const UIEditorPropertyGridPalette& palette = {},
const UIEditorPropertyGridMetrics& metrics = {});
} // namespace XCEngine::UI::Editor::Widgets

View File

@@ -0,0 +1,96 @@
#pragma once
#include <XCEngine/UI/DrawData.h>
#include <XCEngine/UI/Types.h>
#include <cstdint>
namespace XCEngine::UI::Editor::Widgets {
enum class UIEditorScrollViewHitTargetKind : std::uint8_t {
None = 0,
Content,
ScrollbarTrack,
ScrollbarThumb
};
struct UIEditorScrollViewState {
bool focused = false;
bool hovered = false;
bool scrollbarHovered = false;
bool draggingScrollbarThumb = false;
};
struct UIEditorScrollViewMetrics {
float scrollbarWidth = 10.0f;
float scrollbarInset = 4.0f;
float minThumbHeight = 28.0f;
float cornerRounding = 6.0f;
float borderThickness = 1.0f;
float focusedBorderThickness = 2.0f;
float wheelStep = 48.0f;
};
struct UIEditorScrollViewPalette {
::XCEngine::UI::UIColor surfaceColor =
::XCEngine::UI::UIColor(0.14f, 0.14f, 0.14f, 1.0f);
::XCEngine::UI::UIColor borderColor =
::XCEngine::UI::UIColor(0.29f, 0.29f, 0.29f, 1.0f);
::XCEngine::UI::UIColor focusedBorderColor =
::XCEngine::UI::UIColor(0.84f, 0.84f, 0.84f, 1.0f);
::XCEngine::UI::UIColor scrollbarTrackColor =
::XCEngine::UI::UIColor(0.18f, 0.18f, 0.18f, 1.0f);
::XCEngine::UI::UIColor scrollbarThumbColor =
::XCEngine::UI::UIColor(0.31f, 0.31f, 0.31f, 1.0f);
::XCEngine::UI::UIColor scrollbarThumbHoverColor =
::XCEngine::UI::UIColor(0.38f, 0.38f, 0.38f, 1.0f);
::XCEngine::UI::UIColor scrollbarThumbActiveColor =
::XCEngine::UI::UIColor(0.46f, 0.46f, 0.46f, 1.0f);
};
struct UIEditorScrollViewLayout {
::XCEngine::UI::UIRect bounds = {};
::XCEngine::UI::UIRect contentRect = {};
::XCEngine::UI::UIRect scrollbarTrackRect = {};
::XCEngine::UI::UIRect scrollbarThumbRect = {};
float contentHeight = 0.0f;
float verticalOffset = 0.0f;
float maxOffset = 0.0f;
bool hasScrollbar = false;
};
struct UIEditorScrollViewHitTarget {
UIEditorScrollViewHitTargetKind kind = UIEditorScrollViewHitTargetKind::None;
};
bool IsUIEditorScrollViewPointInside(
const ::XCEngine::UI::UIRect& rect,
const ::XCEngine::UI::UIPoint& point);
float ClampUIEditorScrollViewOffset(
const ::XCEngine::UI::UIRect& bounds,
float contentHeight,
float verticalOffset,
const UIEditorScrollViewMetrics& metrics = {});
UIEditorScrollViewLayout BuildUIEditorScrollViewLayout(
const ::XCEngine::UI::UIRect& bounds,
float contentHeight,
float verticalOffset,
const UIEditorScrollViewMetrics& metrics = {});
::XCEngine::UI::UIPoint ResolveUIEditorScrollViewContentOrigin(
const UIEditorScrollViewLayout& layout);
UIEditorScrollViewHitTarget HitTestUIEditorScrollView(
const UIEditorScrollViewLayout& layout,
const ::XCEngine::UI::UIPoint& point);
void AppendUIEditorScrollViewBackground(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorScrollViewLayout& layout,
const UIEditorScrollViewState& state,
const UIEditorScrollViewPalette& palette = {},
const UIEditorScrollViewMetrics& metrics = {});
} // namespace XCEngine::UI::Editor::Widgets