#include "frogpilot/ui/qt/offroad/visual_settings.h" FrogPilotVisualsPanel::FrogPilotVisualsPanel(FrogPilotSettingsWindow *parent) : FrogPilotListWidget(parent), parent(parent) { QJsonObject shownDescriptions = QJsonDocument::fromJson(QString::fromStdString(params.get("ShownToggleDescriptions")).toUtf8()).object(); QString className = this->metaObject()->className(); if (!shownDescriptions.value(className).toBool(false)) { forceOpenDescriptions = true; shownDescriptions.insert(className, true); params.put("ShownToggleDescriptions", QJsonDocument(shownDescriptions).toJson(QJsonDocument::Compact).toStdString()); } QStackedLayout *visualsLayout = new QStackedLayout(); addItem(visualsLayout); FrogPilotListWidget *visualsList = new FrogPilotListWidget(this); ScrollView *visualsPanel = new ScrollView(visualsList, this); visualsLayout->addWidget(visualsPanel); FrogPilotListWidget *advancedCustomList = new FrogPilotListWidget(this); FrogPilotListWidget *customUIList = new FrogPilotListWidget(this); FrogPilotListWidget *developerMetricList = new FrogPilotListWidget(this); FrogPilotListWidget *developerSidebarList = new FrogPilotListWidget(this); FrogPilotListWidget *developerUIList = new FrogPilotListWidget(this); FrogPilotListWidget *developerWidgetList = new FrogPilotListWidget(this); FrogPilotListWidget *modelUIList = new FrogPilotListWidget(this); FrogPilotListWidget *navigationUIList = new FrogPilotListWidget(this); FrogPilotListWidget *qualityOfLifeList = new FrogPilotListWidget(this); ScrollView *advancedCustomPanel = new ScrollView(advancedCustomList, this); ScrollView *customUIPanel = new ScrollView(customUIList, this); ScrollView *developerMetricPanel = new ScrollView(developerMetricList, this); ScrollView *developerSidebarPanel = new ScrollView(developerSidebarList, this); ScrollView *developerUIPanel = new ScrollView(developerUIList, this); ScrollView *developerWidgetPanel = new ScrollView(developerWidgetList, this); ScrollView *modelUIPanel = new ScrollView(modelUIList, this); ScrollView *navigationUIPanel = new ScrollView(navigationUIList, this); ScrollView *qualityOfLifePanel = new ScrollView(qualityOfLifeList, this); visualsLayout->addWidget(advancedCustomPanel); visualsLayout->addWidget(customUIPanel); visualsLayout->addWidget(developerMetricPanel); visualsLayout->addWidget(developerSidebarPanel); visualsLayout->addWidget(developerUIPanel); visualsLayout->addWidget(developerWidgetPanel); visualsLayout->addWidget(modelUIPanel); visualsLayout->addWidget(navigationUIPanel); visualsLayout->addWidget(qualityOfLifePanel); const std::vector> visualToggles { {"AdvancedCustomUI", tr("Advanced UI Controls"), tr("Advanced visual changes to fine-tune how the driving screen looks."), "../../frogpilot/assets/toggle_icons/icon_advanced_device.png"}, {"HideSpeed", tr("Hide Current Speed"), tr("Hide the current speed from the driving screen."), ""}, {"HideLeadMarker", tr("Hide Lead Marker"), tr("Hide the lead-vehicle marker from the driving screen."), ""}, {"HideMapIcon", tr("Hide Map Settings Button"), tr("Hide the map settings button or map from the driving screen."), ""}, {"HideMaxSpeed", tr("Hide Max Speed"), tr("Hide the max speed from the driving screen."), ""}, {"HideAlerts", tr("Hide Non-Critical Alerts"), tr("Hide non-critical alerts from the driving screen."), ""}, {"HideSpeedLimit", tr("Hide Speed Limits"), tr("Hide posted speed limits from the driving screen."), ""}, {"WheelSpeed", tr("Use Wheel Speed"), tr("Use the vehicle's wheel speed instead of the cluster speed. This is purely a visual change and doesn't impact how openpilot drives!"), ""}, {"DeveloperUI", tr("Developer UI"), tr("Detailed information about openpilot's internal operations."), "../assets/offroad/icon_shell.png"}, {"AdjacentPathMetrics", tr("Adjacent Path Metrics"), tr("Show the width of the adjacent lanes."), ""}, {"DeveloperMetrics", tr("Developer Metrics"), tr("Performance data, sensor readings, and system metrics for debugging and optimizing openpilot."), ""}, {"BorderMetrics", tr("Border Metrics"), tr("Show statuses along the border of the driving screen.

