up
CI / setup (all) (push) Canceled after 0s
CI / setup (check) (push) Canceled after 0s
CI / version (push) Canceled after 0s
CI / check (${{ fromJson(needs.setup.outputs.check) }}) (push) Canceled after 0s
CI / build (${{ fromJson(needs.setup.outputs.all) }}) (push) Canceled after 0s
Build Debian Source Package / build-debian-src (push) Canceled after 0s
Package PlatformIO Library Dependencies / pkg-pio-libdeps (push) Canceled after 0s
Run Tests on Native platform / Generate Test Reports (push) Canceled after 0s
Run Tests on Native platform / Native PlatformIO Tests (push) Canceled after 0s
Run Tests on Native platform / Native Simulator Tests (push) Canceled after 0s
CI / gather-artifacts (esp32) (push) Canceled after 0s
CI / gather-artifacts (esp32c3) (push) Canceled after 0s
CI / gather-artifacts (esp32c6) (push) Canceled after 0s
CI / gather-artifacts (esp32s3) (push) Canceled after 0s
CI / gather-artifacts (nrf52840) (push) Canceled after 0s
CI / gather-artifacts (rp2040) (push) Canceled after 0s
CI / gather-artifacts (rp2350) (push) Canceled after 0s
CI / gather-artifacts (stm32) (push) Canceled after 0s
CI / firmware-size-report (push) Canceled after 0s
CI / release-artifacts (push) Canceled after 0s
CI / release-firmware (esp32) (push) Canceled after 0s
CI / release-firmware (esp32c3) (push) Canceled after 0s
CI / release-firmware (esp32c6) (push) Canceled after 0s
CI / release-firmware (esp32s3) (push) Canceled after 0s
CI / release-firmware (nrf52840) (push) Canceled after 0s
CI / release-firmware (rp2040) (push) Canceled after 0s
CI / release-firmware (rp2350) (push) Canceled after 0s
CI / release-firmware (stm32) (push) Canceled after 0s
CI / publish-firmware (push) Canceled after 0s
CI / build-debian-src (push) Canceled after 0s
CI / MacOS (15) (push) Canceled after 0s
CI / MacOS (26) (push) Canceled after 0s
Build MacOS Binary / build-MacOS (push) Canceled after 0s
CI / package-pio-deps-native-tft (push) Canceled after 0s
CI / test-native (push) Canceled after 0s
CI / docker (alpine, native, linux/amd64) (push) Canceled after 0s
CI / docker (alpine, native, linux/arm64) (push) Canceled after 0s
CI / docker (alpine, native-tft, linux/amd64) (push) Canceled after 0s
CI / docker (debian, native, linux/amd64) (push) Canceled after 0s
CI / docker (debian, native, linux/arm/v7) (push) Canceled after 0s
CI / docker (debian, native, linux/arm64) (push) Canceled after 0s
CI / docker (debian, native-tft, linux/amd64) (push) Canceled after 0s
Build Docker / docker-build (push) Canceled after 0s

