feat: Selection Popup (#2358)

Co-authored-by: pablohc <pablonoviello@outlook.com>
This commit is contained in:
Uri Tauber
2026-06-29 16:59:25 -04:00
committed by GitHub
co-authored by pablohc
parent fd43ca2fe1
commit fef39da23c
11 changed files with 324 additions and 12 deletions
+96
View File
@@ -1005,3 +1005,99 @@ void BaseTheme::drawKeyboardKey(const GfxRenderer& renderer, Rect rect, const ch
rect.y + metrics.keyboardSecondaryLabelTopPadding, secondaryLabel, !invert);
}
}
void BaseTheme::drawOptionPopup(const GfxRenderer& renderer, const char* title, const std::vector<std::string>& options,
int selectedIndex) const {
const auto& metrics = UITheme::getInstance().getMetrics();
const auto pageWidth = renderer.getScreenWidth();
const auto pageHeight = renderer.getScreenHeight();
const int optionFontId = metrics.optionPopupUseSmallFont ? UI_10_FONT_ID : UI_12_FONT_ID;
const EpdFontFamily::Style optionStyle =
metrics.optionPopupOptionFontBold ? EpdFontFamily::BOLD : EpdFontFamily::REGULAR;
const int itemSpacing = metrics.optionPopupItemSpacing;
const int innerPadding = metrics.optionPopupInnerPadding;
const int selectionHPadding = metrics.optionPopupSelectionHPadding;
const int selectionVPadding = metrics.optionPopupSelectionVPadding;
const int optionLineHeight = renderer.getLineHeight(optionFontId);
const int titleLineHeight = renderer.getLineHeight(UI_12_FONT_ID);
const int rowHeight = optionLineHeight + selectionVPadding * 2;
int maxTextWidth = renderer.getTextWidth(UI_12_FONT_ID, title, EpdFontFamily::BOLD);
for (const auto& opt : options) {
int w = renderer.getTextWidth(optionFontId, opt.c_str(), optionStyle);
if (w > maxTextWidth) maxTextWidth = w;
}
const int optionCount = static_cast<int>(options.size());
const int listHeight = rowHeight * optionCount + itemSpacing * (optionCount - 1);
const int dialogW = std::min((maxTextWidth + innerPadding * 2 + selectionHPadding * 2) * 12 / 10,
pageWidth - metrics.optionPopupDialogSideMargin * 2);
const int contentHeight = titleLineHeight + metrics.optionPopupTitleGap + listHeight;
const int dialogH = contentHeight + innerPadding * 2;
const int dialogX = (pageWidth - dialogW) / 2;
const int dialogY = (pageHeight - dialogH) / 2;
const int frameThickness = metrics.popupFrameThickness;
const int frameRadius = metrics.popupCornerRadius;
if (frameRadius > 0) {
renderer.fillRoundedRect(dialogX - frameThickness, dialogY - frameThickness, dialogW + frameThickness * 2,
dialogH + frameThickness * 2, frameRadius + frameThickness, Color::White);
renderer.fillRoundedRect(dialogX, dialogY, dialogW, dialogH, frameRadius, Color::Black);
renderer.fillRoundedRect(dialogX + frameThickness, dialogY + frameThickness, dialogW - frameThickness * 2,
dialogH - frameThickness * 2,
frameRadius - frameThickness > 0 ? frameRadius - frameThickness : 0, Color::White);
} else {
renderer.fillRect(dialogX - frameThickness, dialogY - frameThickness, dialogW + frameThickness * 2,
dialogH + frameThickness * 2, true);
renderer.fillRect(dialogX, dialogY, dialogW, dialogH, false);
}
int y = dialogY + innerPadding;
renderer.drawCenteredText(UI_12_FONT_ID, y, title, true, EpdFontFamily::BOLD);
y += titleLineHeight;
if (metrics.optionPopupTitleSeparator) {
const int sepY = y + metrics.optionPopupTitleGap / 2;
renderer.drawLine(dialogX + innerPadding, sepY, dialogX + dialogW - innerPadding, sepY, true);
}
y += metrics.optionPopupTitleGap;
const int itemRectX = dialogX + innerPadding;
const int itemRectW = dialogW - innerPadding * 2;
const int selectionRadius = metrics.optionPopupSelectionRadius;
for (int i = 0; i < optionCount; i++) {
const int itemY = y + i * (rowHeight + itemSpacing);
const bool selected = (i == selectedIndex);
const char* labelText = options[i].c_str();
if (metrics.optionPopupDrawAllRows || selected) {
Color rowColor;
if (selected) {
rowColor = metrics.optionPopupSelectionLight ? Color::LightGray : Color::Black;
} else {
rowColor = Color::White;
}
if (selectionRadius > 0) {
renderer.fillRoundedRect(itemRectX, itemY, itemRectW, rowHeight, selectionRadius, rowColor);
} else {
renderer.fillRect(itemRectX, itemY, itemRectW, rowHeight, rowColor == Color::Black);
}
}
const int textW = renderer.getTextWidth(optionFontId, labelText, optionStyle);
const int textY = itemY + (rowHeight - optionLineHeight) / 2;
const int textX = itemRectX + (itemRectW - textW) / 2;
// Unselected items: text is dark (invert=true means draw on white bg).
// Selected on dark bg: text must be white (invert=false).
// Selected on light bg: text stays dark (invert=true).
const bool invertText = selected ? metrics.optionPopupSelectionLight : true;
renderer.drawText(optionFontId, textX, textY, labelText, invertText, optionStyle);
}
}