Blind Spot: The border turns red when a vehicle is in a blind spot
Steering Torque: The border goes from green to red according to how much steering torque is being used
Turn Signal: The border flashes yellow when a turn signal is on"), ""}, {"LeadInfo", tr("Lead Info"), tr("Show each tracked vehicle's distance and speed below its marker."), ""}, {"FPSCounter", tr("FPS Display"), tr("Show the frames per second (FPS) at the bottom of the driving screen."), ""}, {"NumericalTemp", tr("Numerical Temperature Gauge"), tr("Show a numerical temperature in the sidebar instead of the status labels."), ""}, {"SidebarMetrics", tr("Sidebar Metrics"), tr("Display system information (CPU, GPU, RAM usage, IP address, device storage) in the sidebar."), ""}, {"UseSI", tr("Use International System of Units"), tr("Display measurements using the \"International System of Units\" (SI) standard."), ""}, {"DeveloperSidebar", tr("Developer Sidebar"), tr("Display debugging info and metrics in a dedicated sidebar on the right side of the screen."), ""}, {"DeveloperSidebarMetric1", tr("Metric #1"), tr("Select the metric shown in the first \"Developer Sidebar\" widget."), ""}, {"DeveloperSidebarMetric2", tr("Metric #2"), tr("Select the metric shown in the second \"Developer Sidebar\" widget."), ""}, {"DeveloperSidebarMetric3", tr("Metric #3"), tr("Select the metric shown in the third \"Developer Sidebar\" widget."), ""}, {"DeveloperSidebarMetric4", tr("Metric #4"), tr("Select the metric shown in the fourth \"Developer Sidebar\" widget."), ""}, {"DeveloperSidebarMetric5", tr("Metric #5"), tr("Select the metric shown in the fifth \"Developer Sidebar\" widget."), ""}, {"DeveloperSidebarMetric6", tr("Metric #6"), tr("Select the metric shown in the sixth \"Developer Sidebar\" widget."), ""}, {"DeveloperSidebarMetric7", tr("Metric #7"), tr("Select the metric shown in the seventh \"Developer Sidebar\" widget."), ""}, {"DeveloperWidgets", tr("Developer Widgets"), tr("Overlays for debugging visuals, internal states, and model predictions on the driving screen."), ""}, {"AdjacentLeadsUI", tr("Adjacent Leads Tracking"), tr("Display adjacent leads detected by the car's radar to the left and right of the current driving path."), ""}, {"ShowStoppingPoint", tr("Model Stopping Point"), tr("Show a stop-sign marker where the model intends to stop."), ""}, {"RadarTracksUI", tr("Radar Tracks"), tr("Display all radar points produced by the car's radar."), ""}, {"CustomUI", tr("Driving Screen Widgets"), tr("Custom FrogPilot widgets for the driving screen."), "../assets/offroad/icon_road.png"}, {"AccelerationPath", tr("Acceleration Path"), tr("Color the driving path by planned acceleration and braking."), ""}, {"AdjacentPath", tr("Adjacent Lanes"), tr("Show the driving paths for the left and right lanes."), ""}, {"BlindSpotPath", tr("Blind Spot Path"), tr("Show a red path when a vehicle is in that lane's blind spot."), ""}, {"Compass", tr("Compass"), tr("Show the current driving direction with a simple on-screen compass."), ""}, {"OnroadDistanceButton", tr("Driving Personality Button"), tr("Control and view the current driving personality via a driving screen widget."), ""}, {"PedalsOnUI", tr("Gas / Brake Pedal Indicators"), tr("On-screen gas and brake indicators.