This commit is contained in:
2026-06-25 21:24:54 +08:00
parent bc241481b1
commit d7cbac0156
7 changed files with 509 additions and 155 deletions
+5 -5
View File
@@ -142,10 +142,11 @@ static void drawLockdownLockScreenIntoBuffer(OLEDDisplay *display)
display->setTextAlignment(TEXT_ALIGN_CENTER);
display->setFont(FONT_LARGE);
display->drawString(w / 2, h / 2 - FONT_HEIGHT_LARGE, "LOCKED");
display->drawString(w / 2, h / 2 - FONT_HEIGHT_LARGE, meshtastic::l10n::getString(meshtastic::l10n::StringKey::LOCKED));
display->setFont(FONT_SMALL);
char status[32] = "Connect to unlock";
char status[32];
strncpy(status, meshtastic::l10n::getString(meshtastic::l10n::StringKey::ConnectToUnlock), 31);
if (powerStatus && powerStatus->getHasBattery()) {
int pct = powerStatus->getBatteryChargePercent();
snprintf(status, sizeof(status), "Battery %d%%", pct);
@@ -836,7 +837,7 @@ void Screen::setup()
alertFrames[0] = [this](OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) {
#ifdef ARCH_ESP32
if (wakeCause == ESP_SLEEP_WAKEUP_TIMER || wakeCause == ESP_SLEEP_WAKEUP_EXT1)
graphics::UIRenderer::drawFrameText(display, state, x, y, "Resuming...");
graphics::UIRenderer::drawFrameText(display, state, x, y, meshtastic::l10n::getString(meshtastic::l10n::StringKey::Resuming));
else
#endif
{
@@ -1067,7 +1068,7 @@ int32_t Screen::runOnce()
}
#endif
if (!NotificationRenderer::isOverlayBannerShowing() && rebootAtMsec != 0 && !suppressRebootBanner) {
showSimpleBanner("Rebooting...", 0);
showSimpleBanner(meshtastic::l10n::getString(meshtastic::l10n::StringKey::Rebooting), 0);
}
// Process incoming commands.
@@ -2465,7 +2466,6 @@ void Screen::drawTextL10n(int16_t x, int16_t y, const char *text, OLEDDISPLAY_TE
}
#endif
dispdev->setFont(FONT_SMALL);
dispdev->setTextAlignment(alignment);
dispdev->drawString(x, y, text);
}
+240 -135
View File
@@ -36,6 +36,9 @@
namespace graphics
{
using namespace meshtastic::l10n;
using meshtastic::l10n::StringKey;
namespace
{
@@ -133,10 +136,15 @@ uint8_t test_count = 0;
void menuHandler::loraMenu()
{
static const char *optionsArray[] = {"Back", "Device Role", "Radio Preset", "Frequency Slot", "LoRa Region"};
static const char *optionsArray[5];
optionsArray[0] = getString(StringKey::Back);
optionsArray[1] = getString(StringKey::DeviceRole);
optionsArray[2] = getString(StringKey::RadioPreset);
optionsArray[3] = getString(StringKey::FrequencySlot);
optionsArray[4] = getString(StringKey::LoRaRegion);
enum optionsNumbers { Back = 0, DeviceRolePicker = 1, RadioPresetPicker = 2, FrequencySlot = 3, LoraPicker = 4 };
BannerOverlayOptions bannerOptions;
bannerOptions.message = "LoRa Actions";
bannerOptions.message = getString(StringKey::LoRaActions);
bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsCount = 5;
bannerOptions.bannerCallback = [](int selected) -> void {
@@ -157,7 +165,9 @@ void menuHandler::loraMenu()
void menuHandler::OnboardMessage()
{
static const char *optionsArray[] = {"OK", "Got it!"};
static const char *optionsArray[2];
optionsArray[0] = getString(StringKey::OK);
optionsArray[1] = getString(StringKey::GotIt);
enum optionsNumbers { OK, got };
BannerOverlayOptions bannerOptions;
#if HAS_TFT
@@ -221,7 +231,7 @@ static void applyLoraRegion(meshtastic_Config_LoRaConfig_RegionCode region, bool
void menuHandler::LoraRegionPicker(uint32_t duration)
{
static const LoraRegionOption regionOptions[] = {
{"Back", OptionsAction::Back},
{getString(StringKey::Back), OptionsAction::Back},
{"US", OptionsAction::Select, meshtastic_Config_LoRaConfig_RegionCode_US},
{"EU_433", OptionsAction::Select, meshtastic_Config_LoRaConfig_RegionCode_EU_433},
{"EU_868", OptionsAction::Select, meshtastic_Config_LoRaConfig_RegionCode_EU_868},
@@ -262,7 +272,7 @@ void menuHandler::LoraRegionPicker(uint32_t duration)
const char *bannerMessage = "Set the LoRa region";
if (currentResolution == ScreenResolution::UltraLow) {
bannerMessage = "LoRa Region";
bannerMessage = getString(StringKey::LoRaRegion);
}
auto bannerOptions =
@@ -354,7 +364,12 @@ void menuHandler::licensedToNormalConfirmMenu()
void menuHandler::deviceRolePicker()
{
static const char *optionsArray[] = {"Back", "Client", "Client Mute", "Lost and Found", "Tracker"};
static const char *optionsArray[5];
optionsArray[0] = getString(StringKey::Back);
optionsArray[1] = getString(StringKey::Client);
optionsArray[2] = getString(StringKey::ClientMute);
optionsArray[3] = getString(StringKey::LostAndFound);
optionsArray[4] = getString(StringKey::Tracker);
enum optionsNumbers {
Back = 0,
devicerole_client = 1,
@@ -363,7 +378,7 @@ void menuHandler::deviceRolePicker()
devicerole_tracker = 4
};
BannerOverlayOptions bannerOptions;
bannerOptions.message = "Device Role";
bannerOptions.message = getString(StringKey::DeviceRole);
bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsCount = 5;
bannerOptions.bannerCallback = [](int selected) -> void {
@@ -395,9 +410,9 @@ void menuHandler::FrequencySlotPicker()
static int optionsEnumArray[MAX_CHANNEL_OPTIONS];
static char channelText[MAX_CHANNEL_OPTIONS - 1][12];
int options = 0;
optionsArray[options] = "Back";
optionsArray[options] = getString(StringKey::Back);
optionsEnumArray[options++] = Back;
optionsArray[options] = "Slot 0 (Auto)";
optionsArray[options] = getString(StringKey::Slot0);
optionsEnumArray[options++] = 0;
// Calculate number of channels (copied from RadioInterface::applyModemConfig())
@@ -434,7 +449,7 @@ void menuHandler::FrequencySlotPicker()
}
BannerOverlayOptions bannerOptions;
bannerOptions.message = "Frequency Slot";
bannerOptions.message = getString(StringKey::FrequencySlot);
bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsEnumPtr = optionsEnumArray;
bannerOptions.optionsCount = options;
@@ -470,7 +485,7 @@ static BannerOverlayOptions buildRegionPresetBanner()
static char presetLabelBuf[MAX_PRESET_OPTIONS][12]; // scratch space for name copies
int count = 0;
optionsArray[count] = "Back";
optionsArray[count] = getString(StringKey::Back);
optionsEnumArray[count++] = -1;
if (myRegion && myRegion->profile) {
@@ -494,7 +509,7 @@ static BannerOverlayOptions buildRegionPresetBanner()
}
BannerOverlayOptions bannerOptions;
bannerOptions.message = "Radio Preset";
bannerOptions.message = getString(StringKey::RadioPreset);
bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsEnumPtr = optionsEnumArray;
bannerOptions.optionsCount = static_cast<uint8_t>(count);
@@ -521,10 +536,13 @@ void menuHandler::radioPresetPicker()
void menuHandler::twelveHourPicker()
{
static const char *optionsArray[] = {"Back", "12-hour", "24-hour"};
static const char *optionsArray[3];
optionsArray[0] = getString(StringKey::Back);
optionsArray[1] = getString(StringKey::Hour12);
optionsArray[2] = getString(StringKey::Hour24);
enum optionsNumbers { Back = 0, twelve = 1, twentyfour = 2 };
BannerOverlayOptions bannerOptions;
bannerOptions.message = "Time Format";
bannerOptions.message = getString(StringKey::TimeFormat);
bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsCount = 3;
bannerOptions.bannerCallback = [](int selected) -> void {
@@ -560,7 +578,7 @@ void menuHandler::showConfirmationBanner(const char *message, std::function<void
void menuHandler::clockFacePicker()
{
static const ClockFaceOption clockFaceOptions[] = {
{"Back", OptionsAction::Back},
{getString(StringKey::Back), OptionsAction::Back},
{"Digital", OptionsAction::Select, false},
{"Analog", OptionsAction::Select, true},
};
@@ -596,7 +614,7 @@ void menuHandler::clockFacePicker()
void menuHandler::TZPicker()
{
static const TimezoneOption timezoneOptions[] = {
{"Back", OptionsAction::Back},
{getString(StringKey::Back), OptionsAction::Back},
{"US/Hawaii", OptionsAction::Select, "HST10"},
{"US/Alaska", OptionsAction::Select, "AKST9AKDT,M3.2.0,M11.1.0"},
{"US/Pacific", OptionsAction::Select, "PST8PDT,M3.2.0,M11.1.0"},
@@ -659,13 +677,20 @@ void menuHandler::TZPicker()
void menuHandler::clockMenu()
{
#if defined(OLED_TINY)
static const char *optionsArray[] = {"Back", "Time Format", "Timezone"};
static const char *optionsArray[3];
optionsArray[0] = getString(StringKey::Back);
optionsArray[1] = getString(StringKey::TimeFormat);
optionsArray[2] = getString(StringKey::Timezone);
#else
static const char *optionsArray[] = {"Back", "Clock Face", "Time Format", "Timezone"};
static const char *optionsArray[4];
optionsArray[0] = getString(StringKey::Back);
optionsArray[1] = getString(StringKey::ClockFace);
optionsArray[2] = getString(StringKey::TimeFormat);
optionsArray[3] = getString(StringKey::Timezone);
#endif
enum optionsNumbers { Back = 0, Clock = 1, Time = 2, Timezone = 3 };
BannerOverlayOptions bannerOptions;
bannerOptions.message = "Clock Action";
bannerOptions.message = getString(StringKey::ClockAction);
bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsCount = 4;
bannerOptions.bannerCallback = [](int selected) -> void {
@@ -693,14 +718,14 @@ void menuHandler::messageResponseMenu()
auto mode = graphics::MessageRenderer::getThreadMode();
int threadChannel = graphics::MessageRenderer::getThreadChannel();
optionsArray[options] = "Back";
optionsArray[options] = getString(StringKey::Back);
optionsEnumArray[options++] = Back;
// New Reply submenu (replaces Preset and Freetext directly in this menu)
optionsArray[options] = "Reply";
optionsArray[options] = getString(StringKey::Reply);
optionsEnumArray[options++] = ReplyMenu;
optionsArray[options] = "View Chats";
optionsArray[options] = getString(StringKey::ViewChats);
optionsEnumArray[options++] = ViewMode;
// If viewing ALL chats, hide “Mute Chat”
@@ -708,21 +733,21 @@ void menuHandler::messageResponseMenu()
const uint8_t chIndex = (threadChannel != 0) ? (uint8_t)threadChannel : channels.getPrimaryIndex();
const auto &chan = channels.getByIndex(chIndex);
optionsArray[options] = chan.settings.module_settings.is_muted ? "Unmute Channel" : "Mute Channel";
optionsArray[options] = chan.settings.module_settings.is_muted ? getString(StringKey::UnmuteChannel) : getString(StringKey::MuteChannel);
optionsEnumArray[options++] = MuteChannel;
}
// Delete submenu
optionsArray[options] = "Delete";
optionsArray[options] = getString(StringKey::Delete);
optionsEnumArray[options++] = DeleteMenu;
#ifdef HAS_I2S
optionsArray[options] = "Read Aloud";
optionsArray[options] = getString(StringKey::ReadAloud);
optionsEnumArray[options++] = Aloud;
#endif
BannerOverlayOptions bannerOptions;
bannerOptions.message = "Message Action";
bannerOptions.message = getString(StringKey::MessageAction);
if (currentResolution == ScreenResolution::UltraLow) {
bannerOptions.message = "Message";
}
@@ -782,16 +807,16 @@ void menuHandler::replyMenu()
int options = 0;
// Back
optionsArray[options] = "Back";
optionsArray[options] = getString(StringKey::Back);
optionsEnumArray[options++] = Back;
// Preset reply
optionsArray[options] = "With Preset";
optionsArray[options] = getString(StringKey::WithPreset);
optionsEnumArray[options++] = ReplyPreset;
// Freetext reply (only when keyboard exists)
if (kb_found) {
optionsArray[options] = "With Freetext";
optionsArray[options] = getString(StringKey::WithFreetext);
optionsEnumArray[options++] = ReplyFreetext;
}
@@ -862,21 +887,21 @@ void menuHandler::deleteMessagesMenu()
auto mode = graphics::MessageRenderer::getThreadMode();
optionsArray[options] = "Back";
optionsArray[options] = getString(StringKey::Back);
optionsEnumArray[options++] = Back;
optionsArray[options] = "Delete Oldest";
optionsArray[options] = getString(StringKey::DeleteOldest);
optionsEnumArray[options++] = DeleteOldest;
// If viewing ALL chats → hide “Delete This Chat”
if (mode != graphics::MessageRenderer::ThreadMode::ALL) {
optionsArray[options] = "Delete This Chat";
optionsArray[options] = getString(StringKey::DeleteThisChat);
optionsEnumArray[options++] = DeleteThis;
}
if (currentResolution == ScreenResolution::UltraLow) {
optionsArray[options] = "Delete All";
optionsArray[options] = getString(StringKey::DeleteAll);
} else {
optionsArray[options] = "Delete All Chats";
optionsArray[options] = getString(StringKey::DeleteAllChats);
}
optionsEnumArray[options++] = DeleteAll;
@@ -946,7 +971,7 @@ void menuHandler::messageViewModeMenu()
ids.clear();
idToPeer.clear();
labels.push_back("Back");
labels.push_back(getString(StringKey::Back));
ids.push_back(-1);
labels.push_back("View All Chats");
ids.push_back(-2);
@@ -1077,30 +1102,31 @@ void menuHandler::homeBaseMenu()
{
enum optionsNumbers { Back, Mute, Backlight, Position, Preset, Freetext, Sleep, enumEnd };
static const char *optionsArray[enumEnd] = {"Back"};
static const char *optionsArray[enumEnd];
optionsArray[0] = getString(StringKey::Back);
static int optionsEnumArray[enumEnd] = {Back};
int options = 1;
if (moduleConfig.external_notification.enabled && externalNotificationModule &&
config.device.buzzer_mode != meshtastic_Config_DeviceConfig_BuzzerMode_DISABLED) {
if (!externalNotificationModule->getMute()) {
optionsArray[options] = "Temporarily Mute";
optionsArray[options] = getString(StringKey::TemporarilyMute);
} else {
optionsArray[options] = "Unmute";
optionsArray[options] = getString(StringKey::Unmute);
}
optionsEnumArray[options++] = Mute;
}
#if defined(PIN_EINK_EN) || defined(PCA_PIN_EINK_EN)
optionsArray[options] = "Toggle Backlight";
optionsArray[options] = getString(StringKey::ToggleBacklight);
optionsEnumArray[options++] = Backlight;
#else
optionsArray[options] = "Sleep Screen";
optionsArray[options] = getString(StringKey::SleepScreen);
optionsEnumArray[options++] = Sleep;
#endif
if (config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_ENABLED) {
optionsArray[options] = "Send Position";
optionsArray[options] = getString(StringKey::SendPosition);
} else {
optionsArray[options] = "Send Node Info";
optionsArray[options] = getString(StringKey::SendNodeInfo);
}
optionsEnumArray[options++] = Position;
@@ -1166,18 +1192,19 @@ void menuHandler::textMessageBaseMenu()
{
enum optionsNumbers { Back, Preset, Freetext, enumEnd };
static const char *optionsArray[enumEnd] = {"Back"};
static const char *optionsArray[enumEnd];
optionsArray[0] = getString(StringKey::Back);
static int optionsEnumArray[enumEnd] = {Back};
int options = 1;
optionsArray[options] = "New Preset Msg";
optionsArray[options] = getString(StringKey::NewPresetMsg);
optionsEnumArray[options++] = Preset;
if (kb_found) {
optionsArray[options] = "New Freetext Msg";
optionsArray[options] = getString(StringKey::NewFreetextMsg);
optionsEnumArray[options++] = Freetext;
}
BannerOverlayOptions bannerOptions;
bannerOptions.message = "Message Action";
bannerOptions.message = getString(StringKey::MessageAction);
bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsEnumPtr = optionsEnumArray;
bannerOptions.optionsCount = options;
@@ -1194,36 +1221,37 @@ void menuHandler::textMessageBaseMenu()
void menuHandler::systemBaseMenu()
{
enum optionsNumbers { Back, Notifications, ScreenOptions, Bluetooth, WiFiToggle, PowerMenu, Test, enumEnd };
static const char *optionsArray[enumEnd] = {"Back"};
static const char *optionsArray[enumEnd];
optionsArray[0] = getString(StringKey::Back);
static int optionsEnumArray[enumEnd] = {Back};
int options = 1;
optionsArray[options] = "Notifications";
optionsArray[options] = getString(StringKey::Notifications);
optionsEnumArray[options++] = Notifications;
optionsArray[options] = "Display Options";
optionsArray[options] = getString(StringKey::DisplayOptions);
optionsEnumArray[options++] = ScreenOptions;
if (currentResolution == ScreenResolution::UltraLow) {
optionsArray[options] = "Bluetooth";
optionsArray[options] = getString(StringKey::Bluetooth);
} else {
optionsArray[options] = "Bluetooth Toggle";
optionsArray[options] = getString(StringKey::BluetoothToggle);
}
optionsEnumArray[options++] = Bluetooth;
#if HAS_WIFI && !defined(ARCH_PORTDUINO)
optionsArray[options] = "WiFi Toggle";
optionsArray[options] = getString(StringKey::WifiToggle);
optionsEnumArray[options++] = WiFiToggle;
#endif
if (currentResolution == ScreenResolution::UltraLow) {
optionsArray[options] = "Power";
optionsArray[options] = getString(StringKey::Power);
} else {
optionsArray[options] = "Reboot/Shutdown";
optionsArray[options] = getString(StringKey::RebootShutdown);
}
optionsEnumArray[options++] = PowerMenu;
if (test_enabled) {
optionsArray[options] = "Test Menu";
optionsArray[options] = getString(StringKey::TestMenu);
optionsEnumArray[options++] = Test;
}
@@ -1270,7 +1298,8 @@ void menuHandler::favoriteBaseMenu()
{
enum optionsNumbers { Back, Preset, Freetext, GoToChat, Remove, TraceRoute, enumEnd };
static const char *optionsArray[enumEnd] = {"Back"};
static const char *optionsArray[enumEnd];
optionsArray[0] = getString(StringKey::Back);
static int optionsEnumArray[enumEnd] = {Back};
int options = 1;
@@ -1284,26 +1313,26 @@ void menuHandler::favoriteBaseMenu()
}
}
if (hasConversation) {
optionsArray[options] = "Go To Chat";
optionsArray[options] = getString(StringKey::GoToChat);
optionsEnumArray[options++] = GoToChat;
}
if (currentResolution == ScreenResolution::UltraLow) {
optionsArray[options] = "New Preset";
optionsArray[options] = getString(StringKey::NewPreset);
} else {
optionsArray[options] = "New Preset Msg";
optionsArray[options] = getString(StringKey::NewPresetMsg);
}
optionsEnumArray[options++] = Preset;
if (kb_found) {
optionsArray[options] = "New Freetext Msg";
optionsArray[options] = getString(StringKey::NewFreetextMsg);
optionsEnumArray[options++] = Freetext;
}
if (currentResolution != ScreenResolution::UltraLow) {
optionsArray[options] = "Trace Route";
optionsArray[options] = getString(StringKey::TraceRoute);
optionsEnumArray[options++] = TraceRoute;
}
optionsArray[options] = "Remove Favorite";
optionsArray[options] = getString(StringKey::RemoveFavorite);
optionsEnumArray[options++] = Remove;
BannerOverlayOptions bannerOptions;
@@ -1355,7 +1384,7 @@ void menuHandler::positionBaseMenu()
};
static const PositionMenuOption baseOptions[] = {
{"Back", OptionsAction::Back},
{getString(StringKey::Back), OptionsAction::Back},
{"On/Off Toggle", OptionsAction::Select, static_cast<int>(PositionAction::GpsToggle)},
{"Format", OptionsAction::Select, static_cast<int>(PositionAction::GpsFormat)},
{"Smart Position", OptionsAction::Select, static_cast<int>(PositionAction::GPSSmartPosition)},
@@ -1365,7 +1394,7 @@ void menuHandler::positionBaseMenu()
};
static const PositionMenuOption calibrateOptions[] = {
{"Back", OptionsAction::Back},
{getString(StringKey::Back), OptionsAction::Back},
{"On/Off Toggle", OptionsAction::Select, static_cast<int>(PositionAction::GpsToggle)},
{"Format", OptionsAction::Select, static_cast<int>(PositionAction::GpsFormat)},
{"Smart Position", OptionsAction::Select, static_cast<int>(PositionAction::GPSSmartPosition)},
@@ -1449,18 +1478,19 @@ void menuHandler::positionBaseMenu()
void menuHandler::nodeListMenu()
{
enum optionsNumbers { Back, NodePicker, TraceRoute, Verify, Reset, NodeNameLength, enumEnd };
static const char *optionsArray[enumEnd] = {"Back"};
static const char *optionsArray[enumEnd];
optionsArray[0] = getString(StringKey::Back);
static int optionsEnumArray[enumEnd] = {Back};
int options = 1;
optionsArray[options] = "Node Actions / Settings";
optionsArray[options] = getString(StringKey::NodeActionsSettings);
optionsEnumArray[options++] = NodePicker;
if (currentResolution != ScreenResolution::UltraLow) {
optionsArray[options] = "Show Long/Short Name";
optionsArray[options] = getString(StringKey::ShowNameLength);
optionsEnumArray[options++] = NodeNameLength;
}
optionsArray[options] = "Reset NodeDB";
optionsArray[options] = getString(StringKey::NodeDbReset);
optionsEnumArray[options++] = Reset;
BannerOverlayOptions bannerOptions;
@@ -1510,35 +1540,36 @@ void menuHandler::manageNodeMenu()
return;
}
enum optionsNumbers { Back, Favorite, Mute, TraceRoute, KeyVerification, Ignore, enumEnd };
static const char *optionsArray[enumEnd] = {"Back"};
static const char *optionsArray[enumEnd];
optionsArray[0] = getString(StringKey::Back);
static int optionsEnumArray[enumEnd] = {Back};
int options = 1;
if (nodeInfoLiteIsFavorite(node)) {
optionsArray[options] = "Unfavorite";
optionsArray[options] = getString(StringKey::RemoveFavorite);
} else {
optionsArray[options] = "Favorite";
optionsArray[options] = getString(StringKey::Favorite);
}
optionsEnumArray[options++] = Favorite;
bool isMuted = nodeInfoLiteIsMuted(node);
if (isMuted) {
optionsArray[options] = "Unmute Notifications";
optionsArray[options] = getString(StringKey::UnmuteNotifications);
} else {
optionsArray[options] = "Mute Notifications";
optionsArray[options] = getString(StringKey::MuteNotifications);
}
optionsEnumArray[options++] = Mute;
optionsArray[options] = "Trace Route";
optionsArray[options] = getString(StringKey::TraceRoute);
optionsEnumArray[options++] = TraceRoute;
optionsArray[options] = "Key Verification";
optionsArray[options] = getString(StringKey::KeyVerification);
optionsEnumArray[options++] = KeyVerification;
if (nodeInfoLiteIsIgnored(node)) {
optionsArray[options] = "Unignore Node";
optionsArray[options] = getString(StringKey::UnignoreNode);
} else {
optionsArray[options] = "Ignore Node";
optionsArray[options] = getString(StringKey::IgnoreNode);
}
optionsEnumArray[options++] = Ignore;
@@ -1644,7 +1675,7 @@ void menuHandler::manageNodeMenu()
void menuHandler::nodeNameLengthMenu()
{
static const NodeNameOption nodeNameOptions[] = {
{"Back", OptionsAction::Back},
{getString(StringKey::Back), OptionsAction::Back},
{"Long", OptionsAction::Select, true},
{"Short", OptionsAction::Select, false},
};
@@ -1682,7 +1713,10 @@ void menuHandler::nodeNameLengthMenu()
void menuHandler::resetNodeDBMenu()
{
static const char *optionsArray[] = {"Back", "Reset All", "Preserve Favorites"};
static const char *optionsArray[3];
optionsArray[0] = getString(StringKey::Back);
optionsArray[1] = "Reset All";
optionsArray[2] = "Preserve Favorites";
BannerOverlayOptions bannerOptions;
bannerOptions.message = "Confirm Reset NodeDB";
bannerOptions.optionsArrayPtr = optionsArray;
@@ -1711,7 +1745,7 @@ void menuHandler::resetNodeDBMenu()
void menuHandler::compassNorthMenu()
{
static const CompassOption compassOptions[] = {
{"Back", OptionsAction::Back},
{getString(StringKey::Back), OptionsAction::Back},
{"Dynamic", OptionsAction::Select, meshtastic_CompassMode_DYNAMIC},
{"Fixed Ring", OptionsAction::Select, meshtastic_CompassMode_FIXED_RING},
{"Freeze Heading", OptionsAction::Select, meshtastic_CompassMode_FREEZE_HEADING},
@@ -1757,7 +1791,7 @@ void menuHandler::compassNorthMenu()
void menuHandler::GPSToggleMenu()
{
static const GPSToggleOption gpsToggleOptions[] = {
{"Back", OptionsAction::Back},
{getString(StringKey::Back), OptionsAction::Back},
{"Enabled", OptionsAction::Select, meshtastic_Config_PositionConfig_GpsMode_ENABLED},
{"Disabled", OptionsAction::Select, meshtastic_Config_PositionConfig_GpsMode_DISABLED},
};
@@ -1806,7 +1840,7 @@ void menuHandler::GPSToggleMenu()
void menuHandler::GPSFormatMenu()
{
static const GPSFormatOption formatOptionsHigh[] = {
{"Back", OptionsAction::Back},
{getString(StringKey::Back), OptionsAction::Back},
{"Decimal Degrees", OptionsAction::Select, meshtastic_DeviceUIConfig_GpsCoordinateFormat_DEC},
{"Degrees Minutes Seconds", OptionsAction::Select, meshtastic_DeviceUIConfig_GpsCoordinateFormat_DMS},
{"Universal Transverse Mercator", OptionsAction::Select, meshtastic_DeviceUIConfig_GpsCoordinateFormat_UTM},
@@ -1817,7 +1851,7 @@ void menuHandler::GPSFormatMenu()
};
static const GPSFormatOption formatOptionsLow[] = {
{"Back", OptionsAction::Back},
{getString(StringKey::Back), OptionsAction::Back},
{"DEC", OptionsAction::Select, meshtastic_DeviceUIConfig_GpsCoordinateFormat_DEC},
{"DMS", OptionsAction::Select, meshtastic_DeviceUIConfig_GpsCoordinateFormat_DMS},
{"UTM", OptionsAction::Select, meshtastic_DeviceUIConfig_GpsCoordinateFormat_UTM},
@@ -1855,7 +1889,7 @@ void menuHandler::GPSFormatMenu()
int initialSelection = 0;
if (currentResolution == ScreenResolution::High) {
bannerOptions = createStaticBannerOptions("GPS Format", formatOptionsHigh, formatLabelsHigh, onSelection);
bannerOptions = createStaticBannerOptions(getString(StringKey::GPSFormat), formatOptionsHigh, formatLabelsHigh, onSelection);
for (size_t i = 0; i < formatCount; ++i) {
if (formatOptionsHigh[i].hasValue && uiconfig.gps_format == formatOptionsHigh[i].value) {
initialSelection = static_cast<int>(i);
@@ -1863,7 +1897,7 @@ void menuHandler::GPSFormatMenu()
}
}
} else {
bannerOptions = createStaticBannerOptions("GPS Format", formatOptionsLow, formatLabelsLow, onSelection);
bannerOptions = createStaticBannerOptions(getString(StringKey::GPSFormat), formatOptionsLow, formatLabelsLow, onSelection);
for (size_t i = 0; i < formatCount; ++i) {
if (formatOptionsLow[i].hasValue && uiconfig.gps_format == formatOptionsLow[i].value) {
initialSelection = static_cast<int>(i);
@@ -1878,7 +1912,10 @@ void menuHandler::GPSFormatMenu()
void menuHandler::GPSSmartPositionMenu()
{
static const char *optionsArray[] = {"Back", "Enabled", "Disabled"};
static const char *optionsArray[3];
optionsArray[0] = getString(StringKey::Back);
optionsArray[1] = "Enabled";
optionsArray[2] = "Disabled";
BannerOverlayOptions bannerOptions;
bannerOptions.message = "Toggle Smart Position";
if (currentResolution == ScreenResolution::UltraLow) {
@@ -1908,9 +1945,23 @@ void menuHandler::GPSSmartPositionMenu()
void menuHandler::GPSUpdateIntervalMenu()
{
static const char *optionsArray[] = {"Back", "8 seconds", "20 seconds", "40 seconds", "1 minute", "80 seconds",
"2 minutes", "5 minutes", "10 minutes", "15 minutes", "30 minutes", "1 hour",
"6 hours", "12 hours", "24 hours", "At Boot Only"};
static const char *optionsArray[16];
optionsArray[0] = getString(StringKey::Back);
optionsArray[1] = "8 seconds";
optionsArray[2] = "20 seconds";
optionsArray[3] = "40 seconds";
optionsArray[4] = "1 minute";
optionsArray[5] = "80 seconds";
optionsArray[6] = "2 minutes";
optionsArray[7] = "5 minutes";
optionsArray[8] = "10 minutes";
optionsArray[9] = "15 minutes";
optionsArray[10] = "30 minutes";
optionsArray[11] = "1 hour";
optionsArray[12] = "6 hours";
optionsArray[13] = "12 hours";
optionsArray[14] = "24 hours";
optionsArray[15] = "At Boot Only";
BannerOverlayOptions bannerOptions;
bannerOptions.message = "Update Interval";
bannerOptions.optionsArrayPtr = optionsArray;
@@ -1996,9 +2047,24 @@ void menuHandler::GPSUpdateIntervalMenu()
void menuHandler::GPSPositionBroadcastMenu()
{
static const char *optionsArray[] = {"Back", "1 minute", "90 seconds", "5 minutes", "15 minutes", "1 hour",
"2 hours", "3 hours", "4 hours", "5 hours", "6 hours", "12 hours",
"18 hours", "24 hours", "36 hours", "48 hours", "72 hours"};
static const char *optionsArray[17];
optionsArray[0] = getString(StringKey::Back);
optionsArray[1] = "1 minute";
optionsArray[2] = "90 seconds";
optionsArray[3] = "5 minutes";
optionsArray[4] = "15 minutes";
optionsArray[5] = "1 hour";
optionsArray[6] = "2 hours";
optionsArray[7] = "3 hours";
optionsArray[8] = "4 hours";
optionsArray[9] = "5 hours";
optionsArray[10] = "6 hours";
optionsArray[11] = "12 hours";
optionsArray[12] = "18 hours";
optionsArray[13] = "24 hours";
optionsArray[14] = "36 hours";
optionsArray[15] = "48 hours";
optionsArray[16] = "72 hours";
BannerOverlayOptions bannerOptions;
bannerOptions.message = "Broadcast Interval";
bannerOptions.optionsArrayPtr = optionsArray;
@@ -2090,11 +2156,14 @@ void menuHandler::GPSPositionBroadcastMenu()
void menuHandler::bluetoothToggleMenu()
{
static const char *optionsArray[] = {"Back", "Enabled", "Disabled"};
static const char *optionsArray[3];
optionsArray[0] = getString(StringKey::Back);
optionsArray[1] = "Enabled";
optionsArray[2] = "Disabled";
BannerOverlayOptions bannerOptions;
bannerOptions.message = "Toggle Bluetooth";
if (currentResolution == ScreenResolution::UltraLow) {
bannerOptions.message = "Bluetooth";
bannerOptions.message = getString(StringKey::Bluetooth);
}
bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsCount = 3;
@@ -2112,7 +2181,12 @@ void menuHandler::bluetoothToggleMenu()
void menuHandler::BuzzerModeMenu()
{
static const char *optionsArray[] = {"All Enabled", "All Disabled", "Notifications", "System Only", "DMs Only"};
static const char *optionsArray[5];
optionsArray[0] = "All Enabled";
optionsArray[1] = "All Disabled";
optionsArray[2] = getString(StringKey::Notifications);
optionsArray[3] = "System Only";
optionsArray[4] = "DMs Only";
BannerOverlayOptions bannerOptions;
bannerOptions.message = "Notification Sounds";
bannerOptions.optionsArrayPtr = optionsArray;
@@ -2127,7 +2201,11 @@ void menuHandler::BuzzerModeMenu()
void menuHandler::BrightnessPickerMenu()
{
static const char *optionsArray[] = {"Back", "Low", "Medium", "High"};
static const char *optionsArray[4];
optionsArray[0] = getString(StringKey::Back);
optionsArray[1] = "Low";
optionsArray[2] = "Medium";
optionsArray[3] = "High";
// Get current brightness level to set initial selection
int currentSelection = 1; // Default to Medium
@@ -2140,7 +2218,7 @@ void menuHandler::BrightnessPickerMenu()
}
BannerOverlayOptions bannerOptions;
bannerOptions.message = "Brightness";
bannerOptions.message = getString(StringKey::Brightness);
bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsCount = 4;
bannerOptions.bannerCallback = [](int selected) -> void {
@@ -2152,7 +2230,7 @@ void menuHandler::BrightnessPickerMenu()
uiconfig.screen_brightness = 255;
}
if (selected != 0) { // Not "Back"
if (selected != 0) { // Not getString(StringKey::Back)
// Apply brightness immediately
#if defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190)
// For HELTEC devices, use analogWrite to control backlight
@@ -2175,7 +2253,9 @@ void menuHandler::BrightnessPickerMenu()
void menuHandler::switchToMUIMenu()
{
static const char *optionsArray[] = {"No", "Yes"};
static const char *optionsArray[2];
optionsArray[0] = "No";
optionsArray[1] = "Yes";
BannerOverlayOptions bannerOptions;
bannerOptions.message = "Switch to MUI?";
bannerOptions.optionsArrayPtr = optionsArray;
@@ -2193,11 +2273,13 @@ void menuHandler::switchToMUIMenu()
void menuHandler::rebootMenu()
{
static const char *optionsArray[] = {"Back", "Confirm"};
static const char *optionsArray[2];
optionsArray[0] = getString(StringKey::Back);
optionsArray[1] = "Confirm";
BannerOverlayOptions bannerOptions;
bannerOptions.message = "Reboot Device?";
if (currentResolution == ScreenResolution::UltraLow) {
bannerOptions.message = "Reboot";
bannerOptions.message = getString(StringKey::Reboot);
}
bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsCount = 2;
@@ -2217,11 +2299,13 @@ void menuHandler::rebootMenu()
void menuHandler::shutdownMenu()
{
static const char *optionsArray[] = {"Back", "Confirm"};
static const char *optionsArray[2];
optionsArray[0] = getString(StringKey::Back);
optionsArray[1] = "Confirm";
BannerOverlayOptions bannerOptions;
bannerOptions.message = "Shutdown Device?";
if (currentResolution == ScreenResolution::UltraLow) {
bannerOptions.message = "Shutdown";
bannerOptions.message = getString(StringKey::Shutdown);
}
bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsCount = 2;
@@ -2240,7 +2324,9 @@ void menuHandler::shutdownMenu()
void menuHandler::removeFavoriteMenu()
{
static const char *optionsArray[] = {"Back", "Yes"};
static const char *optionsArray[2];
optionsArray[0] = getString(StringKey::Back);
optionsArray[1] = "Yes";
BannerOverlayOptions bannerOptions;
std::string message = "Unfavorite This Node?\n";
const auto *node = nodeDB->getMeshNode(graphics::UIRenderer::currentFavoriteNodeNum);
@@ -2274,17 +2360,18 @@ void menuHandler::testMenu()
{
enum optionsNumbers { Back, NumberPicker, ShowChirpy, TestAnnounce };
static const char *optionsArray[5] = {"Back"};
static const char *optionsArray[5];
optionsArray[0] = getString(StringKey::Back);
static int optionsEnumArray[5] = {Back};
int options = 1;
optionsArray[options] = "Number Picker";
optionsArray[options] = getString(StringKey::NumberPicker);
optionsEnumArray[options++] = NumberPicker;
optionsArray[options] = screen->isFrameHidden("chirpy") ? "Show Chirpy" : "Hide Chirpy";
optionsEnumArray[options++] = ShowChirpy;
#ifdef HAS_I2S
optionsArray[options] = "Test Announce";
optionsArray[options] = getString(StringKey::TestAnnounce);
optionsEnumArray[options++] = TestAnnounce;
#endif
@@ -2323,7 +2410,9 @@ void menuHandler::wifiBaseMenu()
{
enum optionsNumbers { Back, Wifi_toggle };
static const char *optionsArray[] = {"Back", "WiFi Toggle"};
static const char *optionsArray[2];
optionsArray[0] = getString(StringKey::Back);
optionsArray[1] = getString(StringKey::WifiToggle);
BannerOverlayOptions bannerOptions;
bannerOptions.message = "WiFi Menu";
bannerOptions.optionsArrayPtr = optionsArray;
@@ -2341,7 +2430,10 @@ void menuHandler::wifiToggleMenu()
{
enum optionsNumbers { Back, Wifi_disable, Wifi_enable };
static const char *optionsArray[] = {"Back", "WiFi Disabled", "WiFi Enabled"};
static const char *optionsArray[3];
optionsArray[0] = getString(StringKey::Back);
optionsArray[1] = "WiFi Disabled";
optionsArray[2] = "WiFi Enabled";
BannerOverlayOptions bannerOptions;
bannerOptions.message = "WiFi Actions";
bannerOptions.optionsArrayPtr = optionsArray;
@@ -2379,38 +2471,39 @@ void menuHandler::screenOptionsMenu()
#endif
enum optionsNumbers { Back, Brightness, FrameToggles, DisplayUnits, MessageBubbles, Theme, LangPicker };
static const char *optionsArray[8] = {"Back"};
static const char *optionsArray[8];
optionsArray[0] = getString(StringKey::Back);
static int optionsEnumArray[8] = {Back};
int options = 1;
// Only show brightness for B&W displays
if (hasSupportBrightness) {
optionsArray[options] = "Brightness";
optionsArray[options] = getString(StringKey::Brightness);
optionsEnumArray[options++] = Brightness;
}
optionsArray[options] = "Frame Visibility";
optionsArray[options] = getString(StringKey::FrameVisibility);
optionsEnumArray[options++] = FrameToggles;
optionsArray[options] = "Display Units";
optionsArray[options] = getString(StringKey::DisplayUnits);
optionsEnumArray[options++] = DisplayUnits;
optionsArray[options] = "Message Bubbles";
optionsArray[options] = getString(StringKey::MessageBubbles);
optionsEnumArray[options++] = MessageBubbles;
#if GRAPHICS_TFT_COLORING_ENABLED
optionsArray[options] = "Theme";
optionsArray[options] = getString(StringKey::Theme);
optionsEnumArray[options++] = Theme;
#endif
#if defined(ST7735_CS) || defined(ST7789_CS) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || \
defined(HX8357_CS) || defined(ILI9488_CS) || defined(ST7796_CS) || defined(RAK14014)
optionsArray[options] = "Language";
optionsArray[options] = getString(StringKey::Language);
optionsEnumArray[options++] = LangPicker;
#endif
BannerOverlayOptions bannerOptions;
bannerOptions.message = "Display Options";
bannerOptions.message = getString(StringKey::DisplayOptions);
bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsCount = options;
bannerOptions.optionsEnumPtr = optionsEnumArray;
@@ -2445,25 +2538,26 @@ void menuHandler::powerMenu()
{
enum optionsNumbers { Back, Reboot, Shutdown, MUI };
static const char *optionsArray[4] = {"Back"};
static const char *optionsArray[4];
optionsArray[0] = getString(StringKey::Back);
static int optionsEnumArray[4] = {Back};
int options = 1;
optionsArray[options] = "Reboot";
optionsArray[options] = getString(StringKey::Reboot);
optionsEnumArray[options++] = Reboot;
optionsArray[options] = "Shutdown";
optionsArray[options] = getString(StringKey::Shutdown);
optionsEnumArray[options++] = Shutdown;
#if HAS_TFT
optionsArray[options] = "Switch to MUI";
optionsArray[options] = getString(StringKey::SwitchToMUI);
optionsEnumArray[options++] = MUI;
#endif
BannerOverlayOptions bannerOptions;
bannerOptions.message = "Reboot / Shutdown";
if (currentResolution == ScreenResolution::UltraLow) {
bannerOptions.message = "Power";
bannerOptions.message = getString(StringKey::Power);
}
bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsCount = options;
@@ -2500,7 +2594,9 @@ void menuHandler::keyVerificationFinalPrompt()
keyVerificationModule->generateVerificationCode(message + 15); // send the toPhone packet
if (screen) {
static const char *optionsArray[] = {"Reject", "Accept"};
static const char *optionsArray[2];
optionsArray[0] = "Reject";
optionsArray[1] = "Accept";
graphics::BannerOverlayOptions options;
options.message = message;
options.durationMs = 30000;
@@ -2536,7 +2632,8 @@ void menuHandler::frameTogglesMenu()
show_power,
enumEnd
};
static const char *optionsArray[enumEnd] = {"Finish"};
static const char *optionsArray[enumEnd];
optionsArray[0] = "Finish";
static int optionsEnumArray[enumEnd] = {Finish};
int options = 1;
@@ -2665,7 +2762,10 @@ void menuHandler::displayUnitsMenu()
{
enum optionsNumbers { Back, MetricUnits, ImperialUnits };
static const char *optionsArray[] = {"Back", "Metric", "Imperial"};
static const char *optionsArray[3];
optionsArray[0] = getString(StringKey::Back);
optionsArray[1] = "Metric";
optionsArray[2] = "Imperial";
BannerOverlayOptions bannerOptions;
bannerOptions.message = " Select display units";
bannerOptions.optionsArrayPtr = optionsArray;
@@ -2693,9 +2793,12 @@ void menuHandler::messageBubblesMenu()
{
enum optionsNumbers { Back, ShowBubbles, HideBubbles };
static const char *optionsArray[] = {"Back", "Show Bubbles", "Hide Bubbles"};
static const char *optionsArray[3];
optionsArray[0] = getString(StringKey::Back);
optionsArray[1] = "Show Bubbles";
optionsArray[2] = "Hide Bubbles";
BannerOverlayOptions bannerOptions;
bannerOptions.message = "Message Bubbles";
bannerOptions.message = getString(StringKey::MessageBubbles);
bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsCount = 3;
bannerOptions.InitialSelected = config.display.enable_message_bubbles ? 1 : 2;
@@ -2720,11 +2823,12 @@ void menuHandler::themeMenu()
{
// Build menu dynamically from the theme table.
// Only visible themes appear!
// Slot budget: 1 for "Back" + up to kMaxThemesInMenu visible themes.
// Slot budget: 1 for getString(StringKey::Back) + up to kMaxThemesInMenu visible themes.
// Bump kMaxThemesInMenu if you add more themes than will fit here.
constexpr size_t kMaxThemesInMenu = 15;
const size_t visibleCount = getVisibleThemeCount();
static const char *optionsArray[kMaxThemesInMenu + 1] = {"Back"};
static const char *optionsArray[kMaxThemesInMenu + 1];
optionsArray[0] = getString(StringKey::Back);
const size_t shownCount = (visibleCount < kMaxThemesInMenu) ? visibleCount : kMaxThemesInMenu;
const int options = static_cast<int>(shownCount) + 1; // +1 for Back
@@ -2733,12 +2837,12 @@ void menuHandler::themeMenu()
}
BannerOverlayOptions bannerOptions;
bannerOptions.message = "Theme";
bannerOptions.message = getString(StringKey::Theme);
bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsCount = options;
// Highlight the currently active theme (visible index + 1 for the Back
// offset). If the active theme is hidden, leave selection on "Back".
// offset). If the active theme is hidden, leave selection on getString(StringKey::Back).
const size_t activeVisible = getActiveVisibleThemeIndex();
bannerOptions.InitialSelected = (activeVisible == SIZE_MAX) ? 0 : static_cast<int>(activeVisible) + 1;
@@ -2929,18 +3033,19 @@ void menuHandler::handleMenuSwitch(OLEDDisplay *display)
void menuHandler::languagePickerMenu()
{
enum optionsNumbers { Back, English, Chinese };
static const char *optionsArray[3] = {"Back"};
static const char *optionsArray[3];
optionsArray[0] = getString(StringKey::Back);
static int optionsEnumArray[3] = {Back};
int options = 1;
optionsArray[options] = "English";
optionsArray[options] = getString(StringKey::English);
optionsEnumArray[options++] = English;
optionsArray[options] = "Chinese";
optionsArray[options] = getString(StringKey::Chinese);
optionsEnumArray[options++] = Chinese;
BannerOverlayOptions bannerOptions;
bannerOptions.message = "Language";
bannerOptions.message = getString(StringKey::Language);
bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsCount = options;
bannerOptions.optionsEnumPtr = optionsEnumArray;
+1 -1
View File
@@ -492,7 +492,7 @@ void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
// Still in ALL mode and no messages at all → show placeholder
graphics::drawCommonHeader(display, x, y, titleStr);
didReset = false;
const char *messageString = "No messages";
const char *messageString = meshtastic::l10n::getString(meshtastic::l10n::StringKey::NoMessages);
int center_text = (SCREEN_WIDTH / 2) - (display->getStringWidth(messageString) / 2);
display->drawString(center_text, getTextPositions(display)[2], messageString);
graphics::drawCommonFooter(display, x, y);
+46 -14
View File
@@ -10,6 +10,8 @@
#include "graphics/TFTColorRegions.h"
#include "graphics/TFTPalette.h"
#include "graphics/images.h"
#include "graphics/l10n/Strings.h"
#include "graphics/Screen.h"
#include "input/RotaryEncoderInterruptImpl1.h"
#include "input/UpDownInterruptImpl1.h"
#if HAS_BUTTON
@@ -39,6 +41,27 @@ extern std::vector<std::string> functionSymbol;
extern std::string functionSymbolString;
extern bool hasUnreadMessage;
namespace
{
inline bool isCJKLanguage()
{
return meshtastic::l10n::currentDisplayLanguage == meshtastic_Language_SIMPLIFIED_CHINESE ||
meshtastic::l10n::currentDisplayLanguage == meshtastic_Language_TRADITIONAL_CHINESE;
}
inline void drawBannerText(OLEDDisplay *display, int16_t x, int16_t y, const char *text)
{
#if defined(ST7735_CS) || defined(ST7789_CS) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || \
defined(HX8357_CS) || defined(ILI9488_CS) || defined(ST7796_CS) || defined(RAK14014)
if (isCJKLanguage() && graphics::screen) {
graphics::screen->drawTextL10n(x, y, text);
return;
}
#endif
display->drawString(x, y, text);
}
} // namespace
namespace graphics
{
int bannerSignalBars = -1;
@@ -184,7 +207,7 @@ void NotificationRenderer::drawSSLScreen(OLEDDisplay *display, OLEDDisplayUiStat
{
display->setTextAlignment(TEXT_ALIGN_CENTER);
display->setFont(FONT_SMALL);
display->drawString(64 + x, y, "Creating SSL certificate");
display->drawString(64 + x, y, meshtastic::l10n::getString(meshtastic::l10n::StringKey::CreatingSSLCert));
#ifdef ARCH_ESP32
yield();
@@ -193,9 +216,9 @@ void NotificationRenderer::drawSSLScreen(OLEDDisplay *display, OLEDDisplayUiStat
display->setFont(FONT_SMALL);
if ((millis() / 1000) % 2) {
display->drawString(64 + x, FONT_HEIGHT_SMALL + y + 2, "Please wait . . .");
display->drawString(64 + x, FONT_HEIGHT_SMALL + y + 2, meshtastic::l10n::getString(meshtastic::l10n::StringKey::PleaseWait));
} else {
display->drawString(64 + x, FONT_HEIGHT_SMALL + y + 2, "Please wait . . ");
display->drawString(64 + x, FONT_HEIGHT_SMALL + y + 2, meshtastic::l10n::getString(meshtastic::l10n::StringKey::PleaseWait));
}
}
@@ -919,9 +942,12 @@ void NotificationRenderer::drawNotificationBox(OLEDDisplay *display, OLEDDisplay
display->setColor(BLACK);
int yOffset = 3;
if (current_notification_type == notificationTypeEnum::node_picker) {
UIRenderer::drawStringWithEmotes(display, textX, lineY - yOffset, lineBuffer, FONT_HEIGHT_SMALL, 1, false);
if (isCJKLanguage())
drawBannerText(display, textX, lineY - yOffset, lineBuffer);
else
UIRenderer::drawStringWithEmotes(display, textX, lineY - yOffset, lineBuffer, FONT_HEIGHT_SMALL, 1, false);
} else {
display->drawString(textX, lineY - yOffset, lineBuffer);
drawBannerText(display, textX, lineY - yOffset, lineBuffer);
}
display->setColor(WHITE);
lineY += (thisLineHeight - 2 - background_yOffset);
@@ -943,9 +969,12 @@ void NotificationRenderer::drawNotificationBox(OLEDDisplay *display, OLEDDisplay
int groupStartX = boxLeft + (boxWidth - totalWidth) / 2;
if (current_notification_type == notificationTypeEnum::node_picker) {
UIRenderer::drawStringWithEmotes(display, groupStartX, lineY, lineBuffer, FONT_HEIGHT_SMALL, 1, false);
if (isCJKLanguage())
drawBannerText(display, groupStartX, lineY, lineBuffer);
else
UIRenderer::drawStringWithEmotes(display, groupStartX, lineY, lineBuffer, FONT_HEIGHT_SMALL, 1, false);
} else {
display->drawString(groupStartX, lineY, lineBuffer);
drawBannerText(display, groupStartX, lineY, lineBuffer);
}
int baseX = groupStartX + textWidth + gap;
@@ -977,9 +1006,12 @@ void NotificationRenderer::drawNotificationBox(OLEDDisplay *display, OLEDDisplay
}
} else {
if (current_notification_type == notificationTypeEnum::node_picker) {
UIRenderer::drawStringWithEmotes(display, textX, lineY, lineBuffer, FONT_HEIGHT_SMALL, 1, false);
if (isCJKLanguage())
drawBannerText(display, textX, lineY, lineBuffer);
else
UIRenderer::drawStringWithEmotes(display, textX, lineY, lineBuffer, FONT_HEIGHT_SMALL, 1, false);
} else {
display->drawString(textX, lineY, lineBuffer);
drawBannerText(display, textX, lineY, lineBuffer);
}
}
lineY += thisLineHeight;
@@ -1009,24 +1041,24 @@ void NotificationRenderer::drawCriticalFaultFrame(OLEDDisplay *display, OLEDDisp
display->setTextAlignment(TEXT_ALIGN_LEFT);
display->setFont(FONT_MEDIUM);
char tempBuf[24];
snprintf(tempBuf, sizeof(tempBuf), "Critical fault #%d", error_code);
char tempBuf[40];
snprintf(tempBuf, sizeof(tempBuf), "%s #%d", meshtastic::l10n::getString(meshtastic::l10n::StringKey::CriticalFault), error_code);
display->drawString(0 + x, 0 + y, tempBuf);
display->setTextAlignment(TEXT_ALIGN_LEFT);
display->setFont(FONT_SMALL);
display->drawString(0 + x, FONT_HEIGHT_MEDIUM + y, "For help, please visit \nmeshtastic.org");
display->drawString(0 + x, FONT_HEIGHT_MEDIUM + y, meshtastic::l10n::getString(meshtastic::l10n::StringKey::ForHelpVisit));
}
void NotificationRenderer::drawFrameFirmware(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
display->setTextAlignment(TEXT_ALIGN_CENTER);
display->setFont(FONT_MEDIUM);
display->drawString(64 + x, y, "Updating");
display->drawString(64 + x, y, meshtastic::l10n::getString(meshtastic::l10n::StringKey::Updating));
display->setFont(FONT_SMALL);
display->setTextAlignment(TEXT_ALIGN_LEFT);
display->drawStringMaxWidth(0 + x, 2 + y + FONT_HEIGHT_SMALL * 2, x + display->getWidth(),
"Please be patient and do not power off.");
meshtastic::l10n::getString(meshtastic::l10n::StringKey::PleaseBePatient));
}
void NotificationRenderer::drawTextInput(OLEDDisplay *display, OLEDDisplayUiState *state)
+31
View File
@@ -121,6 +121,37 @@ enum class StringKey : uint8_t {
RegionUnset,
SelectRegionMessage,
Rebooting,
LoRaActions,
Slot0,
WithPreset,
WithFreetext,
DeleteOldest,
DeleteThisChat,
DeleteAll,
DeleteAllChats,
TemporarilyMute,
ToggleBacklight,
SleepScreen,
SendPosition,
SendNodeInfo,
NewPresetMsg,
NewFreetextMsg,
Notifications,
Power,
RebootShutdown,
GoToChat,
NewPreset,
Favorite,
UnmuteNotifications,
MuteNotifications,
UnignoreNode,
IgnoreNode,
NumberPicker,
TestAnnounce,
NodeActionsSettings,
ShowNameLength,
Bluetooth,
NoMessages,
COUNT
};
+93
View File
@@ -123,6 +123,37 @@ static const char STR_KEY_VERIFY_EN[] = "Verify Key";
static const char STR_REGION_UNSET_EN[] = "Select a Region";
static const char STR_SELECT_REGION_EN[] = "Please select your LoRa region:";
static const char STR_REBOOTING_EN[] = "Rebooting...";
static const char STR_LORA_ACTIONS_EN[] = "LoRa Actions";
static const char STR_SLOT0_EN[] = "Slot 0 (Auto)";
static const char STR_WITH_PRESET_EN[] = "With Preset";
static const char STR_WITH_FREETEXT_EN[] = "With Freetext";
static const char STR_DELETE_OLDEST_EN[] = "Delete Oldest";
static const char STR_DELETE_THIS_CHAT_EN[] = "Delete This Chat";
static const char STR_DELETE_ALL_EN[] = "Delete All";
static const char STR_DELETE_ALL_CHATS_EN[] = "Delete All Chats";
static const char STR_TEMPORARILY_MUTE_EN[] = "Temporarily Mute";
static const char STR_TOGGLE_BACKLIGHT_EN[] = "Toggle Backlight";
static const char STR_SLEEP_SCREEN_EN[] = "Sleep Screen";
static const char STR_SEND_POSITION_EN[] = "Send Position";
static const char STR_SEND_NODE_INFO_EN[] = "Send Node Info";
static const char STR_NEW_PRESET_MSG_EN[] = "New Preset Msg";
static const char STR_NEW_FREETEXT_MSG_EN[] = "New Freetext Msg";
static const char STR_NOTIFICATIONS_EN[] = "Notifications";
static const char STR_POWER_EN[] = "Power";
static const char STR_REBOOT_SHUTDOWN_EN[] = "Reboot/Shutdown";
static const char STR_GO_TO_CHAT_EN[] = "Go To Chat";
static const char STR_NEW_PRESET_EN[] = "New Preset";
static const char STR_FAVORITE_EN[] = "Favorite";
static const char STR_UNMUTE_NOTIFICATIONS_EN[] = "Unmute Notifications";
static const char STR_MUTE_NOTIFICATIONS_EN[] = "Mute Notifications";
static const char STR_UNIGNORE_NODE_EN[] = "Unignore Node";
static const char STR_IGNORE_NODE_EN[] = "Ignore Node";
static const char STR_NUMBER_PICKER_EN[] = "Number Picker";
static const char STR_TEST_ANNOUNCE_EN[] = "Test Announce";
static const char STR_NODE_ACTIONS_EN[] = "Node Actions / Settings";
static const char STR_SHOW_NAME_LENGTH_EN[] = "Show Long/Short Name";
static const char STR_BLUETOOTH_EN[] = "Bluetooth";
static const char STR_NO_MESSAGES_EN[] = "No messages";
inline const char *getStringEN(StringKey key)
{
@@ -351,6 +382,68 @@ inline const char *getStringEN(StringKey key)
return STR_SELECT_REGION_EN;
case StringKey::Rebooting:
return STR_REBOOTING_EN;
case StringKey::LoRaActions:
return STR_LORA_ACTIONS_EN;
case StringKey::Slot0:
return STR_SLOT0_EN;
case StringKey::WithPreset:
return STR_WITH_PRESET_EN;
case StringKey::WithFreetext:
return STR_WITH_FREETEXT_EN;
case StringKey::DeleteOldest:
return STR_DELETE_OLDEST_EN;
case StringKey::DeleteThisChat:
return STR_DELETE_THIS_CHAT_EN;
case StringKey::DeleteAll:
return STR_DELETE_ALL_EN;
case StringKey::DeleteAllChats:
return STR_DELETE_ALL_CHATS_EN;
case StringKey::TemporarilyMute:
return STR_TEMPORARILY_MUTE_EN;
case StringKey::ToggleBacklight:
return STR_TOGGLE_BACKLIGHT_EN;
case StringKey::SleepScreen:
return STR_SLEEP_SCREEN_EN;
case StringKey::SendPosition:
return STR_SEND_POSITION_EN;
case StringKey::SendNodeInfo:
return STR_SEND_NODE_INFO_EN;
case StringKey::NewPresetMsg:
return STR_NEW_PRESET_MSG_EN;
case StringKey::NewFreetextMsg:
return STR_NEW_FREETEXT_MSG_EN;
case StringKey::Notifications:
return STR_NOTIFICATIONS_EN;
case StringKey::Power:
return STR_POWER_EN;
case StringKey::RebootShutdown:
return STR_REBOOT_SHUTDOWN_EN;
case StringKey::GoToChat:
return STR_GO_TO_CHAT_EN;
case StringKey::NewPreset:
return STR_NEW_PRESET_EN;
case StringKey::Favorite:
return STR_FAVORITE_EN;
case StringKey::UnmuteNotifications:
return STR_UNMUTE_NOTIFICATIONS_EN;
case StringKey::MuteNotifications:
return STR_MUTE_NOTIFICATIONS_EN;
case StringKey::UnignoreNode:
return STR_UNIGNORE_NODE_EN;
case StringKey::IgnoreNode:
return STR_IGNORE_NODE_EN;
case StringKey::NumberPicker:
return STR_NUMBER_PICKER_EN;
case StringKey::TestAnnounce:
return STR_TEST_ANNOUNCE_EN;
case StringKey::NodeActionsSettings:
return STR_NODE_ACTIONS_EN;
case StringKey::ShowNameLength:
return STR_SHOW_NAME_LENGTH_EN;
case StringKey::Bluetooth:
return STR_BLUETOOTH_EN;
case StringKey::NoMessages:
return STR_NO_MESSAGES_EN;
default:
return "?";
}
+93
View File
@@ -119,6 +119,37 @@ static const char STR_KEY_VERIFY_ZH[] = "\xe9\xaa\x8c\xe8\xaf\x81\xe5\xaf\x86\xe
static const char STR_REGION_UNSET_ZH[] = "\xe8\xaf\xb7\xe9\x80\x89\xe6\x8b\xa9\xe5\x9c\xb0\xe5\x8c\xba";
static const char STR_SELECT_REGION_ZH[] = "\xe8\xaf\xb7\xe9\x80\x89\xe6\x8b\xa9\xe6\x82\xa8\xe7\x9a\x84 LoRa \xe5\x8c\xba\xe5\x9f\x9f:";
static const char STR_REBOOTING_ZH[] = "\xe6\xad\xa3\xe5\x9c\xa8\xe9\x87\x8d\xe5\x90\xaf...";
static const char STR_LORA_ACTIONS_ZH[] = "LoRa \xe6\x93\x8d\xe4\xbd\x9c";
static const char STR_SLOT0_ZH[] = "\xe6\xa7\xbd\xe4\xbd\x8d 0 (\xe8\x87\xaa\xe5\x8a\xa8)";
static const char STR_WITH_PRESET_ZH[] = "\xe9\xa2\x84\xe8\xae\xbe\xe6\xb6\x88\xe6\x81\xaf";
static const char STR_WITH_FREETEXT_ZH[] = "\xe8\x87\xaa\xe5\xae\x9a\xe4\xb9\x89";
static const char STR_DELETE_OLDEST_ZH[] = "\xe5\x88\xa0\xe9\x99\xa4\xe6\x9c\x80\xe6\x97\xa7";
static const char STR_DELETE_THIS_CHAT_ZH[] = "\xe5\x88\xa0\xe9\x99\xa4\xe6\xad\xa4\xe8\x81\x8a\xe5\xa4\xa9";
static const char STR_DELETE_ALL_ZH[] = "\xe5\x88\xa0\xe9\x99\xa4\xe5\x85\xa8\xe9\x83\xa8";
static const char STR_DELETE_ALL_CHATS_ZH[] = "\xe5\x88\xa0\xe9\x99\xa4\xe6\x89\x80\xe6\x9c\x89\xe8\x81\x8a\xe5\xa4\xa9";
static const char STR_TEMPORARILY_MUTE_ZH[] = "\xe4\xb8\xb4\xe6\x97\xb6\xe9\x9d\x99\xe9\x9f\xb3";
static const char STR_TOGGLE_BACKLIGHT_ZH[] = "\xe8\x83\x8c\xe5\x85\x89\xe5\xbc\x80\xe5\x85\xb3";
static const char STR_SLEEP_SCREEN_ZH[] = "\xe4\xbc\x91\xe7\x9c\xa0\xe5\xb1\x8f\xe5\xb9\x95";
static const char STR_SEND_POSITION_ZH[] = "\xe5\x8f\x91\xe9\x80\x81\xe4\xbd\x8d\xe7\xbd\xae";
static const char STR_SEND_NODE_INFO_ZH[] = "\xe5\x8f\x91\xe9\x80\x81\xe8\x8a\x82\xe7\x82\xb9\xe4\xbf\xa1\xe6\x81\xaf";
static const char STR_NEW_PRESET_MSG_ZH[] = "\xe6\x96\xb0\xe9\xa2\x84\xe8\xae\xbe\xe6\xb6\x88\xe6\x81\xaf";
static const char STR_NEW_FREETEXT_MSG_ZH[] = "\xe6\x96\xb0\xe8\x87\xaa\xe5\xae\x9a\xe4\xb9\x89\xe6\xb6\x88\xe6\x81\xaf";
static const char STR_NOTIFICATIONS_ZH[] = "\xe9\x80\x9a\xe7\x9f\xa5";
static const char STR_POWER_ZH[] = "\xe7\x94\xb5\xe6\xba\x90";
static const char STR_REBOOT_SHUTDOWN_ZH[] = "\xe9\x87\x8d\xe5\x90\xaf/\xe5\x85\xb3\xe6\x9c\xba";
static const char STR_GO_TO_CHAT_ZH[] = "\xe8\xbf\x9b\xe5\x85\xa5\xe8\x81\x8a\xe5\xa4\xa9";
static const char STR_NEW_PRESET_ZH[] = "\xe6\x96\xb0\xe9\xa2\x84\xe8\xae\xbe";
static const char STR_FAVORITE_ZH[] = "\xe6\x94\xb6\xe8\x97\x8f";
static const char STR_UNMUTE_NOTIFICATIONS_ZH[] = "\xe5\x8f\x96\xe6\xb6\x88\xe9\x80\x9a\xe7\x9f\xa5\xe9\x9d\x99\xe9\x9f\xb3";
static const char STR_MUTE_NOTIFICATIONS_ZH[] = "\xe9\x80\x9a\xe7\x9f\xa5\xe9\x9d\x99\xe9\x9f\xb3";
static const char STR_UNIGNORE_NODE_ZH[] = "\xe5\x8f\x96\xe6\xb6\x88\xe5\xbf\xbd\xe7\x95\xa5";
static const char STR_IGNORE_NODE_ZH[] = "\xe5\xbf\xbd\xe7\x95\xa5\xe8\x8a\x82\xe7\x82\xb9";
static const char STR_NUMBER_PICKER_ZH[] = "\xe6\x95\xb0\xe5\xad\x97\xe9\x80\x89\xe6\x8b\xa9";
static const char STR_TEST_ANNOUNCE_ZH[] = "\xe6\xb5\x8b\xe8\xaf\x95\xe5\x85\xac\xe5\x91\x8a";
static const char STR_NODE_ACTIONS_ZH[] = "\xe8\x8a\x82\xe7\x82\xb9\xe6\x93\x8d\xe4\xbd\x9c/\xe8\xae\xbe\xe7\xbd\xae";
static const char STR_SHOW_NAME_LENGTH_ZH[] = "\xe6\x98\xbe\xe7\xa4\xba\xe9\x95\xbf/\xe7\x9f\xad\xe5\x90\x8d";
static const char STR_BLUETOOTH_ZH[] = "\xe8\x93\x9d\xe7\x89\x99";
static const char STR_NO_MESSAGES_ZH[] = "\xe6\x97\xa0\xe6\xb6\x88\xe6\x81\xaf";
inline const char *getStringZH(StringKey key)
{
@@ -347,6 +378,68 @@ inline const char *getStringZH(StringKey key)
return STR_SELECT_REGION_ZH;
case StringKey::Rebooting:
return STR_REBOOTING_ZH;
case StringKey::LoRaActions:
return STR_LORA_ACTIONS_ZH;
case StringKey::Slot0:
return STR_SLOT0_ZH;
case StringKey::WithPreset:
return STR_WITH_PRESET_ZH;
case StringKey::WithFreetext:
return STR_WITH_FREETEXT_ZH;
case StringKey::DeleteOldest:
return STR_DELETE_OLDEST_ZH;
case StringKey::DeleteThisChat:
return STR_DELETE_THIS_CHAT_ZH;
case StringKey::DeleteAll:
return STR_DELETE_ALL_ZH;
case StringKey::DeleteAllChats:
return STR_DELETE_ALL_CHATS_ZH;
case StringKey::TemporarilyMute:
return STR_TEMPORARILY_MUTE_ZH;
case StringKey::ToggleBacklight:
return STR_TOGGLE_BACKLIGHT_ZH;
case StringKey::SleepScreen:
return STR_SLEEP_SCREEN_ZH;
case StringKey::SendPosition:
return STR_SEND_POSITION_ZH;
case StringKey::SendNodeInfo:
return STR_SEND_NODE_INFO_ZH;
case StringKey::NewPresetMsg:
return STR_NEW_PRESET_MSG_ZH;
case StringKey::NewFreetextMsg:
return STR_NEW_FREETEXT_MSG_ZH;
case StringKey::Notifications:
return STR_NOTIFICATIONS_ZH;
case StringKey::Power:
return STR_POWER_ZH;
case StringKey::RebootShutdown:
return STR_REBOOT_SHUTDOWN_ZH;
case StringKey::GoToChat:
return STR_GO_TO_CHAT_ZH;
case StringKey::NewPreset:
return STR_NEW_PRESET_ZH;
case StringKey::Favorite:
return STR_FAVORITE_ZH;
case StringKey::UnmuteNotifications:
return STR_UNMUTE_NOTIFICATIONS_ZH;
case StringKey::MuteNotifications:
return STR_MUTE_NOTIFICATIONS_ZH;
case StringKey::UnignoreNode:
return STR_UNIGNORE_NODE_ZH;
case StringKey::IgnoreNode:
return STR_IGNORE_NODE_ZH;
case StringKey::NumberPicker:
return STR_NUMBER_PICKER_ZH;
case StringKey::TestAnnounce:
return STR_TEST_ANNOUNCE_ZH;
case StringKey::NodeActionsSettings:
return STR_NODE_ACTIONS_ZH;
case StringKey::ShowNameLength:
return STR_SHOW_NAME_LENGTH_ZH;
case StringKey::Bluetooth:
return STR_BLUETOOTH_ZH;
case StringKey::NoMessages:
return STR_NO_MESSAGES_ZH;
default:
return "?";
}