diff options
Diffstat (limited to 'src/yuzu/configuration')
| -rw-r--r-- | src/yuzu/configuration/configure.ui | 11 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_dialog.cpp | 17 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_profile_manager.cpp | 300 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_profile_manager.h | 57 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_profile_manager.ui | 172 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_system.cpp | 261 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_system.h | 27 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_system.ui | 144 | 
8 files changed, 562 insertions, 427 deletions
| diff --git a/src/yuzu/configuration/configure.ui b/src/yuzu/configuration/configure.ui index ce833b6c8..3f03f0b77 100644 --- a/src/yuzu/configuration/configure.ui +++ b/src/yuzu/configuration/configure.ui @@ -52,6 +52,11 @@           <string>System</string>          </attribute>         </widget> +       <widget class="ConfigureProfileManager" name="profileManagerTab"> +        <attribute name="title"> +         <string>Profiles</string> +        </attribute> +       </widget>         <widget class="ConfigureInputSimple" name="inputTab">          <attribute name="title">           <string>Input</string> @@ -104,6 +109,12 @@     <container>1</container>    </customwidget>    <customwidget> +   <class>ConfigureProfileManager</class> +   <extends>QWidget</extends> +   <header>configuration/configure_profile_manager.h</header> +   <container>1</container> +  </customwidget> +  <customwidget>     <class>ConfigureAudio</class>     <extends>QWidget</extends>     <header>configuration/configure_audio.h</header> diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp index 90d7c6372..d802443d0 100644 --- a/src/yuzu/configuration/configure_dialog.cpp +++ b/src/yuzu/configuration/configure_dialog.cpp @@ -32,6 +32,7 @@ void ConfigureDialog::applyConfiguration() {      ui->generalTab->applyConfiguration();      ui->gameListTab->applyConfiguration();      ui->systemTab->applyConfiguration(); +    ui->profileManagerTab->applyConfiguration();      ui->inputTab->applyConfiguration();      ui->graphicsTab->applyConfiguration();      ui->audioTab->applyConfiguration(); @@ -43,7 +44,7 @@ void ConfigureDialog::applyConfiguration() {  void ConfigureDialog::PopulateSelectionList() {      const std::array<std::pair<QString, QStringList>, 4> items{          {{tr("General"), {tr("General"), tr("Web"), tr("Debug"), tr("Game List")}}, -         {tr("System"), {tr("System"), tr("Audio")}}, +         {tr("System"), {tr("System"), tr("Profiles"), tr("Audio")}},           {tr("Graphics"), {tr("Graphics")}},           {tr("Controls"), {tr("Input")}}}}; @@ -60,11 +61,15 @@ void ConfigureDialog::UpdateVisibleTabs() {      if (items.isEmpty())          return; -    const std::map<QString, QWidget*> widgets = { -        {tr("General"), ui->generalTab}, {tr("System"), ui->systemTab}, -        {tr("Input"), ui->inputTab},     {tr("Graphics"), ui->graphicsTab}, -        {tr("Audio"), ui->audioTab},     {tr("Debug"), ui->debugTab}, -        {tr("Web"), ui->webTab},         {tr("Game List"), ui->gameListTab}}; +    const std::map<QString, QWidget*> widgets = {{tr("General"), ui->generalTab}, +                                                 {tr("System"), ui->systemTab}, +                                                 {tr("Profiles"), ui->profileManagerTab}, +                                                 {tr("Input"), ui->inputTab}, +                                                 {tr("Graphics"), ui->graphicsTab}, +                                                 {tr("Audio"), ui->audioTab}, +                                                 {tr("Debug"), ui->debugTab}, +                                                 {tr("Web"), ui->webTab}, +                                                 {tr("Game List"), ui->gameListTab}};      ui->tabWidget->clear(); diff --git a/src/yuzu/configuration/configure_profile_manager.cpp b/src/yuzu/configuration/configure_profile_manager.cpp new file mode 100644 index 000000000..41663e39a --- /dev/null +++ b/src/yuzu/configuration/configure_profile_manager.cpp @@ -0,0 +1,300 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <algorithm> +#include <QFileDialog> +#include <QGraphicsItem> +#include <QGraphicsScene> +#include <QHeaderView> +#include <QMessageBox> +#include <QStandardItemModel> +#include <QTreeView> +#include <QVBoxLayout> +#include "common/assert.h" +#include "common/file_util.h" +#include "common/string_util.h" +#include "core/core.h" +#include "core/hle/service/acc/profile_manager.h" +#include "core/settings.h" +#include "ui_configure_profile_manager.h" +#include "yuzu/configuration/configure_profile_manager.h" +#include "yuzu/util/limitable_input_dialog.h" + +namespace { +// Same backup JPEG used by acc IProfile::GetImage if no jpeg found +constexpr std::array<u8, 107> backup_jpeg{ +    0xff, 0xd8, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x02, 0x02, +    0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04, 0x08, 0x06, 0x06, 0x05, +    0x06, 0x09, 0x08, 0x0a, 0x0a, 0x09, 0x08, 0x09, 0x09, 0x0a, 0x0c, 0x0f, 0x0c, 0x0a, 0x0b, 0x0e, +    0x0b, 0x09, 0x09, 0x0d, 0x11, 0x0d, 0x0e, 0x0f, 0x10, 0x10, 0x11, 0x10, 0x0a, 0x0c, 0x12, 0x13, +    0x12, 0x10, 0x13, 0x0f, 0x10, 0x10, 0x10, 0xff, 0xc9, 0x00, 0x0b, 0x08, 0x00, 0x01, 0x00, 0x01, +    0x01, 0x01, 0x11, 0x00, 0xff, 0xcc, 0x00, 0x06, 0x00, 0x10, 0x10, 0x05, 0xff, 0xda, 0x00, 0x08, +    0x01, 0x01, 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9, +}; + +QString GetImagePath(Service::Account::UUID uuid) { +    const auto path = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + +                      "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg"; +    return QString::fromStdString(path); +} + +QString GetAccountUsername(const Service::Account::ProfileManager& manager, +                           Service::Account::UUID uuid) { +    Service::Account::ProfileBase profile; +    if (!manager.GetProfileBase(uuid, profile)) { +        return {}; +    } + +    const auto text = Common::StringFromFixedZeroTerminatedBuffer( +        reinterpret_cast<const char*>(profile.username.data()), profile.username.size()); +    return QString::fromStdString(text); +} + +QString FormatUserEntryText(const QString& username, Service::Account::UUID uuid) { +    return ConfigureProfileManager::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.FormatSwitch())); +} + +QPixmap GetIcon(Service::Account::UUID uuid) { +    QPixmap icon{GetImagePath(uuid)}; + +    if (!icon) { +        icon.fill(Qt::black); +        icon.loadFromData(backup_jpeg.data(), static_cast<u32>(backup_jpeg.size())); +    } + +    return icon.scaled(64, 64, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); +} + +QString GetProfileUsernameFromUser(QWidget* parent, const QString& description_text) { +    return LimitableInputDialog::GetText(parent, ConfigureProfileManager::tr("Enter Username"), +                                         description_text, 1, +                                         static_cast<int>(Service::Account::profile_username_size)); +} +} // Anonymous namespace + +ConfigureProfileManager ::ConfigureProfileManager(QWidget* parent) +    : QWidget(parent), ui(new Ui::ConfigureProfileManager), +      profile_manager(std::make_unique<Service::Account::ProfileManager>()) { +    ui->setupUi(this); + +    layout = new QVBoxLayout; +    tree_view = new QTreeView; +    item_model = new QStandardItemModel(tree_view); +    tree_view->setModel(item_model); + +    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, "Users"); + +    // We must register all custom types with the Qt Automoc system so that we are able to use it +    // with signals/slots. In this case, QList falls under the umbrells of custom types. +    qRegisterMetaType<QList<QStandardItem*>>("QList<QStandardItem*>"); + +    layout->setContentsMargins(0, 0, 0, 0); +    layout->setSpacing(0); +    layout->addWidget(tree_view); + +    ui->scrollArea->setLayout(layout); + +    connect(tree_view, &QTreeView::clicked, this, &ConfigureProfileManager::SelectUser); + +    connect(ui->pm_add, &QPushButton::pressed, this, &ConfigureProfileManager::AddUser); +    connect(ui->pm_rename, &QPushButton::pressed, this, &ConfigureProfileManager::RenameUser); +    connect(ui->pm_remove, &QPushButton::pressed, this, &ConfigureProfileManager::DeleteUser); +    connect(ui->pm_set_image, &QPushButton::pressed, this, &ConfigureProfileManager::SetUserImage); + +    scene = new QGraphicsScene; +    ui->current_user_icon->setScene(scene); + +    this->setConfiguration(); +} + +ConfigureProfileManager::~ConfigureProfileManager() = default; + +void ConfigureProfileManager::setConfiguration() { +    enabled = !Core::System::GetInstance().IsPoweredOn(); +    item_model->removeRows(0, item_model->rowCount()); +    list_items.clear(); + +    PopulateUserList(); +    UpdateCurrentUser(); +} + +void ConfigureProfileManager::PopulateUserList() { +    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); +} + +void ConfigureProfileManager::UpdateCurrentUser() { +    ui->pm_add->setEnabled(profile_manager->GetUserCount() < Service::Account::MAX_USERS); + +    const auto& current_user = profile_manager->GetUser(Settings::values.current_user); +    ASSERT(current_user); +    const auto username = GetAccountUsername(*profile_manager, *current_user); + +    scene->clear(); +    scene->addPixmap( +        GetIcon(*current_user).scaled(48, 48, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); +    ui->current_user_username->setText(username); +} + +void ConfigureProfileManager::applyConfiguration() { +    if (!enabled) +        return; + +    Settings::Apply(); +} + +void ConfigureProfileManager::SelectUser(const QModelIndex& index) { +    Settings::values.current_user = +        std::clamp<s32>(index.row(), 0, static_cast<s32>(profile_manager->GetUserCount() - 1)); + +    UpdateCurrentUser(); + +    ui->pm_remove->setEnabled(profile_manager->GetUserCount() >= 2); +    ui->pm_rename->setEnabled(true); +    ui->pm_set_image->setEnabled(true); +} + +void ConfigureProfileManager::AddUser() { +    const auto username = +        GetProfileUsernameFromUser(this, tr("Enter a username for the new user:")); +    if (username.isEmpty()) { +        return; +    } + +    const auto uuid = Service::Account::UUID::Generate(); +    profile_manager->CreateNewUser(uuid, username.toStdString()); + +    item_model->appendRow(new QStandardItem{GetIcon(uuid), FormatUserEntryText(username, uuid)}); +} + +void ConfigureProfileManager::RenameUser() { +    const auto user = tree_view->currentIndex().row(); +    const auto uuid = profile_manager->GetUser(user); +    ASSERT(uuid); + +    Service::Account::ProfileBase profile; +    if (!profile_manager->GetProfileBase(*uuid, profile)) +        return; + +    const auto new_username = GetProfileUsernameFromUser(this, tr("Enter a new username:")); +    if (new_username.isEmpty()) { +        return; +    } + +    const auto username_std = new_username.toStdString(); +    std::fill(profile.username.begin(), profile.username.end(), '\0'); +    std::copy(username_std.begin(), username_std.end(), profile.username.begin()); + +    profile_manager->SetProfileBase(*uuid, profile); + +    item_model->setItem( +        user, 0, +        new QStandardItem{GetIcon(*uuid), +                          FormatUserEntryText(QString::fromStdString(username_std), *uuid)}); +    UpdateCurrentUser(); +} + +void ConfigureProfileManager::DeleteUser() { +    const auto index = tree_view->currentIndex().row(); +    const auto uuid = profile_manager->GetUser(index); +    ASSERT(uuid); +    const auto username = GetAccountUsername(*profile_manager, *uuid); + +    const auto confirm = QMessageBox::question( +        this, tr("Confirm Delete"), +        tr("You are about to delete user with name \"%1\". Are you sure?").arg(username)); + +    if (confirm == QMessageBox::No) +        return; + +    if (Settings::values.current_user == tree_view->currentIndex().row()) +        Settings::values.current_user = 0; +    UpdateCurrentUser(); + +    if (!profile_manager->RemoveUser(*uuid)) +        return; + +    item_model->removeRows(tree_view->currentIndex().row(), 1); +    tree_view->clearSelection(); + +    ui->pm_remove->setEnabled(false); +    ui->pm_rename->setEnabled(false); +} + +void ConfigureProfileManager::SetUserImage() { +    const auto index = tree_view->currentIndex().row(); +    const auto uuid = profile_manager->GetUser(index); +    ASSERT(uuid); + +    const auto file = QFileDialog::getOpenFileName(this, tr("Select User Image"), QString(), +                                                   tr("JPEG Images (*.jpg *.jpeg)")); + +    if (file.isEmpty()) { +        return; +    } + +    const auto image_path = GetImagePath(*uuid); +    if (QFile::exists(image_path) && !QFile::remove(image_path)) { +        QMessageBox::warning( +            this, tr("Error deleting image"), +            tr("Error occurred attempting to overwrite previous image at: %1.").arg(image_path)); +        return; +    } + +    const auto raw_path = QString::fromStdString( +        FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "/system/save/8000000000000010"); +    const QFileInfo raw_info{raw_path}; +    if (raw_info.exists() && !raw_info.isDir() && !QFile::remove(raw_path)) { +        QMessageBox::warning(this, tr("Error deleting file"), +                             tr("Unable to delete existing file: %1.").arg(raw_path)); +        return; +    } + +    const QString absolute_dst_path = QFileInfo{image_path}.absolutePath(); +    if (!QDir{raw_path}.mkpath(absolute_dst_path)) { +        QMessageBox::warning( +            this, tr("Error creating user image directory"), +            tr("Unable to create directory %1 for storing user images.").arg(absolute_dst_path)); +        return; +    } + +    if (!QFile::copy(file, image_path)) { +        QMessageBox::warning(this, tr("Error copying user image"), +                             tr("Unable to copy image from %1 to %2").arg(file, image_path)); +        return; +    } + +    const auto username = GetAccountUsername(*profile_manager, *uuid); +    item_model->setItem(index, 0, +                        new QStandardItem{GetIcon(*uuid), FormatUserEntryText(username, *uuid)}); +    UpdateCurrentUser(); +} diff --git a/src/yuzu/configuration/configure_profile_manager.h b/src/yuzu/configuration/configure_profile_manager.h new file mode 100644 index 000000000..7fe95a2a8 --- /dev/null +++ b/src/yuzu/configuration/configure_profile_manager.h @@ -0,0 +1,57 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> + +#include <QList> +#include <QWidget> + +class QGraphicsScene; +class QStandardItem; +class QStandardItemModel; +class QTreeView; +class QVBoxLayout; + +namespace Service::Account { +class ProfileManager; +} + +namespace Ui { +class ConfigureProfileManager; +} + +class ConfigureProfileManager : public QWidget { +    Q_OBJECT + +public: +    explicit ConfigureProfileManager(QWidget* parent = nullptr); +    ~ConfigureProfileManager() override; + +    void applyConfiguration(); +    void setConfiguration(); + +private: +    void PopulateUserList(); +    void UpdateCurrentUser(); + +    void SelectUser(const QModelIndex& index); +    void AddUser(); +    void RenameUser(); +    void DeleteUser(); +    void SetUserImage(); + +    QVBoxLayout* layout; +    QTreeView* tree_view; +    QStandardItemModel* item_model; +    QGraphicsScene* scene; + +    std::vector<QList<QStandardItem*>> list_items; + +    std::unique_ptr<Ui::ConfigureProfileManager> ui; +    bool enabled = false; + +    std::unique_ptr<Service::Account::ProfileManager> profile_manager; +}; diff --git a/src/yuzu/configuration/configure_profile_manager.ui b/src/yuzu/configuration/configure_profile_manager.ui new file mode 100644 index 000000000..dedba4998 --- /dev/null +++ b/src/yuzu/configuration/configure_profile_manager.ui @@ -0,0 +1,172 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ConfigureProfileManager</class> + <widget class="QWidget" name="ConfigureProfileManager"> +  <property name="geometry"> +   <rect> +    <x>0</x> +    <y>0</y> +    <width>366</width> +    <height>483</height> +   </rect> +  </property> +  <property name="windowTitle"> +   <string>Form</string> +  </property> +  <layout class="QHBoxLayout" name="horizontalLayout"> +   <item> +    <layout class="QVBoxLayout" name="verticalLayout"> +     <item> +      <widget class="QGroupBox" name="gridGroupBox"> +       <property name="title"> +        <string>Profile Manager</string> +       </property> +       <layout class="QGridLayout" name="gridLayout_2"> +        <property name="sizeConstraint"> +         <enum>QLayout::SetNoConstraint</enum> +        </property> +        <item row="0" column="0"> +         <layout class="QHBoxLayout" name="horizontalLayout_2"> +          <item> +           <widget class="QLabel" name="label"> +            <property name="sizePolicy"> +             <sizepolicy hsizetype="Preferred" vsizetype="Minimum"> +              <horstretch>0</horstretch> +              <verstretch>0</verstretch> +             </sizepolicy> +            </property> +            <property name="text"> +             <string>Current User</string> +            </property> +           </widget> +          </item> +          <item> +           <widget class="QGraphicsView" name="current_user_icon"> +            <property name="minimumSize"> +             <size> +              <width>48</width> +              <height>48</height> +             </size> +            </property> +            <property name="maximumSize"> +             <size> +              <width>48</width> +              <height>48</height> +             </size> +            </property> +            <property name="verticalScrollBarPolicy"> +             <enum>Qt::ScrollBarAlwaysOff</enum> +            </property> +            <property name="horizontalScrollBarPolicy"> +             <enum>Qt::ScrollBarAlwaysOff</enum> +            </property> +            <property name="interactive"> +             <bool>false</bool> +            </property> +           </widget> +          </item> +          <item> +           <widget class="QLabel" name="current_user_username"> +            <property name="sizePolicy"> +             <sizepolicy hsizetype="Preferred" vsizetype="Minimum"> +              <horstretch>0</horstretch> +              <verstretch>0</verstretch> +             </sizepolicy> +            </property> +            <property name="text"> +             <string>Username</string> +            </property> +           </widget> +          </item> +         </layout> +        </item> +        <item row="1" column="0"> +         <widget class="QScrollArea" name="scrollArea"> +          <property name="sizePolicy"> +           <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> +            <horstretch>0</horstretch> +            <verstretch>0</verstretch> +           </sizepolicy> +          </property> +          <property name="frameShape"> +           <enum>QFrame::StyledPanel</enum> +          </property> +          <property name="widgetResizable"> +           <bool>false</bool> +          </property> +         </widget> +        </item> +        <item row="2" column="0"> +         <layout class="QHBoxLayout" name="horizontalLayout_3"> +          <item> +           <widget class="QPushButton" name="pm_set_image"> +            <property name="enabled"> +             <bool>false</bool> +            </property> +            <property name="text"> +             <string>Set Image</string> +            </property> +           </widget> +          </item> +          <item> +           <spacer name="horizontalSpacer"> +            <property name="orientation"> +             <enum>Qt::Horizontal</enum> +            </property> +            <property name="sizeHint" stdset="0"> +             <size> +              <width>40</width> +              <height>20</height> +             </size> +            </property> +           </spacer> +          </item> +          <item> +           <widget class="QPushButton" name="pm_add"> +            <property name="text"> +             <string>Add</string> +            </property> +           </widget> +          </item> +          <item> +           <widget class="QPushButton" name="pm_rename"> +            <property name="enabled"> +             <bool>false</bool> +            </property> +            <property name="text"> +             <string>Rename</string> +            </property> +           </widget> +          </item> +          <item> +           <widget class="QPushButton" name="pm_remove"> +            <property name="enabled"> +             <bool>false</bool> +            </property> +            <property name="text"> +             <string>Remove</string> +            </property> +           </widget> +          </item> +         </layout> +        </item> +       </layout> +      </widget> +     </item> +     <item> +      <widget class="QLabel" name="label_disable_info"> +       <property name="text"> +        <string>Profile management is available only when game is not running.</string> +       </property> +       <property name="wordWrap"> +        <bool>true</bool> +       </property> +      </widget> +     </item> +    </layout> +   </item> +  </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp index ab5d46492..445d01ca0 100644 --- a/src/yuzu/configuration/configure_system.cpp +++ b/src/yuzu/configuration/configure_system.cpp @@ -15,7 +15,6 @@  #include "common/file_util.h"  #include "common/string_util.h"  #include "core/core.h" -#include "core/hle/service/acc/profile_manager.h"  #include "core/settings.h"  #include "ui_configure_system.h"  #include "yuzu/configuration/configure_system.h" @@ -36,64 +35,9 @@ constexpr std::array<int, 12> days_in_month = {{      30,      31,  }}; - -// Same backup JPEG used by acc IProfile::GetImage if no jpeg found -constexpr std::array<u8, 107> backup_jpeg{ -    0xff, 0xd8, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x02, 0x02, -    0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04, 0x08, 0x06, 0x06, 0x05, -    0x06, 0x09, 0x08, 0x0a, 0x0a, 0x09, 0x08, 0x09, 0x09, 0x0a, 0x0c, 0x0f, 0x0c, 0x0a, 0x0b, 0x0e, -    0x0b, 0x09, 0x09, 0x0d, 0x11, 0x0d, 0x0e, 0x0f, 0x10, 0x10, 0x11, 0x10, 0x0a, 0x0c, 0x12, 0x13, -    0x12, 0x10, 0x13, 0x0f, 0x10, 0x10, 0x10, 0xff, 0xc9, 0x00, 0x0b, 0x08, 0x00, 0x01, 0x00, 0x01, -    0x01, 0x01, 0x11, 0x00, 0xff, 0xcc, 0x00, 0x06, 0x00, 0x10, 0x10, 0x05, 0xff, 0xda, 0x00, 0x08, -    0x01, 0x01, 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9, -}; - -QString GetImagePath(Service::Account::UUID uuid) { -    const auto path = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + -                      "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg"; -    return QString::fromStdString(path); -} - -QString GetAccountUsername(const Service::Account::ProfileManager& manager, -                           Service::Account::UUID uuid) { -    Service::Account::ProfileBase profile; -    if (!manager.GetProfileBase(uuid, profile)) { -        return {}; -    } - -    const auto text = Common::StringFromFixedZeroTerminatedBuffer( -        reinterpret_cast<const char*>(profile.username.data()), profile.username.size()); -    return QString::fromStdString(text); -} - -QString FormatUserEntryText(const QString& username, Service::Account::UUID uuid) { -    return ConfigureSystem::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.FormatSwitch())); -} - -QPixmap GetIcon(Service::Account::UUID uuid) { -    QPixmap icon{GetImagePath(uuid)}; - -    if (!icon) { -        icon.fill(Qt::black); -        icon.loadFromData(backup_jpeg.data(), static_cast<u32>(backup_jpeg.size())); -    } - -    return icon.scaled(64, 64, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); -} - -QString GetProfileUsernameFromUser(QWidget* parent, const QString& description_text) { -    return LimitableInputDialog::GetText(parent, ConfigureSystem::tr("Enter Username"), -                                         description_text, 1, -                                         static_cast<int>(Service::Account::profile_username_size)); -}  } // Anonymous namespace -ConfigureSystem::ConfigureSystem(QWidget* parent) -    : QWidget(parent), ui(new Ui::ConfigureSystem), -      profile_manager(std::make_unique<Service::Account::ProfileManager>()) { +ConfigureSystem::ConfigureSystem(QWidget* parent) : QWidget(parent), ui(new Ui::ConfigureSystem) {      ui->setupUi(this);      connect(ui->combo_birthmonth,              static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, @@ -101,51 +45,12 @@ ConfigureSystem::ConfigureSystem(QWidget* parent)      connect(ui->button_regenerate_console_id, &QPushButton::clicked, this,              &ConfigureSystem::RefreshConsoleID); -    layout = new QVBoxLayout; -    tree_view = new QTreeView; -    item_model = new QStandardItemModel(tree_view); -    tree_view->setModel(item_model); - -    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, "Users"); - -    // We must register all custom types with the Qt Automoc system so that we are able to use it -    // with signals/slots. In this case, QList falls under the umbrells of custom types. -    qRegisterMetaType<QList<QStandardItem*>>("QList<QStandardItem*>"); - -    layout->setContentsMargins(0, 0, 0, 0); -    layout->setSpacing(0); -    layout->addWidget(tree_view); - -    ui->scrollArea->setLayout(layout); - -    connect(tree_view, &QTreeView::clicked, this, &ConfigureSystem::SelectUser); - -    connect(ui->pm_add, &QPushButton::pressed, this, &ConfigureSystem::AddUser); -    connect(ui->pm_rename, &QPushButton::pressed, this, &ConfigureSystem::RenameUser); -    connect(ui->pm_remove, &QPushButton::pressed, this, &ConfigureSystem::DeleteUser); -    connect(ui->pm_set_image, &QPushButton::pressed, this, &ConfigureSystem::SetUserImage); -      connect(ui->rng_seed_checkbox, &QCheckBox::stateChanged, this, [this](bool checked) {          ui->rng_seed_edit->setEnabled(checked);          if (!checked)              ui->rng_seed_edit->setText(QStringLiteral("00000000"));      }); -    scene = new QGraphicsScene; -    ui->current_user_icon->setScene(scene); -      this->setConfiguration();  } @@ -156,12 +61,6 @@ void ConfigureSystem::setConfiguration() {      ui->combo_language->setCurrentIndex(Settings::values.language_index); -    item_model->removeRows(0, item_model->rowCount()); -    list_items.clear(); - -    PopulateUserList(); -    UpdateCurrentUser(); -      ui->rng_seed_checkbox->setChecked(Settings::values.rng_seed.has_value());      ui->rng_seed_edit->setEnabled(Settings::values.rng_seed.has_value()); @@ -170,37 +69,6 @@ void ConfigureSystem::setConfiguration() {      ui->rng_seed_edit->setText(rng_seed);  } -void ConfigureSystem::PopulateUserList() { -    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); -} - -void ConfigureSystem::UpdateCurrentUser() { -    ui->pm_add->setEnabled(profile_manager->GetUserCount() < Service::Account::MAX_USERS); - -    const auto& current_user = profile_manager->GetUser(Settings::values.current_user); -    ASSERT(current_user); -    const auto username = GetAccountUsername(*profile_manager, *current_user); - -    scene->clear(); -    scene->addPixmap( -        GetIcon(*current_user).scaled(48, 48, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); -    ui->current_user_username->setText(username); -} -  void ConfigureSystem::ReadSystemSettings() {}  void ConfigureSystem::applyConfiguration() { @@ -256,130 +124,3 @@ void ConfigureSystem::RefreshConsoleID() {      ui->label_console_id->setText(          tr("Console ID: 0x%1").arg(QString::number(console_id, 16).toUpper()));  } - -void ConfigureSystem::SelectUser(const QModelIndex& index) { -    Settings::values.current_user = -        std::clamp<s32>(index.row(), 0, static_cast<s32>(profile_manager->GetUserCount() - 1)); - -    UpdateCurrentUser(); - -    ui->pm_remove->setEnabled(profile_manager->GetUserCount() >= 2); -    ui->pm_rename->setEnabled(true); -    ui->pm_set_image->setEnabled(true); -} - -void ConfigureSystem::AddUser() { -    const auto username = -        GetProfileUsernameFromUser(this, tr("Enter a username for the new user:")); -    if (username.isEmpty()) { -        return; -    } - -    const auto uuid = Service::Account::UUID::Generate(); -    profile_manager->CreateNewUser(uuid, username.toStdString()); - -    item_model->appendRow(new QStandardItem{GetIcon(uuid), FormatUserEntryText(username, uuid)}); -} - -void ConfigureSystem::RenameUser() { -    const auto user = tree_view->currentIndex().row(); -    const auto uuid = profile_manager->GetUser(user); -    ASSERT(uuid); - -    Service::Account::ProfileBase profile; -    if (!profile_manager->GetProfileBase(*uuid, profile)) -        return; - -    const auto new_username = GetProfileUsernameFromUser(this, tr("Enter a new username:")); -    if (new_username.isEmpty()) { -        return; -    } - -    const auto username_std = new_username.toStdString(); -    std::fill(profile.username.begin(), profile.username.end(), '\0'); -    std::copy(username_std.begin(), username_std.end(), profile.username.begin()); - -    profile_manager->SetProfileBase(*uuid, profile); - -    item_model->setItem( -        user, 0, -        new QStandardItem{GetIcon(*uuid), -                          FormatUserEntryText(QString::fromStdString(username_std), *uuid)}); -    UpdateCurrentUser(); -} - -void ConfigureSystem::DeleteUser() { -    const auto index = tree_view->currentIndex().row(); -    const auto uuid = profile_manager->GetUser(index); -    ASSERT(uuid); -    const auto username = GetAccountUsername(*profile_manager, *uuid); - -    const auto confirm = QMessageBox::question( -        this, tr("Confirm Delete"), -        tr("You are about to delete user with name \"%1\". Are you sure?").arg(username)); - -    if (confirm == QMessageBox::No) -        return; - -    if (Settings::values.current_user == tree_view->currentIndex().row()) -        Settings::values.current_user = 0; -    UpdateCurrentUser(); - -    if (!profile_manager->RemoveUser(*uuid)) -        return; - -    item_model->removeRows(tree_view->currentIndex().row(), 1); -    tree_view->clearSelection(); - -    ui->pm_remove->setEnabled(false); -    ui->pm_rename->setEnabled(false); -} - -void ConfigureSystem::SetUserImage() { -    const auto index = tree_view->currentIndex().row(); -    const auto uuid = profile_manager->GetUser(index); -    ASSERT(uuid); - -    const auto file = QFileDialog::getOpenFileName(this, tr("Select User Image"), QString(), -                                                   tr("JPEG Images (*.jpg *.jpeg)")); - -    if (file.isEmpty()) { -        return; -    } - -    const auto image_path = GetImagePath(*uuid); -    if (QFile::exists(image_path) && !QFile::remove(image_path)) { -        QMessageBox::warning( -            this, tr("Error deleting image"), -            tr("Error occurred attempting to overwrite previous image at: %1.").arg(image_path)); -        return; -    } - -    const auto raw_path = QString::fromStdString( -        FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "/system/save/8000000000000010"); -    const QFileInfo raw_info{raw_path}; -    if (raw_info.exists() && !raw_info.isDir() && !QFile::remove(raw_path)) { -        QMessageBox::warning(this, tr("Error deleting file"), -                             tr("Unable to delete existing file: %1.").arg(raw_path)); -        return; -    } - -    const QString absolute_dst_path = QFileInfo{image_path}.absolutePath(); -    if (!QDir{raw_path}.mkpath(absolute_dst_path)) { -        QMessageBox::warning( -            this, tr("Error creating user image directory"), -            tr("Unable to create directory %1 for storing user images.").arg(absolute_dst_path)); -        return; -    } - -    if (!QFile::copy(file, image_path)) { -        QMessageBox::warning(this, tr("Error copying user image"), -                             tr("Unable to copy image from %1 to %2").arg(file, image_path)); -        return; -    } - -    const auto username = GetAccountUsername(*profile_manager, *uuid); -    item_model->setItem(index, 0, -                        new QStandardItem{GetIcon(*uuid), FormatUserEntryText(username, *uuid)}); -    UpdateCurrentUser(); -} diff --git a/src/yuzu/configuration/configure_system.h b/src/yuzu/configuration/configure_system.h index 07764e1f7..cf1e54de5 100644 --- a/src/yuzu/configuration/configure_system.h +++ b/src/yuzu/configuration/configure_system.h @@ -9,16 +9,6 @@  #include <QList>  #include <QWidget> -class QGraphicsScene; -class QStandardItem; -class QStandardItemModel; -class QTreeView; -class QVBoxLayout; - -namespace Service::Account { -class ProfileManager; -} -  namespace Ui {  class ConfigureSystem;  } @@ -39,21 +29,6 @@ private:      void UpdateBirthdayComboBox(int birthmonth_index);      void RefreshConsoleID(); -    void PopulateUserList(); -    void UpdateCurrentUser(); -    void SelectUser(const QModelIndex& index); -    void AddUser(); -    void RenameUser(); -    void DeleteUser(); -    void SetUserImage(); - -    QVBoxLayout* layout; -    QTreeView* tree_view; -    QStandardItemModel* item_model; -    QGraphicsScene* scene; - -    std::vector<QList<QStandardItem*>> list_items; -      std::unique_ptr<Ui::ConfigureSystem> ui;      bool enabled = false; @@ -61,6 +36,4 @@ private:      int birthday = 0;      int language_index = 0;      int sound_index = 0; - -    std::unique_ptr<Service::Account::ProfileManager> profile_manager;  }; diff --git a/src/yuzu/configuration/configure_system.ui b/src/yuzu/configuration/configure_system.ui index a91580893..74e800c2a 100644 --- a/src/yuzu/configuration/configure_system.ui +++ b/src/yuzu/configuration/configure_system.ui @@ -280,141 +280,17 @@        </widget>       </item>       <item> -      <widget class="QGroupBox" name="gridGroupBox"> -       <property name="title"> -        <string>Profile Manager</string> +      <spacer name="verticalSpacer"> +       <property name="orientation"> +        <enum>Qt::Vertical</enum>         </property> -       <layout class="QGridLayout" name="gridLayout_2"> -        <property name="sizeConstraint"> -         <enum>QLayout::SetNoConstraint</enum> -        </property> -        <item row="0" column="0"> -         <layout class="QHBoxLayout" name="horizontalLayout_2"> -          <item> -           <widget class="QLabel" name="label"> -            <property name="sizePolicy"> -             <sizepolicy hsizetype="Preferred" vsizetype="Minimum"> -              <horstretch>0</horstretch> -              <verstretch>0</verstretch> -             </sizepolicy> -            </property> -            <property name="text"> -             <string>Current User</string> -            </property> -           </widget> -          </item> -          <item> -           <widget class="QGraphicsView" name="current_user_icon"> -            <property name="minimumSize"> -             <size> -              <width>48</width> -              <height>48</height> -             </size> -            </property> -            <property name="maximumSize"> -             <size> -              <width>48</width> -              <height>48</height> -             </size> -            </property> -            <property name="verticalScrollBarPolicy"> -             <enum>Qt::ScrollBarAlwaysOff</enum> -            </property> -            <property name="horizontalScrollBarPolicy"> -             <enum>Qt::ScrollBarAlwaysOff</enum> -            </property> -            <property name="interactive"> -             <bool>false</bool> -            </property> -           </widget> -          </item> -          <item> -           <widget class="QLabel" name="current_user_username"> -            <property name="sizePolicy"> -             <sizepolicy hsizetype="Preferred" vsizetype="Minimum"> -              <horstretch>0</horstretch> -              <verstretch>0</verstretch> -             </sizepolicy> -            </property> -            <property name="text"> -             <string>Username</string> -            </property> -           </widget> -          </item> -         </layout> -        </item> -        <item row="1" column="0"> -         <widget class="QScrollArea" name="scrollArea"> -          <property name="sizePolicy"> -           <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> -            <horstretch>0</horstretch> -            <verstretch>0</verstretch> -           </sizepolicy> -          </property> -          <property name="frameShape"> -           <enum>QFrame::StyledPanel</enum> -          </property> -          <property name="widgetResizable"> -           <bool>false</bool> -          </property> -         </widget> -        </item> -        <item row="2" column="0"> -         <layout class="QHBoxLayout" name="horizontalLayout_3"> -          <item> -           <widget class="QPushButton" name="pm_set_image"> -            <property name="enabled"> -             <bool>false</bool> -            </property> -            <property name="text"> -             <string>Set Image</string> -            </property> -           </widget> -          </item> -          <item> -           <spacer name="horizontalSpacer"> -            <property name="orientation"> -             <enum>Qt::Horizontal</enum> -            </property> -            <property name="sizeHint" stdset="0"> -             <size> -              <width>40</width> -              <height>20</height> -             </size> -            </property> -           </spacer> -          </item> -          <item> -           <widget class="QPushButton" name="pm_add"> -            <property name="text"> -             <string>Add</string> -            </property> -           </widget> -          </item> -          <item> -           <widget class="QPushButton" name="pm_rename"> -            <property name="enabled"> -             <bool>false</bool> -            </property> -            <property name="text"> -             <string>Rename</string> -            </property> -           </widget> -          </item> -          <item> -           <widget class="QPushButton" name="pm_remove"> -            <property name="enabled"> -             <bool>false</bool> -            </property> -            <property name="text"> -             <string>Remove</string> -            </property> -           </widget> -          </item> -         </layout> -        </item> -       </layout> -      </widget> +       <property name="sizeHint" stdset="0"> +        <size> +         <width>20</width> +         <height>40</height> +        </size> +       </property> +      </spacer>       </item>       <item>        <widget class="QLabel" name="label_disable_info"> | 
