mirror of
https://github.com/meshtastic/firmware.git
synced 2025-06-08 14:12:05 +00:00
Mute symbol on Header
This commit is contained in:
parent
9fc208df5f
commit
4b770ceade
@ -12,6 +12,7 @@ namespace graphics
|
|||||||
|
|
||||||
// === Shared External State ===
|
// === Shared External State ===
|
||||||
bool hasUnreadMessage = false;
|
bool hasUnreadMessage = false;
|
||||||
|
bool isMuted = false;
|
||||||
|
|
||||||
// === Internal State ===
|
// === Internal State ===
|
||||||
bool isBoltVisibleShared = true;
|
bool isBoltVisibleShared = true;
|
||||||
@ -24,13 +25,16 @@ uint32_t lastMailBlink = 0;
|
|||||||
// *********************************
|
// *********************************
|
||||||
void drawRoundedHighlight(OLEDDisplay *display, int16_t x, int16_t y, int16_t w, int16_t h, int16_t r)
|
void drawRoundedHighlight(OLEDDisplay *display, int16_t x, int16_t y, int16_t w, int16_t h, int16_t r)
|
||||||
{
|
{
|
||||||
display->fillRect(x + r, y, w - 2 * r, h);
|
// Draw the center and side rectangles
|
||||||
display->fillRect(x, y + r, r, h - 2 * r);
|
display->fillRect(x + r, y, w - 2 * r, h); // center bar
|
||||||
display->fillRect(x + w - r, y + r, r, h - 2 * r);
|
display->fillRect(x, y + r, r, h - 2 * r); // left edge
|
||||||
display->fillCircle(x + r + 1, y + r, r);
|
display->fillRect(x + w - r, y + r, r, h - 2 * r); // right edge
|
||||||
display->fillCircle(x + w - r - 1, y + r, r);
|
|
||||||
display->fillCircle(x + r + 1, y + h - r - 1, r);
|
// Draw the rounded corners using filled circles
|
||||||
display->fillCircle(x + w - r - 1, y + h - r - 1, r);
|
display->fillCircle(x + r + 1, y + r, r); // top-left
|
||||||
|
display->fillCircle(x + w - r - 1, y + r, r); // top-right
|
||||||
|
display->fillCircle(x + r + 1, y + h - r - 1, r); // bottom-left
|
||||||
|
display->fillCircle(x + w - r - 1, y + h - r - 1, r); // bottom-right
|
||||||
}
|
}
|
||||||
|
|
||||||
// *************************
|
// *************************
|
||||||
@ -52,13 +56,17 @@ void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y)
|
|||||||
const int screenW = display->getWidth();
|
const int screenW = display->getWidth();
|
||||||
const int screenH = display->getHeight();
|
const int screenH = display->getHeight();
|
||||||
|
|
||||||
|
// === Draw background highlight if inverted ===
|
||||||
if (isInverted) {
|
if (isInverted) {
|
||||||
drawRoundedHighlight(display, x, y, screenW, highlightHeight, 2);
|
drawRoundedHighlight(display, x, y, screenW, highlightHeight, 2);
|
||||||
display->setColor(BLACK);
|
display->setColor(BLACK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// === Get battery charge percentage and charging status ===
|
||||||
int chargePercent = powerStatus->getBatteryChargePercent();
|
int chargePercent = powerStatus->getBatteryChargePercent();
|
||||||
bool isCharging = powerStatus->getIsCharging() == meshtastic::OptionalBool::OptTrue;
|
bool isCharging = powerStatus->getIsCharging() == meshtastic::OptionalBool::OptTrue;
|
||||||
|
|
||||||
|
// === Animate lightning bolt blinking if charging ===
|
||||||
uint32_t now = millis();
|
uint32_t now = millis();
|
||||||
if (isCharging && now - lastBlinkShared > 500) {
|
if (isCharging && now - lastBlinkShared > 500) {
|
||||||
isBoltVisibleShared = !isBoltVisibleShared;
|
isBoltVisibleShared = !isBoltVisibleShared;
|
||||||
@ -68,8 +76,9 @@ void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y)
|
|||||||
bool useHorizontalBattery = (screenW > 128 && screenW > screenH);
|
bool useHorizontalBattery = (screenW > 128 && screenW > screenH);
|
||||||
const int textY = y + (highlightHeight - FONT_HEIGHT_SMALL) / 2;
|
const int textY = y + (highlightHeight - FONT_HEIGHT_SMALL) / 2;
|
||||||
|
|
||||||
// === Battery Icons ===
|
// === Draw battery icon ===
|
||||||
if (useHorizontalBattery) {
|
if (useHorizontalBattery) {
|
||||||
|
// Wide screen battery layout
|
||||||
int batteryX = 2;
|
int batteryX = 2;
|
||||||
int batteryY = HEADER_OFFSET_Y + 2;
|
int batteryY = HEADER_OFFSET_Y + 2;
|
||||||
display->drawXbm(batteryX, batteryY, 29, 15, batteryBitmap_h);
|
display->drawXbm(batteryX, batteryY, 29, 15, batteryBitmap_h);
|
||||||
@ -81,10 +90,11 @@ void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y)
|
|||||||
display->fillRect(batteryX + 1, batteryY + 1, fillWidth, 13);
|
display->fillRect(batteryX + 1, batteryY + 1, fillWidth, 13);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// Tall screen battery layout
|
||||||
int batteryX = 1;
|
int batteryX = 1;
|
||||||
int batteryY = HEADER_OFFSET_Y + 1;
|
int batteryY = HEADER_OFFSET_Y + 1;
|
||||||
#ifdef USE_EINK
|
#ifdef USE_EINK
|
||||||
batteryY += 2;
|
batteryY += 2; // Extra spacing on E-Ink
|
||||||
#endif
|
#endif
|
||||||
display->drawXbm(batteryX, batteryY, 7, 11, batteryBitmap_v);
|
display->drawXbm(batteryX, batteryY, 7, 11, batteryBitmap_v);
|
||||||
if (isCharging && isBoltVisibleShared) {
|
if (isCharging && isBoltVisibleShared) {
|
||||||
@ -114,53 +124,72 @@ void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y)
|
|||||||
display->drawString(percentX + chargeNumWidth, textY, "%");
|
display->drawString(percentX + chargeNumWidth, textY, "%");
|
||||||
}
|
}
|
||||||
|
|
||||||
// === Time and Mail Icon ===
|
// === Handle time display and alignment ===
|
||||||
uint32_t rtc_sec = getValidTime(RTCQuality::RTCQualityDevice, true);
|
uint32_t rtc_sec = getValidTime(RTCQuality::RTCQualityDevice, true);
|
||||||
|
char timeStr[10] = "";
|
||||||
|
int alignX;
|
||||||
|
|
||||||
if (rtc_sec > 0) {
|
if (rtc_sec > 0) {
|
||||||
|
// Format time string (12h or 24h)
|
||||||
long hms = (rtc_sec % SEC_PER_DAY + SEC_PER_DAY) % SEC_PER_DAY;
|
long hms = (rtc_sec % SEC_PER_DAY + SEC_PER_DAY) % SEC_PER_DAY;
|
||||||
int hour = hms / SEC_PER_HOUR;
|
int hour = hms / SEC_PER_HOUR;
|
||||||
int minute = (hms % SEC_PER_HOUR) / SEC_PER_MIN;
|
int minute = (hms % SEC_PER_HOUR) / SEC_PER_MIN;
|
||||||
|
|
||||||
char timeStr[10];
|
|
||||||
snprintf(timeStr, sizeof(timeStr), "%d:%02d", hour, minute);
|
snprintf(timeStr, sizeof(timeStr), "%d:%02d", hour, minute);
|
||||||
if (config.display.use_12h_clock) {
|
if (config.display.use_12h_clock) {
|
||||||
bool isPM = hour >= 12;
|
bool isPM = hour >= 12;
|
||||||
hour %= 12;
|
hour %= 12;
|
||||||
if (hour == 0)
|
if (hour == 0) hour = 12;
|
||||||
hour = 12;
|
|
||||||
snprintf(timeStr, sizeof(timeStr), "%d:%02d%s", hour, minute, isPM ? "p" : "a");
|
snprintf(timeStr, sizeof(timeStr), "%d:%02d%s", hour, minute, isPM ? "p" : "a");
|
||||||
}
|
}
|
||||||
|
|
||||||
int timeStrWidth = display->getStringWidth(timeStr);
|
int timeStrWidth = display->getStringWidth(timeStr);
|
||||||
int timeX = screenW - xOffset - timeStrWidth + 4;
|
alignX = screenW - xOffset - timeStrWidth + 4;
|
||||||
|
} else {
|
||||||
if (hasUnreadMessage) {
|
// If time is not valid, reserve space for alignment anyway
|
||||||
if (now - lastMailBlink > 500) {
|
int fallbackWidth = display->getStringWidth("12:34");
|
||||||
isMailIconVisible = !isMailIconVisible;
|
alignX = screenW - xOffset - fallbackWidth + 4;
|
||||||
lastMailBlink = now;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isMailIconVisible) {
|
|
||||||
if (useHorizontalBattery) {
|
|
||||||
int iconW = 16, iconH = 12;
|
|
||||||
int iconX = timeX - iconW - 4;
|
|
||||||
int iconY = textY + (FONT_HEIGHT_SMALL - iconH) / 2 - 1;
|
|
||||||
display->drawRect(iconX, iconY, iconW + 1, iconH);
|
|
||||||
display->drawLine(iconX, iconY, iconX + iconW / 2, iconY + iconH - 4);
|
|
||||||
display->drawLine(iconX + iconW, iconY, iconX + iconW / 2, iconY + iconH - 4);
|
|
||||||
} else {
|
|
||||||
int iconX = timeX - mail_width;
|
|
||||||
int iconY = textY + (FONT_HEIGHT_SMALL - mail_height) / 2;
|
|
||||||
display->drawXbm(iconX, iconY, mail_width, mail_height, mail);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
display->drawString(timeX, textY, timeStr);
|
|
||||||
if (isBold)
|
|
||||||
display->drawString(timeX - 1, textY, timeStr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// === Determine if mail icon should blink ===
|
||||||
|
bool showMail = false;
|
||||||
|
if (hasUnreadMessage) {
|
||||||
|
if (now - lastMailBlink > 500) {
|
||||||
|
isMailIconVisible = !isMailIconVisible;
|
||||||
|
lastMailBlink = now;
|
||||||
|
}
|
||||||
|
showMail = isMailIconVisible;
|
||||||
|
}
|
||||||
|
|
||||||
|
// === Draw Mail or Mute icon in the top-right corner ===
|
||||||
|
if (showMail) {
|
||||||
|
if (useHorizontalBattery) {
|
||||||
|
int iconW = 16, iconH = 12;
|
||||||
|
int iconX = screenW - xOffset - iconW;
|
||||||
|
int iconY = textY + (FONT_HEIGHT_SMALL - iconH) / 2 - 1;
|
||||||
|
display->drawRect(iconX, iconY, iconW + 1, iconH);
|
||||||
|
display->drawLine(iconX, iconY, iconX + iconW / 2, iconY + iconH - 4);
|
||||||
|
display->drawLine(iconX + iconW, iconY, iconX + iconW / 2, iconY + iconH - 4);
|
||||||
|
} else {
|
||||||
|
int iconX = screenW - xOffset - mail_width;
|
||||||
|
int iconY = textY + (FONT_HEIGHT_SMALL - mail_height) / 2;
|
||||||
|
display->drawXbm(iconX, iconY, mail_width, mail_height, mail);
|
||||||
|
}
|
||||||
|
} else if (isMuted) {
|
||||||
|
const char* muteStr = "M";
|
||||||
|
int mWidth = display->getStringWidth(muteStr);
|
||||||
|
int mX = screenW - xOffset - mWidth;
|
||||||
|
display->drawString(mX, textY, muteStr);
|
||||||
|
if (isBold)
|
||||||
|
display->drawString(mX + 1, textY, muteStr);
|
||||||
|
} else if (rtc_sec > 0) {
|
||||||
|
// Only draw the time if nothing else is shown
|
||||||
|
display->drawString(alignX, textY, timeStr);
|
||||||
|
if (isBold)
|
||||||
|
display->drawString(alignX - 1, textY, timeStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// === Reset color back to white for following content ===
|
||||||
display->setColor(WHITE);
|
display->setColor(WHITE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,6 +27,7 @@ namespace graphics {
|
|||||||
|
|
||||||
// Shared state (declare inside namespace)
|
// Shared state (declare inside namespace)
|
||||||
extern bool hasUnreadMessage;
|
extern bool hasUnreadMessage;
|
||||||
|
extern bool isMuted;
|
||||||
|
|
||||||
// Rounded highlight (used for inverted headers)
|
// Rounded highlight (used for inverted headers)
|
||||||
void drawRoundedHighlight(OLEDDisplay *display, int16_t x, int16_t y, int16_t w, int16_t h, int16_t r);
|
void drawRoundedHighlight(OLEDDisplay *display, int16_t x, int16_t y, int16_t w, int16_t h, int16_t r);
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
#include "input/ScanAndSelect.h"
|
#include "input/ScanAndSelect.h"
|
||||||
#include "mesh/generated/meshtastic/cannedmessages.pb.h"
|
#include "mesh/generated/meshtastic/cannedmessages.pb.h"
|
||||||
#include "modules/AdminModule.h"
|
#include "modules/AdminModule.h"
|
||||||
|
#include "graphics/SharedUIDisplay.h"
|
||||||
|
|
||||||
#include "main.h" // for cardkb_found
|
#include "main.h" // for cardkb_found
|
||||||
#include "modules/ExternalNotificationModule.h" // for buzzer control
|
#include "modules/ExternalNotificationModule.h" // for buzzer control
|
||||||
@ -35,6 +36,7 @@
|
|||||||
#define INACTIVATE_AFTER_MS 20000
|
#define INACTIVATE_AFTER_MS 20000
|
||||||
|
|
||||||
extern ScanI2C::DeviceAddress cardkb_found;
|
extern ScanI2C::DeviceAddress cardkb_found;
|
||||||
|
extern bool graphics::isMuted;
|
||||||
|
|
||||||
static const char *cannedMessagesConfigFile = "/prefs/cannedConf.proto";
|
static const char *cannedMessagesConfigFile = "/prefs/cannedConf.proto";
|
||||||
|
|
||||||
@ -463,23 +465,19 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event)
|
|||||||
break;
|
break;
|
||||||
// mute (switch off/toggle) external notifications on fn+m
|
// mute (switch off/toggle) external notifications on fn+m
|
||||||
case INPUT_BROKER_MSG_MUTE_TOGGLE:
|
case INPUT_BROKER_MSG_MUTE_TOGGLE:
|
||||||
if (moduleConfig.external_notification.enabled == true) {
|
if (moduleConfig.external_notification.enabled == true) {
|
||||||
if (externalNotificationModule->getMute()) {
|
if (externalNotificationModule->getMute()) {
|
||||||
externalNotificationModule->setMute(false);
|
externalNotificationModule->setMute(false);
|
||||||
if (screen) {
|
graphics::isMuted = false;
|
||||||
screen->removeFunctionSymbol("M");
|
if (screen) screen->showOverlayBanner("Notifications\nEnabled", 3000);
|
||||||
screen->showOverlayBanner("Notifications\nEnabled", 3000);
|
} else {
|
||||||
}
|
externalNotificationModule->stopNow();
|
||||||
} else {
|
externalNotificationModule->setMute(true);
|
||||||
externalNotificationModule->stopNow();
|
graphics::isMuted = true;
|
||||||
externalNotificationModule->setMute(true);
|
if (screen) screen->showOverlayBanner("Notifications\nDisabled", 3000);
|
||||||
if (screen) {
|
|
||||||
screen->setFunctionSymbol("M");
|
|
||||||
screen->showOverlayBanner("Notifications\nDisabled", 3000);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
break;
|
||||||
break;
|
|
||||||
|
|
||||||
case INPUT_BROKER_MSG_GPS_TOGGLE:
|
case INPUT_BROKER_MSG_GPS_TOGGLE:
|
||||||
#if !MESHTASTIC_EXCLUDE_GPS
|
#if !MESHTASTIC_EXCLUDE_GPS
|
||||||
|
@ -330,7 +330,8 @@ void EnvironmentTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiSt
|
|||||||
// === Draw Title (Centered under header) ===
|
// === Draw Title (Centered under header) ===
|
||||||
const int highlightHeight = FONT_HEIGHT_SMALL - 1;
|
const int highlightHeight = FONT_HEIGHT_SMALL - 1;
|
||||||
const int titleY = y + 1 + (highlightHeight - FONT_HEIGHT_SMALL) / 2;
|
const int titleY = y + 1 + (highlightHeight - FONT_HEIGHT_SMALL) / 2;
|
||||||
const char *titleStr = "Environment";
|
const char *titleStr = (SCREEN_WIDTH > 128) ? "Environment" : "Env.";
|
||||||
|
|
||||||
const int centerX = x + SCREEN_WIDTH / 2;
|
const int centerX = x + SCREEN_WIDTH / 2;
|
||||||
|
|
||||||
// Use black text on white background if in inverted mode
|
// Use black text on white background if in inverted mode
|
||||||
|
Loading…
Reference in New Issue
Block a user