Refine XCEditor docking and DPI rendering
This commit is contained in:
@@ -18,8 +18,7 @@ using ::XCEngine::UI::Layout::MeasureUITabStripHeaderWidth;
|
||||
|
||||
constexpr float kTabRounding = 0.0f;
|
||||
constexpr float kStripRounding = 0.0f;
|
||||
constexpr float kHeaderFontSize = 11.0f;
|
||||
constexpr float kCloseFontSize = 10.0f;
|
||||
constexpr float kHeaderFontSize = 13.0f;
|
||||
|
||||
float ClampNonNegative(float value) {
|
||||
return (std::max)(value, 0.0f);
|
||||
@@ -44,10 +43,6 @@ float ResolveTabRounding(const UIEditorTabStripMetrics& metrics) {
|
||||
return kTabRounding;
|
||||
}
|
||||
|
||||
float ResolveCloseButtonRounding(const UIEditorTabStripMetrics& metrics) {
|
||||
return (std::min)(ClampNonNegative(metrics.closeButtonExtent) * 0.2f, 2.0f);
|
||||
}
|
||||
|
||||
std::size_t ResolveSelectedIndex(
|
||||
std::size_t itemCount,
|
||||
std::size_t selectedIndex) {
|
||||
@@ -78,20 +73,18 @@ float ResolveTabTextTop(
|
||||
return rect.y + (std::max)(0.0f, (rect.height - kHeaderFontSize) * 0.5f) + metrics.labelInsetY;
|
||||
}
|
||||
|
||||
float ResolveCloseTextTop(const UIRect& rect) {
|
||||
return rect.y + (std::max)(0.0f, (rect.height - kCloseFontSize) * 0.5f) - 0.5f;
|
||||
}
|
||||
|
||||
UIColor ResolveStripBorderColor(
|
||||
const UIEditorTabStripState& state,
|
||||
const UIEditorTabStripPalette& palette) {
|
||||
return state.focused ? palette.focusedBorderColor : palette.stripBorderColor;
|
||||
(void)state;
|
||||
return palette.stripBorderColor;
|
||||
}
|
||||
|
||||
float ResolveStripBorderThickness(
|
||||
const UIEditorTabStripState& state,
|
||||
const UIEditorTabStripMetrics& metrics) {
|
||||
return state.focused ? metrics.focusedBorderThickness : metrics.baseBorderThickness;
|
||||
(void)state;
|
||||
return metrics.baseBorderThickness;
|
||||
}
|
||||
|
||||
UIColor ResolveTabFillColor(
|
||||
@@ -115,7 +108,8 @@ UIColor ResolveTabBorderColor(
|
||||
bool focused,
|
||||
const UIEditorTabStripPalette& palette) {
|
||||
if (selected) {
|
||||
return focused ? palette.focusedBorderColor : palette.tabSelectedBorderColor;
|
||||
(void)focused;
|
||||
return palette.tabSelectedBorderColor;
|
||||
}
|
||||
|
||||
if (hovered) {
|
||||
@@ -125,33 +119,121 @@ UIColor ResolveTabBorderColor(
|
||||
return palette.tabBorderColor;
|
||||
}
|
||||
|
||||
float ResolveTabBorderThickness(
|
||||
bool selected,
|
||||
bool focused,
|
||||
const UIEditorTabStripMetrics& metrics) {
|
||||
float ResolveTabBorderThickness(bool selected, bool focused, const UIEditorTabStripMetrics& metrics) {
|
||||
if (selected) {
|
||||
return focused ? metrics.focusedBorderThickness : metrics.selectedBorderThickness;
|
||||
(void)focused;
|
||||
return metrics.selectedBorderThickness;
|
||||
}
|
||||
|
||||
return metrics.baseBorderThickness;
|
||||
}
|
||||
|
||||
UIRect BuildCloseButtonRect(
|
||||
const UIRect& headerRect,
|
||||
void AppendHeaderContentSeparator(
|
||||
UIDrawList& drawList,
|
||||
const UIEditorTabStripLayout& layout,
|
||||
const UIEditorTabStripPalette& palette,
|
||||
const UIEditorTabStripMetrics& metrics) {
|
||||
const float insetY = ClampNonNegative(metrics.closeInsetY);
|
||||
const float extent = (std::min)(
|
||||
ClampNonNegative(metrics.closeButtonExtent),
|
||||
(std::max)(headerRect.height - insetY * 2.0f, 0.0f));
|
||||
if (extent <= 0.0f) {
|
||||
return {};
|
||||
if (layout.headerRect.width <= 0.0f ||
|
||||
layout.headerRect.height <= 0.0f ||
|
||||
layout.contentRect.height <= 0.0f) {
|
||||
return;
|
||||
}
|
||||
|
||||
return UIRect(
|
||||
headerRect.x + headerRect.width - ClampNonNegative(metrics.closeInsetRight) - extent,
|
||||
headerRect.y + insetY + (std::max)(0.0f, headerRect.height - insetY * 2.0f - extent) * 0.5f,
|
||||
extent,
|
||||
extent);
|
||||
const float thickness = (std::max)(ClampNonNegative(metrics.baseBorderThickness), 1.0f);
|
||||
const float separatorY = layout.contentRect.y;
|
||||
const float separatorLeft = layout.headerRect.x;
|
||||
const float separatorRight = layout.headerRect.x + layout.headerRect.width;
|
||||
|
||||
if (layout.selectedIndex == UIEditorTabStripInvalidIndex ||
|
||||
layout.selectedIndex >= layout.tabHeaderRects.size()) {
|
||||
drawList.AddFilledRect(
|
||||
UIRect(
|
||||
separatorLeft,
|
||||
separatorY,
|
||||
(std::max)(separatorRight - separatorLeft, 0.0f),
|
||||
thickness),
|
||||
palette.headerContentSeparatorColor);
|
||||
return;
|
||||
}
|
||||
|
||||
const UIRect& selectedRect = layout.tabHeaderRects[layout.selectedIndex];
|
||||
const float gapLeft = (std::max)(separatorLeft, selectedRect.x + 1.0f);
|
||||
const float gapRight = (std::min)(separatorRight, selectedRect.x + selectedRect.width - 1.0f);
|
||||
|
||||
if (gapLeft > separatorLeft) {
|
||||
drawList.AddFilledRect(
|
||||
UIRect(
|
||||
separatorLeft,
|
||||
separatorY,
|
||||
(std::max)(gapLeft - separatorLeft, 0.0f),
|
||||
thickness),
|
||||
palette.headerContentSeparatorColor);
|
||||
}
|
||||
|
||||
if (gapRight < separatorRight) {
|
||||
drawList.AddFilledRect(
|
||||
UIRect(
|
||||
gapRight,
|
||||
separatorY,
|
||||
(std::max)(separatorRight - gapRight, 0.0f),
|
||||
thickness),
|
||||
palette.headerContentSeparatorColor);
|
||||
}
|
||||
}
|
||||
|
||||
void AppendSelectedTabBottomBorderMask(
|
||||
UIDrawList& drawList,
|
||||
const UIRect& rect,
|
||||
float thickness,
|
||||
const UIEditorTabStripPalette& palette) {
|
||||
if (rect.width <= 0.0f || rect.height <= 0.0f || thickness <= 0.0f) {
|
||||
return;
|
||||
}
|
||||
|
||||
const float maskHeight = (std::max)(1.0f, thickness + 1.0f);
|
||||
drawList.AddFilledRect(
|
||||
UIRect(
|
||||
rect.x + 1.0f,
|
||||
rect.y + rect.height - maskHeight,
|
||||
(std::max)(rect.width - 2.0f, 0.0f),
|
||||
maskHeight + 1.0f),
|
||||
palette.contentBackgroundColor,
|
||||
0.0f);
|
||||
}
|
||||
|
||||
UIEditorTabStripInsertionPreview BuildInsertionPreview(
|
||||
const UIEditorTabStripLayout& layout,
|
||||
const UIEditorTabStripState& state,
|
||||
const UIEditorTabStripMetrics& metrics) {
|
||||
UIEditorTabStripInsertionPreview preview = {};
|
||||
if (!state.reorder.dragging ||
|
||||
state.reorder.previewInsertionIndex == UIEditorTabStripInvalidIndex ||
|
||||
layout.tabHeaderRects.empty() ||
|
||||
state.reorder.previewInsertionIndex > layout.tabHeaderRects.size() ||
|
||||
layout.headerRect.height <= 0.0f) {
|
||||
return preview;
|
||||
}
|
||||
|
||||
float indicatorX = layout.tabHeaderRects.front().x;
|
||||
if (state.reorder.previewInsertionIndex >= layout.tabHeaderRects.size()) {
|
||||
const UIRect& lastRect = layout.tabHeaderRects.back();
|
||||
indicatorX = lastRect.x + lastRect.width;
|
||||
} else {
|
||||
indicatorX = layout.tabHeaderRects[state.reorder.previewInsertionIndex].x;
|
||||
}
|
||||
|
||||
const float thickness = (std::max)(ClampNonNegative(metrics.reorderPreviewThickness), 1.0f);
|
||||
const float insetY = ClampNonNegative(metrics.reorderPreviewInsetY);
|
||||
const float indicatorHeight = (std::max)(layout.headerRect.height - insetY * 2.0f, thickness);
|
||||
|
||||
preview.visible = true;
|
||||
preview.insertionIndex = state.reorder.previewInsertionIndex;
|
||||
preview.indicatorRect = UIRect(
|
||||
indicatorX - thickness * 0.5f,
|
||||
layout.headerRect.y + insetY,
|
||||
thickness,
|
||||
indicatorHeight);
|
||||
return preview;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -162,13 +244,8 @@ float ResolveUIEditorTabStripDesiredHeaderLabelWidth(
|
||||
const float labelWidth = ResolveEstimatedLabelWidth(item, metrics);
|
||||
const float horizontalPadding = ClampNonNegative(metrics.layoutMetrics.tabHorizontalPadding);
|
||||
const float extraLeftInset = (std::max)(ClampNonNegative(metrics.labelInsetX) - horizontalPadding, 0.0f);
|
||||
const float extraRightInset = (std::max)(ClampNonNegative(metrics.closeInsetRight) - horizontalPadding, 0.0f);
|
||||
const float closeBudget = item.closable
|
||||
? ClampNonNegative(metrics.closeButtonExtent) +
|
||||
ClampNonNegative(metrics.closeButtonGap) +
|
||||
extraRightInset
|
||||
: 0.0f;
|
||||
return labelWidth + extraLeftInset + closeBudget;
|
||||
(void)item;
|
||||
return labelWidth + extraLeftInset;
|
||||
}
|
||||
|
||||
std::size_t ResolveUIEditorTabStripSelectedIndex(
|
||||
@@ -188,34 +265,6 @@ std::size_t ResolveUIEditorTabStripSelectedIndex(
|
||||
return ResolveSelectedIndex(items.size(), fallbackIndex);
|
||||
}
|
||||
|
||||
std::size_t ResolveUIEditorTabStripSelectedIndexAfterClose(
|
||||
std::size_t selectedIndex,
|
||||
std::size_t closedIndex,
|
||||
std::size_t itemCountBeforeClose) {
|
||||
if (itemCountBeforeClose == 0u || closedIndex >= itemCountBeforeClose) {
|
||||
return UIEditorTabStripInvalidIndex;
|
||||
}
|
||||
|
||||
const std::size_t itemCountAfterClose = itemCountBeforeClose - 1u;
|
||||
if (itemCountAfterClose == 0u) {
|
||||
return UIEditorTabStripInvalidIndex;
|
||||
}
|
||||
|
||||
if (selectedIndex == UIEditorTabStripInvalidIndex || selectedIndex >= itemCountBeforeClose) {
|
||||
return (std::min)(closedIndex, itemCountAfterClose - 1u);
|
||||
}
|
||||
|
||||
if (closedIndex < selectedIndex) {
|
||||
return selectedIndex - 1u;
|
||||
}
|
||||
|
||||
if (closedIndex > selectedIndex) {
|
||||
return selectedIndex;
|
||||
}
|
||||
|
||||
return (std::min)(selectedIndex, itemCountAfterClose - 1u);
|
||||
}
|
||||
|
||||
UIEditorTabStripLayout BuildUIEditorTabStripLayout(
|
||||
const UIRect& bounds,
|
||||
const std::vector<UIEditorTabStripItem>& items,
|
||||
@@ -245,17 +294,7 @@ UIEditorTabStripLayout BuildUIEditorTabStripLayout(
|
||||
layout.tabHeaderRects = arranged.tabHeaderRects;
|
||||
layout.closeButtonRects.resize(items.size());
|
||||
layout.showCloseButtons.resize(items.size(), false);
|
||||
|
||||
for (std::size_t index = 0; index < items.size(); ++index) {
|
||||
if (!items[index].closable) {
|
||||
continue;
|
||||
}
|
||||
|
||||
layout.closeButtonRects[index] = BuildCloseButtonRect(layout.tabHeaderRects[index], metrics);
|
||||
layout.showCloseButtons[index] =
|
||||
layout.closeButtonRects[index].width > 0.0f && layout.closeButtonRects[index].height > 0.0f;
|
||||
}
|
||||
|
||||
layout.insertionPreview = BuildInsertionPreview(layout, state, metrics);
|
||||
return layout;
|
||||
}
|
||||
|
||||
@@ -269,15 +308,6 @@ UIEditorTabStripHitTarget HitTestUIEditorTabStrip(
|
||||
return target;
|
||||
}
|
||||
|
||||
for (std::size_t index = 0; index < layout.closeButtonRects.size(); ++index) {
|
||||
if (layout.showCloseButtons[index] &&
|
||||
IsPointInsideRect(layout.closeButtonRects[index], point)) {
|
||||
target.kind = UIEditorTabStripHitTargetKind::CloseButton;
|
||||
target.index = index;
|
||||
return target;
|
||||
}
|
||||
}
|
||||
|
||||
for (std::size_t index = 0; index < layout.tabHeaderRects.size(); ++index) {
|
||||
if (IsPointInsideRect(layout.tabHeaderRects[index], point)) {
|
||||
target.kind = UIEditorTabStripHitTargetKind::Tab;
|
||||
@@ -322,7 +352,7 @@ void AppendUIEditorTabStripBackground(
|
||||
|
||||
for (std::size_t index = 0; index < layout.tabHeaderRects.size(); ++index) {
|
||||
const bool selected = layout.selectedIndex == index;
|
||||
const bool hovered = state.hoveredIndex == index || state.closeHoveredIndex == index;
|
||||
const bool hovered = state.hoveredIndex == index;
|
||||
drawList.AddFilledRect(
|
||||
layout.tabHeaderRects[index],
|
||||
ResolveTabFillColor(selected, hovered, palette),
|
||||
@@ -332,6 +362,20 @@ void AppendUIEditorTabStripBackground(
|
||||
ResolveTabBorderColor(selected, hovered, state.focused, palette),
|
||||
ResolveTabBorderThickness(selected, state.focused, metrics),
|
||||
tabRounding);
|
||||
if (selected) {
|
||||
AppendSelectedTabBottomBorderMask(
|
||||
drawList,
|
||||
layout.tabHeaderRects[index],
|
||||
ResolveTabBorderThickness(selected, state.focused, metrics),
|
||||
palette);
|
||||
}
|
||||
}
|
||||
|
||||
if (layout.insertionPreview.visible) {
|
||||
drawList.AddFilledRect(
|
||||
layout.insertionPreview.indicatorRect,
|
||||
palette.reorderPreviewColor,
|
||||
0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -342,6 +386,8 @@ void AppendUIEditorTabStripForeground(
|
||||
const UIEditorTabStripState& state,
|
||||
const UIEditorTabStripPalette& palette,
|
||||
const UIEditorTabStripMetrics& metrics) {
|
||||
AppendHeaderContentSeparator(drawList, layout, palette, metrics);
|
||||
|
||||
const float leftInset = (std::max)(
|
||||
ClampNonNegative(metrics.layoutMetrics.tabHorizontalPadding),
|
||||
ClampNonNegative(metrics.labelInsetX));
|
||||
@@ -349,12 +395,9 @@ void AppendUIEditorTabStripForeground(
|
||||
for (std::size_t index = 0; index < items.size() && index < layout.tabHeaderRects.size(); ++index) {
|
||||
const UIRect& tabRect = layout.tabHeaderRects[index];
|
||||
const bool selected = layout.selectedIndex == index;
|
||||
const bool hovered = state.hoveredIndex == index || state.closeHoveredIndex == index;
|
||||
const bool hovered = state.hoveredIndex == index;
|
||||
const float textLeft = tabRect.x + leftInset;
|
||||
float textRight = tabRect.x + tabRect.width - ClampNonNegative(metrics.layoutMetrics.tabHorizontalPadding);
|
||||
if (layout.showCloseButtons[index]) {
|
||||
textRight = layout.closeButtonRects[index].x - ClampNonNegative(metrics.closeButtonGap);
|
||||
}
|
||||
const float textRight = tabRect.x + tabRect.width - ClampNonNegative(metrics.layoutMetrics.tabHorizontalPadding);
|
||||
|
||||
if (textRight > textLeft) {
|
||||
const UIRect clipRect(
|
||||
@@ -370,30 +413,6 @@ void AppendUIEditorTabStripForeground(
|
||||
kHeaderFontSize);
|
||||
drawList.PopClipRect();
|
||||
}
|
||||
|
||||
if (!layout.showCloseButtons[index]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const bool closeHovered = state.closeHoveredIndex == index;
|
||||
const UIRect& closeRect = layout.closeButtonRects[index];
|
||||
const float closeRounding = ResolveCloseButtonRounding(metrics);
|
||||
drawList.AddFilledRect(
|
||||
closeRect,
|
||||
closeHovered ? palette.closeButtonHoveredColor : palette.closeButtonColor,
|
||||
closeRounding);
|
||||
drawList.AddRectOutline(
|
||||
closeRect,
|
||||
palette.closeButtonBorderColor,
|
||||
1.0f,
|
||||
closeRounding);
|
||||
drawList.AddText(
|
||||
UIPoint(
|
||||
closeRect.x + (std::max)(0.0f, (closeRect.width - 7.0f) * 0.5f),
|
||||
ResolveCloseTextTop(closeRect)),
|
||||
"X",
|
||||
palette.closeGlyphColor,
|
||||
kCloseFontSize);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user