Improve dialog for confirming ignore pattern changes from file browser
* Show diff in proper text view with basic syntax highlighting * Allow manual edits before applying changes
This commit is contained in:
parent
8a51248791
commit
0729e180cb
|
@ -197,6 +197,10 @@ QString SyncthingFileModel::computeIgnorePatternDiff() const
|
|||
SyncthingIgnores SyncthingFileModel::computeNewIgnorePatterns() const
|
||||
{
|
||||
auto newIgnorePatterns = SyncthingIgnores();
|
||||
if (!m_manuallyEditedIgnorePatterns.isEmpty()) {
|
||||
newIgnorePatterns.ignore = m_manuallyEditedIgnorePatterns.split(QChar('\n'));
|
||||
return newIgnorePatterns;
|
||||
}
|
||||
auto index = std::size_t();
|
||||
if (const auto change = m_stagedChanges.find(beforeFirstLine); change != m_stagedChanges.end()) {
|
||||
for (const auto &line : change->newLines) {
|
||||
|
@ -218,6 +222,11 @@ SyncthingIgnores SyncthingFileModel::computeNewIgnorePatterns() const
|
|||
return newIgnorePatterns;
|
||||
}
|
||||
|
||||
void SyncthingFileModel::editIgnorePatternsManually(const QString &ignorePatterns)
|
||||
{
|
||||
m_manuallyEditedIgnorePatterns = ignorePatterns;
|
||||
}
|
||||
|
||||
QModelIndex SyncthingFileModel::parent(const QModelIndex &child) const
|
||||
{
|
||||
if (!child.isValid()) {
|
||||
|
@ -622,6 +631,7 @@ QList<QAction *> SyncthingFileModel::selectionActions()
|
|||
// allow user to review changes before applying them
|
||||
if (!askedConfirmation) {
|
||||
askedConfirmation = true;
|
||||
m_manuallyEditedIgnorePatterns.clear();
|
||||
emit actionNeedsConfirmation(action, tr("Do you want to apply the folliwng changes?"), computeIgnorePatternDiff());
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -62,6 +62,8 @@ public:
|
|||
Q_INVOKABLE QString path(const QModelIndex &path) const;
|
||||
bool hasIgnorePatterns() const;
|
||||
const std::vector<SyncthingIgnorePattern> &presentIgnorePatterns() const;
|
||||
SyncthingIgnores computeNewIgnorePatterns() const;
|
||||
void editIgnorePatternsManually(const QString &ignorePatterns);
|
||||
|
||||
Q_SIGNALS:
|
||||
void fetchQueueEmpty();
|
||||
|
@ -82,7 +84,6 @@ private:
|
|||
void matchItemAgainstIgnorePatterns(SyncthingItem &item) const;
|
||||
void ignoreSelectedItems(bool ignore = true);
|
||||
QString computeIgnorePatternDiff() const;
|
||||
SyncthingIgnores computeNewIgnorePatterns() const;
|
||||
|
||||
private:
|
||||
using SyncthingItems = std::vector<std::unique_ptr<SyncthingItem>>;
|
||||
|
@ -109,6 +110,7 @@ private:
|
|||
QueryResult m_pendingRequest;
|
||||
QFutureWatcher<LocalLookupRes> m_localItemLookup;
|
||||
std::unique_ptr<SyncthingItem> m_root;
|
||||
QString m_manuallyEditedIgnorePatterns;
|
||||
QChar m_pathSeparator;
|
||||
bool m_selectionMode;
|
||||
bool m_hasIgnorePatterns;
|
||||
|
|
|
@ -63,7 +63,10 @@ set(TS_FILES translations/${META_PROJECT_NAME}_zh_CN.ts translations/${META_PROJ
|
|||
|
||||
set(REQUIRED_ICONS
|
||||
color-profile
|
||||
dialog-ok
|
||||
dialog-cancel
|
||||
document-open
|
||||
document-edit
|
||||
preferences-other
|
||||
process-stop
|
||||
list-add
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <syncthingconnector/syncthingconnection.h>
|
||||
#include <syncthingconnector/syncthingdir.h>
|
||||
|
||||
#include <syncthingmodel/colors.h>
|
||||
#include <syncthingmodel/syncthingfilemodel.h>
|
||||
|
||||
// use meta-data of syncthingtray application here
|
||||
|
@ -12,6 +13,7 @@
|
|||
#include <QClipboard>
|
||||
#include <QDialog>
|
||||
#include <QFont>
|
||||
#include <QFontDatabase>
|
||||
#include <QGuiApplication>
|
||||
#include <QIcon>
|
||||
#include <QLabel>
|
||||
|
@ -30,6 +32,29 @@ using namespace Data;
|
|||
|
||||
namespace QtGui {
|
||||
|
||||
DiffHighlighter::DiffHighlighter(QTextDocument *parent)
|
||||
: QSyntaxHighlighter(parent)
|
||||
, m_enabled(true)
|
||||
{
|
||||
auto font = QFontDatabase::systemFont(QFontDatabase::FixedFont);
|
||||
m_baseFormat.setFont(font);
|
||||
|
||||
font.setBold(true);
|
||||
m_addedFormat.setFont(font);
|
||||
m_addedFormat.setForeground(Colors::green(true));
|
||||
m_deletedFormat.setFont(font);
|
||||
m_deletedFormat.setForeground(Colors::red(true));
|
||||
}
|
||||
|
||||
void DiffHighlighter::highlightBlock(const QString &text)
|
||||
{
|
||||
if (text.startsWith(QChar('-'))) {
|
||||
setFormat(0, static_cast<int>(text.size()), QColor(Qt::red));
|
||||
} else if (text.startsWith(QChar('+'))) {
|
||||
setFormat(0, static_cast<int>(text.size()), QColor(Qt::green));
|
||||
}
|
||||
}
|
||||
|
||||
static void setupOwnDeviceIdDialog(Data::SyncthingConnection &connection, int size, QWidget *dlg)
|
||||
{
|
||||
dlg->setWindowTitle(QCoreApplication::translate("QtGui::OtherDialogs", "Own device ID") + QStringLiteral(" - " APP_NAME));
|
||||
|
@ -141,14 +166,44 @@ QDialog *browseRemoteFilesDialog(Data::SyncthingConnection &connection, const Da
|
|||
messageBox.exec();
|
||||
});
|
||||
QObject::connect(
|
||||
model, &Data::SyncthingFileModel::actionNeedsConfirmation, dlg, [](QAction *action, const QString &message, const QString &details) {
|
||||
auto messageBox
|
||||
= QMessageBox(QMessageBox::Warning, QStringLiteral("Confirm action - " APP_NAME), message, QMessageBox::Yes | QMessageBox::No);
|
||||
messageBox.setDetailedText(details);
|
||||
model, &Data::SyncthingFileModel::actionNeedsConfirmation, dlg, [model](QAction *action, const QString &message, const QString &details) {
|
||||
auto messageBox = TextViewDialog(QStringLiteral("Confirm action - " APP_NAME));
|
||||
auto *const browser = messageBox.browser();
|
||||
auto *const highlighter = new DiffHighlighter(browser->document());
|
||||
auto *const buttonLayout = new QHBoxLayout(&messageBox);
|
||||
auto *const editBtn = new QPushButton(
|
||||
QIcon::fromTheme(QStringLiteral("document-edit")), QCoreApplication::translate("QtGui::OtherDialogs", "Edit manually"), &messageBox);
|
||||
auto *const yesBtn = new QPushButton(
|
||||
QIcon::fromTheme(QStringLiteral("dialog-ok")), QCoreApplication::translate("QtGui::OtherDialogs", "Apply"), &messageBox);
|
||||
auto *const noBtn = new QPushButton(
|
||||
QIcon::fromTheme(QStringLiteral("dialog-cancel")), QCoreApplication::translate("QtGui::OtherDialogs", "No"), &messageBox);
|
||||
QObject::connect(yesBtn, &QAbstractButton::clicked, &messageBox, [&messageBox] { messageBox.accept(); });
|
||||
QObject::connect(noBtn, &QAbstractButton::clicked, &messageBox, [&messageBox] { messageBox.reject(); });
|
||||
QObject::connect(editBtn, &QAbstractButton::clicked, &messageBox, [&messageBox, model, editBtn, highlighter] {
|
||||
auto *const b = messageBox.browser();
|
||||
editBtn->hide();
|
||||
b->clear();
|
||||
highlighter->setEnabled(false);
|
||||
b->setText(model->computeNewIgnorePatterns().ignore.join(QChar('\n')));
|
||||
b->setReadOnly(false);
|
||||
b->setUndoRedoEnabled(true);
|
||||
});
|
||||
buttonLayout->addWidget(editBtn);
|
||||
buttonLayout->addStretch();
|
||||
buttonLayout->addWidget(yesBtn);
|
||||
buttonLayout->addWidget(noBtn);
|
||||
browser->setText(details);
|
||||
messageBox.layout()->insertWidget(0, new QLabel(message, &messageBox));
|
||||
messageBox.layout()->addLayout(buttonLayout);
|
||||
messageBox.setAttribute(Qt::WA_DeleteOnClose, false);
|
||||
action->setParent(&messageBox);
|
||||
if (messageBox.exec() == QMessageBox::Yes) {
|
||||
action->trigger();
|
||||
if (messageBox.exec() != QDialog::Accepted) {
|
||||
return;
|
||||
}
|
||||
if (!browser->isReadOnly()) {
|
||||
model->editIgnorePatternsManually(browser->toPlainText());
|
||||
}
|
||||
action->trigger();
|
||||
});
|
||||
|
||||
// setup layout
|
||||
|
|
|
@ -5,6 +5,9 @@
|
|||
|
||||
#include <QtGlobal>
|
||||
|
||||
#include <QSyntaxHighlighter>
|
||||
#include <QTextCharFormat>
|
||||
|
||||
QT_FORWARD_DECLARE_CLASS(QDialog)
|
||||
QT_FORWARD_DECLARE_CLASS(QWidget)
|
||||
|
||||
|
@ -16,6 +19,32 @@ struct SyncthingDir;
|
|||
namespace QtGui {
|
||||
class TextViewDialog;
|
||||
|
||||
class SYNCTHINGWIDGETS_EXPORT DiffHighlighter : public QSyntaxHighlighter {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled)
|
||||
public:
|
||||
explicit DiffHighlighter(QTextDocument *parent = nullptr);
|
||||
|
||||
bool isEnabled() const
|
||||
{
|
||||
return m_enabled;
|
||||
}
|
||||
void setEnabled(bool enabled)
|
||||
{
|
||||
if (enabled != m_enabled) {
|
||||
m_enabled = enabled;
|
||||
rehighlight();
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
void highlightBlock(const QString &text) override;
|
||||
|
||||
private:
|
||||
QTextCharFormat m_baseFormat, m_addedFormat, m_deletedFormat;
|
||||
bool m_enabled;
|
||||
};
|
||||
|
||||
SYNCTHINGWIDGETS_EXPORT QDialog *ownDeviceIdDialog(Data::SyncthingConnection &connection);
|
||||
SYNCTHINGWIDGETS_EXPORT QWidget *ownDeviceIdWidget(Data::SyncthingConnection &connection, int size, QWidget *parent = nullptr);
|
||||
SYNCTHINGWIDGETS_EXPORT QDialog *browseRemoteFilesDialog(
|
||||
|
|
Loading…
Reference in New Issue