diff --git a/src/components/themes/BaseTheme.cpp b/src/components/themes/BaseTheme.cpp index 250659b5..de3f5450 100644 --- a/src/components/themes/BaseTheme.cpp +++ b/src/components/themes/BaseTheme.cpp @@ -668,34 +668,57 @@ void BaseTheme::drawButtonMenu(GfxRenderer& renderer, Rect rect, int buttonCount } Rect BaseTheme::drawPopup(const GfxRenderer& renderer, const char* message) const { - constexpr int margin = 15; - // Scale y position proportionally to screen height (7.5% from top) - const int y = static_cast(renderer.getScreenHeight() * 0.075f); - const int textWidth = renderer.getTextWidth(UI_12_FONT_ID, message, EpdFontFamily::BOLD); + const auto& metrics = UITheme::getInstance().getMetrics(); + const int marginX = metrics.popupMarginX; + const int marginY = metrics.popupMarginY; + const int frameThickness = metrics.popupFrameThickness; + const EpdFontFamily::Style popupFontFamily = metrics.popupTextBold ? EpdFontFamily::BOLD : EpdFontFamily::REGULAR; + // Scale y position proportionally to screen height + const int y = static_cast(renderer.getScreenHeight() * metrics.popupTopOffsetRatio); + const int textWidth = renderer.getTextWidth(UI_12_FONT_ID, message, popupFontFamily); const int textHeight = renderer.getLineHeight(UI_12_FONT_ID); - const int w = textWidth + margin * 2; - const int h = textHeight + margin * 2; + const int w = textWidth + marginX * 2; + const int h = textHeight + marginY * 2; const int x = (renderer.getScreenWidth() - w) / 2; - renderer.fillRect(x - 2, y - 2, w + 4, h + 4, true); // frame thickness 2 - renderer.fillRect(x, y, w, h, false); + const bool useRoundedPopup = metrics.popupCornerRadius > 0; + if (useRoundedPopup) { + renderer.fillRoundedRect(x - frameThickness, y - frameThickness, w + frameThickness * 2, h + frameThickness * 2, + metrics.popupCornerRadius + frameThickness, Color::White); + renderer.fillRoundedRect(x, y, w, h, metrics.popupCornerRadius, Color::Black); + } else { + renderer.fillRect(x - frameThickness, y - frameThickness, w + frameThickness * 2, h + frameThickness * 2, true); + renderer.fillRect(x, y, w, h, false); + } const int textX = x + (w - textWidth) / 2; - const int textY = y + margin - 2; - renderer.drawText(UI_12_FONT_ID, textX, textY, message, true, EpdFontFamily::BOLD); + const int textY = y + marginY + metrics.popupTextBaselineOffsetY; + renderer.drawText(UI_12_FONT_ID, textX, textY, message, metrics.popupTextInverted, popupFontFamily); renderer.displayBuffer(); return Rect{x, y, w, h}; } void BaseTheme::fillPopupProgress(const GfxRenderer& renderer, const Rect& layout, const int progress) const { - constexpr int barHeight = 4; - const int barWidth = layout.width - 30; // twice the margin in drawPopup to match text width + const auto& metrics = UITheme::getInstance().getMetrics(); + const int barHeight = metrics.popupProgressBarHeight; + const int barWidth = + std::max(0, layout.width - metrics.popupMarginX * 2); // twice the margin in drawPopup to match text width const int barX = layout.x + (layout.width - barWidth) / 2; - const int barY = layout.y + layout.height - 10; + const int barY = layout.y + layout.height - metrics.popupMarginY / 2 - barHeight / 2 - 1; + if (barWidth <= 0 || barHeight <= 0) { + renderer.displayBuffer(HalDisplay::FAST_REFRESH); + return; + } - int fillWidth = barWidth * progress / 100; + const int scaledProgress = metrics.popupProgressClampPercent ? std::clamp(progress, 0, 100) : progress; + const int fillWidth = barWidth * scaledProgress / 100; - renderer.fillRect(barX, barY, fillWidth, barHeight, true); + if (metrics.popupProgressDrawOutline) { + renderer.drawRect(barX, barY, barWidth, barHeight, 1, metrics.popupProgressOutlineInverted); + } + if (fillWidth > 0) { + renderer.fillRect(barX, barY, fillWidth, barHeight, metrics.popupProgressFillInverted); + } renderer.displayBuffer(HalDisplay::FAST_REFRESH); } @@ -821,14 +844,14 @@ void BaseTheme::drawTextField(const GfxRenderer& renderer, Rect rect, const int const auto& metrics = UITheme::getInstance().getMetrics(); const int lineHeight = renderer.getLineHeight(UI_12_FONT_ID); const int lineY = rect.y + rect.height + lineHeight + metrics.verticalSpacing; - const int thickness = cursorMode ? 3 : 1; + const int thickness = cursorMode ? metrics.textFieldCursorThickness : metrics.textFieldNormalThickness; if (contentWidth > 0) { - renderer.drawLine(rect.x + contentStartX, lineY, rect.x + contentStartX + contentWidth, lineY, thickness, true); + renderer.drawLine(rect.x + contentStartX, lineY, + rect.x + contentStartX + contentWidth + metrics.textFieldLineEndOffset, lineY, thickness, true); } else { - const int hPadding = 6; - const int lineW = textWidth + hPadding * 2; - renderer.drawLine(rect.x + (rect.width - lineW) / 2, lineY, rect.x + (rect.width + lineW) / 2, lineY, thickness, - true); + const int lineW = textWidth + metrics.textFieldHorizontalPadding * 2; + const int lineStart = rect.x + (rect.width - lineW) / 2; + renderer.drawLine(lineStart, lineY, lineStart + lineW + metrics.textFieldLineEndOffset, lineY, thickness, true); } } @@ -837,6 +860,9 @@ void BaseTheme::drawKeyboardKey(const GfxRenderer& renderer, Rect rect, const ch const bool inactiveSelection) const { const auto& metrics = UITheme::getInstance().getMetrics(); const int cr = metrics.keyboardKeyCornerRadius; + const bool isSpecialKey = keyType == KeyboardKeyType::Shift || keyType == KeyboardKeyType::Mode || + keyType == KeyboardKeyType::Del || keyType == KeyboardKeyType::Space || + keyType == KeyboardKeyType::Ok || keyType == KeyboardKeyType::Disabled; if (isSelected) { if (inactiveSelection) { @@ -858,13 +884,31 @@ void BaseTheme::drawKeyboardKey(const GfxRenderer& renderer, Rect rect, const ch renderer.fillRect(rect.x, rect.y, rect.width, rect.height, true); } } - } else if (keyType == KeyboardKeyType::Shift || keyType == KeyboardKeyType::Mode || keyType == KeyboardKeyType::Del || - keyType == KeyboardKeyType::Space || keyType == KeyboardKeyType::Ok || - keyType == KeyboardKeyType::Disabled) { - if (cr > 0) { - renderer.drawRoundedRect(rect.x, rect.y, rect.width, rect.height, 1, cr, true); - } else { - renderer.drawRect(rect.x, rect.y, rect.width, rect.height); + } else { + if (metrics.keyboardFillUnselected) { + if (keyType == KeyboardKeyType::Disabled) { + if (cr > 0) { + renderer.fillRoundedRect(rect.x, rect.y, rect.width, rect.height, cr, Color::LightGray); + } else { + renderer.fillRectDither(rect.x, rect.y, rect.width, rect.height, Color::LightGray); + } + } else { + if (cr > 0) { + renderer.fillRoundedRect(rect.x, rect.y, rect.width, rect.height, cr, Color::White); + } else { + renderer.fillRect(rect.x, rect.y, rect.width, rect.height, false); + } + } + } + + const bool shouldDrawOutline = + (metrics.keyboardDrawSpecialOutlineWhenUnselected && isSpecialKey) || metrics.keyboardOutlineAllUnselected; + if (shouldDrawOutline) { + if (cr > 0) { + renderer.drawRoundedRect(rect.x, rect.y, rect.width, rect.height, 1, cr, true); + } else { + renderer.drawRect(rect.x, rect.y, rect.width, rect.height); + } } } @@ -882,7 +926,7 @@ void BaseTheme::drawKeyboardKey(const GfxRenderer& renderer, Rect rect, const ch const int centerX = rect.x + rect.width / 2; const int centerY = rect.y + rect.height / 2; const int arrowLen = rect.width / 4; - const int arrowHead = arrowLen / 2; + const int arrowHead = std::max(metrics.keyboardMinArrowHeadSize, arrowLen / 2); renderer.drawLine(centerX - arrowLen / 2, centerY, centerX + arrowLen / 2, centerY, 3, !invert); renderer.drawLine(centerX - arrowLen / 2, centerY, centerX - arrowLen / 2 + arrowHead, centerY - arrowHead, 3, !invert); @@ -891,6 +935,10 @@ void BaseTheme::drawKeyboardKey(const GfxRenderer& renderer, Rect rect, const ch return; } + if (label == nullptr || label[0] == '\0') { + return; + } + const bool hasSecondary = secondaryLabel != nullptr && secondaryLabel[0] != '\0'; const int itemWidth = renderer.getTextWidth(UI_12_FONT_ID, label); const int textX = rect.x + (rect.width - itemWidth) / 2; @@ -900,6 +948,7 @@ void BaseTheme::drawKeyboardKey(const GfxRenderer& renderer, Rect rect, const ch if (hasSecondary) { const int secWidth = renderer.getTextWidth(SMALL_FONT_ID, secondaryLabel); - renderer.drawText(SMALL_FONT_ID, rect.x + rect.width - secWidth - 1, rect.y, secondaryLabel, !invert); + renderer.drawText(SMALL_FONT_ID, rect.x + rect.width - secWidth - metrics.keyboardSecondaryLabelRightPadding, + rect.y + metrics.keyboardSecondaryLabelTopPadding, secondaryLabel, !invert); } } diff --git a/src/components/themes/BaseTheme.h b/src/components/themes/BaseTheme.h index d21821ec..d6bcb0d2 100644 --- a/src/components/themes/BaseTheme.h +++ b/src/components/themes/BaseTheme.h @@ -70,6 +70,31 @@ struct ThemeMetrics { int keyboardTextFieldWidthPercent; int keyboardWidthPercent; int keyboardKeyCornerRadius; + bool keyboardFillUnselected; + bool keyboardOutlineAllUnselected; + bool keyboardDrawSpecialOutlineWhenUnselected; + int keyboardSecondaryLabelRightPadding; + int keyboardSecondaryLabelTopPadding; + int keyboardMinArrowHeadSize; + + float popupTopOffsetRatio; + int popupMarginX; + int popupMarginY; + int popupFrameThickness; + int popupCornerRadius; + bool popupTextBold; + bool popupTextInverted; + int popupTextBaselineOffsetY; + int popupProgressBarHeight; + bool popupProgressDrawOutline; + bool popupProgressClampPercent; + bool popupProgressFillInverted; + bool popupProgressOutlineInverted; + + int textFieldHorizontalPadding; + int textFieldNormalThickness; + int textFieldCursorThickness; + int textFieldLineEndOffset; }; enum UIIcon { Folder, Text, Image, Book, File, Recent, Settings, Transfer, Library, Wifi, Hotspot }; @@ -117,7 +142,30 @@ constexpr ThemeMetrics values = {.batteryWidth = 15, .keyboardVerticalOffset = -13, .keyboardTextFieldWidthPercent = 85, .keyboardWidthPercent = 90, - .keyboardKeyCornerRadius = 0}; + .keyboardKeyCornerRadius = 0, + .keyboardFillUnselected = false, + .keyboardOutlineAllUnselected = false, + .keyboardDrawSpecialOutlineWhenUnselected = true, + .keyboardSecondaryLabelRightPadding = 1, + .keyboardSecondaryLabelTopPadding = 0, + .keyboardMinArrowHeadSize = 0, + .popupTopOffsetRatio = 0.075f, + .popupMarginX = 15, + .popupMarginY = 15, + .popupFrameThickness = 2, + .popupCornerRadius = 0, + .popupTextBold = true, + .popupTextInverted = true, + .popupTextBaselineOffsetY = -2, + .popupProgressBarHeight = 4, + .popupProgressDrawOutline = false, + .popupProgressClampPercent = false, + .popupProgressFillInverted = true, + .popupProgressOutlineInverted = true, + .textFieldHorizontalPadding = 6, + .textFieldNormalThickness = 1, + .textFieldCursorThickness = 3, + .textFieldLineEndOffset = 0}; } class BaseTheme { diff --git a/src/components/themes/lyra/LyraTheme.cpp b/src/components/themes/lyra/LyraTheme.cpp index 1e11f236..bb3b8ad4 100644 --- a/src/components/themes/lyra/LyraTheme.cpp +++ b/src/components/themes/lyra/LyraTheme.cpp @@ -33,8 +33,6 @@ namespace { constexpr int hPaddingInSelection = 8; constexpr int cornerRadius = 6; constexpr int topHintButtonY = 345; -constexpr int popupMarginX = 16; -constexpr int popupMarginY = 12; constexpr int maxListValueWidth = 200; constexpr int mainMenuIconSize = 32; constexpr int listIconSize = 24; @@ -536,41 +534,3 @@ void LyraTheme::drawButtonMenu(GfxRenderer& renderer, Rect rect, int buttonCount renderer.drawText(UI_12_FONT_ID, textX, textY, label, true); } } - -Rect LyraTheme::drawPopup(const GfxRenderer& renderer, const char* message) const { - // Scale y position proportionally to screen height (16.5% from top) - const int y = static_cast(renderer.getScreenHeight() * 0.165f); - constexpr int outline = 2; - const int textWidth = renderer.getTextWidth(UI_12_FONT_ID, message, EpdFontFamily::REGULAR); - const int textHeight = renderer.getLineHeight(UI_12_FONT_ID); - const int w = textWidth + popupMarginX * 2; - const int h = textHeight + popupMarginY * 2; - const int x = (renderer.getScreenWidth() - w) / 2; - - renderer.fillRoundedRect(x - outline, y - outline, w + outline * 2, h + outline * 2, cornerRadius + outline, - Color::White); - renderer.fillRoundedRect(x, y, w, h, cornerRadius, Color::Black); - - const int textX = x + (w - textWidth) / 2; - const int textY = y + popupMarginY - 2; - renderer.drawText(UI_12_FONT_ID, textX, textY, message, false, EpdFontFamily::REGULAR); - renderer.displayBuffer(); - - return Rect{x, y, w, h}; -} - -void LyraTheme::fillPopupProgress(const GfxRenderer& renderer, const Rect& layout, const int progress) const { - constexpr int barHeight = 4; - - // Twice the margin in drawPopup to match text width - const int barWidth = layout.width - popupMarginX * 2; - const int barX = layout.x + (layout.width - barWidth) / 2; - // Center inside the margin of drawPopup. The - 1 is added to account for the - 2 in drawPopup. - const int barY = layout.y + layout.height - popupMarginY / 2 - barHeight / 2 - 1; - - int fillWidth = barWidth * progress / 100; - - renderer.fillRect(barX, barY, fillWidth, barHeight, false); - - renderer.displayBuffer(HalDisplay::FAST_REFRESH); -} diff --git a/src/components/themes/lyra/LyraTheme.h b/src/components/themes/lyra/LyraTheme.h index d984019e..00a72afc 100644 --- a/src/components/themes/lyra/LyraTheme.h +++ b/src/components/themes/lyra/LyraTheme.h @@ -43,7 +43,30 @@ constexpr ThemeMetrics values = {.batteryWidth = 16, .keyboardVerticalOffset = -7, .keyboardTextFieldWidthPercent = 85, .keyboardWidthPercent = 90, - .keyboardKeyCornerRadius = 6}; + .keyboardKeyCornerRadius = 6, + .keyboardFillUnselected = false, + .keyboardOutlineAllUnselected = false, + .keyboardDrawSpecialOutlineWhenUnselected = true, + .keyboardSecondaryLabelRightPadding = 1, + .keyboardSecondaryLabelTopPadding = 0, + .keyboardMinArrowHeadSize = 0, + .popupTopOffsetRatio = 0.165f, + .popupMarginX = 16, + .popupMarginY = 12, + .popupFrameThickness = 2, + .popupCornerRadius = 6, + .popupTextBold = false, + .popupTextInverted = false, + .popupTextBaselineOffsetY = -2, + .popupProgressBarHeight = 4, + .popupProgressDrawOutline = false, + .popupProgressClampPercent = false, + .popupProgressFillInverted = false, + .popupProgressOutlineInverted = false, + .textFieldHorizontalPadding = 6, + .textFieldNormalThickness = 1, + .textFieldCursorThickness = 3, + .textFieldLineEndOffset = 0}; } class LyraTheme : public BaseTheme { @@ -70,7 +93,5 @@ class LyraTheme : public BaseTheme { const int selectorIndex, bool& coverRendered, bool& coverBufferStored, bool& bufferRestored, std::function storeCoverBuffer) const override; void drawEmptyRecents(const GfxRenderer& renderer, const Rect rect) const; - Rect drawPopup(const GfxRenderer& renderer, const char* message) const override; - void fillPopupProgress(const GfxRenderer& renderer, const Rect& layout, const int progress) const override; bool showsFileIcons() const override { return true; } }; diff --git a/src/components/themes/roundedraff/RoundedRaffTheme.h b/src/components/themes/roundedraff/RoundedRaffTheme.h index b73a5b55..3f916a26 100644 --- a/src/components/themes/roundedraff/RoundedRaffTheme.h +++ b/src/components/themes/roundedraff/RoundedRaffTheme.h @@ -43,7 +43,30 @@ constexpr ThemeMetrics values = {.batteryWidth = 15, .keyboardVerticalOffset = 0, .keyboardTextFieldWidthPercent = 85, .keyboardWidthPercent = 90, - .keyboardKeyCornerRadius = 0}; + .keyboardKeyCornerRadius = 10, + .keyboardFillUnselected = true, + .keyboardOutlineAllUnselected = true, + .keyboardDrawSpecialOutlineWhenUnselected = true, + .keyboardSecondaryLabelRightPadding = 3, + .keyboardSecondaryLabelTopPadding = 1, + .keyboardMinArrowHeadSize = 1, + .popupTopOffsetRatio = 0.12f, + .popupMarginX = 20, + .popupMarginY = 14, + .popupFrameThickness = 2, + .popupCornerRadius = 18, + .popupTextBold = true, + .popupTextInverted = false, + .popupTextBaselineOffsetY = -2, + .popupProgressBarHeight = 4, + .popupProgressDrawOutline = true, + .popupProgressClampPercent = true, + .popupProgressFillInverted = false, + .popupProgressOutlineInverted = false, + .textFieldHorizontalPadding = 8, + .textFieldNormalThickness = 2, + .textFieldCursorThickness = 3, + .textFieldLineEndOffset = -1}; } class RoundedRaffTheme : public BaseTheme {