/*************************************************************************** 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 "bcanimateddelegate.h" #include "bcsliderstyle.h" BCAnimatedDelegate::BCAnimatedDelegate(const BCValueList& valueList, QTableView* view) : QStyledItemDelegate{view}, _valueList{valueList}, _view{view} { } void BCAnimatedDelegate::setEditorData(QWidget *editor, const QModelIndex& index) const { /* // Daten vom Model in den Editor laden const BCValue& bc = *index.data(Qt::EditRole).value(); QSlider *slider = editor->findChild("slider"); QLabel *lblUnit = editor->findChild("lblUnit"); if (slider && lblUnit) { bool olDriverState = slider->blockSignals(true); slider->setValue(bc.formattedValue.toInt()); slider->blockSignals(olDriverState); lblUnit->setText(QString("%1 %2").arg(bc.formattedValue.toInt()).arg( "mm3")); } else { QStyledItemDelegate::setEditorData(editor, index); } */ } void BCAnimatedDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex& index) const { qDebug() << "... SHUHU!"; // Daten vom Editor zurück ins Model speichern (Beim Schließen) QSlider *slider = editor->findChild("slider"); if (slider) { int value = slider->value(); model->setData(index, value, Qt::EditRole); } else { QStyledItemDelegate::setModelData(editor, model, index); } } void BCAnimatedDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex& index) const { Q_UNUSED(index) /* QRect barRect = option.rect.adjusted(option.rect.width() - 55, option.rect.height() / 2 - 2, -8, -option.rect.height() / 2 + 2); */ QRect sliderRect = option.rect.adjusted( option.rect.width() - 125, // Von rechts: 115px (Breite der Progress Bar) 0, // Oben: kein Offset -8, // Rechts: 8px Padding 0 // Unten: kein Offset ); editor->setGeometry(sliderRect); // Slider nur über Progress Bar //editor->setGeometry(option.rect); // __fix! //editor->setGeometry(option.rect); } QSize BCAnimatedDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex& index) const { return QStyledItemDelegate::sizeHint(option,index); /* QStyleOptionViewItem opt = option; initStyleOption(&opt, index); opt.text = formatDisplayString(index); QStyle *style = opt.widget ? opt.widget->style() : QApplication::style(); return style->sizeFromContents(QStyle::CT_ItemViewItem, &opt, QSize(), opt.widget); */ } void BCAnimatedDelegate::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()); //qDebug() << " --- paintSLider: " << bcValue.label << " type: " << (int)bcValue.valueType << " flags:" << bcValue.valueFlags.toInt() << " RO: " << bcValue.isReadOnly(); if( !bcValue.isReadOnly()) paintSliderIndicator(painter,option,bcValue); } if(_rowOpacities.contains(row)) paintHighlightRow(painter,option,index.row()); } } QWidget* BCAnimatedDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex& index) const { const BCValue& bcValue = *(_valueList[ index.row()].get()); Q_UNUSED(option) Q_UNUSED(index) auto* slider = new QSlider(Qt::Horizontal, parent); slider->setRange(0, 100); slider->setSingleStep(1); slider->setPageStep(10); slider->setStyle(new FluentSliderStyle()); // Signal für sofortige Updates connect(slider, &QSlider::valueChanged, this, [this, slider]() { // Commit data sofort bei Änderung emit const_cast(this)->commitData(slider); }); return slider; } void BCAnimatedDelegate::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 4px QRect itemRect = option.rect.adjusted(3, 3, -3, -3); // 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(); } /** * @brief Zeichnet eine passiven Slider, um den Wertebereich des übergebenen BCValue anzuzeigen. * @param painter * @param option * @param bcValue */ void BCAnimatedDelegate::paintSliderIndicator(QPainter* painter, const QStyleOptionViewItem& option, const BCValue& bcValue) const { // Text und kleiner Slider-Indikator zeichnen painter->save(); painter->setRenderHint(QPainter::Antialiasing); /* QRect barRect = option.rect.adjusted(option.rect.width() - 130, option.rect.height() / 2 - 2, -8, -option.rect.height() / 2 + 2); */ QRect barRect = option.rect.adjusted(option.rect.width() - 130, option.rect.height() / 2 + 1, -8, -option.rect.height() / 2 - 3); double ratio = bcValue.calcRatio(); if( ratio) { // Mini Progress Bar painter->setPen(Qt::NoPen); painter->setBrush(QColor(0xE0E0E0)); painter->drawRoundedRect(barRect, 2, 2); QRect fillRect = barRect; fillRect.setWidth(barRect.width() * ratio); painter->setBrush(QColor(0x0078D4)); painter->drawRoundedRect(fillRect, 2, 2); } painter->restore(); /// ------- /* // Hintergrund if (option.state & QStyle::State_Selected) { painter->fillRect(option.rect, option.palette.highlight()); } else { QColor bcColor = option.palette.color(QPalette::Base); painter->fillRect(option.rect, bcColor); } */ /* // baby-Slider-Indikator zeichnen // Anteil zwischen min und max berechnen double ratio = bcValue.calcRatio(); if( !ratio) return; painter->save(); painter->setRenderHint(QPainter::Antialiasing); QRect barRect = option.rect.adjusted ( 8, option.rect.height() / 2 - 2, -8, -option.rect.height() / 2 + 2 ); // Mini Progress Bar painter->setPen(Qt::NoPen); QColor disabledText = option.palette.color(QPalette::Disabled, QPalette::Text); painter->setBrush(disabledText); painter->drawRoundedRect(barRect, 2, 2); barRect.setWidth(barRect.width() * ratio ); painter->setBrush(QColor(0x0078D4)); //painter->setBrush(Qt::green); //painter->setBrush( ); painter->drawRoundedRect(barRect, 2, 2); painter->restore(); */ } /** * @brief Startet die Animation für die übergebene Zeile * @param row */ void BCAnimatedDelegate::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 BCAnimatedDelegate::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 BCAnimatedDelegate::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); } } }