@@ -608,6 +608,13 @@ void game_list_actions::ShowDiskUsageDialog()
608608 });
609609}
610610
611+ // How a game collection is listed in either menu: its name, and how many of its games the game list is
612+ // showing. One pass: a collection may be named "%1", and a chained arg() would substitute into it.
613+ static QString collection_entry_text (const QString& name, qsizetype count)
614+ {
615+ return QString (" %0 (%1)" ).arg (gui::utils::escape_mnemonics (name), QString::number (count));
616+ }
617+
611618void game_list_actions::UpdateGameCollectionMenu (QMenu* menu, QActionGroup* act_group, QMenu* rename_menu, QMenu* remove_menu)
612619{
613620 const QStringList collections = m_gui_settings->GetGameCollections ();
@@ -664,20 +671,25 @@ void game_list_actions::UpdateGameCollectionMenu(QMenu* menu, QActionGroup* act_
664671
665672 for (const QString& name : collections)
666673 {
667- // One pass: a collection may be named "%1", and a chained arg() would substitute into it.
668- add_entry (QString (" %0 (%1)" ).arg (gui::utils::escape_mnemonics (name),
669- QString::number (counts.value (name))), name);
674+ add_entry (collection_entry_text (name, counts.value (name)), name);
670675 }
671676}
672677
673- void game_list_actions::CreateGameCollection ()
678+ void game_list_actions::CreateGameCollection (const QSet<QString>& serials )
674679{
675680 const QString name = AskForCollectionName (tr (" Create Game Collection" ), {},
676681 [this ](const QString& to) { return m_gui_settings->AddGameCollection (to); });
677682
678- if (!name.isEmpty ())
683+ if (name.isEmpty ())
684+ {
685+ return ;
686+ }
687+
688+ game_list_log.notice (" Created game collection '%s'" , name);
689+
690+ if (!serials.isEmpty ())
679691 {
680- game_list_log. notice ( " Created game collection '%s' " , name);
692+ ChangeCollectionMembership (serials , name, true );
681693 }
682694}
683695
@@ -732,7 +744,7 @@ void game_list_actions::SelectGameCollection(const QString& name)
732744 m_game_list_frame->SetGameCollection (name);
733745}
734746
735- void game_list_actions::AddMoveToCollectionMenu (QMenu* parent, const std::vector<game_info>& games)
747+ void game_list_actions::AddCollectionMenu (QMenu* parent, const std::vector<game_info>& games)
736748{
737749 QSet<QString> serials;
738750
@@ -750,51 +762,47 @@ void game_list_actions::AddMoveToCollectionMenu(QMenu* parent, const std::vector
750762 }
751763
752764 const QStringList collections = m_gui_settings->GetGameCollections ();
765+ const QHash<QString, qsizetype> counts = m_game_list_frame->CountGamesPerCollection (collections);
753766
754- QMenu* collection_menu = parent->addMenu (tr (" &Move To Collection" ));
755- collection_menu->setEnabled (!collections.isEmpty ());
767+ QMenu* collection_menu = parent->addMenu (tr (" &Add to Collection" ));
756768
757- if (collections.isEmpty ())
769+ // Always there: with no collection yet this is the only thing the submenu can offer
770+ connect (collection_menu->addAction (tr (" &Create and Add" )), &QAction::triggered, this , [this , serials]()
758771 {
759- return ;
760- }
761-
762- // A single game shows the collection it currently sits in. A multi selection is a bulk move to whatever
763- // entry is picked, no matter which collections the games come from, so it shows no state at all.
764- const bool is_single_game = serials.size () == 1 ;
765- const QString current = is_single_game ? m_gui_settings->GetCollectionOfGame (*serials.cbegin ()) : QString ();
772+ CreateGameCollection (serials);
773+ });
766774
767- QActionGroup* collection_act_group = is_single_game ? new QActionGroup (collection_menu) : nullptr ;
775+ if (!collections.isEmpty ())
776+ {
777+ collection_menu->addSeparator ();
778+ }
768779
769- const auto add_entry = [&] (const QString& text, const QString& name )
780+ for (const QString& collection : collections )
770781 {
771- QAction* act = collection_menu->addAction (text);
782+ // A multi selection is ticked only once every game in it belongs to the collection, so that the
783+ // entry finishes adding the ones that are missing before it starts taking any out
784+ const bool is_member = m_gui_settings->GetGamesInCollection (collection).contains (serials);
772785
773- if (collection_act_group)
774- {
775- act->setCheckable (true );
776- act->setChecked (name == current);
777- collection_act_group->addAction (act);
778- }
786+ QAction* act = collection_menu->addAction (collection_entry_text (collection, counts.value (collection)));
787+ act->setCheckable (true );
788+ act->setChecked (is_member);
779789
780- // Moving one game is trivially undone. Moving a block is not: the games can come from several
781- // collections at once and where each of them was is not recorded anywhere, so that one asks first.
782- connect (act, &QAction::triggered, this , [this , serials, name, is_single_game]()
790+ connect (act, &QAction::triggered, this , [this , serials, collection, is_member]()
783791 {
784- MoveGamesToCollection (serials, name , !is_single_game );
792+ ChangeCollectionMembership (serials, collection , !is_member );
785793 });
786- };
787-
788- const QHash<QString, qsizetype> counts = m_game_list_frame->CountGamesPerCollection (collections);
794+ }
789795
790- add_entry ( gui_settings::GetAllGamesCollectionLabel (), {} );
796+ const QString current = m_gui_settings-> GetCurrentGameCollection ( );
791797
792- for (const QString& collection : collections)
798+ // Acts on the collection the game list is filtered by, which is the one the user is looking at
799+ if (!current.isEmpty () && m_gui_settings->GetGamesInCollection (current).intersects (serials))
793800 {
794- // One pass: a collection may be named "%1", and a chained arg() would substitute into it.
795- const QString text = QString (" %0 (%1)" ).arg (gui::utils::escape_mnemonics (collection),
796- QString::number (counts.value (collection)));
797- add_entry (text, collection);
801+ connect (parent->addAction (tr (" &Remove from Collection '%0'" ).arg (gui::utils::escape_mnemonics (current))),
802+ &QAction::triggered, this , [this , serials, current]()
803+ {
804+ ChangeCollectionMembership (serials, current, false );
805+ });
798806 }
799807}
800808
@@ -851,39 +859,40 @@ QString game_list_actions::AskForCollectionName(const QString& title, const QStr
851859 return {};
852860}
853861
854- void game_list_actions::MoveGamesToCollection (const QSet<QString>& serials, const QString& name, bool is_interactive )
862+ void game_list_actions::ChangeCollectionMembership (const QSet<QString>& serials, const QString& name, bool add )
855863{
856- const bool moves_anything = !m_gui_settings-> GetGamesInCollection (name). contains (serials);
857-
858- if (is_interactive && moves_anything )
864+ // Adding is undone by the very entry that did it, but a removal takes memberships that were given one
865+ // game at a time and cannot be handed back the same way, so a block of them asks first
866+ if (!add && serials. size () > 1 )
859867 {
860- const QString question = name.isEmpty ()
861- ? tr (" Remove %Ln game(s) from their game collection?" , " " , static_cast <int >(serials.size ()))
862- : tr (" Move %Ln game(s) to the '%0' game collection?" , " " , static_cast <int >(serials.size ())).arg (name);
868+ const QString question = tr (" Remove %Ln game(s) from the '%0' game collection?" , " " ,
869+ static_cast <int >(serials.size ())).arg (name);
863870
864- if (gui::utils::plain_message (m_game_list_frame, QMessageBox::Question, tr (" Confirm Move " ), question,
871+ if (gui::utils::plain_message (m_game_list_frame, QMessageBox::Question, tr (" Confirm Removal " ), question,
865872 QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes)
866873 {
867874 return ;
868875 }
869876 }
870877
871- // The user picked the collection the games already sit in
872- if (!m_gui_settings->MoveGamesToCollection (serials, name))
878+ const bool changed = m_gui_settings->SetGameCollectionMembership (serials, name, add);
879+
880+ // The menu was built before the collection changed under it
881+ if (!changed)
873882 {
874883 return ;
875884 }
876885
877- if (name. isEmpty () )
886+ if (add )
878887 {
879- game_list_log.notice (" Removed %d game(s) from their game collection" , serials.size ());
888+ game_list_log.notice (" Added %d game(s) to game collection '%s' " , serials.size (), name );
880889 }
881890 else
882891 {
883- game_list_log.notice (" Moved %d game(s) to game collection '%s'" , serials.size (), name);
892+ game_list_log.notice (" Removed %d game(s) from game collection '%s'" , serials.size (), name);
884893 }
885894
886- // The moved games may have entered or left the collection the game list is filtered by
895+ // The games may have entered or left the collection the game list is filtered by
887896 m_game_list_frame->ReloadGameCollection ();
888897}
889898
0 commit comments