diff options
| author | 2018-11-22 21:03:33 -0500 | |
|---|---|---|
| committer | 2018-12-03 17:26:27 -0500 | |
| commit | bf90f2402dae06ebf4292e59bf8703490596f6ba (patch) | |
| tree | 89bb7e98853f74864d98122c2f8addd8eb7ac40b /src | |
| parent | am: Use ProfileSelect applet (diff) | |
| download | yuzu-bf90f2402dae06ebf4292e59bf8703490596f6ba.tar.gz yuzu-bf90f2402dae06ebf4292e59bf8703490596f6ba.tar.xz yuzu-bf90f2402dae06ebf4292e59bf8703490596f6ba.zip | |
qt: Implement GUI dialog frontend for ProfileSelector
Presents profiles in a list, similar to switch.
Diffstat (limited to 'src')
| -rw-r--r-- | src/core/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/yuzu/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/yuzu/applets/profile_select.cpp | 168 | ||||
| -rw-r--r-- | src/yuzu/applets/profile_select.h | 73 | ||||
| -rw-r--r-- | src/yuzu/main.cpp | 22 | ||||
| -rw-r--r-- | src/yuzu/main.h | 2 |
6 files changed, 269 insertions, 0 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 4b51943ab..4f0ea5d67 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -160,6 +160,8 @@ add_library(core STATIC | |||
| 160 | hle/service/am/applet_oe.h | 160 | hle/service/am/applet_oe.h |
| 161 | hle/service/am/applets/applets.cpp | 161 | hle/service/am/applets/applets.cpp |
| 162 | hle/service/am/applets/applets.h | 162 | hle/service/am/applets/applets.h |
| 163 | hle/service/am/applets/profile_select.cpp | ||
| 164 | hle/service/am/applets/profile_select.h | ||
| 163 | hle/service/am/applets/software_keyboard.cpp | 165 | hle/service/am/applets/software_keyboard.cpp |
| 164 | hle/service/am/applets/software_keyboard.h | 166 | hle/service/am/applets/software_keyboard.h |
| 165 | hle/service/am/applets/stub_applet.cpp | 167 | hle/service/am/applets/stub_applet.cpp |
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index cfca8f4a8..61562b9b2 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt | |||
| @@ -7,6 +7,8 @@ add_executable(yuzu | |||
| 7 | Info.plist | 7 | Info.plist |
| 8 | about_dialog.cpp | 8 | about_dialog.cpp |
| 9 | about_dialog.h | 9 | about_dialog.h |
| 10 | applets/profile_select.cpp | ||
| 11 | applets/profile_select.h | ||
| 10 | applets/software_keyboard.cpp | 12 | applets/software_keyboard.cpp |
| 11 | applets/software_keyboard.h | 13 | applets/software_keyboard.h |
| 12 | bootmanager.cpp | 14 | bootmanager.cpp |
diff --git a/src/yuzu/applets/profile_select.cpp b/src/yuzu/applets/profile_select.cpp new file mode 100644 index 000000000..5c1b65a2c --- /dev/null +++ b/src/yuzu/applets/profile_select.cpp | |||
| @@ -0,0 +1,168 @@ | |||
| 1 | // Copyright 2018 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <mutex> | ||
| 6 | #include <QDialogButtonBox> | ||
| 7 | #include <QLabel> | ||
| 8 | #include <QLineEdit> | ||
| 9 | #include <QScrollArea> | ||
| 10 | #include <QStandardItemModel> | ||
| 11 | #include <QVBoxLayout> | ||
| 12 | #include "common/file_util.h" | ||
| 13 | #include "common/string_util.h" | ||
| 14 | #include "core/hle/lock.h" | ||
| 15 | #include "yuzu/applets/profile_select.h" | ||
| 16 | #include "yuzu/main.h" | ||
| 17 | |||
| 18 | // Same backup JPEG used by acc IProfile::GetImage if no jpeg found | ||
| 19 | constexpr std::array<u8, 107> backup_jpeg{ | ||
| 20 | 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x02, 0x02, | ||
| 21 | 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04, 0x08, 0x06, 0x06, 0x05, | ||
| 22 | 0x06, 0x09, 0x08, 0x0a, 0x0a, 0x09, 0x08, 0x09, 0x09, 0x0a, 0x0c, 0x0f, 0x0c, 0x0a, 0x0b, 0x0e, | ||
| 23 | 0x0b, 0x09, 0x09, 0x0d, 0x11, 0x0d, 0x0e, 0x0f, 0x10, 0x10, 0x11, 0x10, 0x0a, 0x0c, 0x12, 0x13, | ||
| 24 | 0x12, 0x10, 0x13, 0x0f, 0x10, 0x10, 0x10, 0xff, 0xc9, 0x00, 0x0b, 0x08, 0x00, 0x01, 0x00, 0x01, | ||
| 25 | 0x01, 0x01, 0x11, 0x00, 0xff, 0xcc, 0x00, 0x06, 0x00, 0x10, 0x10, 0x05, 0xff, 0xda, 0x00, 0x08, | ||
| 26 | 0x01, 0x01, 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9, | ||
| 27 | }; | ||
| 28 | |||
| 29 | QString FormatUserEntryText(const QString& username, Service::Account::UUID uuid) { | ||
| 30 | return QtProfileSelectionDialog::tr( | ||
| 31 | "%1\n%2", "%1 is the profile username, %2 is the formatted UUID (e.g. " | ||
| 32 | "00112233-4455-6677-8899-AABBCCDDEEFF))") | ||
| 33 | .arg(username, QString::fromStdString(uuid.FormatSwitch())); | ||
| 34 | } | ||
| 35 | |||
| 36 | QString GetImagePath(Service::Account::UUID uuid) { | ||
| 37 | const auto path = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + | ||
| 38 | "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg"; | ||
| 39 | return QString::fromStdString(path); | ||
| 40 | } | ||
| 41 | |||
| 42 | QPixmap GetIcon(Service::Account::UUID uuid) { | ||
| 43 | QPixmap icon{GetImagePath(uuid)}; | ||
| 44 | |||
| 45 | if (!icon) { | ||
| 46 | icon.fill(Qt::black); | ||
| 47 | icon.loadFromData(backup_jpeg.data(), static_cast<u32>(backup_jpeg.size())); | ||
| 48 | } | ||
| 49 | |||
| 50 | return icon.scaled(64, 64, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); | ||
| 51 | } | ||
| 52 | |||
| 53 | QtProfileSelectionDialog::QtProfileSelectionDialog(QWidget* parent) | ||
| 54 | : QDialog(parent), profile_manager(std::make_unique<Service::Account::ProfileManager>()) { | ||
| 55 | outer_layout = new QVBoxLayout; | ||
| 56 | |||
| 57 | instruction_label = new QLabel(tr("Select a user:")); | ||
| 58 | |||
| 59 | scroll_area = new QScrollArea; | ||
| 60 | |||
| 61 | buttons = new QDialogButtonBox; | ||
| 62 | buttons->addButton(tr("Cancel"), QDialogButtonBox::RejectRole); | ||
| 63 | buttons->addButton(tr("OK"), QDialogButtonBox::AcceptRole); | ||
| 64 | |||
| 65 | connect(buttons, &QDialogButtonBox::accepted, this, &QtProfileSelectionDialog::accept); | ||
| 66 | connect(buttons, &QDialogButtonBox::rejected, this, &QtProfileSelectionDialog::reject); | ||
| 67 | |||
| 68 | outer_layout->addWidget(instruction_label); | ||
| 69 | outer_layout->addWidget(scroll_area); | ||
| 70 | outer_layout->addWidget(buttons); | ||
| 71 | |||
| 72 | layout = new QVBoxLayout; | ||
| 73 | tree_view = new QTreeView; | ||
| 74 | item_model = new QStandardItemModel(tree_view); | ||
| 75 | tree_view->setModel(item_model); | ||
| 76 | |||
| 77 | tree_view->setAlternatingRowColors(true); | ||
| 78 | tree_view->setSelectionMode(QHeaderView::SingleSelection); | ||
| 79 | tree_view->setSelectionBehavior(QHeaderView::SelectRows); | ||
| 80 | tree_view->setVerticalScrollMode(QHeaderView::ScrollPerPixel); | ||
| 81 | tree_view->setHorizontalScrollMode(QHeaderView::ScrollPerPixel); | ||
| 82 | tree_view->setSortingEnabled(true); | ||
| 83 | tree_view->setEditTriggers(QHeaderView::NoEditTriggers); | ||
| 84 | tree_view->setUniformRowHeights(true); | ||
| 85 | tree_view->setIconSize({64, 64}); | ||
| 86 | tree_view->setContextMenuPolicy(Qt::NoContextMenu); | ||
| 87 | |||
| 88 | item_model->insertColumns(0, 1); | ||
| 89 | item_model->setHeaderData(0, Qt::Horizontal, "Users"); | ||
| 90 | |||
| 91 | // We must register all custom types with the Qt Automoc system so that we are able to use it | ||
| 92 | // with signals/slots. In this case, QList falls under the umbrells of custom types. | ||
| 93 | qRegisterMetaType<QList<QStandardItem*>>("QList<QStandardItem*>"); | ||
| 94 | |||
| 95 | layout->setContentsMargins(0, 0, 0, 0); | ||
| 96 | layout->setSpacing(0); | ||
| 97 | layout->addWidget(tree_view); | ||
| 98 | |||
| 99 | scroll_area->setLayout(layout); | ||
| 100 | |||
| 101 | connect(tree_view, &QTreeView::clicked, this, &QtProfileSelectionDialog::SelectUser); | ||
| 102 | |||
| 103 | const auto& profiles = profile_manager->GetAllUsers(); | ||
| 104 | for (const auto& user : profiles) { | ||
| 105 | Service::Account::ProfileBase profile; | ||
| 106 | if (!profile_manager->GetProfileBase(user, profile)) | ||
| 107 | continue; | ||
| 108 | |||
| 109 | const auto username = Common::StringFromFixedZeroTerminatedBuffer( | ||
| 110 | reinterpret_cast<const char*>(profile.username.data()), profile.username.size()); | ||
| 111 | |||
| 112 | list_items.push_back(QList<QStandardItem*>{new QStandardItem{ | ||
| 113 | GetIcon(user), FormatUserEntryText(QString::fromStdString(username), user)}}); | ||
| 114 | } | ||
| 115 | |||
| 116 | for (const auto& item : list_items) | ||
| 117 | item_model->appendRow(item); | ||
| 118 | |||
| 119 | setLayout(outer_layout); | ||
| 120 | setWindowTitle(tr("Profile Selector")); | ||
| 121 | resize(550, 400); | ||
| 122 | } | ||
| 123 | |||
| 124 | QtProfileSelectionDialog::~QtProfileSelectionDialog() = default; | ||
| 125 | |||
| 126 | void QtProfileSelectionDialog::accept() { | ||
| 127 | ok = true; | ||
| 128 | QDialog::accept(); | ||
| 129 | } | ||
| 130 | |||
| 131 | void QtProfileSelectionDialog::reject() { | ||
| 132 | ok = false; | ||
| 133 | user_index = 0; | ||
| 134 | QDialog::reject(); | ||
| 135 | } | ||
| 136 | |||
| 137 | bool QtProfileSelectionDialog::GetStatus() const { | ||
| 138 | return ok; | ||
| 139 | } | ||
| 140 | |||
| 141 | u32 QtProfileSelectionDialog::GetIndex() const { | ||
| 142 | return user_index; | ||
| 143 | } | ||
| 144 | |||
| 145 | void QtProfileSelectionDialog::SelectUser(const QModelIndex& index) { | ||
| 146 | user_index = index.row(); | ||
| 147 | } | ||
| 148 | |||
| 149 | QtProfileSelector::QtProfileSelector(GMainWindow& parent) { | ||
| 150 | connect(this, &QtProfileSelector::MainWindowSelectProfile, &parent, | ||
| 151 | &GMainWindow::ProfileSelectorSelectProfile, Qt::QueuedConnection); | ||
| 152 | connect(&parent, &GMainWindow::ProfileSelectorFinishedSelection, this, | ||
| 153 | &QtProfileSelector::MainWindowFinishedSelection, Qt::DirectConnection); | ||
| 154 | } | ||
| 155 | |||
| 156 | QtProfileSelector::~QtProfileSelector() = default; | ||
| 157 | |||
| 158 | void QtProfileSelector::SelectProfile( | ||
| 159 | std::function<void(std::optional<Service::Account::UUID>)> callback) const { | ||
| 160 | this->callback = std::move(callback); | ||
| 161 | emit MainWindowSelectProfile(); | ||
| 162 | } | ||
| 163 | |||
| 164 | void QtProfileSelector::MainWindowFinishedSelection(std::optional<Service::Account::UUID> uuid) { | ||
| 165 | // Acquire the HLE mutex | ||
| 166 | std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); | ||
| 167 | callback(uuid); | ||
| 168 | } | ||
diff --git a/src/yuzu/applets/profile_select.h b/src/yuzu/applets/profile_select.h new file mode 100644 index 000000000..9acb092f3 --- /dev/null +++ b/src/yuzu/applets/profile_select.h | |||
| @@ -0,0 +1,73 @@ | |||
| 1 | // Copyright 2018 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <vector> | ||
| 8 | #include <QDialog> | ||
| 9 | #include <QList> | ||
| 10 | #include "core/frontend/applets/profile_select.h" | ||
| 11 | |||
| 12 | class GMainWindow; | ||
| 13 | class QDialogButtonBox; | ||
| 14 | class QGraphicsScene; | ||
| 15 | class QLabel; | ||
| 16 | class QScrollArea; | ||
| 17 | class QStandardItem; | ||
| 18 | class QStandardItemModel; | ||
| 19 | class QTreeView; | ||
| 20 | class QVBoxLayout; | ||
| 21 | |||
| 22 | class QtProfileSelectionDialog final : public QDialog { | ||
| 23 | Q_OBJECT | ||
| 24 | |||
| 25 | public: | ||
| 26 | QtProfileSelectionDialog(QWidget* parent); | ||
| 27 | ~QtProfileSelectionDialog() override; | ||
| 28 | |||
| 29 | void accept() override; | ||
| 30 | void reject() override; | ||
| 31 | |||
| 32 | bool GetStatus() const; | ||
| 33 | u32 GetIndex() const; | ||
| 34 | |||
| 35 | private: | ||
| 36 | bool ok = false; | ||
| 37 | u32 user_index = 0; | ||
| 38 | |||
| 39 | void SelectUser(const QModelIndex& index); | ||
| 40 | |||
| 41 | QVBoxLayout* layout; | ||
| 42 | QTreeView* tree_view; | ||
| 43 | QStandardItemModel* item_model; | ||
| 44 | QGraphicsScene* scene; | ||
| 45 | |||
| 46 | std::vector<QList<QStandardItem*>> list_items; | ||
| 47 | |||
| 48 | QVBoxLayout* outer_layout; | ||
| 49 | QLabel* instruction_label; | ||
| 50 | QScrollArea* scroll_area; | ||
| 51 | QDialogButtonBox* buttons; | ||
| 52 | |||
| 53 | std::unique_ptr<Service::Account::ProfileManager> profile_manager; | ||
| 54 | }; | ||
| 55 | |||
| 56 | class QtProfileSelector final : public QObject, public Core::Frontend::ProfileSelectApplet { | ||
| 57 | Q_OBJECT | ||
| 58 | |||
| 59 | public: | ||
| 60 | explicit QtProfileSelector(GMainWindow& parent); | ||
| 61 | ~QtProfileSelector() override; | ||
| 62 | |||
| 63 | void SelectProfile( | ||
| 64 | std::function<void(std::optional<Service::Account::UUID>)> callback) const override; | ||
| 65 | |||
| 66 | signals: | ||
| 67 | void MainWindowSelectProfile() const; | ||
| 68 | |||
| 69 | private: | ||
| 70 | void MainWindowFinishedSelection(std::optional<Service::Account::UUID> uuid); | ||
| 71 | |||
| 72 | mutable std::function<void(std::optional<Service::Account::UUID>)> callback; | ||
| 73 | }; | ||
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index b39bf1903..085cab74b 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -208,6 +208,28 @@ GMainWindow::~GMainWindow() { | |||
| 208 | delete render_window; | 208 | delete render_window; |
| 209 | } | 209 | } |
| 210 | 210 | ||
| 211 | void GMainWindow::ProfileSelectorSelectProfile() { | ||
| 212 | QtProfileSelectionDialog dialog(this); | ||
| 213 | dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | | ||
| 214 | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint); | ||
| 215 | dialog.setWindowModality(Qt::WindowModal); | ||
| 216 | dialog.exec(); | ||
| 217 | |||
| 218 | if (!dialog.GetStatus()) { | ||
| 219 | emit ProfileSelectorFinishedSelection(std::nullopt); | ||
| 220 | return; | ||
| 221 | } | ||
| 222 | |||
| 223 | Service::Account::ProfileManager manager; | ||
| 224 | const auto uuid = manager.GetUser(dialog.GetIndex()); | ||
| 225 | if (!uuid.has_value()) { | ||
| 226 | emit ProfileSelectorFinishedSelection(std::nullopt); | ||
| 227 | return; | ||
| 228 | } | ||
| 229 | |||
| 230 | emit ProfileSelectorFinishedSelection(uuid); | ||
| 231 | } | ||
| 232 | |||
| 211 | void GMainWindow::SoftwareKeyboardGetText( | 233 | void GMainWindow::SoftwareKeyboardGetText( |
| 212 | const Core::Frontend::SoftwareKeyboardParameters& parameters) { | 234 | const Core::Frontend::SoftwareKeyboardParameters& parameters) { |
| 213 | QtSoftwareKeyboardDialog dialog(this, parameters); | 235 | QtSoftwareKeyboardDialog dialog(this, parameters); |
diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 674e73412..7c5eaf16b 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h | |||
| @@ -99,10 +99,12 @@ signals: | |||
| 99 | // Signal that tells widgets to update icons to use the current theme | 99 | // Signal that tells widgets to update icons to use the current theme |
| 100 | void UpdateThemedIcons(); | 100 | void UpdateThemedIcons(); |
| 101 | 101 | ||
| 102 | void ProfileSelectorFinishedSelection(std::optional<Service::Account::UUID> uuid); | ||
| 102 | void SoftwareKeyboardFinishedText(std::optional<std::u16string> text); | 103 | void SoftwareKeyboardFinishedText(std::optional<std::u16string> text); |
| 103 | void SoftwareKeyboardFinishedCheckDialog(); | 104 | void SoftwareKeyboardFinishedCheckDialog(); |
| 104 | 105 | ||
| 105 | public slots: | 106 | public slots: |
| 107 | void ProfileSelectorSelectProfile(); | ||
| 106 | void SoftwareKeyboardGetText(const Core::Frontend::SoftwareKeyboardParameters& parameters); | 108 | void SoftwareKeyboardGetText(const Core::Frontend::SoftwareKeyboardParameters& parameters); |
| 107 | void SoftwareKeyboardInvokeCheckDialog(std::u16string error_message); | 109 | void SoftwareKeyboardInvokeCheckDialog(std::u16string error_message); |
| 108 | 110 | ||