#include "selfdrive/frogpilot/ui/visual_settings.h" FrogPilotVisualsPanel::FrogPilotVisualsPanel(SettingsWindow *parent) : FrogPilotListWidget(parent) { const std::vector> visualToggles { {"AlertVolumeControl", "Alert Volume Control", "Control the volume level for each individual sound in openpilot.", "../frogpilot/assets/toggle_icons/icon_mute.png"}, {"DisengageVolume", "Disengage Volume", "Related alerts:\n\nAdaptive Cruise Disabled\nParking Brake Engaged\nPedal Pressed\nSpeed too Low", ""}, {"EngageVolume", "Engage Volume", "Related alerts:\n\nNNFF Torque Controller loaded", ""}, {"PromptVolume", "Prompt Volume", "Related alerts:\n\nCar Detected in Blindspot\nLight turned green\nSpeed too Low\nSteer Unavailable Below 'X'\nTake Control, Turn Exceeds Steering Limit", ""}, {"PromptDistractedVolume", "Prompt Distracted Volume", "Related alerts:\n\nPay Attention, Driver Distracted\nTouch Steering Wheel, Driver Unresponsive", ""}, {"RefuseVolume", "Refuse Volume", "Related alerts:\n\nopenpilot Unavailable", ""}, {"WarningSoftVolume", "Warning Soft Volume", "Related alerts:\n\nBRAKE!, Risk of Collision\nTAKE CONTROL IMMEDIATELY", ""}, {"WarningImmediateVolume", "Warning Immediate Volume", "Related alerts:\n\nDISENGAGE IMMEDIATELY, Driver Distracted\nDISENGAGE IMMEDIATELY, Driver Unresponsive", ""}, {"CustomAlerts", "Custom Alerts", "Enable custom alerts for various logic or situational changes.", "../frogpilot/assets/toggle_icons/icon_green_light.png"}, {"CustomUI", "Custom Onroad UI", "Customize the Onroad UI with some additional visual functions.", "../assets/offroad/icon_road.png"}, {"AccelerationPath", "Acceleration Path", "Visualize the car's intended acceleration or deceleration with a color-coded path.", ""}, {"QOLVisuals", "Quality of Life", "Miscellaneous quality of life changes to improve your overall openpilot experience.", "../frogpilot/assets/toggle_icons/quality_of_life.png"}, {"DriveStats", "Drive Stats In Home Screen", "Display your device's drive stats in the home screen.", ""}, }; for (const auto &[param, title, desc, icon] : visualToggles) { ParamControl *toggle; if (param == "AlertVolumeControl") { FrogPilotParamManageControl *alertVolumeControlToggle = new FrogPilotParamManageControl(param, title, desc, icon, this); QObject::connect(alertVolumeControlToggle, &FrogPilotParamManageControl::manageButtonClicked, this, [this]() { parentToggleClicked(); for (auto &[key, toggle] : toggles) { toggle->setVisible(alertVolumeControlKeys.find(key.c_str()) != alertVolumeControlKeys.end()); } }); toggle = alertVolumeControlToggle; } else if (alertVolumeControlKeys.find(param) != alertVolumeControlKeys.end()) { if (param == "WarningImmediateVolume") { toggle = new FrogPilotParamValueControl(param, title, desc, icon, 25, 100, std::map(), this, false, "%"); } else { toggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 100, std::map(), this, false, "%"); } } else if (param == "CustomAlerts") { FrogPilotParamManageControl *customAlertsToggle = new FrogPilotParamManageControl(param, title, desc, icon, this); QObject::connect(customAlertsToggle, &FrogPilotParamManageControl::manageButtonClicked, this, [this]() { parentToggleClicked(); for (auto &[key, toggle] : toggles) { toggle->setVisible(customAlertsKeys .find(key.c_str()) != customAlertsKeys .end()); } }); toggle = customAlertsToggle; } else if (param == "CustomUI") { FrogPilotParamManageControl *customUIToggle = new FrogPilotParamManageControl(param, title, desc, icon, this); QObject::connect(customUIToggle, &FrogPilotParamManageControl::manageButtonClicked, this, [this]() { parentToggleClicked(); for (auto &[key, toggle] : toggles) { toggle->setVisible(customOnroadUIKeys.find(key.c_str()) != customOnroadUIKeys.end()); } }); toggle = customUIToggle; } else if (param == "QOLVisuals") { FrogPilotParamManageControl *qolToggle = new FrogPilotParamManageControl(param, title, desc, icon, this); QObject::connect(qolToggle, &FrogPilotParamManageControl::manageButtonClicked, this, [this]() { parentToggleClicked(); for (auto &[key, toggle] : toggles) { toggle->setVisible(qolKeys.find(key.c_str()) != qolKeys.end()); } }); toggle = qolToggle; } else { toggle = new ParamControl(param, title, desc, icon, this); } addItem(toggle); toggles[param.toStdString()] = toggle; QObject::connect(toggle, &ToggleControl::toggleFlipped, [this]() { updateToggles(); }); QObject::connect(static_cast(toggle), &FrogPilotButtonParamControl::buttonClicked, [this]() { updateToggles(); }); QObject::connect(static_cast(toggle), &FrogPilotParamValueControl::valueChanged, [this]() { updateToggles(); }); QObject::connect(toggle, &AbstractControl::showDescriptionEvent, [this]() { update(); }); QObject::connect(static_cast(toggle), &FrogPilotParamManageControl::manageButtonClicked, [this]() { update(); }); } std::set rebootKeys = {"DriveStats"}; for (const std::string &key : rebootKeys) { QObject::connect(toggles[key], &ToggleControl::toggleFlipped, [this, key]() { if (started || key == "DriveStats") { if (FrogPilotConfirmationDialog::toggle("Reboot required to take effect.", "Reboot Now", this)) { Hardware::soft_reboot(); } } }); } QObject::connect(device(), &Device::interactiveTimeout, this, &FrogPilotVisualsPanel::hideSubToggles); QObject::connect(parent, &SettingsWindow::closeParentToggle, this, &FrogPilotVisualsPanel::hideSubToggles); QObject::connect(parent, &SettingsWindow::closeSubParentToggle, this, &FrogPilotVisualsPanel::hideSubSubToggles); QObject::connect(parent, &SettingsWindow::updateMetric, this, &FrogPilotVisualsPanel::updateMetric); QObject::connect(uiState(), &UIState::uiUpdate, this, &FrogPilotVisualsPanel::updateState); hideSubToggles(); updateMetric(); } void FrogPilotVisualsPanel::updateState(const UIState &s) { started = s.scene.started; } void FrogPilotVisualsPanel::updateToggles() { std::thread([this]() { paramsMemory.putBool("FrogPilotTogglesUpdated", true); std::this_thread::sleep_for(std::chrono::seconds(1)); paramsMemory.putBool("FrogPilotTogglesUpdated", false); }).detach(); } void FrogPilotVisualsPanel::updateMetric() { bool previousIsMetric = isMetric; isMetric = params.getBool("IsMetric"); if (isMetric != previousIsMetric) { double distanceConversion = isMetric ? INCH_TO_CM : CM_TO_INCH; double speedConversion = isMetric ? FOOT_TO_METER : METER_TO_FOOT; } if (isMetric) { } else { } previousIsMetric = isMetric; } void FrogPilotVisualsPanel::parentToggleClicked() { openParentToggle(); } void FrogPilotVisualsPanel::subParentToggleClicked() { openSubParentToggle(); } void FrogPilotVisualsPanel::hideSubToggles() { for (auto &[key, toggle] : toggles) { bool subToggles = alertVolumeControlKeys.find(key.c_str()) != alertVolumeControlKeys.end() || customAlertsKeys.find(key.c_str()) != customAlertsKeys.end() || customOnroadUIKeys.find(key.c_str()) != customOnroadUIKeys.end() || customThemeKeys.find(key.c_str()) != customThemeKeys.end() || modelUIKeys.find(key.c_str()) != modelUIKeys.end() || qolKeys.find(key.c_str()) != qolKeys.end(); toggle->setVisible(!subToggles); } closeParentToggle(); } void FrogPilotVisualsPanel::hideSubSubToggles() { for (auto &[key, toggle] : toggles) { bool isVisible = false; toggle->setVisible(isVisible); } closeSubParentToggle(); update(); } void FrogPilotVisualsPanel::hideEvent(QHideEvent *event) { hideSubToggles(); }