Refactor XCUI editor module layout
This commit is contained in:
196
new_editor/src/Collections/UIEditorListView.cpp
Normal file
196
new_editor/src/Collections/UIEditorListView.cpp
Normal file
@@ -0,0 +1,196 @@
|
||||
#include <XCEditor/Collections/UIEditorListView.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace XCEngine::UI::Editor::Widgets {
|
||||
|
||||
namespace {
|
||||
|
||||
float ClampNonNegative(float value) {
|
||||
return (std::max)(value, 0.0f);
|
||||
}
|
||||
|
||||
float ResolveListViewRowHeight(
|
||||
const UIEditorListViewItem& item,
|
||||
const UIEditorListViewMetrics& metrics) {
|
||||
return item.desiredHeight > 0.0f ? item.desiredHeight : metrics.rowHeight;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool IsUIEditorListViewPointInside(
|
||||
const ::XCEngine::UI::UIRect& rect,
|
||||
const ::XCEngine::UI::UIPoint& point) {
|
||||
return point.x >= rect.x &&
|
||||
point.x <= rect.x + rect.width &&
|
||||
point.y >= rect.y &&
|
||||
point.y <= rect.y + rect.height;
|
||||
}
|
||||
|
||||
std::size_t FindUIEditorListViewItemIndex(
|
||||
const std::vector<UIEditorListViewItem>& items,
|
||||
std::string_view itemId) {
|
||||
for (std::size_t itemIndex = 0u; itemIndex < items.size(); ++itemIndex) {
|
||||
if (items[itemIndex].itemId == itemId) {
|
||||
return itemIndex;
|
||||
}
|
||||
}
|
||||
|
||||
return UIEditorListViewInvalidIndex;
|
||||
}
|
||||
|
||||
UIEditorListViewLayout BuildUIEditorListViewLayout(
|
||||
const ::XCEngine::UI::UIRect& bounds,
|
||||
const std::vector<UIEditorListViewItem>& items,
|
||||
const UIEditorListViewMetrics& metrics) {
|
||||
UIEditorListViewLayout layout = {};
|
||||
layout.bounds = ::XCEngine::UI::UIRect(
|
||||
bounds.x,
|
||||
bounds.y,
|
||||
ClampNonNegative(bounds.width),
|
||||
ClampNonNegative(bounds.height));
|
||||
layout.itemIndices.reserve(items.size());
|
||||
layout.rowRects.reserve(items.size());
|
||||
layout.primaryTextRects.reserve(items.size());
|
||||
layout.secondaryTextRects.reserve(items.size());
|
||||
layout.hasSecondaryText.reserve(items.size());
|
||||
|
||||
float rowY = layout.bounds.y;
|
||||
for (std::size_t itemIndex = 0u; itemIndex < items.size(); ++itemIndex) {
|
||||
const UIEditorListViewItem& item = items[itemIndex];
|
||||
const bool hasSecondaryText = !item.secondaryText.empty();
|
||||
const float rowHeight = ResolveListViewRowHeight(item, metrics);
|
||||
const ::XCEngine::UI::UIRect rowRect(
|
||||
layout.bounds.x,
|
||||
rowY,
|
||||
layout.bounds.width,
|
||||
rowHeight);
|
||||
const float textX = rowRect.x + ClampNonNegative(metrics.horizontalPadding);
|
||||
const float textWidth =
|
||||
(std::max)(0.0f, rowRect.width - ClampNonNegative(metrics.horizontalPadding) * 2.0f);
|
||||
const ::XCEngine::UI::UIRect primaryTextRect(
|
||||
textX,
|
||||
rowRect.y + (hasSecondaryText
|
||||
? ClampNonNegative(metrics.primaryTextInsetY)
|
||||
: ClampNonNegative(metrics.singleLineTextInsetY)),
|
||||
textWidth,
|
||||
hasSecondaryText ? 14.0f : 18.0f);
|
||||
const ::XCEngine::UI::UIRect secondaryTextRect(
|
||||
textX,
|
||||
rowRect.y + ClampNonNegative(metrics.secondaryTextInsetY),
|
||||
textWidth,
|
||||
hasSecondaryText ? 12.0f : 0.0f);
|
||||
|
||||
layout.itemIndices.push_back(itemIndex);
|
||||
layout.rowRects.push_back(rowRect);
|
||||
layout.primaryTextRects.push_back(primaryTextRect);
|
||||
layout.secondaryTextRects.push_back(secondaryTextRect);
|
||||
layout.hasSecondaryText.push_back(hasSecondaryText);
|
||||
rowY += rowHeight + ClampNonNegative(metrics.rowGap);
|
||||
}
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
UIEditorListViewHitTarget HitTestUIEditorListView(
|
||||
const UIEditorListViewLayout& layout,
|
||||
const ::XCEngine::UI::UIPoint& point) {
|
||||
for (std::size_t visibleIndex = 0u; visibleIndex < layout.rowRects.size(); ++visibleIndex) {
|
||||
if (!IsUIEditorListViewPointInside(layout.rowRects[visibleIndex], point)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
UIEditorListViewHitTarget target = {};
|
||||
target.kind = UIEditorListViewHitTargetKind::Row;
|
||||
target.visibleIndex = visibleIndex;
|
||||
target.itemIndex = layout.itemIndices[visibleIndex];
|
||||
return target;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
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) {
|
||||
drawList.AddFilledRect(layout.bounds, palette.surfaceColor, metrics.cornerRounding);
|
||||
drawList.AddRectOutline(
|
||||
layout.bounds,
|
||||
state.focused ? palette.focusedBorderColor : palette.borderColor,
|
||||
state.focused ? metrics.focusedBorderThickness : metrics.borderThickness,
|
||||
metrics.cornerRounding);
|
||||
|
||||
for (std::size_t visibleIndex = 0u; visibleIndex < layout.rowRects.size(); ++visibleIndex) {
|
||||
const UIEditorListViewItem& item = items[layout.itemIndices[visibleIndex]];
|
||||
const bool selected = selectionModel.IsSelected(item.itemId);
|
||||
const bool hovered = state.hoveredItemId == item.itemId;
|
||||
if (!selected && !hovered) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const ::XCEngine::UI::UIColor rowColor =
|
||||
selected
|
||||
? (state.focused ? palette.rowSelectedFocusedColor : palette.rowSelectedColor)
|
||||
: palette.rowHoverColor;
|
||||
drawList.AddFilledRect(layout.rowRects[visibleIndex], rowColor, metrics.cornerRounding);
|
||||
}
|
||||
}
|
||||
|
||||
void AppendUIEditorListViewForeground(
|
||||
::XCEngine::UI::UIDrawList& drawList,
|
||||
const UIEditorListViewLayout& layout,
|
||||
const std::vector<UIEditorListViewItem>& items,
|
||||
const UIEditorListViewPalette& palette,
|
||||
const UIEditorListViewMetrics&) {
|
||||
drawList.PushClipRect(layout.bounds);
|
||||
for (std::size_t visibleIndex = 0u; visibleIndex < layout.rowRects.size(); ++visibleIndex) {
|
||||
const UIEditorListViewItem& item = items[layout.itemIndices[visibleIndex]];
|
||||
drawList.PushClipRect(layout.rowRects[visibleIndex]);
|
||||
drawList.AddText(
|
||||
::XCEngine::UI::UIPoint(
|
||||
layout.primaryTextRects[visibleIndex].x,
|
||||
layout.primaryTextRects[visibleIndex].y),
|
||||
item.primaryText,
|
||||
palette.primaryTextColor,
|
||||
12.0f);
|
||||
if (layout.hasSecondaryText[visibleIndex]) {
|
||||
drawList.AddText(
|
||||
::XCEngine::UI::UIPoint(
|
||||
layout.secondaryTextRects[visibleIndex].x,
|
||||
layout.secondaryTextRects[visibleIndex].y),
|
||||
item.secondaryText,
|
||||
palette.secondaryTextColor,
|
||||
11.0f);
|
||||
}
|
||||
drawList.PopClipRect();
|
||||
}
|
||||
drawList.PopClipRect();
|
||||
}
|
||||
|
||||
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) {
|
||||
const UIEditorListViewLayout layout =
|
||||
BuildUIEditorListViewLayout(bounds, items, metrics);
|
||||
AppendUIEditorListViewBackground(
|
||||
drawList,
|
||||
layout,
|
||||
items,
|
||||
selectionModel,
|
||||
state,
|
||||
palette,
|
||||
metrics);
|
||||
AppendUIEditorListViewForeground(drawList, layout, items, palette, metrics);
|
||||
}
|
||||
|
||||
} // namespace XCEngine::UI::Editor::Widgets
|
||||
Reference in New Issue
Block a user