| | |
| | |
| |
|
| | #include <mutex> |
| | #include <QApplication> |
| | #include <QDialogButtonBox> |
| | #include <QHeaderView> |
| | #include <QLabel> |
| | #include <QLineEdit> |
| | #include <QScrollArea> |
| | #include <QStandardItemModel> |
| | #include <QTreeView> |
| | #include <QVBoxLayout> |
| | #include "common/fs/path_util.h" |
| | #include "common/string_util.h" |
| | #include "core/constants.h" |
| | #include "core/core.h" |
| | #include "core/hle/service/acc/profile_manager.h" |
| | #include "yuzu/applets/qt_profile_select.h" |
| | #include "yuzu/main.h" |
| | #include "yuzu/util/controller_navigation.h" |
| |
|
| | namespace { |
| | QString FormatUserEntryText(const QString& username, Common::UUID uuid) { |
| | return QtProfileSelectionDialog::tr( |
| | "%1\n%2", "%1 is the profile username, %2 is the formatted UUID (e.g. " |
| | "00112233-4455-6677-8899-AABBCCDDEEFF))") |
| | .arg(username, QString::fromStdString(uuid.FormattedString())); |
| | } |
| |
|
| | QString GetImagePath(Common::UUID uuid) { |
| | const auto path = |
| | Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / |
| | fmt::format("system/save/8000000000000010/su/avators/{}.jpg", uuid.FormattedString()); |
| | return QString::fromStdString(Common::FS::PathToUTF8String(path)); |
| | } |
| |
|
| | QPixmap GetIcon(Common::UUID uuid) { |
| | QPixmap icon{GetImagePath(uuid)}; |
| |
|
| | if (!icon) { |
| | icon.fill(Qt::black); |
| | icon.loadFromData(Core::Constants::ACCOUNT_BACKUP_JPEG.data(), |
| | static_cast<u32>(Core::Constants::ACCOUNT_BACKUP_JPEG.size())); |
| | } |
| |
|
| | return icon.scaled(64, 64, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); |
| | } |
| | } |
| |
|
| | QtProfileSelectionDialog::QtProfileSelectionDialog( |
| | Core::System& system, QWidget* parent, |
| | const Core::Frontend::ProfileSelectParameters& parameters) |
| | : QDialog(parent), profile_manager{system.GetProfileManager()} { |
| | outer_layout = new QVBoxLayout; |
| |
|
| | instruction_label = new QLabel(); |
| |
|
| | scroll_area = new QScrollArea; |
| |
|
| | buttons = new QDialogButtonBox(QDialogButtonBox::Cancel | QDialogButtonBox::Ok); |
| | connect(buttons, &QDialogButtonBox::accepted, this, &QtProfileSelectionDialog::accept); |
| | connect(buttons, &QDialogButtonBox::rejected, this, &QtProfileSelectionDialog::reject); |
| |
|
| | outer_layout->addWidget(instruction_label); |
| | outer_layout->addWidget(scroll_area); |
| | outer_layout->addWidget(buttons); |
| |
|
| | layout = new QVBoxLayout; |
| | tree_view = new QTreeView; |
| | item_model = new QStandardItemModel(tree_view); |
| | tree_view->setModel(item_model); |
| | controller_navigation = new ControllerNavigation(system.HIDCore(), this); |
| |
|
| | tree_view->setAlternatingRowColors(true); |
| | tree_view->setSelectionMode(QHeaderView::SingleSelection); |
| | tree_view->setSelectionBehavior(QHeaderView::SelectRows); |
| | tree_view->setVerticalScrollMode(QHeaderView::ScrollPerPixel); |
| | tree_view->setHorizontalScrollMode(QHeaderView::ScrollPerPixel); |
| | tree_view->setSortingEnabled(true); |
| | tree_view->setEditTriggers(QHeaderView::NoEditTriggers); |
| | tree_view->setUniformRowHeights(true); |
| | tree_view->setIconSize({64, 64}); |
| | tree_view->setContextMenuPolicy(Qt::NoContextMenu); |
| |
|
| | item_model->insertColumns(0, 1); |
| | item_model->setHeaderData(0, Qt::Horizontal, tr("Users")); |
| |
|
| | |
| | |
| | qRegisterMetaType<QList<QStandardItem*>>("QList<QStandardItem*>"); |
| |
|
| | layout->setContentsMargins(0, 0, 0, 0); |
| | layout->setSpacing(0); |
| | layout->addWidget(tree_view); |
| |
|
| | scroll_area->setLayout(layout); |
| |
|
| | connect(tree_view, &QTreeView::clicked, this, &QtProfileSelectionDialog::SelectUser); |
| | connect(tree_view, &QTreeView::doubleClicked, this, &QtProfileSelectionDialog::accept); |
| | connect(controller_navigation, &ControllerNavigation::TriggerKeyboardEvent, |
| | [this](Qt::Key key) { |
| | if (!this->isActiveWindow()) { |
| | return; |
| | } |
| | QKeyEvent* event = new QKeyEvent(QEvent::KeyPress, key, Qt::NoModifier); |
| | QCoreApplication::postEvent(tree_view, event); |
| | SelectUser(tree_view->currentIndex()); |
| | }); |
| |
|
| | const auto& profiles = profile_manager.GetAllUsers(); |
| | for (const auto& user : profiles) { |
| | Service::Account::ProfileBase profile{}; |
| | if (!profile_manager.GetProfileBase(user, profile)) |
| | continue; |
| |
|
| | const auto username = Common::StringFromFixedZeroTerminatedBuffer( |
| | reinterpret_cast<const char*>(profile.username.data()), profile.username.size()); |
| |
|
| | list_items.push_back(QList<QStandardItem*>{new QStandardItem{ |
| | GetIcon(user), FormatUserEntryText(QString::fromStdString(username), user)}}); |
| | } |
| |
|
| | for (const auto& item : list_items) |
| | item_model->appendRow(item); |
| |
|
| | setLayout(outer_layout); |
| | SetWindowTitle(parameters); |
| | SetDialogPurpose(parameters); |
| | resize(550, 400); |
| | } |
| |
|
| | QtProfileSelectionDialog::~QtProfileSelectionDialog() { |
| | controller_navigation->UnloadController(); |
| | }; |
| |
|
| | int QtProfileSelectionDialog::exec() { |
| | |
| | if (profile_manager.GetUserCount() == 1) { |
| | user_index = 0; |
| | return QDialog::Accepted; |
| | } |
| | return QDialog::exec(); |
| | } |
| |
|
| | void QtProfileSelectionDialog::accept() { |
| | QDialog::accept(); |
| | } |
| |
|
| | void QtProfileSelectionDialog::reject() { |
| | user_index = 0; |
| | QDialog::reject(); |
| | } |
| |
|
| | int QtProfileSelectionDialog::GetIndex() const { |
| | return user_index; |
| | } |
| |
|
| | void QtProfileSelectionDialog::SelectUser(const QModelIndex& index) { |
| | user_index = index.row(); |
| | } |
| |
|
| | void QtProfileSelectionDialog::SetWindowTitle( |
| | const Core::Frontend::ProfileSelectParameters& parameters) { |
| | using Service::AM::Frontend::UiMode; |
| | switch (parameters.mode) { |
| | case UiMode::UserCreator: |
| | case UiMode::UserCreatorForStarter: |
| | setWindowTitle(tr("Profile Creator")); |
| | return; |
| | case UiMode::EnsureNetworkServiceAccountAvailable: |
| | setWindowTitle(tr("Profile Selector")); |
| | return; |
| | case UiMode::UserIconEditor: |
| | setWindowTitle(tr("Profile Icon Editor")); |
| | return; |
| | case UiMode::UserNicknameEditor: |
| | setWindowTitle(tr("Profile Nickname Editor")); |
| | return; |
| | case UiMode::NintendoAccountAuthorizationRequestContext: |
| | case UiMode::IntroduceExternalNetworkServiceAccount: |
| | case UiMode::IntroduceExternalNetworkServiceAccountForRegistration: |
| | case UiMode::NintendoAccountNnidLinker: |
| | case UiMode::LicenseRequirementsForNetworkService: |
| | case UiMode::LicenseRequirementsForNetworkServiceWithUserContextImpl: |
| | case UiMode::UserCreatorForImmediateNaLoginTest: |
| | case UiMode::UserQualificationPromoter: |
| | case UiMode::UserSelector: |
| | default: |
| | setWindowTitle(tr("Profile Selector")); |
| | } |
| | } |
| |
|
| | void QtProfileSelectionDialog::SetDialogPurpose( |
| | const Core::Frontend::ProfileSelectParameters& parameters) { |
| | using Service::AM::Frontend::UserSelectionPurpose; |
| |
|
| | switch (parameters.purpose) { |
| | case UserSelectionPurpose::GameCardRegistration: |
| | instruction_label->setText(tr("Who will receive the points?")); |
| | return; |
| | case UserSelectionPurpose::EShopLaunch: |
| | instruction_label->setText(tr("Who is using Nintendo eShop?")); |
| | return; |
| | case UserSelectionPurpose::EShopItemShow: |
| | instruction_label->setText(tr("Who is making this purchase?")); |
| | return; |
| | case UserSelectionPurpose::PicturePost: |
| | instruction_label->setText(tr("Who is posting?")); |
| | return; |
| | case UserSelectionPurpose::NintendoAccountLinkage: |
| | instruction_label->setText(tr("Select a user to link to a Nintendo Account.")); |
| | return; |
| | case UserSelectionPurpose::SettingsUpdate: |
| | instruction_label->setText(tr("Change settings for which user?")); |
| | return; |
| | case UserSelectionPurpose::SaveDataDeletion: |
| | instruction_label->setText(tr("Format data for which user?")); |
| | return; |
| | case UserSelectionPurpose::UserMigration: |
| | instruction_label->setText(tr("Which user will be transferred to another console?")); |
| | return; |
| | case UserSelectionPurpose::SaveDataTransfer: |
| | instruction_label->setText(tr("Send save data for which user?")); |
| | return; |
| | case UserSelectionPurpose::General: |
| | default: |
| | instruction_label->setText(tr("Select a user:")); |
| | return; |
| | } |
| | } |
| |
|
| | QtProfileSelector::QtProfileSelector(GMainWindow& parent) { |
| | connect(this, &QtProfileSelector::MainWindowSelectProfile, &parent, |
| | &GMainWindow::ProfileSelectorSelectProfile, Qt::QueuedConnection); |
| | connect(this, &QtProfileSelector::MainWindowRequestExit, &parent, |
| | &GMainWindow::ProfileSelectorRequestExit, Qt::QueuedConnection); |
| | connect(&parent, &GMainWindow::ProfileSelectorFinishedSelection, this, |
| | &QtProfileSelector::MainWindowFinishedSelection, Qt::DirectConnection); |
| | } |
| |
|
| | QtProfileSelector::~QtProfileSelector() = default; |
| |
|
| | void QtProfileSelector::Close() const { |
| | callback = {}; |
| | emit MainWindowRequestExit(); |
| | } |
| |
|
| | void QtProfileSelector::SelectProfile( |
| | SelectProfileCallback callback_, |
| | const Core::Frontend::ProfileSelectParameters& parameters) const { |
| | callback = std::move(callback_); |
| | emit MainWindowSelectProfile(parameters); |
| | } |
| |
|
| | void QtProfileSelector::MainWindowFinishedSelection(std::optional<Common::UUID> uuid) { |
| | if (callback) { |
| | callback(uuid); |
| | } |
| | } |
| |
|