Added toggable config and default for larger screens to enable / hide bubbles on chat messages (#9560)

* Added toggable config and default for largeer screens to enable / hide bubbles on chat messages

* Refactor message bubble rendering logic for improved layout and consistency

* Move osk_found initialization for trackball/encoder devices before module setup to fix missing keyboard for L1

* Utilize current checks for consistency

* Reverted last changes

---------

Co-authored-by: Jason P <applewiz@mac.com>
Co-authored-by: HarukiToreda <116696711+HarukiToreda@users.noreply.github.com>
This commit is contained in:
Ben Meadors
2026-02-07 15:41:31 -06:00
committed by GitHub
co-authored by Jason P HarukiToreda
parent 5280caf9d8
commit 53231ae4b1
6 changed files with 330 additions and 293 deletions
+168 -132
View File
@@ -58,7 +58,7 @@ BannerOverlayOptions createStaticBannerOptions(const char *message, const MenuOp
} // namespace } // namespace
menuHandler::screenMenus menuHandler::menuQueue = menu_none; menuHandler::screenMenus menuHandler::menuQueue = MenuNone;
uint32_t menuHandler::pickedNodeNum = 0; uint32_t menuHandler::pickedNodeNum = 0;
bool test_enabled = false; bool test_enabled = false;
uint8_t test_count = 0; uint8_t test_count = 0;
@@ -66,7 +66,7 @@ uint8_t test_count = 0;
void menuHandler::loraMenu() void menuHandler::loraMenu()
{ {
static const char *optionsArray[] = {"Back", "Device Role", "Radio Preset", "Frequency Slot", "LoRa Region"}; static const char *optionsArray[] = {"Back", "Device Role", "Radio Preset", "Frequency Slot", "LoRa Region"};
enum optionsNumbers { Back = 0, device_role_picker = 1, radio_preset_picker = 2, frequency_slot = 3, lora_picker = 4 }; enum optionsNumbers { Back = 0, DeviceRolePicker = 1, RadioPresetPicker = 2, FrequencySlot = 3, LoraPicker = 4 };
BannerOverlayOptions bannerOptions; BannerOverlayOptions bannerOptions;
bannerOptions.message = "LoRa Actions"; bannerOptions.message = "LoRa Actions";
bannerOptions.optionsArrayPtr = optionsArray; bannerOptions.optionsArrayPtr = optionsArray;
@@ -74,14 +74,14 @@ void menuHandler::loraMenu()
bannerOptions.bannerCallback = [](int selected) -> void { bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == Back) { if (selected == Back) {
// No action // No action
} else if (selected == device_role_picker) { } else if (selected == DeviceRolePicker) {
menuHandler::menuQueue = menuHandler::device_role_picker; menuHandler::menuQueue = menuHandler::DeviceRolePicker;
} else if (selected == radio_preset_picker) { } else if (selected == RadioPresetPicker) {
menuHandler::menuQueue = menuHandler::radio_preset_picker; menuHandler::menuQueue = menuHandler::RadioPresetPicker;
} else if (selected == frequency_slot) { } else if (selected == FrequencySlot) {
menuHandler::menuQueue = menuHandler::frequency_slot; menuHandler::menuQueue = menuHandler::FrequencySlot;
} else if (selected == lora_picker) { } else if (selected == LoraPicker) {
menuHandler::menuQueue = menuHandler::lora_picker; menuHandler::menuQueue = menuHandler::LoraPicker;
} }
}; };
screen->showOverlayBanner(bannerOptions); screen->showOverlayBanner(bannerOptions);
@@ -102,7 +102,7 @@ void menuHandler::OnboardMessage()
bannerOptions.optionsArrayPtr = optionsArray; bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsCount = 2; bannerOptions.optionsCount = 2;
bannerOptions.bannerCallback = [](int selected) -> void { bannerOptions.bannerCallback = [](int selected) -> void {
menuHandler::menuQueue = menuHandler::no_timeout_lora_picker; menuHandler::menuQueue = menuHandler::NoTimeoutLoraPicker;
screen->runNow(); screen->runNow();
}; };
screen->showOverlayBanner(bannerOptions); screen->showOverlayBanner(bannerOptions);
@@ -216,7 +216,7 @@ void menuHandler::LoraRegionPicker(uint32_t duration)
screen->showOverlayBanner(bannerOptions); screen->showOverlayBanner(bannerOptions);
} }
void menuHandler::DeviceRolePicker() void menuHandler::deviceRolePicker()
{ {
static const char *optionsArray[] = {"Back", "Client", "Client Mute", "Lost and Found", "Tracker"}; static const char *optionsArray[] = {"Back", "Client", "Client Mute", "Lost and Found", "Tracker"};
enum optionsNumbers { enum optionsNumbers {
@@ -232,7 +232,7 @@ void menuHandler::DeviceRolePicker()
bannerOptions.optionsCount = 5; bannerOptions.optionsCount = 5;
bannerOptions.bannerCallback = [](int selected) -> void { bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == Back) { if (selected == Back) {
menuHandler::menuQueue = menuHandler::lora_Menu; menuHandler::menuQueue = menuHandler::LoraMenu;
screen->runNow(); screen->runNow();
return; return;
} else if (selected == devicerole_client) { } else if (selected == devicerole_client) {
@@ -300,7 +300,7 @@ void menuHandler::FrequencySlotPicker()
bannerOptions.bannerCallback = [](int selected) -> void { bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == Back) { if (selected == Back) {
menuHandler::menuQueue = menuHandler::lora_Menu; menuHandler::menuQueue = menuHandler::LoraMenu;
screen->runNow(); screen->runNow();
return; return;
} }
@@ -313,7 +313,7 @@ void menuHandler::FrequencySlotPicker()
screen->showOverlayBanner(bannerOptions); screen->showOverlayBanner(bannerOptions);
} }
void menuHandler::RadioPresetPicker() void menuHandler::radioPresetPicker()
{ {
static const RadioPresetOption presetOptions[] = { static const RadioPresetOption presetOptions[] = {
{"Back", OptionsAction::Back}, {"Back", OptionsAction::Back},
@@ -333,7 +333,7 @@ void menuHandler::RadioPresetPicker()
auto bannerOptions = auto bannerOptions =
createStaticBannerOptions("Radio Preset", presetOptions, presetLabels, [](const RadioPresetOption &option, int) -> void { createStaticBannerOptions("Radio Preset", presetOptions, presetLabels, [](const RadioPresetOption &option, int) -> void {
if (option.action == OptionsAction::Back) { if (option.action == OptionsAction::Back) {
menuHandler::menuQueue = menuHandler::lora_Menu; menuHandler::menuQueue = menuHandler::LoraMenu;
screen->runNow(); screen->runNow();
return; return;
} }
@@ -352,7 +352,7 @@ void menuHandler::RadioPresetPicker()
screen->showOverlayBanner(bannerOptions); screen->showOverlayBanner(bannerOptions);
} }
void menuHandler::TwelveHourPicker() void menuHandler::twelveHourPicker()
{ {
static const char *optionsArray[] = {"Back", "12-hour", "24-hour"}; static const char *optionsArray[] = {"Back", "12-hour", "24-hour"};
enum optionsNumbers { Back = 0, twelve = 1, twentyfour = 2 }; enum optionsNumbers { Back = 0, twelve = 1, twentyfour = 2 };
@@ -362,7 +362,7 @@ void menuHandler::TwelveHourPicker()
bannerOptions.optionsCount = 3; bannerOptions.optionsCount = 3;
bannerOptions.bannerCallback = [](int selected) -> void { bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == Back) { if (selected == Back) {
menuHandler::menuQueue = menuHandler::clock_menu; menuHandler::menuQueue = menuHandler::ClockMenu;
screen->runNow(); screen->runNow();
} else if (selected == twelve) { } else if (selected == twelve) {
config.display.use_12h_clock = true; config.display.use_12h_clock = true;
@@ -390,7 +390,7 @@ void menuHandler::showConfirmationBanner(const char *message, std::function<void
screen->showOverlayBanner(confirmBanner); screen->showOverlayBanner(confirmBanner);
} }
void menuHandler::ClockFacePicker() void menuHandler::clockFacePicker()
{ {
static const ClockFaceOption clockFaceOptions[] = { static const ClockFaceOption clockFaceOptions[] = {
{"Back", OptionsAction::Back}, {"Back", OptionsAction::Back},
@@ -404,7 +404,7 @@ void menuHandler::ClockFacePicker()
auto bannerOptions = createStaticBannerOptions("Which Face?", clockFaceOptions, clockFaceLabels, auto bannerOptions = createStaticBannerOptions("Which Face?", clockFaceOptions, clockFaceLabels,
[](const ClockFaceOption &option, int) -> void { [](const ClockFaceOption &option, int) -> void {
if (option.action == OptionsAction::Back) { if (option.action == OptionsAction::Back) {
menuHandler::menuQueue = menuHandler::clock_menu; menuHandler::menuQueue = menuHandler::ClockMenu;
screen->runNow(); screen->runNow();
return; return;
} }
@@ -456,7 +456,7 @@ void menuHandler::TZPicker()
auto bannerOptions = createStaticBannerOptions( auto bannerOptions = createStaticBannerOptions(
"Pick Timezone", timezoneOptions, timezoneLabels, [](const TimezoneOption &option, int) -> void { "Pick Timezone", timezoneOptions, timezoneLabels, [](const TimezoneOption &option, int) -> void {
if (option.action == OptionsAction::Back) { if (option.action == OptionsAction::Back) {
menuHandler::menuQueue = menuHandler::clock_menu; menuHandler::menuQueue = menuHandler::ClockMenu;
screen->runNow(); screen->runNow();
return; return;
} }
@@ -503,13 +503,13 @@ void menuHandler::clockMenu()
bannerOptions.optionsCount = 4; bannerOptions.optionsCount = 4;
bannerOptions.bannerCallback = [](int selected) -> void { bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == Clock) { if (selected == Clock) {
menuHandler::menuQueue = menuHandler::clock_face_picker; menuHandler::menuQueue = menuHandler::ClockFacePicker;
screen->runNow(); screen->runNow();
} else if (selected == Time) { } else if (selected == Time) {
menuHandler::menuQueue = menuHandler::twelve_hour_picker; menuHandler::menuQueue = menuHandler::TwelveHourPicker;
screen->runNow(); screen->runNow();
} else if (selected == Timezone) { } else if (selected == Timezone) {
menuHandler::menuQueue = menuHandler::TZ_picker; menuHandler::menuQueue = menuHandler::TzPicker;
screen->runNow(); screen->runNow();
} }
}; };
@@ -572,12 +572,12 @@ void menuHandler::messageResponseMenu()
LOG_DEBUG("[ReplyCtx] mode=%d ch=%d peer=0x%08x", (int)mode, ch, (unsigned int)peer); LOG_DEBUG("[ReplyCtx] mode=%d ch=%d peer=0x%08x", (int)mode, ch, (unsigned int)peer);
if (selected == ViewMode) { if (selected == ViewMode) {
menuHandler::menuQueue = menuHandler::message_viewmode_menu; menuHandler::menuQueue = menuHandler::MessageViewModeMenu;
screen->runNow(); screen->runNow();
// Reply submenu // Reply submenu
} else if (selected == ReplyMenu) { } else if (selected == ReplyMenu) {
menuHandler::menuQueue = menuHandler::reply_menu; menuHandler::menuQueue = menuHandler::ReplyMenu;
screen->runNow(); screen->runNow();
} else if (selected == MuteChannel) { } else if (selected == MuteChannel) {
@@ -589,7 +589,7 @@ void menuHandler::messageResponseMenu()
} }
} else if (selected == DeleteMenu) { } else if (selected == DeleteMenu) {
menuHandler::menuQueue = menuHandler::delete_messages_menu; menuHandler::menuQueue = menuHandler::DeleteMessagesMenu;
screen->runNow(); screen->runNow();
#ifdef HAS_I2S #ifdef HAS_I2S
@@ -649,7 +649,7 @@ void menuHandler::replyMenu()
uint32_t peer = graphics::MessageRenderer::getThreadPeer(); uint32_t peer = graphics::MessageRenderer::getThreadPeer();
if (selected == Back) { if (selected == Back) {
menuHandler::menuQueue = menuHandler::message_response_menu; menuHandler::menuQueue = menuHandler::MessageResponseMenu;
screen->runNow(); screen->runNow();
return; return;
} }
@@ -737,7 +737,7 @@ void menuHandler::deleteMessagesMenu()
uint32_t peer = graphics::MessageRenderer::getThreadPeer(); uint32_t peer = graphics::MessageRenderer::getThreadPeer();
if (selected == Back) { if (selected == Back) {
menuHandler::menuQueue = menuHandler::message_response_menu; menuHandler::menuQueue = menuHandler::MessageResponseMenu;
screen->runNow(); screen->runNow();
return; return;
} }
@@ -901,7 +901,7 @@ void menuHandler::messageViewModeMenu()
bannerOptions.bannerCallback = [=](int selected) -> void { bannerOptions.bannerCallback = [=](int selected) -> void {
LOG_DEBUG("messageViewModeMenu: selected=%d", selected); LOG_DEBUG("messageViewModeMenu: selected=%d", selected);
if (selected == -1) { if (selected == -1) {
menuHandler::menuQueue = menuHandler::message_response_menu; menuHandler::menuQueue = menuHandler::MessageResponseMenu;
screen->runNow(); screen->runNow();
} else if (selected == -2) { } else if (selected == -2) {
graphics::MessageRenderer::setThreadMode(graphics::MessageRenderer::ThreadMode::ALL); graphics::MessageRenderer::setThreadMode(graphics::MessageRenderer::ThreadMode::ALL);
@@ -1083,23 +1083,23 @@ void menuHandler::systemBaseMenu()
bannerOptions.optionsEnumPtr = optionsEnumArray; bannerOptions.optionsEnumPtr = optionsEnumArray;
bannerOptions.bannerCallback = [](int selected) -> void { bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == Notifications) { if (selected == Notifications) {
menuHandler::menuQueue = menuHandler::buzzermodemenupicker; menuHandler::menuQueue = menuHandler::BuzzerModeMenuPicker;
screen->runNow(); screen->runNow();
} else if (selected == ScreenOptions) { } else if (selected == ScreenOptions) {
menuHandler::menuQueue = menuHandler::screen_options_menu; menuHandler::menuQueue = menuHandler::ScreenOptionsMenu;
screen->runNow(); screen->runNow();
} else if (selected == PowerMenu) { } else if (selected == PowerMenu) {
menuHandler::menuQueue = menuHandler::power_menu; menuHandler::menuQueue = menuHandler::PowerMenu;
screen->runNow(); screen->runNow();
} else if (selected == Test) { } else if (selected == Test) {
menuHandler::menuQueue = menuHandler::test_menu; menuHandler::menuQueue = menuHandler::TestMenu;
screen->runNow(); screen->runNow();
} else if (selected == Bluetooth) { } else if (selected == Bluetooth) {
menuQueue = bluetooth_toggle_menu; menuQueue = BluetoothToggleMenu;
screen->runNow(); screen->runNow();
#if HAS_WIFI && !defined(ARCH_PORTDUINO) #if HAS_WIFI && !defined(ARCH_PORTDUINO)
} else if (selected == WiFiToggle) { } else if (selected == WiFiToggle) {
menuQueue = wifi_toggle_menu; menuQueue = WifiToggleMenu;
screen->runNow(); screen->runNow();
#endif #endif
} else if (selected == Back && !test_enabled) { } else if (selected == Back && !test_enabled) {
@@ -1177,7 +1177,7 @@ void menuHandler::favoriteBaseMenu()
evt.action = UIFrameEvent::Action::SWITCH_TO_TEXTMESSAGE; evt.action = UIFrameEvent::Action::SWITCH_TO_TEXTMESSAGE;
screen->handleUIFrameEvent(&evt); screen->handleUIFrameEvent(&evt);
} else if (selected == Remove) { } else if (selected == Remove) {
menuHandler::menuQueue = menuHandler::remove_favorite; menuHandler::menuQueue = menuHandler::RemoveFavorite;
screen->runNow(); screen->runNow();
} else if (selected == TraceRoute) { } else if (selected == TraceRoute) {
if (traceRouteModule) { if (traceRouteModule) {
@@ -1238,15 +1238,15 @@ void menuHandler::positionBaseMenu()
auto action = static_cast<PositionAction>(option.value); auto action = static_cast<PositionAction>(option.value);
switch (action) { switch (action) {
case PositionAction::GpsToggle: case PositionAction::GpsToggle:
menuQueue = gps_toggle_menu; menuQueue = GpsToggleMenu;
screen->runNow(); screen->runNow();
break; break;
case PositionAction::GpsFormat: case PositionAction::GpsFormat:
menuQueue = gps_format_menu; menuQueue = GpsFormatMenu;
screen->runNow(); screen->runNow();
break; break;
case PositionAction::CompassMenu: case PositionAction::CompassMenu:
menuQueue = compass_point_north_menu; menuQueue = CompassPointNorthMenu;
screen->runNow(); screen->runNow();
break; break;
case PositionAction::CompassCalibrate: case PositionAction::CompassCalibrate:
@@ -1255,15 +1255,15 @@ void menuHandler::positionBaseMenu()
} }
break; break;
case PositionAction::GPSSmartPosition: case PositionAction::GPSSmartPosition:
menuQueue = gps_smart_position_menu; menuQueue = GpsSmartPositionMenu;
screen->runNow(); screen->runNow();
break; break;
case PositionAction::GPSUpdateInterval: case PositionAction::GPSUpdateInterval:
menuQueue = gps_update_interval_menu; menuQueue = GpsUpdateIntervalMenu;
screen->runNow(); screen->runNow();
break; break;
case PositionAction::GPSPositionBroadcast: case PositionAction::GPSPositionBroadcast:
menuQueue = gps_position_broadcast_menu; menuQueue = GpsPositionBroadcastMenu;
screen->runNow(); screen->runNow();
break; break;
} }
@@ -1303,13 +1303,13 @@ void menuHandler::nodeListMenu()
bannerOptions.optionsEnumPtr = optionsEnumArray; bannerOptions.optionsEnumPtr = optionsEnumArray;
bannerOptions.bannerCallback = [](int selected) -> void { bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == NodePicker) { if (selected == NodePicker) {
menuQueue = NodePicker_menu; menuQueue = NodePickerMenu;
screen->runNow(); screen->runNow();
} else if (selected == Reset) { } else if (selected == Reset) {
menuQueue = reset_node_db_menu; menuQueue = ResetNodeDbMenu;
screen->runNow(); screen->runNow();
} else if (selected == NodeNameLength) { } else if (selected == NodeNameLength) {
menuHandler::menuQueue = menuHandler::node_name_length_menu; menuHandler::menuQueue = menuHandler::NodeNameLengthMenu;
screen->runNow(); screen->runNow();
} }
}; };
@@ -1330,12 +1330,12 @@ void menuHandler::NodePicker()
menuHandler::pickedNodeNum = nodenum; menuHandler::pickedNodeNum = nodenum;
// Keep UI favorite context in sync (used elsewhere for some node-based actions) // Keep UI favorite context in sync (used elsewhere for some node-based actions)
graphics::UIRenderer::currentFavoriteNodeNum = nodenum; graphics::UIRenderer::currentFavoriteNodeNum = nodenum;
menuQueue = Manage_Node_menu; menuQueue = ManageNodeMenu;
screen->runNow(); screen->runNow();
}); });
} }
void menuHandler::ManageNodeMenu() void menuHandler::manageNodeMenu()
{ {
// If we don't have a node selected yet, go fast exit // If we don't have a node selected yet, go fast exit
auto node = nodeDB->getMeshNode(menuHandler::pickedNodeNum); auto node = nodeDB->getMeshNode(menuHandler::pickedNodeNum);
@@ -1391,7 +1391,7 @@ void menuHandler::ManageNodeMenu()
bannerOptions.optionsEnumPtr = optionsEnumArray; bannerOptions.optionsEnumPtr = optionsEnumArray;
bannerOptions.bannerCallback = [](int selected) -> void { bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == Back) { if (selected == Back) {
menuQueue = node_base_menu; menuQueue = NodeBaseMenu;
screen->runNow(); screen->runNow();
return; return;
} }
@@ -1483,7 +1483,7 @@ void menuHandler::nodeNameLengthMenu()
auto bannerOptions = createStaticBannerOptions("Node Name Length", nodeNameOptions, nodeNameLabels, auto bannerOptions = createStaticBannerOptions("Node Name Length", nodeNameOptions, nodeNameLabels,
[](const NodeNameOption &option, int) -> void { [](const NodeNameOption &option, int) -> void {
if (option.action == OptionsAction::Back) { if (option.action == OptionsAction::Back) {
menuQueue = node_base_menu; menuQueue = NodeBaseMenu;
screen->runNow(); screen->runNow();
return; return;
} }
@@ -1529,7 +1529,7 @@ void menuHandler::resetNodeDBMenu()
nodeDB->resetNodes(1); nodeDB->resetNodes(1);
rebootAtMsec = (millis() + DEFAULT_REBOOT_SECONDS * 1000); rebootAtMsec = (millis() + DEFAULT_REBOOT_SECONDS * 1000);
} else if (selected == 0) { } else if (selected == 0) {
menuQueue = node_base_menu; menuQueue = NodeBaseMenu;
screen->runNow(); screen->runNow();
} }
}; };
@@ -1551,7 +1551,7 @@ void menuHandler::compassNorthMenu()
auto bannerOptions = createStaticBannerOptions("North Directions?", compassOptions, compassLabels, auto bannerOptions = createStaticBannerOptions("North Directions?", compassOptions, compassLabels,
[](const CompassOption &option, int) -> void { [](const CompassOption &option, int) -> void {
if (option.action == OptionsAction::Back) { if (option.action == OptionsAction::Back) {
menuQueue = position_base_menu; menuQueue = PositionBaseMenu;
screen->runNow(); screen->runNow();
return; return;
} }
@@ -1596,7 +1596,7 @@ void menuHandler::GPSToggleMenu()
auto bannerOptions = auto bannerOptions =
createStaticBannerOptions("Toggle GPS", gpsToggleOptions, toggleLabels, [](const GPSToggleOption &option, int) -> void { createStaticBannerOptions("Toggle GPS", gpsToggleOptions, toggleLabels, [](const GPSToggleOption &option, int) -> void {
if (option.action == OptionsAction::Back) { if (option.action == OptionsAction::Back) {
menuQueue = position_base_menu; menuQueue = PositionBaseMenu;
screen->runNow(); screen->runNow();
return; return;
} }
@@ -1661,7 +1661,7 @@ void menuHandler::GPSFormatMenu()
auto onSelection = [](const GPSFormatOption &option, int) -> void { auto onSelection = [](const GPSFormatOption &option, int) -> void {
if (option.action == OptionsAction::Back) { if (option.action == OptionsAction::Back) {
menuQueue = position_base_menu; menuQueue = PositionBaseMenu;
screen->runNow(); screen->runNow();
return; return;
} }
@@ -1716,7 +1716,7 @@ void menuHandler::GPSSmartPositionMenu()
bannerOptions.optionsCount = 3; bannerOptions.optionsCount = 3;
bannerOptions.bannerCallback = [](int selected) -> void { bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == 0) { if (selected == 0) {
menuQueue = position_base_menu; menuQueue = PositionBaseMenu;
screen->runNow(); screen->runNow();
} else if (selected == 1) { } else if (selected == 1) {
config.position.position_broadcast_smart_enabled = true; config.position.position_broadcast_smart_enabled = true;
@@ -1745,7 +1745,7 @@ void menuHandler::GPSUpdateIntervalMenu()
bannerOptions.optionsCount = 16; bannerOptions.optionsCount = 16;
bannerOptions.bannerCallback = [](int selected) -> void { bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == 0) { if (selected == 0) {
menuQueue = position_base_menu; menuQueue = PositionBaseMenu;
screen->runNow(); screen->runNow();
} else if (selected == 1) { } else if (selected == 1) {
config.position.gps_update_interval = 8; config.position.gps_update_interval = 8;
@@ -1833,7 +1833,7 @@ void menuHandler::GPSPositionBroadcastMenu()
bannerOptions.optionsCount = 17; bannerOptions.optionsCount = 17;
bannerOptions.bannerCallback = [](int selected) -> void { bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == 0) { if (selected == 0) {
menuQueue = position_base_menu; menuQueue = PositionBaseMenu;
screen->runNow(); screen->runNow();
} else if (selected == 1) { } else if (selected == 1) {
config.position.position_broadcast_secs = 60; config.position.position_broadcast_secs = 60;
@@ -1916,7 +1916,7 @@ void menuHandler::GPSPositionBroadcastMenu()
#endif #endif
void menuHandler::BluetoothToggleMenu() void menuHandler::bluetoothToggleMenu()
{ {
static const char *optionsArray[] = {"Back", "Enabled", "Disabled"}; static const char *optionsArray[] = {"Back", "Enabled", "Disabled"};
BannerOverlayOptions bannerOptions; BannerOverlayOptions bannerOptions;
@@ -2044,7 +2044,7 @@ void menuHandler::TFTColorPickerMenu(OLEDDisplay *display)
auto bannerOptions = createStaticBannerOptions( auto bannerOptions = createStaticBannerOptions(
"Select Screen Color", colorOptions, colorLabels, [display](const ScreenColorOption &option, int) -> void { "Select Screen Color", colorOptions, colorLabels, [display](const ScreenColorOption &option, int) -> void {
if (option.action == OptionsAction::Back) { if (option.action == OptionsAction::Back) {
menuQueue = system_base_menu; menuQueue = SystemBaseMenu;
screen->runNow(); screen->runNow();
return; return;
} }
@@ -2139,7 +2139,7 @@ void menuHandler::rebootMenu()
messageStore.saveToFlash(); messageStore.saveToFlash();
rebootAtMsec = millis() + DEFAULT_REBOOT_SECONDS * 1000; rebootAtMsec = millis() + DEFAULT_REBOOT_SECONDS * 1000;
} else { } else {
menuQueue = power_menu; menuQueue = PowerMenu;
screen->runNow(); screen->runNow();
} }
}; };
@@ -2161,7 +2161,7 @@ void menuHandler::shutdownMenu()
InputEvent event = {.inputEvent = (input_broker_event)INPUT_BROKER_SHUTDOWN, .kbchar = 0, .touchX = 0, .touchY = 0}; InputEvent event = {.inputEvent = (input_broker_event)INPUT_BROKER_SHUTDOWN, .kbchar = 0, .touchX = 0, .touchY = 0};
inputBroker->injectInputEvent(&event); inputBroker->injectInputEvent(&event);
} else { } else {
menuQueue = power_menu; menuQueue = PowerMenu;
screen->runNow(); screen->runNow();
} }
}; };
@@ -2222,14 +2222,14 @@ void menuHandler::testMenu()
bannerOptions.optionsEnumPtr = optionsEnumArray; bannerOptions.optionsEnumPtr = optionsEnumArray;
bannerOptions.bannerCallback = [](int selected) -> void { bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == NumberPicker) { if (selected == NumberPicker) {
menuQueue = number_test; menuQueue = NumberTest;
screen->runNow(); screen->runNow();
} else if (selected == ShowChirpy) { } else if (selected == ShowChirpy) {
screen->toggleFrameVisibility("chirpy"); screen->toggleFrameVisibility("chirpy");
screen->setFrames(Screen::FOCUS_SYSTEM); screen->setFrames(Screen::FOCUS_SYSTEM);
} else { } else {
menuQueue = system_base_menu; menuQueue = SystemBaseMenu;
screen->runNow(); screen->runNow();
} }
}; };
@@ -2253,7 +2253,7 @@ void menuHandler::wifiBaseMenu()
bannerOptions.optionsCount = 2; bannerOptions.optionsCount = 2;
bannerOptions.bannerCallback = [](int selected) -> void { bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == Wifi_toggle) { if (selected == Wifi_toggle) {
menuQueue = wifi_toggle_menu; menuQueue = WifiToggleMenu;
screen->runNow(); screen->runNow();
} }
}; };
@@ -2302,9 +2302,9 @@ void menuHandler::screenOptionsMenu()
hasSupportBrightness = false; hasSupportBrightness = false;
#endif #endif
enum optionsNumbers { Back, Brightness, ScreenColor, FrameToggles, DisplayUnits }; enum optionsNumbers { Back, Brightness, ScreenColor, FrameToggles, DisplayUnits, MessageBubbles };
static const char *optionsArray[5] = {"Back"}; static const char *optionsArray[6] = {"Back"};
static int optionsEnumArray[5] = {Back}; static int optionsEnumArray[6] = {Back};
int options = 1; int options = 1;
// Only show brightness for B&W displays // Only show brightness for B&W displays
@@ -2326,6 +2326,9 @@ void menuHandler::screenOptionsMenu()
optionsArray[options] = "Display Units"; optionsArray[options] = "Display Units";
optionsEnumArray[options++] = DisplayUnits; optionsEnumArray[options++] = DisplayUnits;
optionsArray[options] = "Message Bubbles";
optionsEnumArray[options++] = MessageBubbles;
BannerOverlayOptions bannerOptions; BannerOverlayOptions bannerOptions;
bannerOptions.message = "Display Options"; bannerOptions.message = "Display Options";
bannerOptions.optionsArrayPtr = optionsArray; bannerOptions.optionsArrayPtr = optionsArray;
@@ -2333,10 +2336,10 @@ void menuHandler::screenOptionsMenu()
bannerOptions.optionsEnumPtr = optionsEnumArray; bannerOptions.optionsEnumPtr = optionsEnumArray;
bannerOptions.bannerCallback = [](int selected) -> void { bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == Brightness) { if (selected == Brightness) {
menuHandler::menuQueue = menuHandler::brightness_picker; menuHandler::menuQueue = menuHandler::BrightnessPicker;
screen->runNow(); screen->runNow();
} else if (selected == ScreenColor) { } else if (selected == ScreenColor) {
menuHandler::menuQueue = menuHandler::tftcolormenupicker; menuHandler::menuQueue = menuHandler::TftColorMenuPicker;
screen->runNow(); screen->runNow();
} else if (selected == FrameToggles) { } else if (selected == FrameToggles) {
menuHandler::menuQueue = menuHandler::FrameToggles; menuHandler::menuQueue = menuHandler::FrameToggles;
@@ -2344,8 +2347,11 @@ void menuHandler::screenOptionsMenu()
} else if (selected == DisplayUnits) { } else if (selected == DisplayUnits) {
menuHandler::menuQueue = menuHandler::DisplayUnits; menuHandler::menuQueue = menuHandler::DisplayUnits;
screen->runNow(); screen->runNow();
} else if (selected == MessageBubbles) {
menuHandler::menuQueue = menuHandler::MessageBubblesMenu;
screen->runNow();
} else { } else {
menuQueue = system_base_menu; menuQueue = SystemBaseMenu;
screen->runNow(); screen->runNow();
} }
}; };
@@ -2381,16 +2387,16 @@ void menuHandler::powerMenu()
bannerOptions.optionsEnumPtr = optionsEnumArray; bannerOptions.optionsEnumPtr = optionsEnumArray;
bannerOptions.bannerCallback = [](int selected) -> void { bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == Reboot) { if (selected == Reboot) {
menuHandler::menuQueue = menuHandler::reboot_menu; menuHandler::menuQueue = menuHandler::RebootMenu;
screen->runNow(); screen->runNow();
} else if (selected == Shutdown) { } else if (selected == Shutdown) {
menuHandler::menuQueue = menuHandler::shutdown_menu; menuHandler::menuQueue = menuHandler::ShutdownMenu;
screen->runNow(); screen->runNow();
} else if (selected == MUI) { } else if (selected == MUI) {
menuHandler::menuQueue = menuHandler::mui_picker; menuHandler::menuQueue = menuHandler::MuiPicker;
screen->runNow(); screen->runNow();
} else { } else {
menuQueue = system_base_menu; menuQueue = SystemBaseMenu;
screen->runNow(); screen->runNow();
} }
}; };
@@ -2428,7 +2434,7 @@ void menuHandler::keyVerificationFinalPrompt()
} }
} }
void menuHandler::FrameToggles_menu() void menuHandler::frameTogglesMenu()
{ {
enum optionsNumbers { enum optionsNumbers {
Finish, Finish,
@@ -2572,7 +2578,7 @@ void menuHandler::FrameToggles_menu()
screen->showOverlayBanner(bannerOptions); screen->showOverlayBanner(bannerOptions);
} }
void menuHandler::DisplayUnits_menu() void menuHandler::displayUnitsMenu()
{ {
enum optionsNumbers { Back, MetricUnits, ImperialUnits }; enum optionsNumbers { Back, MetricUnits, ImperialUnits };
@@ -2593,7 +2599,34 @@ void menuHandler::DisplayUnits_menu()
config.display.units = meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL; config.display.units = meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL;
service->reloadConfig(SEGMENT_CONFIG); service->reloadConfig(SEGMENT_CONFIG);
} else { } else {
menuHandler::menuQueue = menuHandler::screen_options_menu; menuHandler::menuQueue = menuHandler::ScreenOptionsMenu;
screen->runNow();
}
};
screen->showOverlayBanner(bannerOptions);
}
void menuHandler::messageBubblesMenu()
{
enum optionsNumbers { Back, ShowBubbles, HideBubbles };
static const char *optionsArray[] = {"Back", "Show Bubbles", "Hide Bubbles"};
BannerOverlayOptions bannerOptions;
bannerOptions.message = "Message Bubbles";
bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsCount = 3;
bannerOptions.InitialSelected = config.display.enable_message_bubbles ? 1 : 2;
bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == ShowBubbles) {
config.display.enable_message_bubbles = true;
service->reloadConfig(SEGMENT_CONFIG);
LOG_INFO("Message bubbles enabled");
} else if (selected == HideBubbles) {
config.display.enable_message_bubbles = false;
service->reloadConfig(SEGMENT_CONFIG);
LOG_INFO("Message bubbles disabled");
} else {
menuHandler::menuQueue = menuHandler::ScreenOptionsMenu;
screen->runNow(); screen->runNow();
} }
}; };
@@ -2602,153 +2635,156 @@ void menuHandler::DisplayUnits_menu()
void menuHandler::handleMenuSwitch(OLEDDisplay *display) void menuHandler::handleMenuSwitch(OLEDDisplay *display)
{ {
if (menuQueue != menu_none) if (menuQueue != MenuNone)
test_count = 0; test_count = 0;
switch (menuQueue) { switch (menuQueue) {
case menu_none: case MenuNone:
break; break;
case lora_Menu: case LoraMenu:
loraMenu(); loraMenu();
break; break;
case lora_picker: case LoraPicker:
LoraRegionPicker(); LoraRegionPicker();
break; break;
case device_role_picker: case DeviceRolePicker:
DeviceRolePicker(); deviceRolePicker();
break; break;
case radio_preset_picker: case RadioPresetPicker:
RadioPresetPicker(); radioPresetPicker();
break; break;
case frequency_slot: case FrequencySlot:
FrequencySlotPicker(); FrequencySlotPicker();
break; break;
case no_timeout_lora_picker: case NoTimeoutLoraPicker:
LoraRegionPicker(0); LoraRegionPicker(0);
break; break;
case TZ_picker: case TzPicker:
TZPicker(); TZPicker();
break; break;
case twelve_hour_picker: case TwelveHourPicker:
TwelveHourPicker(); twelveHourPicker();
break; break;
case clock_face_picker: case ClockFacePicker:
ClockFacePicker(); clockFacePicker();
break; break;
case clock_menu: case ClockMenu:
clockMenu(); clockMenu();
break; break;
case system_base_menu: case SystemBaseMenu:
systemBaseMenu(); systemBaseMenu();
break; break;
case position_base_menu: case PositionBaseMenu:
positionBaseMenu(); positionBaseMenu();
break; break;
case node_base_menu: case NodeBaseMenu:
nodeListMenu(); nodeListMenu();
break; break;
#if !MESHTASTIC_EXCLUDE_GPS #if !MESHTASTIC_EXCLUDE_GPS
case gps_toggle_menu: case GpsToggleMenu:
GPSToggleMenu(); GPSToggleMenu();
break; break;
case gps_format_menu: case GpsFormatMenu:
GPSFormatMenu(); GPSFormatMenu();
break; break;
case gps_smart_position_menu: case GpsSmartPositionMenu:
GPSSmartPositionMenu(); GPSSmartPositionMenu();
break; break;
case gps_update_interval_menu: case GpsUpdateIntervalMenu:
GPSUpdateIntervalMenu(); GPSUpdateIntervalMenu();
break; break;
case gps_position_broadcast_menu: case GpsPositionBroadcastMenu:
GPSPositionBroadcastMenu(); GPSPositionBroadcastMenu();
break; break;
#endif #endif
case compass_point_north_menu: case CompassPointNorthMenu:
compassNorthMenu(); compassNorthMenu();
break; break;
case reset_node_db_menu: case ResetNodeDbMenu:
resetNodeDBMenu(); resetNodeDBMenu();
break; break;
case buzzermodemenupicker: case BuzzerModeMenuPicker:
BuzzerModeMenu(); BuzzerModeMenu();
break; break;
case mui_picker: case MuiPicker:
switchToMUIMenu(); switchToMUIMenu();
break; break;
case tftcolormenupicker: case TftColorMenuPicker:
TFTColorPickerMenu(display); TFTColorPickerMenu(display);
break; break;
case brightness_picker: case BrightnessPicker:
BrightnessPickerMenu(); BrightnessPickerMenu();
break; break;
case node_name_length_menu: case NodeNameLengthMenu:
nodeNameLengthMenu(); nodeNameLengthMenu();
break; break;
case reboot_menu: case RebootMenu:
rebootMenu(); rebootMenu();
break; break;
case shutdown_menu: case ShutdownMenu:
shutdownMenu(); shutdownMenu();
break; break;
case NodePicker_menu: case NodePickerMenu:
NodePicker(); NodePicker();
break; break;
case Manage_Node_menu: case ManageNodeMenu:
ManageNodeMenu(); manageNodeMenu();
break; break;
case remove_favorite: case RemoveFavorite:
removeFavoriteMenu(); removeFavoriteMenu();
break; break;
case trace_route_menu: case TraceRouteMenu:
traceRouteMenu(); traceRouteMenu();
break; break;
case test_menu: case TestMenu:
testMenu(); testMenu();
break; break;
case number_test: case NumberTest:
numberTest(); numberTest();
break; break;
case wifi_toggle_menu: case WifiToggleMenu:
wifiToggleMenu(); wifiToggleMenu();
break; break;
case key_verification_init: case KeyVerificationInit:
keyVerificationInitMenu(); keyVerificationInitMenu();
break; break;
case key_verification_final_prompt: case KeyVerificationFinalPrompt:
keyVerificationFinalPrompt(); keyVerificationFinalPrompt();
break; break;
case bluetooth_toggle_menu: case BluetoothToggleMenu:
BluetoothToggleMenu(); bluetoothToggleMenu();
break; break;
case screen_options_menu: case ScreenOptionsMenu:
screenOptionsMenu(); screenOptionsMenu();
break; break;
case power_menu: case PowerMenu:
powerMenu(); powerMenu();
break; break;
case FrameToggles: case FrameToggles:
FrameToggles_menu(); frameTogglesMenu();
break; break;
case DisplayUnits: case DisplayUnits:
DisplayUnits_menu(); displayUnitsMenu();
break; break;
case throttle_message: case ThrottleMessage:
screen->showSimpleBanner("Too Many Attempts\nTry again in 60 seconds.", 5000); screen->showSimpleBanner("Too Many Attempts\nTry again in 60 seconds.", 5000);
break; break;
case message_response_menu: case MessageResponseMenu:
messageResponseMenu(); messageResponseMenu();
break; break;
case reply_menu: case ReplyMenu:
replyMenu(); replyMenu();
break; break;
case delete_messages_menu: case DeleteMessagesMenu:
deleteMessagesMenu(); deleteMessagesMenu();
break; break;
case message_viewmode_menu: case MessageViewModeMenu:
messageViewModeMenu(); messageViewModeMenu();
break; break;
case MessageBubblesMenu:
messageBubblesMenu();
break;
} }
menuQueue = menu_none; menuQueue = MenuNone;
} }
void menuHandler::saveUIConfig() void menuHandler::saveUIConfig()
+56 -54
View File
@@ -8,53 +8,54 @@ class menuHandler
{ {
public: public:
enum screenMenus { enum screenMenus {
menu_none, MenuNone,
lora_Menu, LoraMenu,
lora_picker, LoraPicker,
device_role_picker, DeviceRolePicker,
radio_preset_picker, RadioPresetPicker,
frequency_slot, FrequencySlot,
no_timeout_lora_picker, NoTimeoutLoraPicker,
TZ_picker, TzPicker,
twelve_hour_picker, TwelveHourPicker,
clock_face_picker, ClockFacePicker,
clock_menu, ClockMenu,
position_base_menu, PositionBaseMenu,
node_base_menu, NodeBaseMenu,
gps_toggle_menu, GpsToggleMenu,
gps_format_menu, GpsFormatMenu,
gps_smart_position_menu, GpsSmartPositionMenu,
gps_update_interval_menu, GpsUpdateIntervalMenu,
gps_position_broadcast_menu, GpsPositionBroadcastMenu,
compass_point_north_menu, CompassPointNorthMenu,
reset_node_db_menu, ResetNodeDbMenu,
buzzermodemenupicker, BuzzerModeMenuPicker,
mui_picker, MuiPicker,
tftcolormenupicker, TftColorMenuPicker,
brightness_picker, BrightnessPicker,
reboot_menu, RebootMenu,
shutdown_menu, ShutdownMenu,
NodePicker_menu, NodePickerMenu,
Manage_Node_menu, ManageNodeMenu,
remove_favorite, RemoveFavorite,
test_menu, TestMenu,
number_test, NumberTest,
wifi_toggle_menu, WifiToggleMenu,
bluetooth_toggle_menu, BluetoothToggleMenu,
screen_options_menu, ScreenOptionsMenu,
power_menu, PowerMenu,
system_base_menu, SystemBaseMenu,
key_verification_init, KeyVerificationInit,
key_verification_final_prompt, KeyVerificationFinalPrompt,
trace_route_menu, TraceRouteMenu,
throttle_message, ThrottleMessage,
message_response_menu, MessageResponseMenu,
message_viewmode_menu, MessageViewModeMenu,
reply_menu, ReplyMenu,
delete_messages_menu, DeleteMessagesMenu,
node_name_length_menu, NodeNameLengthMenu,
FrameToggles, FrameToggles,
DisplayUnits DisplayUnits,
MessageBubblesMenu
}; };
static screenMenus menuQueue; static screenMenus menuQueue;
static uint32_t pickedNodeNum; // node selected by NodePicker for ManageNodeMenu static uint32_t pickedNodeNum; // node selected by NodePicker for ManageNodeMenu
@@ -62,15 +63,15 @@ class menuHandler
static void OnboardMessage(); static void OnboardMessage();
static void LoraRegionPicker(uint32_t duration = 30000); static void LoraRegionPicker(uint32_t duration = 30000);
static void loraMenu(); static void loraMenu();
static void DeviceRolePicker(); static void deviceRolePicker();
static void RadioPresetPicker(); static void radioPresetPicker();
static void FrequencySlotPicker(); static void FrequencySlotPicker();
static void handleMenuSwitch(OLEDDisplay *display); static void handleMenuSwitch(OLEDDisplay *display);
static void showConfirmationBanner(const char *message, std::function<void()> onConfirm); static void showConfirmationBanner(const char *message, std::function<void()> onConfirm);
static void clockMenu(); static void clockMenu();
static void TZPicker(); static void TZPicker();
static void TwelveHourPicker(); static void twelveHourPicker();
static void ClockFacePicker(); static void clockFacePicker();
static void messageResponseMenu(); static void messageResponseMenu();
static void messageViewModeMenu(); static void messageViewModeMenu();
static void replyMenu(); static void replyMenu();
@@ -95,7 +96,7 @@ class menuHandler
static void rebootMenu(); static void rebootMenu();
static void shutdownMenu(); static void shutdownMenu();
static void NodePicker(); static void NodePicker();
static void ManageNodeMenu(); static void manageNodeMenu();
static void addFavoriteMenu(); static void addFavoriteMenu();
static void removeFavoriteMenu(); static void removeFavoriteMenu();
static void traceRouteMenu(); static void traceRouteMenu();
@@ -106,15 +107,16 @@ class menuHandler
static void screenOptionsMenu(); static void screenOptionsMenu();
static void powerMenu(); static void powerMenu();
static void nodeNameLengthMenu(); static void nodeNameLengthMenu();
static void FrameToggles_menu(); static void frameTogglesMenu();
static void DisplayUnits_menu(); static void displayUnitsMenu();
static void messageBubblesMenu();
static void textMessageMenu(); static void textMessageMenu();
private: private:
static void saveUIConfig(); static void saveUIConfig();
static void keyVerificationInitMenu(); static void keyVerificationInitMenu();
static void keyVerificationFinalPrompt(); static void keyVerificationFinalPrompt();
static void BluetoothToggleMenu(); static void bluetoothToggleMenu();
}; };
/* Generic Menu Options designations */ /* Generic Menu Options designations */
+93 -99
View File
@@ -527,8 +527,12 @@ void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
constexpr int BUBBLE_MIN_W = 24; constexpr int BUBBLE_MIN_W = 24;
constexpr int BUBBLE_TEXT_INDENT = 2; constexpr int BUBBLE_TEXT_INDENT = 2;
// Check if bubbles are enabled
const bool showBubbles = config.display.enable_message_bubbles;
const int textIndent = showBubbles ? (BUBBLE_PAD_X + BUBBLE_TEXT_INDENT) : LEFT_MARGIN;
// Derived widths // Derived widths
const int leftTextWidth = SCREEN_WIDTH - LEFT_MARGIN - RIGHT_MARGIN - (BUBBLE_PAD_X * 2); const int leftTextWidth = SCREEN_WIDTH - LEFT_MARGIN - RIGHT_MARGIN - (showBubbles ? (BUBBLE_PAD_X * 2) : 0);
const int rightTextWidth = SCREEN_WIDTH - LEFT_MARGIN - RIGHT_MARGIN - SCROLLBAR_WIDTH; const int rightTextWidth = SCREEN_WIDTH - LEFT_MARGIN - RIGHT_MARGIN - SCROLLBAR_WIDTH;
// Title string depending on mode // Title string depending on mode
@@ -796,114 +800,105 @@ void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
} }
} }
// Draw bubbles // Draw bubbles (only if enabled)
for (size_t bi = 0; bi < blocks.size(); ++bi) { if (showBubbles) {
const auto &b = blocks[bi]; for (size_t bi = 0; bi < blocks.size(); ++bi) {
if (b.start >= cachedLines.size() || b.end >= cachedLines.size() || b.start > b.end) const auto &b = blocks[bi];
continue; if (b.start >= cachedLines.size() || b.end >= cachedLines.size() || b.start > b.end)
continue;
int visualTop = lineTop[b.start]; int visualTop = lineTop[b.start];
int topY; int topY;
if (isHeader[b.start]) { if (isHeader[b.start]) {
// Header start // Header start
constexpr int BUBBLE_PAD_TOP_HEADER = 1; // try 1 or 2 constexpr int BUBBLE_PAD_TOP_HEADER = 1; // try 1 or 2
topY = visualTop - BUBBLE_PAD_TOP_HEADER; topY = visualTop - BUBBLE_PAD_TOP_HEADER;
} else { } else {
// Body start // Body start
bool thisLineHasEmote = false; bool thisLineHasEmote = false;
for (int e = 0; e < numEmotes; ++e) { for (int e = 0; e < numEmotes; ++e) {
if (cachedLines[b.start].find(emotes[e].label) != std::string::npos) { if (cachedLines[b.start].find(emotes[e].label) != std::string::npos) {
thisLineHasEmote = true; thisLineHasEmote = true;
break; break;
}
} }
if (thisLineHasEmote) {
constexpr int EMOTE_PADDING_ABOVE = 4;
visualTop -= EMOTE_PADDING_ABOVE;
}
topY = visualTop - BUBBLE_PAD_Y;
} }
if (thisLineHasEmote) { int visualBottom = getDrawnLinePixelBottom(lineTop[b.end], cachedLines[b.end], isHeader[b.end]);
constexpr int EMOTE_PADDING_ABOVE = 4; int bottomY = visualBottom + BUBBLE_PAD_Y;
visualTop -= EMOTE_PADDING_ABOVE;
if (bi + 1 < blocks.size()) {
int nextHeaderIndex = (int)blocks[bi + 1].start;
int nextTop = lineTop[nextHeaderIndex];
int maxBottom = nextTop - 1 - bubbleGapY;
if (bottomY > maxBottom)
bottomY = maxBottom;
} }
topY = visualTop - BUBBLE_PAD_Y;
}
int visualBottom = getDrawnLinePixelBottom(lineTop[b.end], cachedLines[b.end], isHeader[b.end]);
int bottomY = visualBottom + BUBBLE_PAD_Y;
if (bi + 1 < blocks.size()) { if (bottomY <= topY + 2)
int nextHeaderIndex = (int)blocks[bi + 1].start; continue;
int nextTop = lineTop[nextHeaderIndex];
int maxBottom = nextTop - 1 - bubbleGapY;
if (bottomY > maxBottom)
bottomY = maxBottom;
}
if (bottomY <= topY + 2) if (bottomY < contentTop || topY > contentBottom - 1)
continue; continue;
if (bottomY < contentTop || topY > contentBottom - 1) int maxLineW = 0;
continue;
int maxLineW = 0; for (size_t i = b.start; i <= b.end; ++i) {
int w = 0;
for (size_t i = b.start; i <= b.end; ++i) { if (isHeader[i]) {
int w = 0; w = display->getStringWidth(cachedLines[i].c_str());
if (isHeader[i]) { if (b.mine)
w = display->getStringWidth(cachedLines[i].c_str()); w += 12; // room for ACK/NACK/relay mark
if (b.mine) } else {
w += 12; // room for ACK/NACK/relay mark w = getRenderedLineWidth(display, cachedLines[i], emotes, numEmotes);
} else { }
w = getRenderedLineWidth(display, cachedLines[i], emotes, numEmotes); if (w > maxLineW)
maxLineW = w;
} }
if (w > maxLineW)
maxLineW = w;
}
int bubbleW = std::max(BUBBLE_MIN_W, maxLineW + (BUBBLE_PAD_X * 2));
int bubbleH = (bottomY - topY) + 1;
int bubbleX = 0;
if (b.mine) {
bubbleX = rightEdge - bubbleW;
} else {
bubbleX = x;
}
if (bubbleX < x)
bubbleX = x;
if (bubbleX + bubbleW > rightEdge)
bubbleW = std::max(1, rightEdge - bubbleX);
if (bubbleW > 1 && bubbleH > 1) {
int x1 = bubbleX + bubbleW - 1;
int y1 = topY + bubbleH - 1;
int bubbleW = std::max(BUBBLE_MIN_W, maxLineW + (textIndent * 2));
int bubbleH = (bottomY - topY) + 1;
int bubbleX = 0;
if (b.mine) { if (b.mine) {
// Send Message (Right side) bubbleX = rightEdge - bubbleW;
display->drawRect(x1 + 2 - bubbleW, y1 - bubbleH, bubbleW, bubbleH);
// Top Right Corner
display->drawRect(x1, topY, 2, 1);
display->drawRect(x1, topY, 1, 2);
// Bottom Right Corner
display->drawRect(x1 - 1, bottomY - 2, 2, 1);
display->drawRect(x1, bottomY - 3, 1, 2);
// Knock the corners off to make a bubble
display->setColor(BLACK);
display->drawRect(x1 - bubbleW, topY - 1, 1, 1);
display->drawRect(x1 - bubbleW, bottomY - 1, 1, 1);
display->setColor(WHITE);
} else { } else {
// Received Message (Left Side) bubbleX = x;
display->drawRect(bubbleX, topY, bubbleW + 1, bubbleH); }
// Top Left Corner if (bubbleX < x)
display->drawRect(bubbleX + 1, topY + 1, 2, 1); bubbleX = x;
display->drawRect(bubbleX + 1, topY + 1, 1, 2); if (bubbleX + bubbleW > rightEdge)
// Bottom Left Corner bubbleW = std::max(1, rightEdge - bubbleX);
display->drawRect(bubbleX + 1, bottomY - 1, 2, 1);
display->drawRect(bubbleX + 1, bottomY - 2, 1, 2); // Draw rounded rectangle bubble
// Knock the corners off to make a bubble if (bubbleW > BUBBLE_RADIUS * 2 && bubbleH > BUBBLE_RADIUS * 2) {
display->setColor(BLACK); const int r = BUBBLE_RADIUS;
display->drawRect(bubbleX + bubbleW, topY, 1, 1); const int bx = bubbleX;
display->drawRect(bubbleX + bubbleW, bottomY, 1, 1); const int by = topY;
display->setColor(WHITE); const int bw = bubbleW;
const int bh = bubbleH;
// Draw the 4 corner arcs using drawCircleQuads
display->drawCircleQuads(bx + r, by + r, r, 0x2); // Top-left
display->drawCircleQuads(bx + bw - r - 1, by + r, r, 0x1); // Top-right
display->drawCircleQuads(bx + r, by + bh - r - 1, r, 0x4); // Bottom-left
display->drawCircleQuads(bx + bw - r - 1, by + bh - r - 1, r, 0x8); // Bottom-right
// Draw the 4 edges between corners
display->drawHorizontalLine(bx + r, by, bw - 2 * r); // Top edge
display->drawHorizontalLine(bx + r, by + bh - 1, bw - 2 * r); // Bottom edge
display->drawVerticalLine(bx, by + r, bh - 2 * r); // Left edge
display->drawVerticalLine(bx + bw - 1, by + r, bh - 2 * r); // Right edge
} else if (bubbleW > 1 && bubbleH > 1) {
// Fallback to simple rectangle for very small bubbles
display->drawRect(bubbleX, topY, bubbleW, bubbleH);
} }
} }
} } // end if (showBubbles)
// Render visible lines // Render visible lines
int lineY = yOffset; int lineY = yOffset;
@@ -916,11 +911,11 @@ void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
int headerX; int headerX;
if (isMine[i]) { if (isMine[i]) {
// push header left to avoid overlap with scrollbar // push header left to avoid overlap with scrollbar
headerX = (SCREEN_WIDTH - SCROLLBAR_WIDTH - RIGHT_MARGIN) - w - BUBBLE_TEXT_INDENT; headerX = (SCREEN_WIDTH - SCROLLBAR_WIDTH - RIGHT_MARGIN) - w - (showBubbles ? textIndent : 0);
if (headerX < LEFT_MARGIN) if (headerX < LEFT_MARGIN)
headerX = LEFT_MARGIN; headerX = LEFT_MARGIN;
} else { } else {
headerX = x + BUBBLE_PAD_X + BUBBLE_TEXT_INDENT; headerX = x + textIndent;
} }
display->drawString(headerX, lineY, cachedLines[i].c_str()); display->drawString(headerX, lineY, cachedLines[i].c_str());
@@ -960,14 +955,13 @@ void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
if (isMine[i]) { if (isMine[i]) {
// Calculate actual rendered width including emotes // Calculate actual rendered width including emotes
int renderedWidth = getRenderedLineWidth(display, cachedLines[i], emotes, numEmotes); int renderedWidth = getRenderedLineWidth(display, cachedLines[i], emotes, numEmotes);
int rightX = (SCREEN_WIDTH - SCROLLBAR_WIDTH - RIGHT_MARGIN) - renderedWidth - BUBBLE_TEXT_INDENT; int rightX = (SCREEN_WIDTH - SCROLLBAR_WIDTH - RIGHT_MARGIN) - renderedWidth - (showBubbles ? textIndent : 0);
if (rightX < LEFT_MARGIN) if (rightX < LEFT_MARGIN)
rightX = LEFT_MARGIN; rightX = LEFT_MARGIN;
drawStringWithEmotes(display, rightX, lineY, cachedLines[i], emotes, numEmotes); drawStringWithEmotes(display, rightX, lineY, cachedLines[i], emotes, numEmotes);
} else { } else {
drawStringWithEmotes(display, x + BUBBLE_PAD_X + BUBBLE_TEXT_INDENT, lineY, cachedLines[i], emotes, drawStringWithEmotes(display, x + textIndent, lineY, cachedLines[i], emotes, numEmotes);
numEmotes);
} }
} }
} }
+7 -6
View File
@@ -929,6 +929,13 @@ void setup()
service = new MeshService(); service = new MeshService();
service->init(); service->init();
// Set osk_found for trackball/encoder devices BEFORE setupModules so CannedMessageModule can detect it
#if defined(HAS_TRACKBALL) || (defined(INPUTDRIVER_ENCODER_TYPE) && INPUTDRIVER_ENCODER_TYPE == 2)
#ifndef HAS_PHYSICAL_KEYBOARD
osk_found = true;
#endif
#endif
// Now that the mesh service is created, create any modules // Now that the mesh service is created, create any modules
setupModules(); setupModules();
@@ -1019,12 +1026,6 @@ void setup()
#endif #endif
#endif #endif
#if defined(HAS_TRACKBALL) || (defined(INPUTDRIVER_ENCODER_TYPE) && INPUTDRIVER_ENCODER_TYPE == 2)
#ifndef HAS_PHYSICAL_KEYBOARD
osk_found = true;
#endif
#endif
#if defined(ARCH_ESP32) && !MESHTASTIC_EXCLUDE_WEBSERVER #if defined(ARCH_ESP32) && !MESHTASTIC_EXCLUDE_WEBSERVER
// Start web server thread. // Start web server thread.
webServerThread = new WebServerThread(); webServerThread = new WebServerThread();
+4
View File
@@ -574,6 +574,10 @@ void NodeDB::installDefaultConfig(bool preserveKey = false)
config.display.displaymode = meshtastic_Config_DisplayConfig_DisplayMode_COLOR; config.display.displaymode = meshtastic_Config_DisplayConfig_DisplayMode_COLOR;
#endif #endif
#if defined(TFT_WIDTH) && defined(TFT_HEIGHT) && (TFT_WIDTH >= 200 || TFT_HEIGHT >= 200)
config.display.enable_message_bubbles = true;
#endif
#ifdef USERPREFS_CONFIG_DEVICE_ROLE #ifdef USERPREFS_CONFIG_DEVICE_ROLE
// Restrict ROUTER*, LOST AND FOUND roles for security reasons // Restrict ROUTER*, LOST AND FOUND roles for security reasons
if (IS_ONE_OF(USERPREFS_CONFIG_DEVICE_ROLE, meshtastic_Config_DeviceConfig_Role_ROUTER, if (IS_ONE_OF(USERPREFS_CONFIG_DEVICE_ROLE, meshtastic_Config_DeviceConfig_Role_ROUTER,
+2 -2
View File
@@ -123,7 +123,7 @@ bool KeyVerificationModule::sendInitialRequest(NodeNum remoteNode)
// generate nonce // generate nonce
updateState(); updateState();
if (currentState != KEY_VERIFICATION_IDLE) { if (currentState != KEY_VERIFICATION_IDLE) {
IF_SCREEN(graphics::menuHandler::menuQueue = graphics::menuHandler::throttle_message;) IF_SCREEN(graphics::menuHandler::menuQueue = graphics::menuHandler::ThrottleMessage;)
return false; return false;
} }
currentNonce = random(); currentNonce = random();
@@ -259,7 +259,7 @@ void KeyVerificationModule::processSecurityNumber(uint32_t incomingNumber)
p->priority = meshtastic_MeshPacket_Priority_HIGH; p->priority = meshtastic_MeshPacket_Priority_HIGH;
service->sendToMesh(p, RX_SRC_LOCAL, true); service->sendToMesh(p, RX_SRC_LOCAL, true);
currentState = KEY_VERIFICATION_SENDER_AWAITING_USER; currentState = KEY_VERIFICATION_SENDER_AWAITING_USER;
IF_SCREEN(screen->requestMenu(graphics::menuHandler::key_verification_final_prompt);) IF_SCREEN(screen->requestMenu(graphics::menuHandler::KeyVerificationFinalPrompt);)
meshtastic_ClientNotification *cn = clientNotificationPool.allocZeroed(); meshtastic_ClientNotification *cn = clientNotificationPool.allocZeroed();
cn->level = meshtastic_LogRecord_Level_WARNING; cn->level = meshtastic_LogRecord_Level_WARNING;
sprintf(cn->message, "Final confirmation for outgoing manual key verification %s", message); sprintf(cn->message, "Final confirmation for outgoing manual key verification %s", message);