Merge a478fa60d3016445d076bd9d746aa8e2b40a9e49 into 79b7e277f1f06f6b315e293b029423fe35e57431

This commit is contained in:
Naomi 2025-08-01 11:26:32 -07:00 committed by GitHub
commit 48ecb8b11f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 723 additions and 4 deletions

View File

@ -49,6 +49,7 @@
#include "settings/OverrideSetting.h"
#include "settings/Setting.h"
#include "Application.h"
#include "BuildConfig.h"
#include "Commandline.h"
#include "FileSystem.h"
@ -124,6 +125,14 @@ BaseInstance::BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr s
m_settings->registerSetting("ManagedPackVersionName", "");
m_settings->registerSetting("Profiler", "");
// Shared folders
m_settings->registerSetting("UseSharedScreenshotsFolder", false);
m_settings->registerSetting("SharedScreenshotsPath", FS::PathCombine(APPLICATION->dataRoot(), "screenshots"));
m_settings->registerSetting("UseSharedSavesFolder", false);
m_settings->registerSetting("SharedSavesPath", FS::PathCombine(APPLICATION->dataRoot(), "saves"));
m_settings->registerSetting("UseSharedResourcePacksFolder", false);
m_settings->registerSetting("SharedResourcePacksPath", FS::PathCombine(APPLICATION->dataRoot(), "resourcepacks"));
}
QString BaseInstance::getPreLaunchCommand()

View File

@ -284,6 +284,11 @@ class BaseInstance : public QObject, public std::enable_shared_from_this<BaseIns
bool isLegacy();
/**
* \brief Should be called whenever settings have changed that need to be re-applied.
*/
virtual void applySettings() {}
protected:
void changeStatus(Status newStatus);

View File