Dynamic: Opacity changes according to how much openpilot is accelerating or braking
Static: Full when active, dim when not"), ""}, {"RotatingWheel", tr("Rotating Steering Wheel"), tr("Rotate the driving screen wheel with the physical steering wheel."), ""}, {"ModelUI", tr("Model UI"), tr("Model visualizations for the driving path, lane lines, path edges, and road edges."), "../../frogpilot/assets/toggle_icons/icon_vtc.png"}, {"DynamicPathWidth", tr("Dynamic Path Width"), tr("Change the path width based on engagement.

Fully Engaged: 100%
Always On Lateral: 75%
Disengaged: 50%"), ""}, {"LaneLinesWidth", tr("Lane Lines Width"), tr("Set the lane-line thickness.

Default matches the MUTCD lane-line width standard of 4 inches."), ""}, {"PathEdgeWidth", tr("Path Edges Width"), tr("Set the driving-path edge width that represents different driving modes and statuses.

Default is 20% of the total path width.

Color Guide:

- Blue: Navigation
- Light Blue: Always On Lateral
- Green: Default
- Orange: Experimental Mode
- Red: Traffic Mode
- Yellow: Conditional Experimental Mode overridden"), ""}, {"PathWidth", tr("Path Width"), tr("Set the driving-path width.

Default (6.1 feet) matches the width of a 2019 Lexus ES 350."), ""}, {"RoadEdgesWidth", tr("Road Edges Width"), tr("Set the road-edge thickness.

