feat: Selection Popup (#2358)
Co-authored-by: pablohc <pablonoviello@outlook.com>
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,6 +94,19 @@ struct ThemeMetrics {
|
||||
bool popupProgressFillInverted;
|
||||
bool popupProgressOutlineInverted;
|
||||
|
||||
int optionPopupItemSpacing;
|
||||
int optionPopupInnerPadding;
|
||||
int optionPopupSelectionHPadding;
|
||||
int optionPopupSelectionVPadding;
|
||||
int optionPopupTitleGap;
|
||||
bool optionPopupUseSmallFont;
|
||||
bool optionPopupOptionFontBold;
|
||||
int optionPopupSelectionRadius;
|
||||
bool optionPopupSelectionLight;
|
||||
bool optionPopupDrawAllRows;
|
||||
int optionPopupDialogSideMargin;
|
||||
bool optionPopupTitleSeparator;
|
||||
|
||||
int textFieldHorizontalPadding;
|
||||
int textFieldNormalThickness;
|
||||
int textFieldCursorThickness;
|
||||
@@ -167,6 +180,18 @@ constexpr ThemeMetrics values = {.batteryWidth = 15,
|
||||
.popupProgressClampPercent = false,
|
||||
.popupProgressFillInverted = true,
|
||||
.popupProgressOutlineInverted = true,
|
||||
.optionPopupItemSpacing = 6,
|
||||
.optionPopupInnerPadding = 16,
|
||||
.optionPopupSelectionHPadding = 8,
|
||||
.optionPopupSelectionVPadding = 4,
|
||||
.optionPopupTitleGap = 10,
|
||||
.optionPopupUseSmallFont = true,
|
||||
.optionPopupOptionFontBold = true,
|
||||
.optionPopupSelectionRadius = 0,
|
||||
.optionPopupSelectionLight = false,
|
||||
.optionPopupDrawAllRows = false,
|
||||
.optionPopupDialogSideMargin = 20,
|
||||
.optionPopupTitleSeparator = true,
|
||||
.textFieldHorizontalPadding = 6,
|
||||
.textFieldNormalThickness = 1,
|
||||
.textFieldCursorThickness = 3,
|
||||
@@ -207,6 +232,8 @@ class BaseTheme {
|
||||
const std::function<std::string(int index)>& buttonLabel,
|
||||
const std::function<UIIcon(int index)>& rowIcon) const;
|
||||
virtual Rect drawPopup(const GfxRenderer& renderer, const char* message) const;
|
||||
virtual void drawOptionPopup(const GfxRenderer& renderer, const char* title, const std::vector<std::string>& options,
|
||||
int selectedIndex) const;
|
||||
virtual void fillPopupProgress(const GfxRenderer& renderer, const Rect& layout, const int progress) const;
|
||||
void drawStatusBar(GfxRenderer& renderer, const float bookProgress, const int currentPage, const int pageCount,
|
||||
std::string title, const int paddingBottom = 0, const int textYOffset = 0,
|
||||
|
||||
@@ -65,6 +65,18 @@ constexpr ThemeMetrics values = {.batteryWidth = 16,
|
||||
.popupProgressClampPercent = false,
|
||||
.popupProgressFillInverted = false,
|
||||
.popupProgressOutlineInverted = false,
|
||||
.optionPopupItemSpacing = 8,
|
||||
.optionPopupInnerPadding = 20,
|
||||
.optionPopupSelectionHPadding = 16,
|
||||
.optionPopupSelectionVPadding = 12,
|
||||
.optionPopupTitleGap = 16,
|
||||
.optionPopupUseSmallFont = true,
|
||||
.optionPopupOptionFontBold = false,
|
||||
.optionPopupSelectionRadius = 6,
|
||||
.optionPopupSelectionLight = true,
|
||||
.optionPopupDrawAllRows = false,
|
||||
.optionPopupDialogSideMargin = 20,
|
||||
.optionPopupTitleSeparator = true,
|
||||
.textFieldHorizontalPadding = 6,
|
||||
.textFieldNormalThickness = 1,
|
||||
.textFieldCursorThickness = 3,
|
||||
|
||||
@@ -65,6 +65,18 @@ constexpr ThemeMetrics values = {.batteryWidth = 15,
|
||||
.popupProgressClampPercent = true,
|
||||
.popupProgressFillInverted = false,
|
||||
.popupProgressOutlineInverted = false,
|
||||
.optionPopupItemSpacing = 6,
|
||||
.optionPopupInnerPadding = 24,
|
||||
.optionPopupSelectionHPadding = 20,
|
||||
.optionPopupSelectionVPadding = 10,
|
||||
.optionPopupTitleGap = 16,
|
||||
.optionPopupUseSmallFont = false,
|
||||
.optionPopupOptionFontBold = true,
|
||||
.optionPopupSelectionRadius = 30,
|
||||
.optionPopupSelectionLight = false,
|
||||
.optionPopupDrawAllRows = true,
|
||||
.optionPopupDialogSideMargin = 20,
|
||||
.optionPopupTitleSeparator = true,
|
||||
.textFieldHorizontalPadding = 8,
|
||||
.textFieldNormalThickness = 2,
|
||||
.textFieldCursorThickness = 3,
|
||||
|
||||
Reference in New Issue
Block a user