@ -1070,6 +1070,8 @@ SET(LAUNCHER_SOURCES
ui/dialogs/ExportPackDialog.h
ui/dialogs/ExportToModListDialog.cpp
ui/dialogs/ExportToModListDialog.h
ui/dialogs/FileConflictDialog.cpp
ui/dialogs/FileConflictDialog.h
ui/dialogs/IconPickerDialog.cpp
ui/dialogs/IconPickerDialog.h
ui/dialogs/ImportResourceDialog.cpp
@ -1124,6 +1126,8 @@ SET(LAUNCHER_SOURCES
ui/widgets/CustomCommands.h
ui/widgets/EnvironmentVariables.cpp
ui/widgets/EnvironmentVariables.h
ui/widgets/SharedFolderWidget.cpp
ui/widgets/SharedFolderWidget.h
ui/widgets/IconLabel.cpp
ui/widgets/IconLabel.h
ui/widgets/JavaWizardWidget.cpp
@ -1238,6 +1242,7 @@ qt_wrap_ui(LAUNCHER_UI
ui/pages/modplatform/technic/TechnicPage.ui
ui/widgets/CustomCommands.ui
ui/widgets/EnvironmentVariables.ui
ui/widgets/SharedFolderWidget.ui
ui/widgets/InfoFrame.ui
ui/widgets/ModFilterWidget.ui
ui/widgets/SubTaskProgressBar.ui
@ -1255,6 +1260,7 @@ qt_wrap_ui(LAUNCHER_UI
ui/dialogs/ExportInstanceDialog.ui
ui/dialogs/ExportPackDialog.ui
ui/dialogs/ExportToModListDialog.ui
ui/dialogs/FileConflictDialog.ui
ui/dialogs/IconPickerDialog.ui
ui/dialogs/ImportResourceDialog.ui
ui/dialogs/MSALoginDialog.ui

View File

@ -55,6 +55,8 @@
#include "DesktopServices.h"
#include "PSaveFile.h"
#include "StringUtils.h"
#include "ui/dialogs/CustomMessageBox.h"
#include "ui/dialogs/FileConflictDialog.h"
#if defined Q_OS_WIN32
#define NOMINMAX
@ -277,6 +279,11 @@ bool ensureFolderPathExists(const QString folderPathName)
return ensureFolderPathExists(QFileInfo(folderPathName));
}
bool checkFolderPathEmpty(const QString& folderPathName)
{
return QDir(folderPathName).isEmpty(QDir::Filters(QDir::AllEntries | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System));
}
bool copyFileAttributes(QString src, QString dst)
{
#ifdef Q_OS_WIN32
@ -669,6 +676,44 @@ bool move(const QString& source, const QString& dest)
return true;
}
bool interactiveMove(const QString& source, const QString& destination, bool recursive /* = false */, QWidget* parent /* = nullptr */)
{
const QFileInfo sourceInfo(source);
// Make sure the source exists.
if (!sourceInfo.exists())
return false;
// Recursive doesn't make sense if the source isn't a directory.
if (recursive && sourceInfo.isDir()) {
QDirIterator sourceIt(source, QDir::Filter::Files | QDir::Filter::Dirs | QDir::Filter::Hidden | QDir::Filter::NoDotAndDotDot);
while (sourceIt.hasNext()) {
if (!interactiveMove(sourceIt.next(), FS::PathCombine(destination, sourceIt.fileName()), false))
return false;
}
return true;
}
if (QFile(destination).exists()) {
FileConflictDialog dialog(source, destination, true, parent);
FileConflictDialog::Result result = dialog.execWithResult();
switch (result) {
case FileConflictDialog::Cancel:
return false;
case FileConflictDialog::ChooseDestination:
return FS::deletePath(source);
case FileConflictDialog::ChooseSource:
FS::deletePath(destination);
break;
}
}
return FS::move(source, destination);
}
bool deletePath(QString path)
{
std::error_code err;
@ -1701,4 +1746,76 @@ QString getUniqueResourceName(const QString& filePath)
return newFileName;
}
bool isSymLink(const QString& path)
{
return QFileInfo(path).isSymLink();
}
QString getSymLinkTarget(const QString& path)
{
return QFileInfo(path).symLinkTarget();
}
bool tryCreateSymlink(const QString& source, const QString& destination, const QString& symlinkName /* = "symbolic link" */)
{
// Make sure that symbolic links are supported.
if (!FS::canLink(source, destination)) {
CustomMessageBox::selectable(
nullptr, QObject::tr("Failed"),
QObject::tr("Failed to create %1.\nSymbolic links are not supported on the filesystem").arg(symlinkName), QMessageBox::Warning,
QMessageBox::Ok)
->exec();
return false;
}
// Check if the destination already exists.
// If it's already a symlink, it might already be correct.
if (FS::isSymLink(destination)) {
// If the target of the symlink is already the source, there's nothing to do.
if (FS::getSymLinkTarget(destination) == source) {
return true;
}
FS::deletePath(destination);
} else if (QFileInfo::exists(destination)) {
if (!FS::checkFolderPathEmpty(destination)) {
if (!interactiveMove(destination, source, true)) {
CustomMessageBox::selectable(
nullptr, QObject::tr("Failed"),
QObject::tr("Failed to create %1.\nEnsure that \"%2\" is empty.").arg(symlinkName).arg(destination),
QMessageBox::Warning, QMessageBox::Ok)
->exec();
return false;
}
}
FS::deletePath(destination);
}
// Make sure the source folder exists
if (!FS::ensureFolderPathExists(source)) {
CustomMessageBox::selectable(nullptr, QObject::tr("Failed"),
QObject::tr("Failed to create %1.\nEnsure that \"%2\" exists.").arg(symlinkName).arg(source),
QMessageBox::Warning, QMessageBox::Ok)
->exec();
return false;
}
FS::create_link folderLink(source, destination);
folderLink.linkRecursively(false);
if (!folderLink()) {
CustomMessageBox::selectable(nullptr, QObject::tr("Failed"),
QObject::tr("Failed to create %1. Error %2: %3")
.arg(symlinkName)
.arg(folderLink.getOSError().value())
.arg(folderLink.getOSError().message().c_str()),
QMessageBox::Warning, QMessageBox::Ok)
->exec();
}
return true;
}
} // namespace FS

View File

@ -99,6 +99,13 @@ bool ensureFolderPathExists(const QFileInfo folderPath);
*/
bool ensureFolderPathExists(const QString folderPathName);
/**
* @brief Check if the given folder is empty or doesn't exist
* @param folderPathName The path to a folder to check
* @return True if the given folder is empty or doesn't exist
*/
bool checkFolderPathEmpty(const QString& folderPathName);
/**
* @brief Copies a directory and it's contents from src to dest
*/
@ -286,6 +293,17 @@ class create_link : public QObject {
*/
bool move(const QString& source, const QString& dest);
/**
* @brief Move a file or folder, and ask the user what to do in case of a conflict.
* @param source What to move.
* @param destination Where to move it to.
* @param recursive If true, all direct children will be moved 1 by 1.
* If false, the source will be directly moved to the destination.
* @param parent The parent of the dialog.
* @return True if everything could be moved.
*/
bool interactiveMove(const QString& source, const QString& destination, bool recursive = false, QWidget* parent = nullptr);
/**
* Delete a folder recursively
*/
@ -566,4 +584,20 @@ QString getPathNameInLocal8bit(const QString& file);
QString getUniqueResourceName(const QString& filePath);
/**
* @brief Check if a file or folder is a symbolic link
* @param path The path to check
* @return True if the object exists and is an symbolic link
*/
bool isSymLink(const QString& path);
/**
* @brief Get the target of a symbolic link
* @param path The path to check
* @return The target of a symbolic link. Empty if path is not a symbolic link.
*/
QString getSymLinkTarget(const QString& path);
bool tryCreateSymlink(const QString& source, const QString& destination, const QString& symlinkName = "symbolic link");
} // namespace FS

View File

@ -413,6 +413,11 @@ QString MinecraftInstance::dataPacksDir()
return QDir(gameRoot()).filePath(relativePath);
}
QString MinecraftInstance::screenshotsDir() const
{
return FS::PathCombine(gameRoot(), "screenshots");
}
QString MinecraftInstance::resourcePacksDir() const
{
return FS::PathCombine(gameRoot(), "resourcepacks");
@ -1308,4 +1313,39 @@ QList<Mod*> MinecraftInstance::getJarMods() const
return mods;
}
void MinecraftInstance::applySettings()
{
// Shared directories
updateSharedDirectories();
}
bool MinecraftInstance::updateSharedDirectories()
{
const std::vector<std::tuple<QString, QString, QString>> sharedDirectories = {
{ "UseSharedScreenshotsFolder", m_settings->get("SharedScreenshotsPath").toString(), screenshotsDir() },
{ "UseSharedSavesFolder", m_settings->get("SharedSavesPath").toString(), worldDir() },
{ "UseSharedResourcePacksFolder", m_settings->get("SharedResourcePacksPath").toString(), resourcePacksDir() },
{ "UseSharedResourcePacksFolder", m_settings->get("SharedResourcePacksPath").toString(), texturePacksDir() }
};
bool success = true;
for (const auto& [useSetting, source, destination] : sharedDirectories) {
// Create symlink if useSetting is true, remove symlink if false.
if (m_settings->get(useSetting).toBool()) {
// Try to create symlink, set setting to false if failed.
if (!FS::tryCreateSymlink(source, destination, tr("shared folder"))) {
m_settings->set(useSetting, false);
success = false;
}
} else {
// Safety check
if (FS::isSymLink(destination)) {
success = FS::deletePath(destination) && success;
}
}
}
return success;
}
#include "MinecraftInstance.moc"

View File

@ -75,6 +75,7 @@ class MinecraftInstance : public BaseInstance {
////// Directories and files //////
QString jarModsDir() const;
QString screenshotsDir() const;
QString resourcePacksDir() const;
QString texturePacksDir() const;
QString shaderPacksDir() const;
@ -158,9 +159,13 @@ class MinecraftInstance : public BaseInstance {
virtual JavaVersion getJavaVersion();
virtual void applySettings() override;
protected:
QMap<QString, QString> createCensorFilterFromSession(AuthSessionPtr session);
bool updateSharedDirectories();
protected: // data
std::shared_ptr<PackProfile> m_components;
mutable std::shared_ptr<ModFolderModel> m_loader_mod_list;

View File

@ -0,0 +1,96 @@
#include "FileConflictDialog.h"
#include "ui_FileConflictDialog.h"
#include <QDialogButtonBox>
#include <QDir>
#include <QFileInfo>
#include <QPushButton>
#include "Application.h"
FileConflictDialog::FileConflictDialog(QString source, QString destination, bool move, QWidget* parent)
: QDialog(parent), ui(new Ui::FileConflictDialog), m_result(Result::Cancel)
{
ui->setupUi(this);
// Setup buttons
connect(ui->buttonBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, this, &FileConflictDialog::cancel);
if (move) {
setWindowTitle(tr("File conflict while moving files"));
auto chooseSourceButton = ui->buttonBox->addButton(tr("Keep source"), QDialogButtonBox::DestructiveRole);
chooseSourceButton->setIcon(APPLICATION->getThemedIcon("delete"));
connect(chooseSourceButton, &QPushButton::clicked, this, &FileConflictDialog::chooseSource);
auto chooseDestinationButton = ui->buttonBox->addButton(tr("Keep destination"), QDialogButtonBox::DestructiveRole);
chooseDestinationButton->setIcon(APPLICATION->getThemedIcon("delete"));
connect(chooseDestinationButton, &QPushButton::clicked, this, &FileConflictDialog::chooseDestination);
} else {
setWindowTitle(tr("File conflict while copying files"));
auto chooseSourceButton = ui->buttonBox->addButton(tr("Overwrite destination"), QDialogButtonBox::DestructiveRole);
chooseSourceButton->setIcon(APPLICATION->getThemedIcon("delete"));
connect(chooseSourceButton, &QPushButton::clicked, this, &FileConflictDialog::chooseSource);
auto chooseDestinationButton = ui->buttonBox->addButton(tr("Skip"), QDialogButtonBox::DestructiveRole);
connect(chooseDestinationButton, &QPushButton::clicked, this, &FileConflictDialog::chooseDestination);
}
// Setup info
ui->sourceInfoLabel->setText(GetFileInfoText(source));
ui->destinationInfoLabel->setText(GetFileInfoText(destination));
}
FileConflictDialog::~FileConflictDialog()
{
delete ui;
}
FileConflictDialog::Result FileConflictDialog::execWithResult()
{
exec();
return m_result;
}
FileConflictDialog::Result FileConflictDialog::getResult() const
{
return m_result;
}
void FileConflictDialog::chooseSource()
{
m_result = Result::ChooseSource;
accept();
}
void FileConflictDialog::chooseDestination()
{
m_result = Result::ChooseDestination;
accept();
}
void FileConflictDialog::cancel()
{
m_result = Result::Cancel;
reject();
}
QString FileConflictDialog::GetFileInfoText(const QString& filePath) const
{
QLocale locale;
QFileInfo fileInfo(filePath);
if (fileInfo.isDir()) {
QDir dirInfo(filePath);
return tr("<b>Name:</b> %1<br/><b>Size:</b> %2<br/><b>Last modified:</b> %3<br/><b>Items:</b> %4")
.arg(filePath)
.arg("-")
.arg(fileInfo.lastModified().toString(locale.dateTimeFormat()))
.arg(dirInfo.entryList(QDir::AllEntries | QDir::NoDotAndDotDot).count());
} else {
return tr("<b>Name:</b> %1<br/><b>Size:</b> %2<br/><b>Last modified:</b> %3")
.arg(filePath)
.arg(locale.formattedDataSize(fileInfo.size()))
.arg(fileInfo.lastModified().toString(locale.dateTimeFormat()));
}
}

View File

@ -0,0 +1,36 @@
#pragma once
#include <QDialog>
namespace Ui {
class FileConflictDialog;
}
class FileConflictDialog : public QDialog {
Q_OBJECT
public:
enum Result { Cancel, ChooseSource, ChooseDestination };
/// @brief Create a new file conflict dialog
/// @param source The source path. What to copy/move.
/// @param destination The destination path. Where to copy/move.
/// @param move Whether the conflict is for a move or copy action
/// @param parent The parent of the dialog
explicit FileConflictDialog(QString source, QString destination, bool move = false, QWidget* parent = nullptr);
~FileConflictDialog() override;
Result execWithResult();
Result getResult() const;
private slots:
void chooseSource();
void chooseDestination();
void cancel();
private:
QString GetFileInfoText(const QString& filePath) const;
Ui::FileConflictDialog* ui;
Result m_result;
};

View File

@ -0,0 +1,114 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>FileConflictDialog</class>
<widget class="QDialog" name="FileConflictDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>411</width>
<height>219</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string/>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item alignment="Qt::AlignmentFlag::AlignTop">
<widget class="QLabel" name="label">
<property name="text">
<string>Would you like to overwrite the destination?</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
</property>
<item>
<layout class="QVBoxLayout" name="sourceLayout" stretch="0,1">
<item>
<widget class="QLabel" name="sourceLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:700;&quot;&gt;Source&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="alignment">
<set>Qt::AlignHCenter|Qt::AlignTop</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="sourceInfoLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;b&gt;Name:&lt;/b&gt;&lt;/p&gt;&lt;p&gt;Size:&lt;/p&gt;&lt;p&gt;Date:&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="destinationLayout" stretch="0,1">
<item>
<widget class="QLabel" name="destinationLabel">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:700;&quot;&gt;Destination&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="alignment">
<set>Qt::AlignHCenter|Qt::AlignTop</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="destinationInfoLabel">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Name:&lt;/p&gt;&lt;p&gt;Size:&lt;/p&gt;&lt;p&gt;Date:&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -38,6 +38,7 @@
#include "ui_ExternalResourcesPage.h"
#include "DesktopServices.h"
#include "FileSystem.h"
#include "Version.h"
#include "minecraft/mod/ResourceFolderModel.h"
#include "ui/GuiUtil.h"
@ -149,6 +150,9 @@ void ExternalResourcesPage::openedImpl()
m_wide_bar_setting = APPLICATION->settings()->getOrRegisterSetting(setting_name);
ui->actionsToolbar->setVisibilityState(QByteArray::fromBase64(m_wide_bar_setting->get().toString().toUtf8()));
// Enable the symbolic link warning when the folder is a symbolic link
ui->isSymlinkWarning->setVisible(FS::isSymLink(m_model->dir().absolutePath()));
}
void ExternalResourcesPage::closedImpl()

View File

@ -67,6 +67,13 @@
</property>
</widget>
</item>
<item row="0" column="1" colspan="3">
<widget class="QLabel" name="isSymlinkWarning">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:700; color:#f5c211;&quot;&gt;Warning&lt;/span&gt;&lt;span style=&quot; color:#f5c211;&quot;&gt;: This is a shared folder and potentially shared with multiple instances&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="WideBar" name="actionsToolbar">

View File

@ -563,6 +563,9 @@ void ScreenshotsPage::openedImpl()
m_wide_bar_setting = APPLICATION->settings()->getOrRegisterSetting(setting_name);
ui->toolBar->setVisibilityState(QByteArray::fromBase64(m_wide_bar_setting->get().toString().toUtf8()));
// Enable the symbolic link warning when the screenshots folder is a symbolic link
ui->sharedScreenshotsFolderWarninglabel->setVisible(FS::isSymLink(m_folder));
}
void ScreenshotsPage::closedImpl()

View File

@ -11,7 +11,7 @@
</rect>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QHBoxLayout" name="horizontalLayout">
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
@ -24,6 +24,19 @@
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="sharedScreenshotsFolderWarninglabel">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; color:#f5c211;&quot;&gt;Warning&lt;/span&gt;&lt;span style=&quot; color:#f5c211;&quot;&gt;: This is a shared folder and potentially shared with multiple instances&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="margin">
<number>0</number>
</property>
</widget>
</item>
<item>
<widget class="QListView" name="listView">
<property name="selectionMode">

View File

@ -48,10 +48,10 @@
#include <QKeyEvent>
#include <QMenu>
#include <QMessageBox>
#include <QPushButton>
#include <QSortFilterProxyModel>
#include <QTreeView>
#include <Qt>
#include <QPushButton>
#include "FileSystem.h"
#include "tools/MCEditTool.h"
@ -126,6 +126,9 @@ void WorldListPage::openedImpl()
m_wide_bar_setting = APPLICATION->settings()->getOrRegisterSetting(setting_name);
ui->toolBar->setVisibilityState(QByteArray::fromBase64(m_wide_bar_setting->get().toString().toUtf8()));
// Enable the symbolic link warning when the saves folder is a symbolic link
ui->sharedSavesFolderWarninglabel->setVisible(FS::isSymLink(FS::PathCombine(m_inst->gameRoot(), "saves")));
}
void WorldListPage::closedImpl()