Default matches half of the MUTCD lane-line width standard of 4 inches."), ""}, {"UnlimitedLength", tr("\"Unlimited\" Road UI"), tr("Extend the length of the driving path, lane lines, and road edges for as far as the model can see."), ""}, {"NavigationUI", tr("Navigation Widgets"), tr("Map style, speed limits, and other navigation widgets."), "../../frogpilot/assets/toggle_icons/icon_map.png"}, {"BigMap", tr("Larger Map Display"), tr("Increase the map size for easier navigation readings."), ""}, {"MapStyle", tr("Map Style"), tr("Select the map style for \"Navigate on openpilot\" (NOO):

Stock openpilot: Default comma.ai style
FrogPilot: Official FrogPilot map style
Mapbox Streets: Standard street-focused view
Mapbox Outdoors: Emphasizes outdoor and terrain features
Mapbox Light: Minimalist, bright theme
Mapbox Dark: Minimalist, dark theme
Mapbox Navigation Day: Optimized for daytime navigation
Mapbox Navigation Night: Optimized for nighttime navigation
Mapbox Satellite: Satellite imagery only
Mapbox Satellite Streets: Hybrid satellite imagery with street labels
Mapbox Traffic Night: Dark theme emphasizing traffic conditions
Mike's Personalized Style: Customized hybrid satellite view"), ""}, {"RoadNameUI", tr("Road Name"), tr("Display the road name at the bottom of the driving screen using data from \"OpenStreetMap (OSM)\"."), ""}, {"ShowSpeedLimits", tr("Show Speed Limits"), tr("Show speed limits in the top-left corner of the driving screen. Uses data from the car's dashboard (if supported) and \"OpenStreetMap (OSM)\"."), ""}, {"SLCMapboxFiller", tr("Show Speed Limits from Mapbox"), tr("Use Mapbox speed-limit data when no other source is available."), ""}, {"UseVienna", tr("Use Vienna-Style Speed Signs"), tr("Show Vienna-style (EU) speed-limit signs instead of MUTCD (US)."), ""}, {"QOLVisuals", tr("Quality of Life"), tr("Miscellaneous visual changes to fine-tune how the driving screen looks."), "../../frogpilot/assets/toggle_icons/icon_quality_of_life.png"}, {"CameraView", tr("Camera View"), tr("Select the active camera view. This is purely a visual change and doesn't impact how openpilot drives!"), ""}, {"DriverCamera", tr("Show Driver Camera When In Reverse"), tr("Show the driver camera feed when the vehicle is in reverse."), ""}, {"StoppedTimer", tr("Stopped Timer"), tr("Show a timer when stopped in place of the current speed to indicate how long the vehicle has been stopped."), ""} }; for (const auto &[param, title, desc, icon] : visualToggles) { AbstractControl *visualToggle; if (param == "AdvancedCustomUI") { FrogPilotManageControl *advancedCustomUIToggle = new FrogPilotManageControl(param, title, desc, icon); QObject::connect(advancedCustomUIToggle, &FrogPilotManageControl::manageButtonClicked, [visualsLayout, advancedCustomPanel]() { visualsLayout->setCurrentWidget(advancedCustomPanel); }); visualToggle = advancedCustomUIToggle; } else if (param == "HideMapIcon") { std::vector mapIconToggles{"HideMap"}; std::vector mapIconToggleNames{tr("Hide Map")}; visualToggle = new FrogPilotButtonToggleControl(param, title, desc, icon, mapIconToggles, mapIconToggleNames); } else if (param == "DeveloperUI") { FrogPilotManageControl *developerUIToggle = new FrogPilotManageControl(param, title, desc, icon); QObject::connect(developerUIToggle, &FrogPilotManageControl::manageButtonClicked, [visualsLayout, developerUIPanel]() { visualsLayout->setCurrentWidget(developerUIPanel); }); visualToggle = developerUIToggle; } else if (param == "DeveloperMetrics") { FrogPilotManageControl *developerMetricsToggle = new FrogPilotManageControl(param, title, desc, icon); QObject::connect(developerMetricsToggle, &FrogPilotManageControl::manageButtonClicked, [visualsLayout, developerMetricPanel, this]() { openSubSubPanel(); visualsLayout->setCurrentWidget(developerMetricPanel); developerUIOpen = true; }); visualToggle = developerMetricsToggle; } else if (param == "BorderMetrics") { std::vector borderToggles{"BlindSpotMetrics", "ShowSteering", "SignalMetrics"}; std::vector borderToggleNames{tr("Blind Spot"), tr("Steering Torque"), tr("Turn Signal")}; borderMetricsButton = new FrogPilotButtonToggleControl(param, title, desc, icon, borderToggles, borderToggleNames); visualToggle = borderMetricsButton; } else if (param == "NumericalTemp") { std::vector temperatureToggles{"Fahrenheit"}; std::vector temperatureToggleNames{tr("Fahrenheit")}; visualToggle = new FrogPilotButtonToggleControl(param, title, desc, icon, temperatureToggles, temperatureToggleNames); } else if (param == "SidebarMetrics") { sidebarMetricsToggles = {"ShowCPU", "ShowGPU", "ShowIP", "ShowMemoryUsage", "ShowStorageLeft", "ShowStorageUsed"}; std::vector sidebarMetricsToggleNames{tr("CPU"), tr("GPU"), tr("IP"), tr("RAM"), tr("SSD Left"), tr("SSD Used")}; sidebarMetricsToggle = new FrogPilotButtonsControl(title, desc, icon, sidebarMetricsToggleNames, true, false, 150); for (int i = 0; i < sidebarMetricsToggles.size(); ++i) { if (params.getBool(sidebarMetricsToggles[i].toStdString())) { sidebarMetricsToggle->setCheckedButton(i); } } QObject::connect(sidebarMetricsToggle, &FrogPilotButtonsControl::buttonClicked, [this](int id) { params.putBool(sidebarMetricsToggles[id].toStdString(), !params.getBool(sidebarMetricsToggles[id].toStdString())); if (id == 0) { params.putBool("ShowGPU", false); } else if (id == 1) { params.putBool("ShowCPU", false); } else if (id == 3) { params.putBool("ShowStorageLeft", false); params.putBool("ShowStorageUsed", false); } else if (id == 4) { params.putBool("ShowMemoryUsage", false); params.putBool("ShowStorageUsed", false); } else if (id == 5) { params.putBool("ShowMemoryUsage", false); params.putBool("ShowStorageLeft", false); } sidebarMetricsToggle->clearCheckedButtons(); for (int i = 0; i < sidebarMetricsToggles.size(); ++i) { if (params.getBool(sidebarMetricsToggles[i].toStdString())) { sidebarMetricsToggle->setCheckedButton(i); } } }); visualToggle = sidebarMetricsToggle; } else if (param == "DeveloperSidebar") { FrogPilotManageControl *developerSidebarToggle = new FrogPilotManageControl(param, title, desc, icon); QObject::connect(developerSidebarToggle, &FrogPilotManageControl::manageButtonClicked, [visualsLayout, developerSidebarPanel, this]() { openSubSubPanel(); visualsLayout->setCurrentWidget(developerSidebarPanel); developerUIOpen = true; }); visualToggle = developerSidebarToggle; } else if (developerSidebarKeys.contains(param)) { QMap developerSidebarMetricOptions { {0, tr("None")}, {1, tr("Acceleration: Current")}, {2, tr("Acceleration: Max")}, {3, tr("Auto Tune: Actuator Delay")}, {4, tr("Auto Tune: Friction")}, {5, tr("Auto Tune: Lateral Acceleration")}, {6, tr("Auto Tune: Steer Ratio")}, {7, tr("Auto Tune: Stiffness Factor")}, {8, tr("Engagement %: Lateral")}, {9, tr("Engagement %: Longitudinal")}, {10, tr("Lateral Control: Steering Angle")}, {11, tr("Lateral Control: Torque % Used")}, {12, tr("Longitudinal Control: Actuator Acceleration Output")}, {13, tr("Longitudinal MPC Jerk: Acceleration")}, {14, tr("Longitudinal MPC Jerk: Danger Zone")}, {15, tr("Longitudinal MPC Jerk: Speed Control")}, {16, tr("Driving Model: Current")}, }; ButtonControl *metricToggle = new ButtonControl(title, tr("SELECT"), desc); QObject::connect(metricToggle, &ButtonControl::clicked, [metricToggle, key = param, developerSidebarMetricOptions, this]() mutable { QString current = developerSidebarMetricOptions.value(params.getInt(key.toStdString()), tr("None")); QString selection = MultiOptionDialog::getSelection(tr("Select a metric to display"), developerSidebarMetricOptions.values(), current, this); if (!selection.isEmpty()) { int selectedMetric = developerSidebarMetricOptions.key(selection); params.putInt(key.toStdString(), selectedMetric); metricToggle->setValue(selection); } }); metricToggle->setValue(developerSidebarMetricOptions.value(params.getInt(param.toStdString()), tr("None"))); visualToggle = metricToggle; } else if (param == "DeveloperWidgets") { FrogPilotManageControl *developerWidgetsToggle = new FrogPilotManageControl(param, title, desc, icon); QObject::connect(developerWidgetsToggle, &FrogPilotManageControl::manageButtonClicked, [visualsLayout, developerWidgetPanel, this]() { openSubSubPanel(); visualsLayout->setCurrentWidget(developerWidgetPanel); developerUIOpen = true; }); visualToggle = developerWidgetsToggle; } else if (param == "ShowStoppingPoint") { std::vector stoppingPointToggles{"ShowStoppingPointMetrics"}; std::vector stoppingPointToggleNames{tr("Show Distance")}; visualToggle = new FrogPilotButtonToggleControl(param, title, desc, icon, stoppingPointToggles, stoppingPointToggleNames); } else if (param == "CustomUI") { FrogPilotManageControl *customUIToggle = new FrogPilotManageControl(param, title, desc, icon); QObject::connect(customUIToggle, &FrogPilotManageControl::manageButtonClicked, [visualsLayout, customUIPanel]() { visualsLayout->setCurrentWidget(customUIPanel); }); visualToggle = customUIToggle; } else if (param == "PedalsOnUI") { std::vector pedalsToggles{"DynamicPedalsOnUI", "StaticPedalsOnUI"}; std::vector pedalsToggleNames{tr("Dynamic"), tr("Static")}; FrogPilotButtonToggleControl *pedalsToggle = new FrogPilotButtonToggleControl(param, title, desc, icon, pedalsToggles, pedalsToggleNames, true); QObject::connect(pedalsToggle, &FrogPilotButtonToggleControl::buttonClicked, [this](int id) { if (id == 0) { params.putBool("StaticPedalsOnUI", false); } else if (id == 1) { params.putBool("DynamicPedalsOnUI", false); } }); visualToggle = pedalsToggle; } else if (param == "ModelUI") { FrogPilotManageControl *modelUIToggle = new FrogPilotManageControl(param, title, desc, icon); QObject::connect(modelUIToggle, &FrogPilotManageControl::manageButtonClicked, [visualsLayout, modelUIPanel]() { visualsLayout->setCurrentWidget(modelUIPanel); }); visualToggle = modelUIToggle; } else if (param == "LaneLinesWidth" || param == "RoadEdgesWidth") { visualToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 24, tr(" inches")); } else if (param == "PathEdgeWidth") { std::map pathEdgeLabels; for (int i = 0; i <= 100; ++i) { pathEdgeLabels[i] = i == 0 ? tr("Off") : QString::number(i) + "%"; } visualToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 100, QString(), pathEdgeLabels); } else if (param == "PathWidth") { visualToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 10, tr(" feet"), std::map(), 0.1); } else if (param == "NavigationUI") { FrogPilotManageControl *navigationUIToggle = new FrogPilotManageControl(param, title, desc, icon); QObject::connect(navigationUIToggle, &FrogPilotManageControl::manageButtonClicked, [visualsLayout, navigationUIPanel]() { visualsLayout->setCurrentWidget(navigationUIPanel); }); visualToggle = navigationUIToggle; } else if (param == "BigMap") { std::vector mapToggles{"FullMap"}; std::vector mapToggleNames{tr("Full Map")}; visualToggle = new FrogPilotButtonToggleControl(param, title, desc, icon, mapToggles, mapToggleNames); } else if (param == "MapStyle") { QMap styleMap { {0, tr("Stock openpilot")}, {1, tr("FrogPilot")}, {2, tr("Mapbox Streets")}, {3, tr("Mapbox Outdoors")}, {4, tr("Mapbox Light")}, {5, tr("Mapbox Dark")}, {6, tr("Mapbox Navigation Day")}, {7, tr("Mapbox Navigation Night")}, {8, tr("Mapbox Satellite")}, {9, tr("Mapbox Satellite Streets")}, {10, tr("Mapbox Traffic Night")}, {11, tr("Mike's Personalized Style")} }; ButtonControl *mapStyleButton = new ButtonControl(title, tr("SELECT"), desc); QObject::connect(mapStyleButton, &ButtonControl::clicked, [mapStyleButton, styleMap, this]() { QString selection = MultiOptionDialog::getSelection(tr("Select a map style"), styleMap.values(), "", this); if (!selection.isEmpty()) { int selectedStyle = styleMap.key(selection); params.putInt("MapStyle", selectedStyle); mapStyleButton->setValue(selection); } }); int currentStyle = params.getInt("MapStyle"); mapStyleButton->setValue(styleMap[currentStyle]); visualToggle = mapStyleButton; } else if (param == "QOLVisuals") { FrogPilotManageControl *qolToggle = new FrogPilotManageControl(param, title, desc, icon); QObject::connect(qolToggle, &FrogPilotManageControl::manageButtonClicked, [visualsLayout, qualityOfLifePanel]() { visualsLayout->setCurrentWidget(qualityOfLifePanel); }); visualToggle = qolToggle; } else if (param == "CameraView") { std::vector cameraOptions{tr("Auto"), tr("Driver"), tr("Standard"), tr("Wide")}; ButtonParamControl *cameraSelection = new ButtonParamControl(param, title, desc, icon, cameraOptions); visualToggle = cameraSelection; } else { visualToggle = new ParamControl(param, title, desc, icon); } toggles[param] = visualToggle; if (advancedCustomOnroadUIKeys.contains(param)) { advancedCustomList->addItem(visualToggle); } else if (customOnroadUIKeys.contains(param)) { customUIList->addItem(visualToggle); } else if (developerMetricKeys.contains(param)) { developerMetricList->addItem(visualToggle); } else if (developerSidebarKeys.contains(param)) { developerSidebarList->addItem(visualToggle); } else if (developerUIKeys.contains(param)) { developerUIList->addItem(visualToggle); } else if (developerWidgetKeys.contains(param)) { developerWidgetList->addItem(visualToggle); } else if (modelUIKeys.contains(param)) { modelUIList->addItem(visualToggle); } else if (navigationUIKeys.contains(param)) { navigationUIList->addItem(visualToggle); } else if (qualityOfLifeKeys.contains(param)) { qualityOfLifeList->addItem(visualToggle); } else { visualsList->addItem(visualToggle); parentKeys.insert(param); } if (FrogPilotManageControl *frogPilotManageToggle = qobject_cast(visualToggle)) { QObject::connect(frogPilotManageToggle, &FrogPilotManageControl::manageButtonClicked, [this]() { emit openSubPanel(); openDescriptions(forceOpenDescriptions, toggles); }); } QObject::connect(visualToggle, &AbstractControl::hideDescriptionEvent, [this]() { update(); }); QObject::connect(visualToggle, &AbstractControl::showDescriptionEvent, [this]() { update(); }); } QSet forceUpdateKeys = {"HideLeadMarker", "ShowSpeedLimits"}; for (const QString &key : forceUpdateKeys) { QObject::connect(static_cast(toggles[key]), &ToggleControl::toggleFlipped, this, &FrogPilotVisualsPanel::updateToggles); } openDescriptions(forceOpenDescriptions, toggles); QObject::connect(parent, &FrogPilotSettingsWindow::closeSubPanel, [visualsLayout, visualsPanel, this] { openDescriptions(forceOpenDescriptions, toggles); visualsLayout->setCurrentWidget(visualsPanel); }); QObject::connect(parent, &FrogPilotSettingsWindow::closeSubSubPanel, [visualsLayout, developerUIPanel, this]() { openDescriptions(forceOpenDescriptions, toggles); if (developerUIOpen) { visualsLayout->setCurrentWidget(developerUIPanel); developerUIOpen = false; } }); QObject::connect(parent, &FrogPilotSettingsWindow::updateMetric, this, &FrogPilotVisualsPanel::updateMetric); } void FrogPilotVisualsPanel::showEvent(QShowEvent *event) { frogpilotToggleLevels = parent->frogpilotToggleLevels; hasAutoTune = parent->hasAutoTune; hasBSM = parent->hasBSM; hasOpenpilotLongitudinal = parent->hasOpenpilotLongitudinal; hasRadar = parent->hasRadar; tuningLevel = parent->tuningLevel; for (int i = 0; i < sidebarMetricsToggles.size(); ++i) { if (params.getBool(sidebarMetricsToggles[i].toStdString())) { sidebarMetricsToggle->setCheckedButton(i); } } updateToggles(); } void FrogPilotVisualsPanel::updateMetric(bool metric, bool bootRun) { static bool previousMetric; if (metric != previousMetric && !bootRun) { double distanceConversion = metric ? FOOT_TO_METER : METER_TO_FOOT; double smallDistanceConversion = metric ? INCH_TO_CM : CM_TO_INCH; params.putIntNonBlocking("LaneLinesWidth", params.getInt("LaneLinesWidth") * smallDistanceConversion); params.putIntNonBlocking("RoadEdgesWidth", params.getInt("RoadEdgesWidth") * smallDistanceConversion); params.putFloatNonBlocking("PathWidth", params.getFloat("PathWidth") * distanceConversion); } previousMetric = metric; static std::map imperialDistanceLabels; static std::map imperialSmallDistanceLabels; static std::map metricDistanceLabels; static std::map metricSmallDistanceLabels; static bool labelsInitialized = false; if (!labelsInitialized) { for (int i = 0; i <= 10; ++i) { imperialDistanceLabels[i] = i == 0 ? tr("Off") : i == 1 ? QString::number(i) + tr(" foot") : QString::number(i) + tr(" feet"); } for (int i = 0; i <= 24; ++i) { imperialSmallDistanceLabels[i] = i == 0 ? tr("Off") : i == 1 ? QString::number(i) + tr(" inch") : QString::number(i) + tr(" inches"); } for (float i = 0.0f; i <= 3.0f; i += 0.1f) { metricDistanceLabels[i] = i == 0.0f ? tr("Off") : i == 1.0 ? QString::number(i) + tr(" meter") : QString::number(i, 'f', 1) + tr(" meters"); } for (int i = 0; i <= 60; ++i) { metricSmallDistanceLabels[i] = i == 0 ? tr("Off") : i == 1 ? QString::number(i) + tr(" centimeter") : QString::number(i) + tr(" centimeters"); } labelsInitialized = true; } FrogPilotParamValueControl *laneLinesWidthToggle = static_cast(toggles["LaneLinesWidth"]); FrogPilotParamValueControl *pathWidthToggle = static_cast(toggles["PathWidth"]); FrogPilotParamValueControl *roadEdgesWidthToggle = static_cast(toggles["RoadEdgesWidth"]); if (metric) { laneLinesWidthToggle->setDescription(tr("Set the lane-line thickness.

Default matches the MUTCD lane-line width standard of 10 centimeters.")); pathWidthToggle->setDescription(tr("Set the driving-path width.

Default (1.9 meters) matches the width of a 2019 Lexus ES 350.")); roadEdgesWidthToggle->setDescription(tr("Set the road-edge thickness.

Default matches half of the MUTCD lane-line width standard of 10 centimeters.")); laneLinesWidthToggle->updateControl(0, 60, metricSmallDistanceLabels); roadEdgesWidthToggle->updateControl(0, 60, metricSmallDistanceLabels); pathWidthToggle->updateControl(0, 3, metricDistanceLabels); } else { laneLinesWidthToggle->setDescription(tr("Set the lane-line thickness.

Default matches the MUTCD lane-line width standard of 4 inches.")); pathWidthToggle->setDescription(tr("Set the driving-path width.

Default (6.1 feet) matches the width of a 2019 Lexus ES 350.")); roadEdgesWidthToggle->setDescription(tr("Set the road-edge thickness.

Default matches half of the MUTCD lane-line width standard of 4 inches.")); laneLinesWidthToggle->updateControl(0, 24, imperialSmallDistanceLabels); roadEdgesWidthToggle->updateControl(0, 24, imperialSmallDistanceLabels); pathWidthToggle->updateControl(0, 10, imperialDistanceLabels); } } void FrogPilotVisualsPanel::updateToggles() { for (auto &[key, toggle] : toggles) { if (parentKeys.contains(key)) { toggle->setVisible(false); } } for (auto &[key, toggle] : toggles) { if (parentKeys.contains(key)) { continue; } bool setVisible = tuningLevel >= frogpilotToggleLevels[key].toDouble(); if (key == "AccelerationPath") { setVisible &= hasOpenpilotLongitudinal; } else if (key == "AdjacentLeadsUI") { setVisible &= hasRadar && !(params.getBool("AdvancedCustomUI") && params.getBool("HideLeadMarker")); } else if (key == "BlindSpotPath") { setVisible &= hasBSM; } else if (key == "HideLeadMarker") { setVisible &= hasOpenpilotLongitudinal; } else if (key == "LeadInfo") { setVisible &= hasOpenpilotLongitudinal; } else if (key == "OnroadDistanceButton") { setVisible &= hasOpenpilotLongitudinal; } else if (key == "PedalsOnUI") { setVisible &= hasOpenpilotLongitudinal; } else if (key == "RadarTracksUI") { setVisible &= hasRadar; } else if (key == "ShowSpeedLimits") { setVisible &= !params.getBool("SpeedLimitController") || !hasOpenpilotLongitudinal; } else if (key == "ShowStoppingPoint") { setVisible &= hasOpenpilotLongitudinal; } else if (key == "SLCMapboxFiller") { setVisible &= params.getBool("ShowSpeedLimits") && !(hasOpenpilotLongitudinal && params.getBool("SpeedLimitController")); setVisible &= !params.get("MapboxSecretKey").empty(); } toggle->setVisible(setVisible); if (setVisible) { if (advancedCustomOnroadUIKeys.contains(key)) { toggles["AdvancedCustomUI"]->setVisible(true); } else if (customOnroadUIKeys.contains(key)) { toggles["CustomUI"]->setVisible(true); } else if (developerMetricKeys.contains(key)) { toggles["DeveloperMetrics"]->setVisible(true); } else if (developerUIKeys.contains(key)) { toggles["DeveloperUI"]->setVisible(true); } else if (developerWidgetKeys.contains(key)) { toggles["DeveloperWidgets"]->setVisible(true); } else if (modelUIKeys.contains(key)) { toggles["ModelUI"]->setVisible(true); } else if (navigationUIKeys.contains(key)) { toggles["NavigationUI"]->setVisible(true); } else if (qualityOfLifeKeys.contains(key)) { toggles["QOLVisuals"]->setVisible(true); } } } borderMetricsButton->setVisibleButton(0, hasBSM); openDescriptions(forceOpenDescriptions, toggles); update(); }