From a1752182c5f06685df296e2b28f18994b68d82ce Mon Sep 17 00:00:00 2001 From: Teo Mrnjavac Date: Mon, 13 Aug 2012 23:30:50 +0200 Subject: [PATCH] Implemented connection status management in the Accounts widget. --- src/CMakeLists.txt | 1 + src/widgets/AccountListWidget.cpp | 2 + src/widgets/AccountWidget.cpp | 80 ++++++++++--- src/widgets/AccountWidget.h | 29 +++-- src/widgets/SlideSwitchButton.cpp | 190 ++++++++++++++++++++++++++++++ src/widgets/SlideSwitchButton.h | 72 +++++++++++ 6 files changed, 348 insertions(+), 26 deletions(-) create mode 100644 src/widgets/SlideSwitchButton.cpp create mode 100644 src/widgets/SlideSwitchButton.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index cd0cdd914..d9c2338d0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -80,6 +80,7 @@ SET( tomahawkSourcesGui ${tomahawkSourcesGui} widgets/AccountWidget.cpp widgets/AccountsPopupWidget.cpp widgets/AccountsToolButton.cpp + widgets/SlideSwitchButton.cpp widgets/UnstyledFrame.cpp ) diff --git a/src/widgets/AccountListWidget.cpp b/src/widgets/AccountListWidget.cpp index d3b51122c..a19e6d046 100644 --- a/src/widgets/AccountListWidget.cpp +++ b/src/widgets/AccountListWidget.cpp @@ -98,6 +98,8 @@ AccountListWidget::insertEntries( const QModelIndex& parent, int start, int end m_layout->insertWidget( i+j, entryAccounts.at( j ) ); updateEntry( idx ); + for ( int j = 0; j < entryAccounts.length(); ++j ) + entryAccounts[ j ]->setupConnections( idx, j ); } } diff --git a/src/widgets/AccountWidget.cpp b/src/widgets/AccountWidget.cpp index 85a1566de..05afef7af 100644 --- a/src/widgets/AccountWidget.cpp +++ b/src/widgets/AccountWidget.cpp @@ -19,6 +19,7 @@ #include "AccountWidget.h" #include "UnstyledFrame.h" +#include "SlideSwitchButton.h" #include "accounts/Account.h" #include "accounts/AccountModel.h" #include "utils/TomahawkUtilsGui.h" @@ -30,7 +31,6 @@ #include #include #include -#include #include #include #include @@ -79,13 +79,20 @@ AccountWidget::AccountWidget( QWidget* parent ) m_spinner = new AnimatedSpinner( m_spinnerWidget->size(), m_spinnerWidget ); idContainer->setStyleSheet( QString( "QFrame {" - "border: 1px solid #c9c9c9;" + "border: 1px solid #e9e9e9;" "border-radius: %1px;" - "background: #c9c9c9;" + "background: #e9e9e9;" "}" ).arg( idContainer->sizeHint().height() / 2 + 1 ) ); - m_statusToggle = new QCheckBox( this ); - vLayout->addWidget( m_statusToggle, 0, 1 ); + m_statusToggle = new SlideSwitchButton( this ); + m_statusToggle->setContentsMargins( 0, 0, 0, 0 ); + m_statusToggle->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Expanding ); + m_statusToggle->setFixedWidth( m_statusToggle->sizeHint().width() ); + QHBoxLayout *statusToggleLayout = new QHBoxLayout( this ); + vLayout->addLayout( statusToggleLayout, 0, 1, 1, 1 ); + statusToggleLayout->addStretch(); + statusToggleLayout->addWidget( m_statusToggle ); + //vLayout->addWidget( m_statusToggle, 0, 1 ); UnstyledFrame* inviteContainer = new UnstyledFrame( this ); vLayout->addWidget( inviteContainer, 1, 0 ); @@ -129,7 +136,6 @@ AccountWidget::AccountWidget( QWidget* parent ) m_inviteButton = new QPushButton( this ); m_inviteButton->setFixedWidth( m_inviteButton->logicalDpiX() * 0.8 ); vLayout->addWidget( m_inviteButton, 1, 1 ); - } AccountWidget::~AccountWidget() @@ -158,9 +164,6 @@ AccountWidget::update( const QPersistentModelIndex& idx, int accountIdx ) "
" + account->accountFriendlyName() ); - //TODO: make it handle all connection states - m_statusToggle->setChecked( account->connectionState() == Tomahawk::Accounts::Account::Connected ); - //we already know it's a factory because of the FactoryProxy Tomahawk::Accounts::AccountFactory* fac = qobject_cast< Tomahawk::Accounts::AccountFactory* >( @@ -179,14 +182,63 @@ AccountWidget::update( const QPersistentModelIndex& idx, int accountIdx ) m_addAccountIcon->setVisible( true ); } - if ( account->connectionState() == Tomahawk::Accounts::Account::Connected || - account->connectionState() == Tomahawk::Accounts::Account::Disconnected ) + switch ( account->connectionState() ) { + case Tomahawk::Accounts::Account::Disconnected: m_spinner->fadeOut(); - } - else - { + m_statusToggle->setChecked( false ); + m_statusToggle->setBackChecked( false ); + break; + case Tomahawk::Accounts::Account::Connecting: m_spinner->fadeIn(); + m_statusToggle->setChecked( true ); + m_statusToggle->setBackChecked( false ); + break; + case Tomahawk::Accounts::Account::Connected: + m_spinner->fadeOut(); + m_statusToggle->setChecked( true ); + m_statusToggle->setBackChecked( true ); + break; + case Tomahawk::Accounts::Account::Disconnecting: + m_spinner->fadeIn(); + m_statusToggle->setChecked( false ); + m_statusToggle->setBackChecked( true ); } } } + +void +AccountWidget::changeAccountConnectionState( bool connected ) +{ + Tomahawk::Accounts::Account* account = + m_myFactoryIdx.data( Tomahawk::Accounts::AccountModel::ChildrenOfFactoryRole ) + .value< QList< Tomahawk::Accounts::Account* > >().at( m_myAccountIdx ); + if ( account ) + { + if ( connected ) + { + account->authenticate(); + } + else + { + account->deauthenticate(); + } + } +} + +void +AccountWidget::setupConnections( const QPersistentModelIndex& idx, int accountIdx ) +{ + m_myFactoryIdx = idx; + m_myAccountIdx = accountIdx; + + Tomahawk::Accounts::Account* account = + idx.data( Tomahawk::Accounts::AccountModel::ChildrenOfFactoryRole ) + .value< QList< Tomahawk::Accounts::Account* > >().at( accountIdx ); + if ( account ) + { + connect( m_statusToggle, SIGNAL( toggled( bool ) ), + this, SLOT( changeAccountConnectionState( bool ) ) ); + //TODO: invite/tweet + } +} diff --git a/src/widgets/AccountWidget.h b/src/widgets/AccountWidget.h index 54ff132ab..42194eb17 100644 --- a/src/widgets/AccountWidget.h +++ b/src/widgets/AccountWidget.h @@ -20,14 +20,14 @@ #define ACCOUNTWIDGET_H #include +#include class AnimatedSpinner; class ElidedLabel; -class QCheckBox; +class SlideSwitchButton; class QLabel; class QLineEdit; class QPushButton; -class QPersistentModelIndex; class QToolButton; class AccountWidget : public QWidget @@ -39,19 +39,24 @@ public: virtual ~AccountWidget(); void update( const QPersistentModelIndex& idx, int accountIdx ); + void setupConnections( const QPersistentModelIndex& idx, int accountIdx ); + +private slots: + void changeAccountConnectionState( bool connected ); private: - QLabel* m_imageLabel; - ElidedLabel* m_idLabel; - QWidget* m_spinnerWidget; - AnimatedSpinner* m_spinner; - QCheckBox* m_statusToggle; - QLineEdit* m_inviteEdit; - QPushButton* m_inviteButton; - QLabel* m_addAccountIcon; - QToolButton* m_tweetMenuButton; + QLabel* m_imageLabel; + ElidedLabel* m_idLabel; + QWidget* m_spinnerWidget; + AnimatedSpinner* m_spinner; + SlideSwitchButton* m_statusToggle; + QLineEdit* m_inviteEdit; + QPushButton* m_inviteButton; + QLabel* m_addAccountIcon; + QToolButton* m_tweetMenuButton; - //TODO: on/off button + QPersistentModelIndex m_myFactoryIdx; + int m_myAccountIdx; }; #endif // ACCOUNTWIDGET_H diff --git a/src/widgets/SlideSwitchButton.cpp b/src/widgets/SlideSwitchButton.cpp new file mode 100644 index 000000000..8d6f66841 --- /dev/null +++ b/src/widgets/SlideSwitchButton.cpp @@ -0,0 +1,190 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2012 Teo Mrnjavac + * + * Tomahawk 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. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + +#include "SlideSwitchButton.h" + +#include +#include +#include + +SlideSwitchButton::SlideSwitchButton( QWidget* parent ) + : QPushButton( parent ) + , m_checkedText( tr( "On" ) ) + , m_uncheckedText( tr( "Off" ) ) +{ + init(); +} + +SlideSwitchButton::SlideSwitchButton( const QString& checkedText, + const QString& uncheckedText, + QWidget* parent ) + : QPushButton( parent ) + , m_checkedText( checkedText ) + , m_uncheckedText( uncheckedText ) +{ + init(); +} + +void +SlideSwitchButton::init() +{ + setCheckable( true ); + + m_backCheckedColor = QColor( 167, 183, 211 ); + m_backUncheckedColor = QColor( "#d9d9d9" ); + + m_baseColor = m_backUncheckedColor; + m_textColor = QColor( "#606060" ); + setFocusPolicy( Qt::NoFocus ); + + m_knobX = 0.; + + m_textFont = font(); + m_textFont.setBold( true ); + m_textFont.setCapitalization( QFont::AllUppercase ); + + + connect( this, SIGNAL( toggled( bool ) ), + this, SLOT( onCheckedStateChanged() ) ); +} + +QSize +SlideSwitchButton::sizeHint() +{ + QSize size = QPushButton::sizeHint(); + size.rheight() += 6; //margins + QFontMetrics fm( m_textFont ); + int maxTextLength = qMax( fm.boundingRect( m_checkedText ).width(), + fm.boundingRect( m_uncheckedText ).width() ); + size.rwidth() = contentsMargins().left() + contentsMargins().right() + + 2 /*a bit of margin*/ + maxTextLength + ( height() - 4 ) * 1.25; + return size; +} + + +void +SlideSwitchButton::paintEvent( QPaintEvent* event ) +{ + QPainter painter( this ); + + painter.initFrom( this ); + painter.setPen( m_baseColor ); + + QStyleOptionButton option; + option.initFrom( this ); + + QPalette palette; + + if ( option.state & QStyle::State_MouseOver ) + painter.setPen( palette.color( QPalette::Highlight ) ); + //TODO: should the whole thing be highlighted or just the knob? + + QLinearGradient gradient( 0, 0, 0, 1 ); + gradient.setCoordinateMode( QGradient::ObjectBoundingMode ); + gradient.setColorAt( 0, m_baseColor.lighter( 95 ) ); + gradient.setColorAt( 1, m_baseColor.lighter( 115 ) ); + + + painter.setBrush( QBrush( gradient ) ); + painter.setRenderHints( QPainter::Antialiasing, true ); + painter.drawRoundedRect( QRect( 0, 0, width() - 0, height() - 0 ), + 5, 5 ); + + //knob + QRect knobRect( 2, 2, ( height() - 4 ) * 1.25, height() - 4 ); + knobRect.moveTo( QPoint( 2 + m_knobX * ( width() - 4 - knobRect.width() ), knobRect.y() ) ); + QLinearGradient knobGradient( 0, 0, 0, 1 ); + knobGradient.setCoordinateMode( QGradient::ObjectBoundingMode ); + knobGradient.setColorAt( 0, m_backUncheckedColor.lighter( 115 ) ); + knobGradient.setColorAt( 1, m_backUncheckedColor.lighter( 95 ) ); + painter.setBrush( QBrush( knobGradient ) ); + + painter.drawRoundedRect( knobRect, 1, 1 ); + +// if we ever want to try with primitives... +// QStyleOptionButton option; +// option.initFrom( this ); +// option.rect = QRect( m_knobX + 1, 1, ( height() - 2 ) * 1.25, height() - 2 ); +// style()->drawPrimitive( QStyle::PE_PanelButtonBevel, &option, &painter, this ); + + //let's draw some text... + QRect textRect = rect().adjusted( 4, 3, -4, -3 ); + painter.setFont( m_textFont ); + painter.setPen( m_textColor ); + if ( m_knobX == 0. ) + { + //draw on right + painter.drawText( textRect, Qt::AlignRight | Qt::AlignVCenter, m_uncheckedText ); + } + else if ( m_knobX == 1. ) + { + //draw on left + painter.drawText( textRect, Qt::AlignLeft | Qt::AlignVCenter, m_checkedText ); + } + //otherwise don't draw because the knob is being animated + + painter.end(); +} + +void +SlideSwitchButton::onCheckedStateChanged() +{ + QPropertyAnimation *animation = new QPropertyAnimation( this, "knobX" ); + animation->setDuration( 50 ); + + if ( isChecked() ) + { + animation->setStartValue( 0. ); + animation->setEndValue( 1. ); + } + else + { + animation->setStartValue( 1. ); + animation->setEndValue( 0. ); + } + animation->start( QAbstractAnimation::DeleteWhenStopped ); +} + + +void +SlideSwitchButton::setBackChecked( bool state ) +{ + if ( state != m_backChecked ) + { + m_backChecked = state; + QPropertyAnimation *animation = new QPropertyAnimation( this, "baseColor" ); + animation->setDuration( 300 ); + if ( state ) + { + animation->setStartValue( m_backUncheckedColor ); + animation->setEndValue( m_backCheckedColor ); + } + else + { + animation->setStartValue( m_backCheckedColor ); + animation->setEndValue( m_backUncheckedColor ); + } + animation->start( QAbstractAnimation::DeleteWhenStopped ); + } +} + +bool +SlideSwitchButton::backChecked() const +{ + return m_backChecked; +} diff --git a/src/widgets/SlideSwitchButton.h b/src/widgets/SlideSwitchButton.h new file mode 100644 index 000000000..067df5151 --- /dev/null +++ b/src/widgets/SlideSwitchButton.h @@ -0,0 +1,72 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2012 Teo Mrnjavac + * + * Tomahawk 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. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + +#ifndef SLIDESWITCHBUTTON_H +#define SLIDESWITCHBUTTON_H + +#include +#include +#include +#include + +class SlideSwitchButton : public QPushButton +{ + Q_OBJECT +public: + explicit SlideSwitchButton( QWidget* parent = 0 ); + explicit SlideSwitchButton( const QString& checkedText, + const QString& uncheckedText, + QWidget* parent = 0 ); + + void init(); + + virtual QSize sizeHint(); + virtual QSize minimumSizeHint() { return sizeHint(); } + + //the back check-state cannot be changed by the user, only programmatically + //to notify that the user-requested operation has completed + void setBackChecked( bool state ); + bool backChecked() const; + + void setKnobX( double x ) { m_knobX = x; repaint(); } + double knobX() const { return m_knobX; } + Q_PROPERTY( double knobX READ knobX WRITE setKnobX ) + + void setBaseColor( const QColor& color ) { m_baseColor = color; repaint(); } + QColor baseColor() const { return m_baseColor; } + Q_PROPERTY( QColor baseColor READ baseColor WRITE setBaseColor ) + +protected: + void paintEvent( QPaintEvent* event ); + +private slots: + void onCheckedStateChanged(); + +private: + QString m_checkedText; + QString m_uncheckedText; + QColor m_baseColor; + QColor m_textColor; + QColor m_backUncheckedColor; + QColor m_backCheckedColor; + QFont m_textFont; //needed for sizeHint + bool m_backChecked; + double m_knobX; +}; + +#endif // SLIDESWITCHBUTTON_H