/*************************************************************************** BionxControl © 2025 -2026 christoph holzheuer christoph.holzheuer@gmail.com Using: mhs_can_drv.c © 2011 - 2023 by MHS-Elektronik GmbH & Co. KG, Germany Klaus Demlehner, klaus@mhs-elektronik.de @see www.mhs-elektronik.de Based on Bionx data type descriptions from: BigXionFlasher USB V 0.2.4 rev. 97 © 2011-2013 by Thomas Koenig @see www.bigxionflasher.org Bionx Bike Info © 2018 Thorsten Schmidt (tschmidt@ts-soft.de) @see www.ts-soft.de This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. @see https://github.com/bikemike/bionx-bikeinfo ***************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include BCValueDelegate::BCValueDelegate(const BCValueList& valueList, BCDeviceView* view) : QStyledItemDelegate{view}, _valueList{valueList}, _view{view} { } QWidget* BCValueDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex& index) const { Q_UNUSED(option) Q_UNUSED(index) const BCValue& bcValue = *(_valueList[ index.row()].get()); int value,min,max; bool hasData = bcValue.valuesForSlider( value, min, max ); if( !hasData ) return nullptr; qDebug() << " --- Create Editor: " << bcValue.label() << " value: " << value << " min: " << min << " max: " << max << " ratio:" << bcValue.calcMinMaxRatio()*100.0 << '%'; auto* valueEditor = new BCValueEditor(parent); valueEditor->setValue( value ); valueEditor->setRange( min, max ); // Signal für sofortige Updates connect(valueEditor, &BCValueEditor::valueChanged, this, [this, valueEditor]() { // Commit data sofort bei Änderung emit const_cast(this)->commitData(valueEditor); }); // Signal für sofortige Updates connect(valueEditor, &BCValueEditor::valueCommited, this, [this, valueEditor](int newValue) { qDebug() << " --- value set:" << newValue; // Commit data sofort bei Änderung //emit const_cast(this)->commitData(valueEditor); }); return valueEditor; } void BCValueDelegate::setEditorData(QWidget *editor, const QModelIndex& index) const { Q_UNUSED(editor) Q_UNUSED(index) // tue nix. } void BCValueDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex& index) const { if( index.column() == 1) { // Daten vom Editor zurück ins Model speichern (Beim Schließen) BCValueEditor* slider = qobject_cast(editor); if (slider) { int value = slider->getValue(); model->setData(index, value, Qt::EditRole); } return; } QStyledItemDelegate::setModelData(editor, model, index); } void BCValueDelegate::paint(QPainter *painter, const QStyleOptionViewItem& option, const QModelIndex& index) const { // Standard-Zeichnen (Text, Hintergrund, Selection) durchführen QStyledItemDelegate::paint(painter, option, index); int row = index.row(); if( index.column() == 1 ) { if( row>-1 && row <= _valueList.size() ) { const BCValue& bcValue = *(_valueList[ index.row()].get()); if( !bcValue.isReadOnly() ) { if( bcValue.valueType() == BCValue::ValueType::Bool ) paintButtonIndicator(painter,option,bcValue); else paintSliderIndicator(painter,option,bcValue); } } if(_rowOpacities.contains(row)) paintHighlightRow(painter,option,index.row()); } } void BCValueDelegate::paintHighlightRow(QPainter* painter, const QStyleOptionViewItem& option, int row) const { painter->save(); painter->setRenderHint(QPainter::Antialiasing); qreal opacity =_rowOpacities.value(row); painter->setOpacity(opacity); // Margin von 2px const int m = 3; QRect itemRect = option.rect.adjusted(m,m,-m,-m); // Border (2px solid #2196F3) // oranger rahmen QPen borderPen( QColor(0xFF8C00), 1); painter->setPen(borderPen); painter->setBrush(Qt::NoBrush); // highlight background //QColor highlightColor = option.palette.highlight().color(); //highlightColor.setAlphaF(0.3); // 0.0 bis 1.0 (float ist oft lesbarer) //painter->fillRect(option.rect, highlightColor); painter->drawRoundedRect(itemRect, 2, 2); painter->restore(); } void BCValueDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex& index) const { Q_UNUSED(index) QRect sliderRect = option.rect.adjusted( option.rect.width() - cTextBlockOffset, // Von rechts: cTextBlockOffset (==130) px (Breite der Progress Bar) 0, // Oben: kein Offset -cPaddingRight, // Rechts: 8px Padding 0 // Unten: kein Offset ); editor->setGeometry(sliderRect); // Slider nur über Progress Bar } void BCValueDelegate::paintButtonIndicator(QPainter* painter, const QStyleOptionViewItem& option, const BCValue& bcValue) const { } void BCValueDelegate::paintSliderIndicator(QPainter* painter, const QStyleOptionViewItem& option, const BCValue& bcValue) const { double ratio = bcValue.calcMinMaxRatio(); qDebug() << " --- paint: " << bcValue.label() << ":" << ratio; // Text und kleiner Slider-Indikator zeichnen painter->save(); painter->setRenderHint(QPainter::Antialiasing); int adjX = option.rect.width() - cTextBlockOffset; // Mini Progress Bar: der Gesamtbereich QRect barRect = option.rect.adjusted( adjX, 12, -10-24, -12 ); painter->setPen(Qt::NoPen); painter->setBrush(QColor(0xE0E0E0)); painter->drawRoundedRect(barRect, 2, 2); // Mini Progress Bar: der Wertebereich barRect.setWidth( ratio * barRect.width() ); painter->setBrush(QColor(0x0078D4)); painter->drawRoundedRect(barRect, 2, 2); painter->restore(); } /** * @brief Zeichnet eine passiven Slider, um den möglichen Wertebereich des übergebenen BCValue anzuzeigen. */ void BCValueDelegate::paintSliderIndicatorXX(QPainter* painter, const QStyleOptionViewItem& option, const BCValue& bcValue) const { // Text und kleiner Slider-Indikator zeichnen painter->save(); painter->setRenderHint(QPainter::Antialiasing); int newX = option.rect.width() - cTextBlockOffset; QRect barRect = option.rect.adjusted( newX, option.rect.height() / 2 + 1, - option.rect.width() + cTextBlockOffset + 117 - 18, -option.rect.height() / 2 - 3); double ratio = bcValue.calcMinMaxRatio(); qDebug() << " --- paint: " << bcValue.label() << ":" << ratio; if(ratio) { // Mini Progress Bar: der Gesamtbereich painter->setPen(Qt::NoPen); painter->setBrush(QColor(0xE0E0E0)); //QColor disabledText = option.palette.color(QPalette::Disabled, QPalette::Text); //painter->setBrush(disabledText); painter->drawRoundedRect(barRect, 2, 2); QRect fillRect = barRect; // ein wert darf näturlich nie über 100% eingestellt werden ratio = qBound(0.0,ratio,1.0); fillRect.setWidth(barRect.width() * ratio); painter->setBrush(QColor(0x0078D4)); painter->drawRoundedRect(fillRect, 2, 2); } painter->restore(); } /** * @brief Startet die Animation für die übergebene Zeile * @param row */ void BCValueDelegate::onHighlightRow(int row) { // Alte Animation für diese Zeile stoppen falls vorhanden if (_rowAnimations.contains(row)) { _rowAnimations[row]->stop(); _rowAnimations[row]->deleteLater(); } // QVariantAnimation ist flexibler als QPropertyAnimation auto* anim = new QVariantAnimation(this); anim->setDuration(800); anim->setStartValue(0.0); anim->setEndValue(1.0); // Custom Easing für Fade-in/out Effekt anim->setEasingCurve(QEasingCurve::OutQuad); connect(anim, &QVariantAnimation::valueChanged, this, [this, row](const QVariant& value) { qreal progress = value.toReal(); qreal opacity; // Schnelles Fade-in (20%), langsames Fade-out (80%) if (progress < 0.2) { opacity = progress * 5.0; // 0->1 in 20% } else { opacity = 1.0 - ((progress - 0.2) / 0.8); // 1->0 in 80% } _rowOpacities[row] = opacity; updateRow(row); }); connect(anim, &QVariantAnimation::finished, this, [this, row, anim]() { _rowOpacities.remove(row); _rowAnimations.remove(row); updateRow(row); anim->deleteLater(); }); _rowAnimations[row] = anim; anim->start(QAbstractAnimation::DeleteWhenStopped); } /** * @brief Sopt alle gerade laufenden Animationen */ void BCValueDelegate::clearAllHighlights() { for(auto* anim : std::as_const(_rowAnimations)) { anim->stop(); anim->deleteLater(); } _rowAnimations.clear(); _rowOpacities.clear(); if (_view) { _view->viewport()->update(); } } /** * @brief Zeichnet die übegebene Zeile neu. * @param row */ void BCValueDelegate::updateRow(int row) { if (_view && _view->model() && row >= 0) { QModelIndex idx = _view->model()->index(row,1); QRect rect = _view->visualRect(idx); if (!rect.isEmpty()) { _view->viewport()->update(rect); } } }