View File

@ -11,7 +11,7 @@
</rect>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QHBoxLayout" name="horizontalLayout">
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
@ -24,6 +24,13 @@
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="sharedSavesFolderWarninglabel">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:700; color:#f5c211;&quot;&gt;Warning&lt;/span&gt;&lt;span style=&quot; color:#f5c211;&quot;&gt;: This is a shared folder and potentially shared with multiple instances&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item>
<widget class="QTreeView" name="worldTreeView">
<property name="sizePolicy">

View File

@ -53,7 +53,8 @@ MinecraftSettingsWidget::MinecraftSettingsWidget(MinecraftInstancePtr instance,
m_ui->setupUi(this);
if (m_instance == nullptr) {
m_ui->settingsTabs->removeTab(1);
m_ui->settingsTabs->removeTab(m_ui->settingsTabs->indexOf(m_ui->javaPage));
m_ui->settingsTabs->removeTab(m_ui->settingsTabs->indexOf(m_ui->sharedFoldersTab));
m_ui->openGlobalSettingsButton->setVisible(false);
m_ui->instanceAccountGroupBox->hide();
@ -281,6 +282,13 @@ void MinecraftSettingsWidget::loadSettings()
m_ui->fabric->blockSignals(false);
m_ui->quilt->blockSignals(false);
m_ui->liteLoader->blockSignals(false);
// Shared folders
m_ui->sharedScreenshotsFolder->initialize(settings->get("UseSharedScreenshotsFolder").toBool(),
settings->get("SharedScreenshotsPath").toString());
m_ui->sharedSavesFolder->initialize(settings->get("UseSharedSavesFolder").toBool(), settings->get("SharedSavesPath").toString());
m_ui->sharedResourcePacksFolder->initialize(settings->get("UseSharedResourcePacksFolder").toBool(),
settings->get("SharedResourcePacksPath").toString());
}
m_ui->legacySettingsGroupBox->setChecked(settings->get("OverrideLegacySettings").toBool());
@ -469,8 +477,21 @@ void MinecraftSettingsWidget::saveSettings()
} else {
settings->reset("OnlineFixes");
}
if (m_instance != nullptr) {
// Shared folders
settings->set("UseSharedScreenshotsFolder", m_ui->sharedScreenshotsFolder->isEnabled());
settings->set("SharedScreenshotsPath", m_ui->sharedScreenshotsFolder->getPath());
settings->set("UseSharedSavesFolder", m_ui->sharedSavesFolder->isEnabled());
settings->set("SharedSavesPath", m_ui->sharedSavesFolder->getPath());
settings->set("UseSharedResourcePacksFolder", m_ui->sharedResourcePacksFolder->isEnabled());
settings->set("SharedResourcePacksPath", m_ui->sharedResourcePacksFolder->getPath());
}
}
if (m_instance != nullptr)
m_instance->applySettings();
if (m_javaSettings != nullptr)
m_javaSettings->saveSettings();
}

