From 0a2fd7bd61186cab6c0937a821b1e2ee48a1fbdb Mon Sep 17 00:00:00 2001 From: Nayan Date: Thu, 10 Apr 2025 15:20:41 -0400 Subject: [PATCH] UI: Update `AbstractControlSP_SELECTOR` and `OptionControlSP` (#800) * controls * Adjust label width dynamically based on layout type. Updated the label's fixed width to be conditional on the layout type, improving adaptability for different inline layouts. Additionally, corrected indentation in the width calculation loop for consistency. * Refactor OptionControlSP to improve parameter value handling and encapsulate logic in dedicated methods * Refactor getParamValue to return an integer and ensure value is updated correctly in button click handler * Trying to unify a bit the logic. still WIP * Reducing a bit the change footprint * Refactor spacing item handling to prevent duplicate insertion and improve layout management --------- Co-authored-by: DevTekVE --- .../ui/sunnypilot/qt/widgets/controls.cc | 11 +--- selfdrive/ui/sunnypilot/qt/widgets/controls.h | 63 +++++++++++++++---- 2 files changed, 53 insertions(+), 21 deletions(-) diff --git a/selfdrive/ui/sunnypilot/qt/widgets/controls.cc b/selfdrive/ui/sunnypilot/qt/widgets/controls.cc index d02efc0a8..09ab5417e 100644 --- a/selfdrive/ui/sunnypilot/qt/widgets/controls.cc +++ b/selfdrive/ui/sunnypilot/qt/widgets/controls.cc @@ -132,10 +132,7 @@ AbstractControlSP_SELECTOR::AbstractControlSP_SELECTOR(const QString &title, con if (isVisible && spacingItem) { main_layout->removeItem(spacingItem); - delete spacingItem; - spacingItem = nullptr; - } else if (!isVisible && spacingItem == nullptr) { - spacingItem = new QSpacerItem(44, 44, QSizePolicy::Minimum, QSizePolicy::Fixed); + } else if (!isVisible && spacingItem != nullptr && main_layout->indexOf(spacingItem) == -1) { main_layout->insertItem(main_layout->indexOf(description), spacingItem); } } @@ -145,8 +142,7 @@ AbstractControlSP_SELECTOR::AbstractControlSP_SELECTOR(const QString &title, con } main_layout->addLayout(hlayout); - if (!desc.isEmpty() && spacingItem == nullptr) { - spacingItem = new QSpacerItem(44, 44, QSizePolicy::Minimum, QSizePolicy::Fixed); + if (!desc.isEmpty() && spacingItem != nullptr && main_layout->indexOf(spacingItem) == -1) { main_layout->insertItem(main_layout->count(), spacingItem); } @@ -166,8 +162,7 @@ void AbstractControlSP_SELECTOR::hideEvent(QHideEvent *e) { description->hide(); } - if (spacingItem == nullptr) { - spacingItem = new QSpacerItem(44, 44, QSizePolicy::Minimum, QSizePolicy::Fixed); + if (spacingItem != nullptr && main_layout->indexOf(spacingItem) == -1) { main_layout->insertItem(main_layout->indexOf(description), spacingItem); } } diff --git a/selfdrive/ui/sunnypilot/qt/widgets/controls.h b/selfdrive/ui/sunnypilot/qt/widgets/controls.h index f46031088..bb3cec88f 100644 --- a/selfdrive/ui/sunnypilot/qt/widgets/controls.h +++ b/selfdrive/ui/sunnypilot/qt/widgets/controls.h @@ -96,11 +96,10 @@ class AbstractControlSP_SELECTOR : public AbstractControlSP { Q_OBJECT protected: + QSpacerItem *spacingItem = new QSpacerItem(44, 44, QSizePolicy::Minimum, QSizePolicy::Fixed); AbstractControlSP_SELECTOR(const QString &title, const QString &desc = "", const QString &icon = "", QWidget *parent = nullptr); void hideEvent(QHideEvent *e) override; -private: - QSpacerItem *spacingItem = nullptr; }; // widget to display a value @@ -417,14 +416,29 @@ class OptionControlSP : public AbstractControlSP_SELECTOR { Q_OBJECT private: + bool isInlineLayout; + QHBoxLayout *optionSelectorLayout = isInlineLayout ? new QHBoxLayout() : hlayout; + struct MinMaxValue { int min_value; int max_value; }; + + int getParamValue() { + const auto param_value = QString::fromStdString(params.get(key)); + const auto result = valueMap != nullptr ? valueMap->key(param_value) : param_value; + return result.toInt(); + } + + // Although the method is not static, and thus has access to the value property, I prefer to be explicit about the value. + void setParamValue(const int new_value) { + const auto value_str = valueMap != nullptr ? valueMap->value(QString::number(new_value)) : QString::number(new_value); + params.put(key, value_str.toStdString()); + } public: OptionControlSP(const QString ¶m, const QString &title, const QString &desc, const QString &icon, - const MinMaxValue &range, const int per_value_change = 1) : _title(title), AbstractControlSP_SELECTOR(title, desc, icon) { + const MinMaxValue &range, const int per_value_change = 1, const bool inline_layout = false, const QMap *valMap = nullptr) : AbstractControlSP_SELECTOR(title, desc, icon, nullptr), _title(title), valueMap(valMap), isInlineLayout(inline_layout) { const QString style = R"( QPushButton { border-radius: 20px; @@ -444,14 +458,27 @@ public: } )"; + if (inline_layout) { + optionSelectorLayout->setMargin(0); + optionSelectorLayout->setSpacing(0); + if (!title.isEmpty()) { + main_layout->removeWidget(title_label); + hlayout->addWidget(title_label, 1); + } + if (spacingItem != nullptr && main_layout->indexOf(spacingItem) != -1) { + main_layout->removeItem(spacingItem); + spacingItem = nullptr; + } + } + label.setStyleSheet(label_enabled_style); - label.setFixedWidth(300); + label.setFixedWidth(inline_layout ? 350 : 300); label.setAlignment(Qt::AlignCenter); const std::vector button_texts{"-", "+"}; key = param.toStdString(); - value = atoi(params.get(key).c_str()); + value = getParamValue(); button_group = new QButtonGroup(this); button_group->setExclusive(true); @@ -459,19 +486,18 @@ public: QPushButton *button = new QPushButton(button_texts[i], this); button->setStyleSheet(style + ((i == 0) ? "QPushButton { text-align: left; }" : "QPushButton { text-align: right; }")); - hlayout->addWidget(button, 0, ((i == 0) ? Qt::AlignLeft : Qt::AlignRight) | Qt::AlignVCenter); + optionSelectorLayout->addWidget(button, 0, ((i == 0) ? Qt::AlignLeft : Qt::AlignRight) | Qt::AlignVCenter); if (i == 0) { - hlayout->addWidget(&label, 0, Qt::AlignCenter); + optionSelectorLayout->addWidget(&label, 0, Qt::AlignCenter); } button_group->addButton(button, i); QObject::connect(button, &QPushButton::clicked, [=]() { int change_value = (i == 0) ? -per_value_change : per_value_change; - key = param.toStdString(); - value = atoi(params.get(key).c_str()); + value = getParamValue(); // in case it changed externally, we need to get the latest value. value += change_value; value = std::clamp(value, range.min_value, range.max_value); - params.put(key, QString::number(value).toStdString()); + setParamValue(value); button_group->button(0)->setEnabled(!(value <= range.min_value)); button_group->button(1)->setEnabled(!(value >= range.max_value)); @@ -484,7 +510,13 @@ public: }); } - hlayout->setAlignment(Qt::AlignLeft); + optionSelectorLayout->setAlignment(Qt::AlignLeft); + if (isInlineLayout) { + QFrame *container = new QFrame; + container->setLayout(optionSelectorLayout); + container->setStyleSheet("background-color: #393939; border-radius: 20px;"); + hlayout->addWidget(container); + } } void setUpdateOtherToggles(bool _update) { @@ -506,6 +538,10 @@ public: protected: void paintEvent(QPaintEvent *event) override { + if (isInlineLayout) { + return; + } + QPainter p(this); p.setRenderHint(QPainter::Antialiasing); @@ -513,8 +549,8 @@ protected: int w = 0; int h = 150; - for (int i = 0; i < hlayout->count(); ++i) { - QWidget *widget = qobject_cast(hlayout->itemAt(i)->widget()); + for (int i = 0; i < optionSelectorLayout->count(); ++i) { + QWidget *widget = qobject_cast(optionSelectorLayout->itemAt(i)->widget()); if (widget) { w += widget->width(); } @@ -544,6 +580,7 @@ private: std::map option_label = {}; bool request_update = false; QString _title = ""; + const QMap *valueMap; const QString label_enabled_style = "font-size: 50px; font-weight: 450; color: #FFFFFF;"; const QString label_disabled_style = "font-size: 50px; font-weight: 450; color: #5C5C5C;";