Skip to content

Commit 89a4e4c

Browse files
authored
[DC-84] feature: implement advanced settings page of wizard (#12221)
* laid out the widgets depending on the vfs mode availability and whether forceVfs is in play the folder picker works like the old one did folder validation error reporting still needs work returning the result is not yet complete either. * finally got the gui to behave nicely (on windows at least) errors related to the user defined sync root are now shown in the page, and they are auto-cleared when you start editing the root again also had to add some special handling as on windows, when you hit enter in the sync root text field, it automatically triggers the finish button. now it will block finish if the last edit was bad, so at least you can see the error, and also see the value was reset to the default sync root. * added a few extra checks on the sync root path fixed a couple of typos in folderman * filter out any non existing directory name from the suggested sync root path when opening the folder picker * make squish pass * take care of review feedback
1 parent 8f6a10c commit 89a4e4c

File tree

3 files changed

+223
-5
lines changed

3 files changed

+223
-5
lines changed

src/gui/folderman.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -849,9 +849,9 @@ QString FolderMan::checkPathValidityRecursive(const QString &path, FolderMan::Ne
849849
Utility::NtfsPermissionLookupRAII ntfs_perm;
850850
#endif
851851

852-
auto pathLenghtCheck = Folder::checkPathLength(path);
853-
if (!pathLenghtCheck) {
854-
return pathLenghtCheck.error();
852+
auto pathLengthCheck = Folder::checkPathLength(path);
853+
if (!pathLengthCheck) {
854+
return pathLengthCheck.error();
855855
}
856856

857857
const QFileInfo selectedPathInfo(path);
@@ -867,7 +867,7 @@ QString FolderMan::checkPathValidityRecursive(const QString &path, FolderMan::Ne
867867
return FolderMan::tr("The folder %1 is used in a folder sync connection!").arg(QDir::toNativeSeparators(selectedPathInfo.filePath()));
868868
}
869869

870-
// At this point we know there is no syncdb in the parent hyrarchy, check for spaces sync root.
870+
// At this point we know there is no syncdb in the parent hierarchy, check for spaces sync root.
871871

872872
if (!selectedPathInfo.isDir()) {
873873
return FolderMan::tr("The selected path is not a folder!");

src/gui/newaccountwizard/advancedsettingspagecontroller.cpp

Lines changed: 187 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,29 @@
1212
* for more details.
1313
*/
1414
#include "advancedsettingspagecontroller.h"
15+
#include "common/vfs.h"
16+
#include "folderman.h"
1517

18+
#include "theme.h"
19+
20+
#include <QButtonGroup>
21+
#include <QFileDialog>
22+
#include <QHBoxLayout>
23+
#include <QLabel>
24+
#include <QLineEdit>
25+
#include <QPushButton>
26+
#include <QRadioButton>
27+
#include <QVBoxLayout>
1628
#include <QWizardPage>
1729

1830
namespace OCC {
1931

32+
2033
AdvancedSettingsPageController::AdvancedSettingsPageController(QWizardPage *page, QObject *parent)
2134
: QObject{parent}
2235
, _page(page)
2336
{
37+
gatherSyncInfo();
2438
buildPage();
2539
}
2640

@@ -29,11 +43,183 @@ void AdvancedSettingsPageController::buildPage()
2943
if (!_page)
3044
return;
3145

32-
_page->setTitle(QStringLiteral("AdvancedSetttingsPage"));
46+
_buttonGroup = new QButtonGroup(_page);
47+
48+
QLabel *titleLabel = new QLabel(tr("Advanced settings"), _page);
49+
QFont titleFont = titleLabel->font();
50+
titleFont.setPixelSize(20);
51+
titleFont.setWeight(QFont::DemiBold);
52+
titleLabel->setFont(titleFont);
53+
titleLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
54+
55+
QLabel *syncOptionsLabel = new QLabel(tr("Sync and download options"), _page);
56+
QLabel *syncRootLabel = new QLabel(tr("Folder location"));
57+
QFont sectionFont = syncOptionsLabel->font();
58+
sectionFont.setPixelSize(16);
59+
sectionFont.setWeight(QFont::DemiBold);
60+
syncOptionsLabel->setFont(sectionFont);
61+
syncRootLabel->setFont(sectionFont);
62+
syncOptionsLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
63+
syncRootLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
64+
65+
66+
QVBoxLayout *layout = new QVBoxLayout();
67+
layout->setContentsMargins(50, 0, 50, 0);
68+
layout->setSpacing(12);
69+
layout->addStretch(1);
70+
layout->addWidget(titleLabel, Qt::AlignLeft);
71+
layout->addSpacing(8);
72+
layout->addWidget(syncOptionsLabel, Qt::AlignLeft);
73+
74+
if (_vfsIsAvailable) {
75+
QRadioButton *vfsButton = new QRadioButton(tr("Only sync and dowload files as you use them to save hard drive space"), _page);
76+
vfsButton->setFocusPolicy(Qt::StrongFocus);
77+
_buttonGroup->addButton(vfsButton, SyncType::USE_VFS);
78+
layout->addWidget(vfsButton, Qt::AlignLeft);
79+
}
80+
if (!_forceVfs) {
81+
QRadioButton *selectiveSyncButton = new QRadioButton(tr("Sync and download specific folders"), _page);
82+
selectiveSyncButton->setFocusPolicy(Qt::StrongFocus);
83+
QRadioButton *syncAllButton = new QRadioButton(tr("Automatically sync and download all current folders and files"), _page);
84+
syncAllButton->setFocusPolicy(Qt::StrongFocus);
85+
_buttonGroup->addButton(selectiveSyncButton, SyncType::SELECTIVE_SYNC);
86+
_buttonGroup->addButton(syncAllButton, SyncType::SYNC_ALL);
87+
layout->addWidget(selectiveSyncButton, Qt::AlignLeft);
88+
layout->addWidget(syncAllButton, Qt::AlignLeft);
89+
}
90+
91+
Q_ASSERT(_buttonGroup->button(_defaultSyncType));
92+
_buttonGroup->button(_defaultSyncType)->setChecked(true);
93+
94+
_rootDirEdit = new QLineEdit(_page);
95+
_rootDirEdit->setText(_defaultSyncRoot);
96+
_rootDirEdit->setFocusPolicy(Qt::StrongFocus);
97+
// just clear the error if the user starts typing in the text edit
98+
connect(_rootDirEdit, &QLineEdit::textEdited, this, [this] {
99+
if (!_errorField->text().isEmpty())
100+
_errorField->setText({});
101+
});
102+
connect(_rootDirEdit, &QLineEdit::editingFinished, this, &AdvancedSettingsPageController::onRootDirFieldEdited);
103+
104+
QPushButton *folderButton = new QPushButton(tr("Choose..."), _page);
105+
folderButton->setFocusPolicy(Qt::StrongFocus);
106+
connect(folderButton, &QPushButton::clicked, this, &AdvancedSettingsPageController::showFolderPicker);
107+
108+
layout->addSpacing(8);
109+
layout->addWidget(syncRootLabel, Qt::AlignLeft);
110+
111+
QHBoxLayout *folderPickerLayout = new QHBoxLayout();
112+
folderPickerLayout->addWidget(_rootDirEdit, Qt::AlignLeft);
113+
folderPickerLayout->addWidget(folderButton);
114+
layout->addLayout(folderPickerLayout);
115+
116+
_errorField = new QLabel(QString(), _page);
117+
QPalette errorPalette = _errorField->palette();
118+
errorPalette.setColor(QPalette::Text, Qt::red);
119+
_errorField->setPalette(errorPalette);
120+
_errorField->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
121+
_errorField->setWordWrap(true);
122+
_errorField->setAlignment(Qt::AlignLeft);
123+
_errorField->setTextInteractionFlags(Qt::TextSelectableByKeyboard | Qt::TextSelectableByMouse);
124+
layout->addWidget(_errorField);
125+
126+
layout->addStretch(1);
127+
_page->setLayout(layout);
128+
}
129+
130+
void AdvancedSettingsPageController::gatherSyncInfo()
131+
{
132+
_vfsIsAvailable = VfsPluginManager::instance().bestAvailableVfsMode() == Vfs::WindowsCfApi;
133+
_forceVfs = _vfsIsAvailable && Theme::instance()->forceVirtualFilesOption();
134+
if (!_vfsIsAvailable)
135+
_defaultSyncType = SyncType::SYNC_ALL;
136+
else
137+
_defaultSyncType = SyncType::USE_VFS;
138+
139+
_defaultSyncRoot = FolderMan::suggestSyncFolder(FolderMan::NewFolderType::SpacesSyncRoot, {});
33140
}
34141

35142
bool AdvancedSettingsPageController::validate()
36143
{
144+
// this is a safety net because for unknown reasons, on windows when you hit enter to commit hand edited text
145+
// in the QLineEdit, it ALSO triggers the finish button. No idea but this should block the finish so the user
146+
// can see their last choice failed, and that the root has been reset to the default.
147+
if (_lastHandEditedRootFailed)
148+
return false;
149+
150+
// normally I don't like taking values directly from the gui but in this case, it would be complete overkill to
151+
// create a dedicated model just for these two values that only need to be collected when the user is done.
152+
AdvancedSettingsResult result;
153+
result._syncRoot = _rootDirEdit->text();
154+
result._syncType = static_cast<SyncType>(_buttonGroup->checkedId());
155+
Q_EMIT success(result);
37156
return true;
38157
}
158+
159+
bool AdvancedSettingsPageController::validateSyncRoot(const QString &rootPath)
160+
{
161+
QString errorMessageTemplate = tr("Invalid local download directory %1: %2");
162+
163+
if (rootPath == QDir::homePath()) {
164+
_errorField->setText(errorMessageTemplate.arg(rootPath, tr("your user directory may not be chosen as the sync root.")));
165+
return false;
166+
}
167+
168+
if (Utility::isMac()) {
169+
QString filesystemType = FileSystem::fileSystemForPath(rootPath);
170+
if (filesystemType != QStringLiteral("apfs")) {
171+
_errorField->setText(errorMessageTemplate.arg(rootPath, tr("path is not located on a supported Apple File System.")));
172+
}
173+
}
174+
175+
if (!QDir::isAbsolutePath(rootPath)) {
176+
_errorField->setText(errorMessageTemplate.arg(rootPath, tr("path must be absolute.")));
177+
return false;
178+
}
179+
180+
QString invalidPathErrorMessage = FolderMan::checkPathValidityRecursive(rootPath, FolderMan::NewFolderType::SpacesSyncRoot, {});
181+
if (!invalidPathErrorMessage.isEmpty()) {
182+
_errorField->setText(errorMessageTemplate.arg(rootPath, invalidPathErrorMessage));
183+
return false;
184+
}
185+
186+
// I'm not testing the case that vfs is actually the chosen mode here as if vfs is available we should just block dirs that don't support it,
187+
// the idea being that eg if they change the mode in the page after they select the dir, or use selective sync with vfs later, at least we know the default
188+
// root they select here will not cause any issues down the road
189+
if (_vfsIsAvailable && !FolderMan::instance()->checkVfsAvailability(rootPath)) {
190+
_errorField->setText(errorMessageTemplate.arg(rootPath, tr("selected path does not support using virtual file system.")));
191+
return false;
192+
}
193+
194+
return true;
195+
}
196+
197+
void AdvancedSettingsPageController::showFolderPicker()
198+
{
199+
_errorField->setText({});
200+
_lastHandEditedRootFailed = false;
201+
202+
QDir defaultDir(_defaultSyncRoot);
203+
while (!defaultDir.exists())
204+
defaultDir.cdUp();
205+
QString chosenRoot = QFileDialog::getExistingDirectory(_page->parentWidget(), tr("Select sync root"), defaultDir.path());
206+
207+
if (chosenRoot.isEmpty())
208+
return;
209+
210+
if (validateSyncRoot(chosenRoot))
211+
_rootDirEdit->setText(chosenRoot);
212+
}
213+
214+
void AdvancedSettingsPageController::onRootDirFieldEdited()
215+
{
216+
QString chosenRoot = _rootDirEdit->text();
217+
if (!validateSyncRoot(chosenRoot)) {
218+
_rootDirEdit->setText(_defaultSyncRoot);
219+
_lastHandEditedRootFailed = true;
220+
} else {
221+
_lastHandEditedRootFailed = false;
222+
_errorField->setText({});
223+
}
224+
}
39225
}

src/gui/newaccountwizard/advancedsettingspagecontroller.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,51 @@
1717
#include <QObject>
1818

1919
class QWizardPage;
20+
class QButtonGroup;
21+
class QLineEdit;
22+
class QLabel;
2023

2124
namespace OCC {
2225

26+
// I am not making this a strongly typed enum class as it's only used to support the radio button ids in the gui.
27+
// an old fashioned enum does not require casts back and forth, which for this use case is a good thing.
28+
enum SyncType { NONE, USE_VFS, SYNC_ALL, SELECTIVE_SYNC };
29+
30+
struct AdvancedSettingsResult
31+
{
32+
SyncType _syncType = SyncType::NONE;
33+
QString _syncRoot;
34+
};
35+
2336
class AdvancedSettingsPageController : public QObject, public WizardPageValidator
2437
{
2538
Q_OBJECT
39+
2640
public:
2741
explicit AdvancedSettingsPageController(QWizardPage *page, QObject *parent);
2842
bool validate() override;
2943

44+
Q_SIGNALS:
45+
void success(const AdvancedSettingsResult &result);
46+
3047
private:
48+
void gatherSyncInfo();
3149
void buildPage();
50+
void showFolderPicker();
51+
void onRootDirFieldEdited();
52+
bool validateSyncRoot(const QString &rootPath);
53+
54+
SyncType _defaultSyncType = SyncType::NONE;
55+
QString _defaultSyncRoot;
56+
bool _vfsIsAvailable = false;
57+
bool _forceVfs = false;
58+
bool _lastHandEditedRootFailed = false;
59+
60+
AdvancedSettingsResult _results;
3261

3362
QWizardPage *_page = nullptr;
63+
QButtonGroup *_buttonGroup = nullptr;
64+
QLineEdit *_rootDirEdit = nullptr;
65+
QLabel *_errorField;
3466
};
3567
}

0 commit comments

Comments
 (0)