Merge pull request #1992 from DarkLordZach/move-profile-manager-ui
qt: Move profile manager to own UI tab
This commit is contained in:
		| @@ -37,6 +37,8 @@ add_executable(yuzu | ||||
|     configuration/configure_input_simple.h | ||||
|     configuration/configure_mouse_advanced.cpp | ||||
|     configuration/configure_mouse_advanced.h | ||||
|     configuration/configure_profile_manager.cpp | ||||
|     configuration/configure_profile_manager.h | ||||
|     configuration/configure_system.cpp | ||||
|     configuration/configure_system.h | ||||
|     configuration/configure_per_general.cpp | ||||
| @@ -94,6 +96,7 @@ set(UIS | ||||
|     configuration/configure_input_simple.ui | ||||
|     configuration/configure_mouse_advanced.ui | ||||
|     configuration/configure_per_general.ui | ||||
|     configuration/configure_profile_manager.ui | ||||
|     configuration/configure_system.ui | ||||
|     configuration/configure_touchscreen_advanced.ui | ||||
|     configuration/configure_web.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> | ||||
| @@ -103,6 +108,12 @@ | ||||
|    <header>configuration/configure_system.h</header> | ||||
|    <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> | ||||
|   | ||||
| @@ -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(); | ||||
|  | ||||
|   | ||||
							
								
								
									
										300
									
								
								src/yuzu/configuration/configure_profile_manager.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										300
									
								
								src/yuzu/configuration/configure_profile_manager.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -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(); | ||||
| } | ||||
							
								
								
									
										57
									
								
								src/yuzu/configuration/configure_profile_manager.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								src/yuzu/configuration/configure_profile_manager.h
									
									
									
									
									
										Normal file
									
								
							| @@ -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; | ||||
| }; | ||||
							
								
								
									
										172
									
								
								src/yuzu/configuration/configure_profile_manager.ui
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										172
									
								
								src/yuzu/configuration/configure_profile_manager.ui
									
									
									
									
									
										Normal file
									
								
							| @@ -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> | ||||
| @@ -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(); | ||||
| } | ||||
|   | ||||
| @@ -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; | ||||
| }; | ||||
|   | ||||
| @@ -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"> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user