197 lines
7.1 KiB
C++
197 lines
7.1 KiB
C++
#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
|