void PlayableProxyModel::setSourcePlayableModel( PlayableModel* sourceModel ) { if ( m_model ) { disconnect( m_model, SIGNAL( loadingStarted() ), this, SIGNAL( loadingStarted() ) ); disconnect( m_model, SIGNAL( loadingFinished() ), this, SIGNAL( loadingFinished() ) ); disconnect( m_model, SIGNAL( itemCountChanged( unsigned int ) ), this, SIGNAL( itemCountChanged( unsigned int ) ) ); disconnect( m_model, SIGNAL( indexPlayable( QModelIndex ) ), this, SLOT( onIndexPlayable( QModelIndex ) ) ); disconnect( m_model, SIGNAL( indexResolved( QModelIndex ) ), this, SLOT( onIndexResolved( QModelIndex ) ) ); disconnect( m_model, SIGNAL( currentIndexChanged( QModelIndex, QModelIndex ) ), this, SLOT( onCurrentIndexChanged( QModelIndex, QModelIndex ) ) ); disconnect( m_model, SIGNAL( expandRequest( QPersistentModelIndex ) ), this, SLOT( expandRequested( QPersistentModelIndex ) ) ); disconnect( m_model, SIGNAL( selectRequest( QPersistentModelIndex ) ), this, SLOT( selectRequested( QPersistentModelIndex ) ) ); } m_model = sourceModel; if ( m_model ) { connect( m_model, SIGNAL( loadingStarted() ), SIGNAL( loadingStarted() ) ); connect( m_model, SIGNAL( loadingFinished() ), SIGNAL( loadingFinished() ) ); connect( m_model, SIGNAL( itemCountChanged( unsigned int ) ), SIGNAL( itemCountChanged( unsigned int ) ) ); connect( m_model, SIGNAL( indexPlayable( QModelIndex ) ), SLOT( onIndexPlayable( QModelIndex ) ) ); connect( m_model, SIGNAL( indexResolved( QModelIndex ) ), SLOT( onIndexResolved( QModelIndex ) ) ); connect( m_model, SIGNAL( currentIndexChanged( QModelIndex, QModelIndex ) ), SLOT( onCurrentIndexChanged( QModelIndex, QModelIndex ) ) ); connect( m_model, SIGNAL( expandRequest( QPersistentModelIndex ) ), SLOT( expandRequested( QPersistentModelIndex ) ) ); connect( m_model, SIGNAL( selectRequest( QPersistentModelIndex ) ), SLOT( selectRequested( QPersistentModelIndex ) ) ); } QSortFilterProxyModel::setSourceModel( m_model ); }
// Set top-level layout to show cards. // Entry conditions: // - mCardWidgetsList contains CardWidgets from previous layout (any // widgets not reused should be cleaned up) // Exit requirements: // - mCardsList must be set to the list of cards // - mCardWidgetsList must contain all CardWidgets in the layout void GridCardViewerWidget::setCards( const QList<CardDataSharedPtr>& cards ) { // No need to lay everything out again if nothing changed. if( mCardsList == cards ) return; mCardsList = cards; // // Performance gets bad if creating all new widgets from scratch, // especially when images are scaled. Instead need to extract the // widgets from the layout, then only create new stuff as needed. // // Liberate the widgets we want to retain before clearing out the layout. for( auto w : mCardWidgetsList ) { w->setParent( 0 ); } for( int col = 0; col < 3; ++col ) { mTopColButtons[col]->setParent( 0 ); mBottomColButtons[col]->setParent( 0 ); } for( int row = 0; row < 3; ++row ) { mLeftRowButtons[row]->setParent( 0 ); mRightRowButtons[row]->setParent( 0 ); } // Our grid layout will be actually deleted below by clearLayout, but // need to null out our pointer. mGridCardsLayout = nullptr; // Remove any previously created widget from the alertable set. clearAlertableSubwidgets(); // Clear out the top-level layout - we're rebuilding it here. qtutils::clearLayout( mLayout ); // Function to clean up cardwidgets in the tracking list. auto cleanupCardWidgetsListFn = [this]() { for( auto w : mCardWidgetsList ) { w->deleteLater(); } mCardWidgetsList.clear(); }; // At this point everything is cleared. Finish cleanup and exit if // there aren't exactly 9 cards in the list. if( mCardsList.size() != 9 ) { // Zero is normal, but non-zero is not. if( mCardsList.size() > 0 ) { mLogger->warn( "card list size ({}) != 9, cannot perform grid layout!", cards.size() ); } // Clean up any cardwidgets remaining in the original list. cleanupCardWidgetsListFn(); return; } // This will contain all widgets once they're created/reused. QList<CardWidget*> newCardWidgetsList; // This local function picks a matching already-created CardWidget from // our tracking list to speed up the overall "setCards" operation which happens // on every sort, categorize, etc. auto takeWidgetFromCardWidgetsList = [this]( const CardDataSharedPtr& cardDataSharedPtr ) -> CardWidget* { for( int i = 0; i < mCardWidgetsList.count(); ++i ) { CardWidget* w = mCardWidgetsList[i]; if( w->getCardData() == cardDataSharedPtr ) { mLogger->debug( "reusing CardWidget for name={} muid={}", cardDataSharedPtr->getName(), cardDataSharedPtr->getMultiverseId() ); mCardWidgetsList.takeAt( i ); w->resetTraits(); updateCardWidgetSelectedAppearance( w ); return w; } } return nullptr; }; // This local function creates and connects a CardWidget. auto createCardWidget = [this]( const CardDataSharedPtr& cardDataSharedPtr ) -> CardWidget* { mLogger->debug( "creating CardWidget name={} muid={}", cardDataSharedPtr->getName(), cardDataSharedPtr->getMultiverseId() ); QString card = QString::fromStdString( cardDataSharedPtr->getName() ); CardWidget* cardWidget = new CardWidget( cardDataSharedPtr, mImageLoaderFactory, QSize( 223, 310 ), mLoggingConfig.createChildConfig( "cardwidget" ) ); cardWidget->setZoomFactor( mZoomFactor ); updateCardWidgetSelectedAppearance( cardWidget ); cardWidget->loadImage(); connect(cardWidget, SIGNAL(preselectRequested()), this, SLOT(handleCardPreselectRequested())); connect(cardWidget, SIGNAL(selectRequested()), this, SLOT(handleCardSelectRequested())); connect(cardWidget, SIGNAL(moveRequested()), this, SLOT(handleCardMoveRequested())); cardWidget->setContextMenuPolicy( Qt::CustomContextMenu ); connect(cardWidget, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(handleCardContextMenu(const QPoint&))); return cardWidget; }; // // Lay out the cards and buttons in a grid. // // This widget shouldn't be needed but Qt gets confused when the // layout is added directly. Having a widget own it seems to help. QWidget* gridCardsLayoutWidget = new QWidget(); // The widget needs to turn red when the alert is active. addAlertableSubwidget( gridCardsLayoutWidget ); mGridCardsLayout = new QGridLayout( gridCardsLayoutWidget ); // Center the gridlayout using a QHBoxLayout and add to parent layout // before adding widgets as mentioned in Qt docs. QHBoxLayout* alignmentLayout = new QHBoxLayout(); alignmentLayout->addStretch(); alignmentLayout->addWidget( gridCardsLayoutWidget ); alignmentLayout->addStretch(); mLayout->addLayout( alignmentLayout ); // Lay out column buttons. for( int col = 0; col < 3; ++col ) { mGridCardsLayout->addWidget( mTopColButtons[col], 0, 1 + col, Qt::AlignCenter ); mGridCardsLayout->addWidget( mBottomColButtons[col], 4, 1 + col, Qt::AlignCenter ); } // Lay out row buttons. for( int row = 0; row < 3; ++row ) { mGridCardsLayout->addWidget( mLeftRowButtons[row], 1 + row, 0, Qt::AlignCenter ); mGridCardsLayout->addWidget( mRightRowButtons[row], 1 + row, 4, Qt::AlignCenter ); } updateButtonVisibility(); // Create card widgets and add to layout. for( int i = 0; i < 9; ++i ) { auto cardDataSharedPtr = mCardsList[i]; // Look for an existing card widget that matches our card data, // and extract it from the list if found. CardWidget* cardWidget = nullptr; cardWidget = takeWidgetFromCardWidgetsList( cardDataSharedPtr ); if( !cardWidget ) { // The widget doesn't exist. Create it. cardWidget = createCardWidget( cardDataSharedPtr ); } mGridCardsLayout->addWidget( cardWidget, 1+ i/3, 1 + i%3 ); newCardWidgetsList.push_back( cardWidget ); } if( mFooterSpacing > 0 ) mLayout->addSpacing( mFooterSpacing ); // This keeps everything pushed nicely to the top of the main area. mLayout->addStretch(); // Clean up any cardwidgets remaining in the original list. cleanupCardWidgetsListFn(); // Old is new. mCardWidgetsList = newCardWidgetsList; }