View File

@ -801,6 +801,75 @@ It is most likely you will need to change the path - please refer to the mod's w
</item>
</layout>
</widget>
<widget class="QWidget" name="sharedFoldersTab">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<attribute name="title">
<string>Shared Folders</string>
</attribute>
<layout class="QVBoxLayout" name="sharedFoldersVerticalLayout">
<item>
<widget class="QGroupBox" name="sharedFoldersGroupBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Settings</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<layout class="QVBoxLayout" name="sharedFoldersSettingsVerticalLayout">
<item>
<widget class="QLabel" name="sharedFoldersWarning">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; color:#f5c211;&quot;&gt;Warning&lt;/span&gt;&lt;span style=&quot; color:#f5c211;&quot;&gt;: After enabling files cannot be automatically restored to their original folder.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item>
<widget class="SharedFolderWidget" name="sharedScreenshotsFolder" native="true"/>
</item>
<item>
<widget class="SharedFolderWidget" name="sharedSavesFolder" native="true"/>
</item>
<item>
<widget class="SharedFolderWidget" name="sharedResourcePacksFolder" native="true"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QLabel" name="sharedFoldersLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>11</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;A shared folder is a folder that can be shared across instances.&lt;/p&gt;&lt;p&gt;For example: If two instances share the same shared saves folder, they can both play on the same worlds without having to copy the world over.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="scaledContents">
<bool>false</bool>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
@ -818,6 +887,12 @@ It is most likely you will need to change the path - please refer to the mod's w
<header>ui/widgets/EnvironmentVariables.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>SharedFolderWidget</class>
<extends>QWidget</extends>
<header>ui/widgets/SharedFolderWidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>openGlobalSettingsButton</tabstop>

