diff --git a/external/libphysis b/external/libphysis index 07193fc..1b8a8dd 160000 --- a/external/libphysis +++ b/external/libphysis @@ -1 +1 @@ -Subproject commit 07193fc6e272a41c498112b3eb0e5808a6095e53 +Subproject commit 1b8a8dddcafe31799dfb5597ab41cc32ac3ec7a3 diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index b45195d..a49f226 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -47,6 +47,7 @@ target_sources(astra PRIVATE include/benchmarkinstaller.h include/compatibilitytoolinstaller.h include/encryptedarg.h + include/existinginstallmodel.h include/gamerunner.h include/gameinstaller.h include/headline.h @@ -68,6 +69,7 @@ target_sources(astra PRIVATE src/benchmarkinstaller.cpp src/compatibilitytoolinstaller.cpp src/encryptedarg.cpp + src/existinginstallmodel.cpp src/gamerunner.cpp src/headline.cpp src/gameinstaller.cpp diff --git a/launcher/include/existinginstallmodel.h b/launcher/include/existinginstallmodel.h new file mode 100644 index 0000000..b135d5f --- /dev/null +++ b/launcher/include/existinginstallmodel.h @@ -0,0 +1,35 @@ +// SPDX-FileCopyrightText: 2024 Joshua Goins +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include +#include + +class ExistingInstallModel : public QAbstractListModel +{ + Q_OBJECT + QML_ELEMENT + +public: + enum CustomRoles { + TypeRole = Qt::UserRole, + PathRole, + }; + + explicit ExistingInstallModel(QObject *parent = nullptr); + + QVariant data(const QModelIndex &index, int role) const override; + int rowCount(const QModelIndex &parent) const override; + QHash roleNames() const override; + +private: + void fill(); + + struct ExistingInstall { + ExistingInstallType type; + QString path; + }; + + QList m_existingInstalls; +}; diff --git a/launcher/src/existinginstallmodel.cpp b/launcher/src/existinginstallmodel.cpp new file mode 100644 index 0000000..557acca --- /dev/null +++ b/launcher/src/existinginstallmodel.cpp @@ -0,0 +1,68 @@ +// SPDX-FileCopyrightText: 2024 Joshua Goins +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "existinginstallmodel.h" + +#include + +ExistingInstallModel::ExistingInstallModel(QObject *parent) + : QAbstractListModel(parent) +{ + fill(); +} + +QVariant ExistingInstallModel::data(const QModelIndex &index, int role) const +{ + Q_ASSERT(checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid)); + + const auto &install = m_existingInstalls[index.row()]; + + switch (role) { + case TypeRole: { + switch (install.type) { + case ExistingInstallType::OfficialLauncher: + return i18n("Official Launcher"); + case ExistingInstallType::XIVLauncherCore: + return QStringLiteral("XIVLauncher.Core"); + case ExistingInstallType::XIVOnMac: + return QStringLiteral("XIV on Mac"); + case ExistingInstallType::XIVQuickLauncher: + return QStringLiteral("XIVQuickLauncher"); + default: + return i18n("Unknown"); + } + } + case PathRole: + return install.path; + default: + return {}; + } +} + +int ExistingInstallModel::rowCount(const QModelIndex &parent) const +{ + return parent.isValid() ? 0 : m_existingInstalls.size(); +} + +QHash ExistingInstallModel::roleNames() const +{ + return { + {TypeRole, "type"}, + {PathRole, "path"}, + }; +} + +void ExistingInstallModel::fill() +{ + auto dirs = physis_find_existing_game_dirs(); + for (int i = 0; i < dirs.count; i++) { + // We shouldn't be able to import our own game installs, that's handled elsewhere in the UI + if (dirs.entries[i].install_type != ExistingInstallType::Astra) { + beginInsertRows({}, m_existingInstalls.size(), m_existingInstalls.size()); + m_existingInstalls.push_back(ExistingInstall{.type = dirs.entries[i].install_type, .path = QString::fromUtf8(dirs.entries[i].path)}); + endInsertRows(); + } + } +} + +#include "moc_existinginstallmodel.cpp" \ No newline at end of file diff --git a/launcher/ui/Setup/ExistingSetup.qml b/launcher/ui/Setup/ExistingSetup.qml index dfa65c8..725403a 100644 --- a/launcher/ui/Setup/ExistingSetup.qml +++ b/launcher/ui/Setup/ExistingSetup.qml @@ -19,30 +19,6 @@ FormCard.FormCardPage { title: i18n("Find Existing Installation") - FormCard.FormCard { - Layout.topMargin: Kirigami.Units.largeSpacing - Layout.fillWidth: true - - FormCard.FormTextDelegate { - id: helpTextDelegate - - text: i18n("Please select the path to your existing installation.") - } - - FormCard.FormDelegateSeparator { - above: helpTextDelegate - below: selectDelegate - } - - FormCard.FormButtonDelegate { - id: selectDelegate - - text: i18n("Select Existing Path") - icon.name: "document-open-folder" - onClicked: dialog.open() - } - } - data: FolderDialog { id: dialog @@ -51,4 +27,50 @@ FormCard.FormCardPage { applicationWindow().checkSetup(); } } -} \ No newline at end of file + + FormCard.FormCard { + Layout.fillWidth: true + Layout.topMargin: Kirigami.Units.largeSpacing + + Repeater { + model: ExistingInstallModel {} + + delegate: FormCard.FormButtonDelegate { + required property var path + required property var type + + text: path + description: type + + onClicked: { + page.profile.gamePath = path; + applicationWindow().checkSetup(); + } + } + } + } + + FormCard.FormCard { + Layout.fillWidth: true + Layout.topMargin: Kirigami.Units.largeSpacing + + FormCard.FormTextDelegate { + id: helpTextDelegate + + text: i18n("If you can't find your existing game installation, manually select the path below.") + } + FormCard.FormDelegateSeparator { + above: helpTextDelegate + below: selectDelegate + } + FormCard.FormButtonDelegate { + id: selectDelegate + + icon.name: "document-open-folder" + text: i18n("Select Existing Path") + + onClicked: dialog.open() + } + } +} +