diff --git a/bcitemdelegate.cpp b/bcitemdelegate.cpp index ae45af4..33176aa 100644 --- a/bcitemdelegate.cpp +++ b/bcitemdelegate.cpp @@ -7,11 +7,16 @@ #include #include +#include +#include +#include + #include "bcitemdelegate.h" #include "bcvalue.h" #include "qapplication.h" + BCItemDelegate::BCItemDelegate(QListView *view) : QStyledItemDelegate(view), _view{view} { @@ -136,28 +141,9 @@ QSize BCItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelI } -void BCItemDelegate::onHighlightRow(int row) -{ - qDebug() << " --- should highlight: " << row; - _highlightedRow = row; - - // Trigger Repaint der View (damit der Rahmen sofort erscheint) - _view->viewport()->update(); - - // Timer: Nach 1 Sekunde wieder ausschalten (Temporär) - QTimer::singleShot(1000, this, [=, this]() - { - if(_highlightedRow == row) - { // Nur zurücksetzen, wenn es noch dieselbe Zeile ist - _highlightedRow = -1; - if (_view) - _view->viewport()->update(); - } - }); -} - void BCItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { + /* // 1. Standard-Zeichnen (Text, Hintergrund, Selection) durchführen QStyledItemDelegate::paint(painter, option, index); @@ -179,7 +165,122 @@ void BCItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option painter->restore(); } + */ + + QStyledItemDelegate::paint(painter, option, index); + + /* + if (index.row() == _highlightedRow && _opacity > 0.0) + { + painter->save(); + + qDebug() << " --- is highlight: " << index.row(); + + QColor highlightColor( 0xFF9800 ); + highlightColor.setAlphaF(_opacity); + + QPen pen(highlightColor, 3); + painter->setPen(pen); + painter->setBrush(Qt::NoBrush); + painter->drawRoundedRect(option.rect.adjusted(2, 2, -2, -2), 8, 8); + + painter->restore(); + } + +*/ + + int row = index.row(); + + if (m_rowOpacities.contains(row)) + { + qreal opacity = m_rowOpacities.value(row); + if (opacity > 0.01) { + painter->save(); + painter->setOpacity(opacity); + painter->fillRect(option.rect, QColor(255, 140, 0, 120)); + painter->restore(); + } + } + } + + +void BCItemDelegate::onHighlightRow(int row) +{ + qDebug() << " --- should highlight: " << row; + + // Alte Animation für diese Zeile stoppen falls vorhanden + if (m_rowAnimations.contains(row)) + { + m_rowAnimations[row]->stop(); + m_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% + } + + m_rowOpacities[row] = opacity; + updateRow(row); + }); + + connect(anim, &QVariantAnimation::finished, this, [this, row, anim]() + { + m_rowOpacities.remove(row); + m_rowAnimations.remove(row); + updateRow(row); + anim->deleteLater(); + }); + + m_rowAnimations[row] = anim; + anim->start(QAbstractAnimation::DeleteWhenStopped); +} + +// Optional: alle Highlights sofort clearen +void BCItemDelegate::clearAllHighlights() +{ + for(auto* anim : std::as_const(m_rowAnimations)) + { + anim->stop(); + anim->deleteLater(); + } + m_rowAnimations.clear(); + m_rowOpacities.clear(); + + if (_view) + { + _view->viewport()->update(); + } +} + +void BCItemDelegate::updateRow(int row) +{ + if (_view && row >= 0) { + QModelIndex idx = _view->model()->index(row, 0); + QRect rect = _view->visualRect(idx); + if (!rect.isEmpty()) { + _view->viewport()->update(rect); + } + } +} + QString BCItemDelegate::formatDisplayString(const QModelIndex &index) const { if (!index.isValid()) diff --git a/bcitemdelegate.h b/bcitemdelegate.h index de7726a..0803fbb 100644 --- a/bcitemdelegate.h +++ b/bcitemdelegate.h @@ -4,11 +4,14 @@ #include +class QPropertyAnimation; +class QVariantAnimation; class QListView; class BCItemDelegate : public QStyledItemDelegate { Q_OBJECT + Q_PROPERTY(qreal highlightOpacity READ highlightOpacity WRITE setHighlightOpacity) public: @@ -25,17 +28,45 @@ public: QSize sizeHint(const QStyleOptionViewItem &option,const QModelIndex &index) const override; void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; + qreal highlightOpacity() const + { + return _opacity; + } + + void setHighlightOpacity(qreal opacity) + { + _opacity = opacity; + //qDebug() << " --- opa: " << opacity; + emit viewUpdateNeeded(); + } + + void clearAllHighlights(); + public slots: void onHighlightRow(int row); + +signals: + + void viewUpdateNeeded(); + private: + void updateRow(int row); QString formatDisplayString(const QModelIndex &index) const; int _highlightedRow{-1}; - QListView* _view{nullptr}; + qreal _opacity{1.0}; + QListView* _view{}; + QPropertyAnimation *_animation{}; +private: + + + + QHash m_rowOpacities; + QHash m_rowAnimations; }; diff --git a/bcmainwindow.cpp b/bcmainwindow.cpp index 4b090da..1714ff6 100644 --- a/bcmainwindow.cpp +++ b/bcmainwindow.cpp @@ -31,6 +31,8 @@ #include #include + + BCMainWindow::BCMainWindow(QWidget *parent) : QMainWindow(parent) { @@ -40,12 +42,26 @@ BCMainWindow::BCMainWindow(QWidget *parent) if( model) _valueView->setModel( *model ); - BCItemDelegate* delegate = new BCItemDelegate( _valueView); - _valueView->setItemDelegate( delegate ); + BCItemDelegate* _delegate = new BCItemDelegate( _valueView); + //_delegate = new AnimatedDelegate(_valueView ); + _valueView->setItemDelegate( _delegate ); - connect( &_valueManager, &BCValueManager::valueTouched, delegate, &BCItemDelegate::onHighlightRow ); + + // Verwendung: + + connect(_delegate, &BCItemDelegate::viewUpdateNeeded, _valueView->viewport(), QOverload<>::of(&QWidget::update)); + + //_valueView->setItemDelegate(_delegate); + + // Highlight mit Fade-Out: + _delegate->onHighlightRow(2); // 2 Sekunden Fade + + // besser: model::emit dataChanged + // also: emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole, ValueRole}); + connect( &_valueManager, &BCValueManager::valueTouched, _delegate, &BCItemDelegate::onHighlightRow ); connect( _connectButton, &QPushButton::clicked, &_valueManager, &BCValueManager::onToggleConnectionState ); connect( _syncButton, &QPushButton::clicked, &_valueManager, &BCValueManager::onSyncFromDevice ); + } BCMainWindow::~BCMainWindow() diff --git a/bcmainwindow.h b/bcmainwindow.h index 53cd15a..6452ed7 100644 --- a/bcmainwindow.h +++ b/bcmainwindow.h @@ -36,7 +36,7 @@ #include - +class AnimatedDelegate; class BCMainWindow : public QMainWindow, public Ui_BCMainWindow { @@ -54,6 +54,7 @@ public slots: protected: BCValueManager _valueManager; + AnimatedDelegate* _delegate; }; diff --git a/bcvaluemanager.cpp b/bcvaluemanager.cpp index d79f3ea..668fd24 100644 --- a/bcvaluemanager.cpp +++ b/bcvaluemanager.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include @@ -42,6 +43,16 @@ using namespace Qt::StringLiterals; +void processEventsFor(int milliseconds) +{ + QElapsedTimer timer; + timer.start(); + + while (timer.elapsed() < milliseconds) { + QApplication::processEvents(QEventLoop::AllEvents, 50); + } +} + BCValueManager::BCValueManager(QObject *parent) : QObject(parent) { @@ -54,11 +65,11 @@ BCValueManager::BCValueManager(QObject *parent) // 4. Verbindungen herstellen (Signal/Slot über Thread-Grenzen) // A) Befehl senden (Manager -> Runner) - connect(this, &BCValueManager::newCommandArrived, &_transmitter, &BCTransmitter::enqueueValue); + connect(this, &BCValueManager::valueCreated, &_transmitter, &BCTransmitter::enqueueValue); // B) Ergebnisse empfangen (Runner -> Manager) - connect(&_transmitter, &BCTransmitter::commandFinished, this, &BCValueManager::onCommandFinished); - connect(&_transmitter, &BCTransmitter::messageLogged, this, &BCValueManager::onRunnerMessage); + //connect(&_transmitter, &BCTransmitter::commandFinished, this, &BCValueManager::onCommandFinished); + //connect(&_transmitter, &BCTransmitter::messageLogged, this, &BCValueManager::onRunnerMessage); // C) Aufräumen: Wenn Thread endet, lösche den Runner connect(&_worker, &QThread::finished, &_transmitter, &QObject::deleteLater); @@ -77,14 +88,6 @@ BCValueManager::~BCValueManager() } -void BCValueManager::transmitValue(const BCValue& value) -{ - // Wir rufen keine Methode am Runner direkt auf! - // Wir emitten ein Signal. Qt kümmert sich um den Thread-Wechsel. - - emit newCommandArrived(value); - -} void BCValueManager::onCommandFinished(int id, bool success) { @@ -144,14 +147,25 @@ void BCValueManager::onSyncFromDevice() { BCValueList& currentList = _valueModels[_currentDeviceID]->getValueList(); - for( BCValue& value : currentList ) + + BCValue& value = currentList[4]; + + for( const BCValue& value : currentList ) { qDebug() << " --- value: " << value.label; - _transmitter.enqueueValue( value ); + + // statt '_transmitter.enqueueValue( value )' entkoppeln + // wir das eleganter über emit valueCreated() + + //_transmitter.enqueueValue( value ); + emit valueCreated(value); + emit valueTouched( value.rowInModel ); - QApplication::processEvents(); + + processEventsFor(500); + //QApplication::processEvents(); // Thread schlafen lassen (Simulation einer blockierenden Operation) - QThread::msleep(500); + //QThread::msleep(500); } diff --git a/bcvaluemanager.h b/bcvaluemanager.h index 1014784..26c0f54 100644 --- a/bcvaluemanager.h +++ b/bcvaluemanager.h @@ -54,9 +54,6 @@ public: BCValue makeValue(BCDevice::ID deviceID, const BCValueParams& params ); - // Public API für die Applikation - void transmitValue( const BCValue& value); - public slots: void loadBikeData(); @@ -67,7 +64,7 @@ public slots: signals: // Internes Signal, um Daten an den Worker Thread zu senden - void newCommandArrived(const BCValue& cmd); + void valueCreated(const BCValue& cmd); //void valuedTouched(const BCValue& cmd); void valueTouched(int rowInModel );