View File

@ -0,0 +1,53 @@
#include "SharedFolderWidget.h"
#include "ui_SharedFolderWidget.h"
#include <QFileDialog>
#include "FileSystem.h"
SharedFolderWidget::SharedFolderWidget(QWidget* parent) : QWidget(parent), ui(new Ui::SharedFolderWidget)
{
ui->setupUi(this);
}
SharedFolderWidget::~SharedFolderWidget()
{
delete ui;
}
void SharedFolderWidget::initialize(bool enabled, const QString& path, const QString& label)
{
ui->enabledCheckBox->setChecked(enabled);
ui->enabledCheckBox->setText(label);
ui->pathTextBox->setEnabled(enabled);
ui->pathTextBox->setText(path);
ui->pathBrowseBtn->setEnabled(enabled);
}
bool SharedFolderWidget::isEnabled() const
{
return ui->enabledCheckBox->isChecked();
}
QString SharedFolderWidget::getPath() const
{
return ui->pathTextBox->text();
}
void SharedFolderWidget::on_enabledCheckBox_toggled(bool checked)
{
ui->pathTextBox->setEnabled(checked);
ui->pathBrowseBtn->setEnabled(checked);
}
void SharedFolderWidget::on_pathBrowseBtn_clicked()
{
QString path = QFileDialog::getExistingDirectory(this, tr("Select shared folder"), ui->pathTextBox->text());
if (path.isEmpty()) {
return;
}
ui->pathTextBox->setText(path);
}

View File

@ -0,0 +1,26 @@
#pragma once
#include <QWidget>
namespace Ui {
class SharedFolderWidget;
}
class SharedFolderWidget : public QWidget {
Q_OBJECT
public:
explicit SharedFolderWidget(QWidget* parent = 0);
virtual ~SharedFolderWidget();
void initialize(bool enabled, const QString& path, const QString& label = "");
bool isEnabled() const;
QString getPath() const;
private slots:
void on_enabledCheckBox_toggled(bool checked);
void on_pathBrowseBtn_clicked();
private:
Ui::SharedFolderWidget* ui;
};

View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SharedFolderWidget</class>
<widget class="QWidget" name="SharedFolderWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>518</width>
<height>44</height>
</rect>
</property>
<property name="windowTitle">
<string/>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QCheckBox" name="enabledCheckBox">
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="pathTextBox">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pathBrowseBtn">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Browse</string>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>