This commit is contained in:
Your Name
2024-04-27 13:43:16 -05:00
parent 21363ce751
commit ea1aad5ed1
128 changed files with 3533 additions and 1918 deletions

385
selfdrive/ui/qt/offroad/settings.cc Executable file → Normal file
View File

@@ -2,6 +2,8 @@
#include <cassert>
#include <cmath>
#include <iomanip>
#include <iostream>
#include <string>
#include <tuple>
#include <vector>
@@ -25,9 +27,9 @@
#include "selfdrive/ui/qt/qt_window.h"
#include "selfdrive/frogpilot/navigation/ui/navigation_settings.h"
#include "selfdrive/frogpilot/ui/control_settings.h"
#include "selfdrive/frogpilot/ui/vehicle_settings.h"
#include "selfdrive/frogpilot/ui/visual_settings.h"
#include "selfdrive/frogpilot/ui/qt/offroad/control_settings.h"
#include "selfdrive/frogpilot/ui/qt/offroad/vehicle_settings.h"
#include "selfdrive/frogpilot/ui/qt/offroad/visual_settings.h"
TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) {
// param, title, desc, icon
@@ -97,9 +99,14 @@ TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) {
std::vector<QString> longi_button_texts{tr("Aggressive"), tr("Standard"), tr("Relaxed")};
long_personality_setting = new ButtonParamControl("LongitudinalPersonality", tr("Driving Personality"),
tr("Standard is recommended. In aggressive mode, openpilot will follow lead cars closer and be more aggressive with the gas and brake. "
"In relaxed mode openpilot will stay further away from lead cars."),
"In relaxed mode openpilot will stay further away from lead cars. On supported cars, you can cycle through these personalities with "
"your steering wheel distance button."),
"../assets/offroad/icon_speed_limit.png",
longi_button_texts);
// set up uiState update for personality setting
QObject::connect(uiState(), &UIState::uiUpdate, this, &TogglesPanel::updateState);
for (auto &[param, title, desc, icon] : toggle_defs) {
auto toggle = new ParamControl(param, title, desc, icon, this);
@@ -110,7 +117,7 @@ TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) {
toggles[param.toStdString()] = toggle;
// insert longitudinal personality after NDOG toggle
if (param == "DisengageOnAccelerator" && !params.getInt("AdjustablePersonalities")) {
if (param == "DisengageOnAccelerator") {
addItem(long_personality_setting);
}
}
@@ -129,6 +136,18 @@ TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) {
});
}
void TogglesPanel::updateState(const UIState &s) {
const SubMaster &sm = *(s.sm);
if (sm.updated("controlsState")) {
auto personality = sm["controlsState"].getControlsState().getPersonality();
if (personality != s.scene.personality && s.scene.started && isVisible()) {
long_personality_setting->setCheckedButton(static_cast<int>(personality));
}
uiState()->scene.personality = personality;
}
}
void TogglesPanel::expandToggleDescription(const QString &param) {
toggles[param.toStdString()]->showDescription();
}
@@ -138,6 +157,13 @@ void TogglesPanel::showEvent(QShowEvent *event) {
}
void TogglesPanel::updateToggles() {
auto disengage_on_accelerator_toggle = toggles["DisengageOnAccelerator"];
disengage_on_accelerator_toggle->setVisible(!params.getBool("AlwaysOnLateral"));
auto driver_camera_toggle = toggles["RecordFront"];
driver_camera_toggle->setVisible(!(params.getBool("DeviceManagement") && params.getBool("NoLogging") && params.getBool("NoUploads")));
auto nav_settings_left_toggle = toggles["NavSettingLeftSide"];
nav_settings_left_toggle->setVisible(!params.getBool("FullMap"));
auto experimental_mode_toggle = toggles["ExperimentalMode"];
auto op_long_toggle = toggles["ExperimentalLongitudinalEnabled"];
const QString e2e_description = QString("%1<br>"
@@ -173,7 +199,12 @@ void TogglesPanel::updateToggles() {
op_long_toggle->setVisible(CP.getExperimentalLongitudinalAvailable() && !is_release);
if (hasLongitudinalControl(CP)) {
// normal description and toggle
experimental_mode_toggle->setEnabled(!params.getBool("ConditionalExperimental"));
bool conditional_experimental = params.getBool("ConditionalExperimental");
if (conditional_experimental) {
params.putBool("ExperimentalMode", true);
experimental_mode_toggle->refresh();
}
experimental_mode_toggle->setEnabled(!conditional_experimental);
experimental_mode_toggle->setDescription(e2e_description);
long_personality_setting->setEnabled(true);
} else {
@@ -225,15 +256,13 @@ DevicePanel::DevicePanel(SettingsWindow *parent) : ListWidget(parent) {
});
addItem(resetCalibBtn);
if (!params.getBool("Passive")) {
auto retrainingBtn = new ButtonControl(tr("Review Training Guide"), tr("REVIEW"), tr("Review the rules, features, and limitations of openpilot"));
connect(retrainingBtn, &ButtonControl::clicked, [=]() {
if (ConfirmationDialog::confirm(tr("Are you sure you want to review the training guide?"), tr("Review"), this)) {
emit reviewTrainingGuide();
}
});
addItem(retrainingBtn);
}
auto retrainingBtn = new ButtonControl(tr("Review Training Guide"), tr("REVIEW"), tr("Review the rules, features, and limitations of openpilot"));
connect(retrainingBtn, &ButtonControl::clicked, [=]() {
if (ConfirmationDialog::confirm(tr("Are you sure you want to review the training guide?"), tr("Review"), this)) {
emit reviewTrainingGuide();
}
});
addItem(retrainingBtn);
if (Hardware::TICI()) {
auto regulatoryBtn = new ButtonControl(tr("Regulatory"), tr("VIEW"), "");
@@ -258,44 +287,251 @@ DevicePanel::DevicePanel(SettingsWindow *parent) : ListWidget(parent) {
addItem(translateBtn);
// Delete driving footage button
auto deleteFootageBtn = new ButtonControl(tr("Delete Driving Data"), tr("DELETE"), tr("This button provides a swift and secure way to permanently delete all "
auto deleteDrivingDataBtn = new ButtonControl(tr("Delete Driving Data"), tr("DELETE"), tr("This button provides a swift and secure way to permanently delete all "
"stored driving footage and data from your device. Ideal for maintaining privacy or freeing up space.")
);
connect(deleteFootageBtn, &ButtonControl::clicked, [this]() {
connect(deleteDrivingDataBtn, &ButtonControl::clicked, [=]() {
if (!ConfirmationDialog::confirm(tr("Are you sure you want to permanently delete all of your driving footage and data?"), tr("Delete"), this)) return;
std::thread([&] {
deleteDrivingDataBtn->setValue(tr("Deleting footage..."));
std::system("rm -rf /data/media/0/realdata");
deleteDrivingDataBtn->setValue(tr("Deleted!"));
std::this_thread::sleep_for(std::chrono::seconds(3));
deleteDrivingDataBtn->setValue("");
}).detach();
});
addItem(deleteFootageBtn);
addItem(deleteDrivingDataBtn);
// Panda flashing button
auto flashPandaBtn = new ButtonControl(tr("Flash Panda"), tr("FLASH"), "Use this button to troubleshoot and update the Panda device's firmware.");
connect(flashPandaBtn, &ButtonControl::clicked, [this]() {
if (!ConfirmationDialog::confirm(tr("Are you sure you want to flash the Panda?"), tr("Flash"), this)) return;
QProcess process;
// Get Panda type
SubMaster &sm = *(uiState()->sm);
auto pandaStates = sm["pandaStates"].getPandaStates();
// Choose recovery script based on Panda type
if (pandaStates.size() != 0) {
auto pandaType = pandaStates[0].getPandaType();
bool isRedPanda = (pandaType == cereal::PandaState::PandaType::RED_PANDA ||
pandaType == cereal::PandaState::PandaType::RED_PANDA_V2);
QString recoveryScript = isRedPanda ? "./recover.sh" : "./recover.py";
// Run recovery script and flash Panda
process.setWorkingDirectory("/data/openpilot/panda/board");
process.start("/bin/sh", QStringList{"-c", recoveryScript});
process.waitForFinished();
auto flashPandaBtn = new ButtonControl(tr("Flash Panda"), tr("FLASH"), tr("Use this button to troubleshoot and update the Panda device's firmware."));
connect(flashPandaBtn, &ButtonControl::clicked, [=]() {
if (ConfirmationDialog::confirm(tr("Are you sure you want to flash the Panda?"), tr("Flash"), this)) {
std::thread([=]() {
flashPandaBtn->setValue(tr("Flashing..."));
QProcess process;
process.setWorkingDirectory("/data/openpilot/panda/board");
process.start("/bin/sh", QStringList{"-c", "./recover.py"});
process.waitForFinished();
process.start("/bin/sh", QStringList{"-c", "./flash.py"});
process.waitForFinished();
process.setWorkingDirectory("/data/openpilot/panda/tests");
process.start("/bin/sh", QStringList{"-c", "python reflash_internal_panda.py"});
process.waitForFinished();
Hardware::soft_reboot();
}).detach();
}
// Run the killall script as a redundancy
process.setWorkingDirectory("/data/openpilot/panda");
process.start("/bin/sh", QStringList{"-c", "pkill -f boardd; PYTHONPATH=.. python -c \"from panda import Panda; Panda().flash()\""});
process.waitForFinished();
Hardware::reboot();
});
addItem(flashPandaBtn);
// Reset toggle button
auto resetTogglesBtn = new ButtonControl(tr("Reset Toggle Settings"), tr("RESET"), tr("Reset your toggle settings back to default."));
connect(resetTogglesBtn, &ButtonControl::clicked, [=]() {
if (!ConfirmationDialog::confirm(tr("Are you sure you want to completely reset your toggle settings? This is irreversible!"), tr("Reset"), this)) return;
std::thread([&] {
resetTogglesBtn->setValue(tr("Resetting toggles..."));
std::system("find /data/params -type f ! -name 'FrogPilotDrives' ! -name 'FrogPilotMinutes' ! -name 'FrogPilotKilometers' -exec rm {} +");
std::system("find /persist/params -type f ! -name 'FrogPilotDrives' ! -name 'FrogPilotMinutes' ! -name 'FrogPilotKilometers' -exec rm {} +");
Hardware::soft_reboot();
}).detach();
});
addItem(resetTogglesBtn);
// Backup FrogPilot
std::vector<QString> frogpilotBackupOptions{tr("Backup"), tr("Delete"), tr("Restore")};
FrogPilotButtonsControl *frogpilotBackup = new FrogPilotButtonsControl(tr("FrogPilot Backups"), tr("Backup, delete, or restore your FrogPilot backups."), "", frogpilotBackupOptions);
connect(frogpilotBackup, &FrogPilotButtonsControl::buttonClicked, [=](int id) {
QDir backupDir("/data/backups");
if (id == 0) {
QString nameSelection = InputDialog::getText(tr("Name your backup"), this, "", false, 1);
if (!nameSelection.isEmpty()) {
std::thread([=]() {
frogpilotBackup->setValue(tr("Backing up..."));
std::string fullBackupPath = backupDir.absolutePath().toStdString() + "/" + nameSelection.toStdString();
std::ostringstream commandStream;
commandStream << "mkdir -p " << std::quoted(fullBackupPath)
<< " && rsync -av /data/openpilot/ " << std::quoted(fullBackupPath + "/");
std::string command = commandStream.str();
int result = std::system(command.c_str());
if (result == 0) {
std::cout << "Backup successful to " << fullBackupPath << std::endl;
frogpilotBackup->setValue(tr("Success!"));
std::this_thread::sleep_for(std::chrono::seconds(3));
frogpilotBackup->setValue("");
} else {
std::cerr << "Backup failed with error code: " << result << std::endl;
frogpilotBackup->setValue(tr("Failed..."));
std::this_thread::sleep_for(std::chrono::seconds(3));
frogpilotBackup->setValue("");
}
}).detach();
}
} else if (id == 1) {
QStringList backupNames = backupDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
QString selection = MultiOptionDialog::getSelection(tr("Select a backup to delete"), backupNames, "", this);
if (!selection.isEmpty()) {
if (!ConfirmationDialog::confirm(tr("Are you sure you want to delete this backup?"), tr("Delete"), this)) return;
std::thread([=]() {
frogpilotBackup->setValue(tr("Deleting..."));
QDir dirToDelete(backupDir.absoluteFilePath(selection));
if (dirToDelete.removeRecursively()) {
frogpilotBackup->setValue(tr("Deleted!"));
} else {
frogpilotBackup->setValue(tr("Failed..."));
}
std::this_thread::sleep_for(std::chrono::seconds(3));
frogpilotBackup->setValue("");
}).detach();
}
} else {
QStringList backupNames = backupDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
QString selection = MultiOptionDialog::getSelection(tr("Select a restore point"), backupNames, "", this);
if (!selection.isEmpty()) {
if (!ConfirmationDialog::confirm(tr("Are you sure you want to restore this version of FrogPilot?"), tr("Restore"), this)) return;
std::thread([=]() {
frogpilotBackup->setValue(tr("Restoring..."));
std::string sourcePath = backupDir.absolutePath().toStdString() + "/" + selection.toStdString();
std::string targetPath = "/data/safe_staging/finalized";
std::string consistentFilePath = targetPath + "/.overlay_consistent";
std::ostringstream commandStream;
commandStream << "rsync -av --delete --exclude='.overlay_consistent' " << std::quoted(sourcePath + "/") << " " << std::quoted(targetPath + "/");
std::string command = commandStream.str();
int result = std::system(command.c_str());
if (result == 0) {
std::cout << "Restore successful from " << sourcePath << " to " << targetPath << std::endl;
std::ofstream consistentFile(consistentFilePath);
if (consistentFile) {
consistentFile.close();
std::cout << ".overlay_consistent file created successfully." << std::endl;
} else {
std::cerr << "Failed to create .overlay_consistent file." << std::endl;
frogpilotBackup->setValue(tr("Failed..."));
std::this_thread::sleep_for(std::chrono::seconds(3));
frogpilotBackup->setValue("");
}
Hardware::soft_reboot();
} else {
std::cerr << "Restore failed with error code: " << result << std::endl;
}
}).detach();
}
}
});
addItem(frogpilotBackup);
// Backup toggles
std::vector<QString> toggleBackupOptions{tr("Backup"), tr("Delete"), tr("Restore")};
FrogPilotButtonsControl *toggleBackup = new FrogPilotButtonsControl(tr("Toggle Backups"), tr("Backup, delete, or restore your toggle backups."), "", toggleBackupOptions);
connect(toggleBackup, &FrogPilotButtonsControl::buttonClicked, [=](int id) {
QDir backupDir("/data/toggle_backups");
if (id == 0) {
QString nameSelection = InputDialog::getText(tr("Name your backup"), this, "", false, 1);
if (!nameSelection.isEmpty()) {
std::thread([=]() {
toggleBackup->setValue(tr("Backing up..."));
std::string fullBackupPath = backupDir.absolutePath().toStdString() + "/" + nameSelection.toStdString() + "/";
std::ostringstream commandStream;
commandStream << "mkdir -p " << std::quoted(fullBackupPath)
<< " && rsync -av /data/params/d/ " << std::quoted(fullBackupPath);
std::string command = commandStream.str();
int result = std::system(command.c_str());
if (result == 0) {
std::cout << "Backup successful to " << fullBackupPath << std::endl;
toggleBackup->setValue(tr("Success!"));
std::this_thread::sleep_for(std::chrono::seconds(3));
toggleBackup->setValue("");
} else {
std::cerr << "Backup failed with error code: " << result << std::endl;
toggleBackup->setValue(tr("Failed..."));
std::this_thread::sleep_for(std::chrono::seconds(3));
toggleBackup->setValue("");
}
}).detach();
}
} else if (id == 1) {
QStringList backupNames = backupDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
QString selection = MultiOptionDialog::getSelection(tr("Select a backup to delete"), backupNames, "", this);
if (!selection.isEmpty()) {
if (!ConfirmationDialog::confirm(tr("Are you sure you want to delete this backup?"), tr("Delete"), this)) return;
std::thread([=]() {
toggleBackup->setValue(tr("Deleting..."));
QDir dirToDelete(backupDir.absoluteFilePath(selection));
if (dirToDelete.removeRecursively()) {
toggleBackup->setValue(tr("Deleted!"));
} else {
toggleBackup->setValue(tr("Failed..."));
}
std::this_thread::sleep_for(std::chrono::seconds(3));
toggleBackup->setValue("");
}).detach();
}
} else {
QStringList backupNames = backupDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
QString selection = MultiOptionDialog::getSelection(tr("Select a restore point"), backupNames, "", this);
if (!selection.isEmpty()) {
if (!ConfirmationDialog::confirm(tr("Are you sure you want to restore this toggle backup?"), tr("Restore"), this)) return;
std::thread([=]() {
toggleBackup->setValue(tr("Restoring..."));
std::string sourcePath = backupDir.absolutePath().toStdString() + "/" + selection.toStdString() + "/";
std::string targetPath = "/data/params/d/";
std::ostringstream commandStream;
commandStream << "rsync -av --delete " << std::quoted(sourcePath) << " " << std::quoted(targetPath);
std::string command = commandStream.str();
int result = std::system(command.c_str());
if (result == 0) {
std::cout << "Restore successful from " << sourcePath << " to " << targetPath << std::endl;
toggleBackup->setValue(tr("Success!"));
paramsMemory.putBool("FrogPilotTogglesUpdated", true);
std::this_thread::sleep_for(std::chrono::seconds(3));
toggleBackup->setValue("");
paramsMemory.putBool("FrogPilotTogglesUpdated", false);
} else {
std::cerr << "Restore failed with error code: " << result << std::endl;
toggleBackup->setValue(tr("Failed..."));
std::this_thread::sleep_for(std::chrono::seconds(3));
toggleBackup->setValue("");
}
}).detach();
}
}
});
addItem(toggleBackup);
auto lockDoorsButton = new ButtonControl(tr("Lock Doors"), tr("LOCK"), tr("Use this button to lock the doors on Toyota/Lexus vehicles."));
connect(lockDoorsButton, &ButtonControl::clicked, [this]() {
QProcess *process = new QProcess(this);
QString scriptPath = "/data/openpilot/frogpilot/controls/lib/lock_doors.py";
QStringList arguments{"--lock"};
process->start("python3", QStringList() << scriptPath << arguments);
});
addItem(lockDoorsButton);
QObject::connect(uiState(), &UIState::offroadTransition, [=](bool offroad) {
for (auto btn : findChildren<ButtonControl *>()) {
btn->setEnabled(offroad);
@@ -311,6 +547,11 @@ DevicePanel::DevicePanel(SettingsWindow *parent) : ListWidget(parent) {
power_layout->addWidget(reboot_btn);
QObject::connect(reboot_btn, &QPushButton::clicked, this, &DevicePanel::reboot);
QPushButton *softreboot_btn = new QPushButton(tr("Soft Reboot"));
softreboot_btn->setObjectName("softreboot_btn");
power_layout->addWidget(softreboot_btn);
QObject::connect(softreboot_btn, &QPushButton::clicked, this, &DevicePanel::softreboot);
QPushButton *poweroff_btn = new QPushButton(tr("Power Off"));
poweroff_btn->setObjectName("poweroff_btn");
power_layout->addWidget(poweroff_btn);
@@ -321,6 +562,8 @@ DevicePanel::DevicePanel(SettingsWindow *parent) : ListWidget(parent) {
}
setStyleSheet(R"(
#softreboot_btn { height: 120px; border-radius: 15px; background-color: #e2e22c; }
#softreboot_btn:pressed { background-color: #ffe224; }
#reboot_btn { height: 120px; border-radius: 15px; background-color: #393939; }
#reboot_btn:pressed { background-color: #4a4a4a; }
#poweroff_btn { height: 120px; border-radius: 15px; background-color: #E22C2C; }
@@ -366,6 +609,18 @@ void DevicePanel::reboot() {
}
}
void DevicePanel::softreboot() {
if (!uiState()->engaged()) {
if (ConfirmationDialog::confirm(tr("Are you sure you want to soft reboot?"), tr("Soft Reboot"), this)) {
if (!uiState()->engaged()) {
params.putBool("DoSoftReboot", true);
}
}
} else {
ConfirmationDialog::alert(tr("Disengage to Soft Reboot"), this);
}
}
void DevicePanel::poweroff() {
if (!uiState()->engaged()) {
if (ConfirmationDialog::confirm(tr("Are you sure you want to power off?"), tr("Power Off"), this)) {
@@ -379,6 +634,15 @@ void DevicePanel::poweroff() {
}
}
void SettingsWindow::hideEvent(QHideEvent *event) {
closeParentToggle();
parentToggleOpen = false;
subParentToggleOpen = false;
previousScrollPosition = 0;
}
void SettingsWindow::showEvent(QShowEvent *event) {
setCurrentPanel(0);
}
@@ -396,33 +660,34 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) {
// setup two main layouts
sidebar_widget = new QWidget;
QVBoxLayout *sidebar_layout = new QVBoxLayout(sidebar_widget);
sidebar_layout->setMargin(0);
panel_widget = new QStackedWidget();
// close button
QPushButton *close_btn = new QPushButton(tr("← Back"));
close_btn->setStyleSheet(R"(
QPushButton {
font-size: 50px;
padding-bottom: 0px;
border 1px grey solid;
color: white;
border-radius: 25px;
background-color: #292929;
background: #292929;
font-size: 50px;
font-weight: 500;
}
QPushButton:pressed {
background-color: #3B3B3B;
color: #ADADAD;
}
)");
close_btn->setFixedSize(300, 125);
sidebar_layout->addSpacing(10);
sidebar_layout->addWidget(close_btn, 0, Qt::AlignRight);
QObject::connect(close_btn, &QPushButton::clicked, [this]() {
if (frogPilotTogglesOpen) {
frogPilotTogglesOpen = false;
this->closeParentToggle();
if (subParentToggleOpen) {
closeSubParentToggle();
subParentToggleOpen = false;
} else if (parentToggleOpen) {
closeParentToggle();
parentToggleOpen = false;
} else {
this->closeSettings();
closeSettings();
}
});
@@ -435,20 +700,19 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) {
QObject::connect(this, &SettingsWindow::expandToggleDescription, toggles, &TogglesPanel::expandToggleDescription);
QObject::connect(toggles, &TogglesPanel::updateMetric, this, &SettingsWindow::updateMetric);
// FrogPilotControlsPanel *frogpilotControls = new FrogPilotControlsPanel(this);
// QObject::connect(frogpilotControls, &FrogPilotControlsPanel::closeParentToggle, this, [this]() {frogPilotTogglesOpen = false;});
// QObject::connect(frogpilotControls, &FrogPilotControlsPanel::openParentToggle, this, [this]() {frogPilotTogglesOpen = true;});
FrogPilotControlsPanel *frogpilotControls = new FrogPilotControlsPanel(this);
QObject::connect(frogpilotControls, &FrogPilotControlsPanel::openSubParentToggle, this, [this]() {subParentToggleOpen = true;});
QObject::connect(frogpilotControls, &FrogPilotControlsPanel::openParentToggle, this, [this]() {parentToggleOpen = true;});
FrogPilotVisualsPanel *frogpilotVisuals = new FrogPilotVisualsPanel(this);
QObject::connect(frogpilotVisuals, &FrogPilotVisualsPanel::closeParentToggle, this, [this]() {frogPilotTogglesOpen = false;});
QObject::connect(frogpilotVisuals, &FrogPilotVisualsPanel::openParentToggle, this, [this]() {frogPilotTogglesOpen = true;});
QObject::connect(frogpilotVisuals, &FrogPilotVisualsPanel::openParentToggle, this, [this]() {parentToggleOpen = true;});
QList<QPair<QString, QWidget *>> panels = {
{tr("Device"), device},
{tr("Network"), new Networking(this)},
{tr("Toggles"), toggles},
{tr("Software"), new SoftwarePanel(this)},
// {tr("Controls"), frogpilotControls},
{tr("Controls"), frogpilotControls},
{tr("Navigation"), new FrogPilotNavigationPanel(this)},
{tr("Vehicles"), new FrogPilotVehiclesPanel(this)},
{tr("Visuals"), frogpilotVisuals},
@@ -484,21 +748,24 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) {
ScrollView *panel_frame = new ScrollView(panel, this);
panel_widget->addWidget(panel_frame);
if (name == tr("Controls") || name == tr("Visuals")) {
if (name == tr("Controls")) {
QScrollBar *scrollbar = panel_frame->verticalScrollBar();
QObject::connect(scrollbar, &QScrollBar::valueChanged, this, [this](int value) {
if (!frogPilotTogglesOpen) {
if (!parentToggleOpen) {
previousScrollPosition = value;
}
});
QObject::connect(scrollbar, &QScrollBar::rangeChanged, this, [this, panel_frame]() {
panel_frame->restorePosition(previousScrollPosition);
if (!parentToggleOpen) {
panel_frame->restorePosition(previousScrollPosition);
}
});
}
QObject::connect(btn, &QPushButton::clicked, [=, w = panel_frame]() {
closeParentToggle();
previousScrollPosition = 0;
btn->setChecked(true);
panel_widget->setCurrentWidget(w);