From ad604f1511f4941d66db841147b6ef49574233df Mon Sep 17 00:00:00 2001 From: Martchus Date: Fri, 31 Jan 2025 22:54:37 +0100 Subject: [PATCH] Improve file browser further * Use push buttons in tool bar because they have better behavior when using menus in case the button itself is not supposed to do anything but show the menu * Note that using QToolButton with QToolButton::InstantPopup gives the correct behavior but no arrow is shown * Improve text used on primary button * Use clear method instead of loop * Avoid floating/moving behavior of toolbar as there is no other place to put it anyway * Prevent review action from disappearing (due to reparenting) * Show confirmation dialog when triggering review action a second time from toolbar * Update translations --- syncthingmodel/syncthingfilemodel.cpp | 6 +- .../translations/syncthingmodel_cs_CZ.ts | 43 +++++++---- .../translations/syncthingmodel_de_DE.ts | 56 +++++++++------ .../translations/syncthingmodel_en_US.ts | 43 +++++++---- .../translations/syncthingmodel_zh_CN.ts | 43 +++++++---- syncthingwidgets/misc/otherdialogs.cpp | 72 +++++++++++-------- .../translations/syncthingwidgets_cs_CZ.ts | 53 +++++++++----- .../translations/syncthingwidgets_de_DE.ts | 59 +++++++++------ .../translations/syncthingwidgets_en_US.ts | 53 +++++++++----- .../translations/syncthingwidgets_zh_CN.ts | 53 +++++++++----- 10 files changed, 306 insertions(+), 175 deletions(-) diff --git a/syncthingmodel/syncthingfilemodel.cpp b/syncthingmodel/syncthingfilemodel.cpp index f2da6d29..9b44d7fc 100644 --- a/syncthingmodel/syncthingfilemodel.cpp +++ b/syncthingmodel/syncthingfilemodel.cpp @@ -773,7 +773,7 @@ QList SyncthingFileModel::selectionActions() if (!m_selectionMode) { auto *const startSelectionAction = new QAction(tr("Select items to sync/ignore"), this); startSelectionAction->setIcon(QIcon::fromTheme(QStringLiteral("edit-select"))); - startSelectionAction->setData(QStringLiteral("primary")); + startSelectionAction->setData(QStringLiteral("primary:") + tr("Select")); connect(startSelectionAction, &QAction::triggered, this, [this] { setSelectionModeEnabled(true); }); res << startSelectionAction; @@ -790,7 +790,7 @@ QList SyncthingFileModel::selectionActions() } else { auto *const discardAction = new QAction(tr("Uncheck all and discard staged changes"), this); discardAction->setIcon(QIcon::fromTheme(QStringLiteral("edit-undo"))); - discardAction->setData(QStringLiteral("primary")); + discardAction->setData(QStringLiteral("primary:") + tr("Discard")); connect(discardAction, &QAction::triggered, this, [this] { if (const auto rootIndex = index(0, 0); rootIndex.isValid()) { setCheckState(index(0, 0), Qt::Unchecked, true); @@ -885,7 +885,7 @@ QList SyncthingFileModel::selectionActions() if (!m_stagedChanges.isEmpty() || !m_stagedLocalFileDeletions.isEmpty()) { auto *const applyStagedChangesAction = new RejectableAction(tr("Review and apply staged changes"), this); applyStagedChangesAction->setIcon(QIcon::fromTheme(QStringLiteral("dialog-ok-apply"))); - applyStagedChangesAction->setData(QStringLiteral("primary")); + applyStagedChangesAction->setData(QStringLiteral("primary:") + tr("Apply")); connect(applyStagedChangesAction, &QAction::triggered, this, [this, action = applyStagedChangesAction]() mutable { // allow user to review changes before applying them if (action->needsConfirmation) { diff --git a/syncthingmodel/translations/syncthingmodel_cs_CZ.ts b/syncthingmodel/translations/syncthingmodel_cs_CZ.ts index bb514a7f..38b8e504 100644 --- a/syncthingmodel/translations/syncthingmodel_cs_CZ.ts +++ b/syncthingmodel/translations/syncthingmodel_cs_CZ.ts @@ -493,79 +493,94 @@ - + + Select + + + + Discard staged changes - + Uncheck all and discard staged changes - + + Discard + + + + Ignore checked items (and their children) - + Include checked items (and their children) - + Remove ignore patterns matching checked items (may affect other items as well) - + Include all items by default - + Ignore and locally delete checked items (and their children) - + Ignore all items by default - + Review and apply staged changes + Apply + + + + Do you want to apply the following changes? - + Cannot apply ignore patterns while a previous request for ignore patterns is still pending. - + Unable to change ignore patterns: %1 - + Ignore patterns have been changed. - + Ignore patterns have been changed but the following local files could not be deleted: - + Cannot query ignore patterns while a previous request for ignore patterns is still pending. diff --git a/syncthingmodel/translations/syncthingmodel_de_DE.ts b/syncthingmodel/translations/syncthingmodel_de_DE.ts index a646da67..fb2f13e5 100644 --- a/syncthingmodel/translations/syncthingmodel_de_DE.ts +++ b/syncthingmodel/translations/syncthingmodel_de_DE.ts @@ -571,34 +571,44 @@ Elemente zum Synchronisieren/Ignorieren auswählen - + + Select + Auswahl + + + Discard staged changes Vorgemerkte Änderungen verwerfen - + Uncheck all and discard staged changes - + Vorgemerkte Änderungen und Auswahl verwerfen + + + + Discard + Verwerfen - + Ignore checked items (and their children) - + Ausgewählte Elemente ignorieren (einschließlich untergeordneter Elemente) - + Ignore and locally delete checked items (and their children) - + Ausgewählte Elemente ignorieren und lokal löschen (einschließlich untergeordneter Elemente) - + Include checked items (and their children) - + Ausgewählte Elemente synchronisieren (einschließlich untergeordneter Elemente) - + Remove ignore patterns matching checked items (may affect other items as well) - + Lösche Ignoriermuster die mit ausgewählten Elementen übereinstimmen (kann auch andere Elemente betreffen) Discard selection and staged changes @@ -613,12 +623,12 @@ Ausgewählte Elemente synchronisieren (und untergeordnete Elemente) - + Include all items by default Alle Elemente standardmäßig synchronisieren - + Ignore all items by default Alle Elemente standardmäßig ignorieren @@ -627,40 +637,46 @@ Lösche Ignoriermuster die mit ausgewählten Elementen übereinstimmen (kann auch andere Elemente betreffen) - + Review and apply staged changes Vorgemerkte Änderungen überprüfen und anwenden + Apply + Anwenden + + + Do you want to apply the following changes? Sollen die folgenden Änderungen angewandt werden? - + Cannot apply ignore patterns while a previous request for ignore patterns is still pending. Ignoriermuster können nicht gespeichert werden, da noch eine vorherige Abfrage in Gange ist. - + Unable to change ignore patterns: %1 Kann Ignoriermuster nicht speichern: %1 - + Ignore patterns have been changed. Ignoriermuster wurden gespeichert. - + Ignore patterns have been changed but the following local files could not be deleted: - + Ignoriermuster wurden gespeichert, aber die folgenden lokalen Dateien konnten nicht gelöscht werden: + - + Cannot query ignore patterns while a previous request for ignore patterns is still pending. Ignoriermuster können nicht abgefragt werden, da noch eine vorherige Abfrage in Gange ist. diff --git a/syncthingmodel/translations/syncthingmodel_en_US.ts b/syncthingmodel/translations/syncthingmodel_en_US.ts index 3acb1796..2e54a8df 100644 --- a/syncthingmodel/translations/syncthingmodel_en_US.ts +++ b/syncthingmodel/translations/syncthingmodel_en_US.ts @@ -411,79 +411,94 @@ - + + Select + + + + Discard staged changes - + Uncheck all and discard staged changes - + + Discard + + + + Ignore checked items (and their children) - + Include checked items (and their children) - + Remove ignore patterns matching checked items (may affect other items as well) - + Include all items by default - + Ignore and locally delete checked items (and their children) - + Ignore all items by default - + Review and apply staged changes + Apply + + + + Do you want to apply the following changes? - + Cannot apply ignore patterns while a previous request for ignore patterns is still pending. - + Unable to change ignore patterns: %1 - + Ignore patterns have been changed. - + Ignore patterns have been changed but the following local files could not be deleted: - + Cannot query ignore patterns while a previous request for ignore patterns is still pending. diff --git a/syncthingmodel/translations/syncthingmodel_zh_CN.ts b/syncthingmodel/translations/syncthingmodel_zh_CN.ts index 1119b0a6..7e3f6746 100644 --- a/syncthingmodel/translations/syncthingmodel_zh_CN.ts +++ b/syncthingmodel/translations/syncthingmodel_zh_CN.ts @@ -505,79 +505,94 @@ - + + Select + + + + Discard staged changes - + Uncheck all and discard staged changes - + + Discard + + + + Ignore checked items (and their children) - + Include checked items (and their children) - + Remove ignore patterns matching checked items (may affect other items as well) - + Include all items by default - + Ignore and locally delete checked items (and their children) - + Ignore all items by default - + Review and apply staged changes + Apply + + + + Do you want to apply the following changes? - + Cannot apply ignore patterns while a previous request for ignore patterns is still pending. - + Unable to change ignore patterns: %1 - + Ignore patterns have been changed. - + Ignore patterns have been changed but the following local files could not be deleted: - + Cannot query ignore patterns while a previous request for ignore patterns is still pending. diff --git a/syncthingwidgets/misc/otherdialogs.cpp b/syncthingwidgets/misc/otherdialogs.cpp index 842d30be..4a52379f 100644 --- a/syncthingwidgets/misc/otherdialogs.cpp +++ b/syncthingwidgets/misc/otherdialogs.cpp @@ -35,9 +35,10 @@ #include #include #include -#include #include +#include + using namespace std; using namespace Data; @@ -98,22 +99,21 @@ QWidget *ownDeviceIdWidget(Data::SyncthingConnection &connection, int size, QWid } /// \cond -static QToolButton *initToolButton(const QString &text, QAction *action, QWidget *parent) +static QPushButton *initToolButton(const QString &text, QAction *action, QWidget *parent, bool single = false) { - auto btn = new QToolButton(parent); + auto btn = new QPushButton(parent); btn->setText(text); btn->setIcon(action->icon()); - btn->setToolButtonStyle(Qt::ToolButtonTextUnderIcon); - btn->setPopupMode(QToolButton::InstantPopup); - btn->addAction(action); + btn->setFlat(true); + if (single) { + QObject::connect(btn, &QAbstractButton::clicked, action, &QAction::trigger); + } else { + auto *menu = new QMenu(btn); + menu->addAction(action); + btn->setMenu(menu); + } return btn; } - -static QString firstWord(const QString &text) -{ - const auto spacePos = text.indexOf(QChar(' ')); - return spacePos > 0 ? text.mid(0, spacePos) : text; -} /// \endcond QDialog *browseRemoteFilesDialog(Data::SyncthingConnection &connection, const Data::SyncthingDir &dir, QWidget *parent) @@ -131,43 +131,43 @@ QDialog *browseRemoteFilesDialog(Data::SyncthingConnection &connection, const Da // setup toolbar auto toolBar = new QToolBar(dlg); - toolBar->setToolButtonStyle(Qt::ToolButtonTextUnderIcon); - auto updateToolBarActions = [toolBar, model] { - const auto existingActions = toolBar->actions(); - for (auto *const action : existingActions) { - toolBar->removeAction(action); - } - const auto newActions = model->selectionActions(); - QToolButton *primaryBtn = nullptr, *ignoreBtn = nullptr, *includeBtn = nullptr, *otherBtn = nullptr; + toolBar->setFloatable(false); + toolBar->setMovable(false); + auto updateToolBarActions = [toolBar, model, actions = QList()] () mutable { + toolBar->clear(); + qDeleteAll(actions); + actions = model->selectionActions(); + QPushButton *primaryBtn = nullptr, *ignoreBtn = nullptr, *includeBtn = nullptr, *otherBtn = nullptr; QStringList primaryTexts; QAction *firstPrimaryAction = nullptr; - for (auto *const action : newActions) { + primaryTexts.reserve(4); + for (auto *const action : std::as_const(actions)) { + action->setParent(toolBar); const auto category = action->data().toString(); - if (category == QStringLiteral("primary")) { + if (category.startsWith(QStringLiteral("primary:"))) { + primaryTexts << category.mid(8); if (!firstPrimaryAction) { firstPrimaryAction = action; continue; } else if (!primaryBtn) { primaryBtn = initToolButton(QString(), firstPrimaryAction, toolBar); - primaryTexts << firstWord(firstPrimaryAction->text()); } - primaryBtn->addAction(action); - primaryTexts << firstWord(action->text()); + primaryBtn->menu()->addAction(action); } else if (category == QStringLiteral("ignore")) { if (ignoreBtn) { - ignoreBtn->addAction(action); + ignoreBtn->menu()->addAction(action); } else { ignoreBtn = initToolButton(QCoreApplication::translate("QtGui::OtherDialogs", "Ignore"), action, toolBar); } } else if (category == QStringLiteral("include")) { if (includeBtn) { - includeBtn->addAction(action); + includeBtn->menu()->addAction(action); } else { includeBtn = initToolButton(QCoreApplication::translate("QtGui::OtherDialogs", "Include"), action, toolBar); } } else { if (otherBtn) { - otherBtn->addAction(action); + otherBtn->menu()->addAction(action); } else { otherBtn = initToolButton(QCoreApplication::translate("QtGui::OtherDialogs", "Other"), action, toolBar); } @@ -178,7 +178,7 @@ QDialog *browseRemoteFilesDialog(Data::SyncthingConnection &connection, const Da primaryBtn->setText(primaryTexts.join(QChar('/'))); toolBar->addWidget(primaryBtn); } else { - toolBar->addAction(firstPrimaryAction); + toolBar->addWidget(initToolButton(firstPrimaryAction->text(), firstPrimaryAction, toolBar, true)); } } if (ignoreBtn) { @@ -239,7 +239,8 @@ QDialog *browseRemoteFilesDialog(Data::SyncthingConnection &connection, const Da messageBox.exec(); }); QObject::connect( - model, &Data::SyncthingFileModel::actionNeedsConfirmation, dlg, [model](QAction *action, const QString &message, const QString &details, const QSet &localDeletions) { + model, &Data::SyncthingFileModel::actionNeedsConfirmation, toolBar, [model, toolBar](QAction *action, const QString &message, const QString &details, const QSet &localDeletions) { + auto *const rejectableAction = qobject_cast(action); auto messageBox = TextViewDialog(QStringLiteral("Confirm action - " APP_NAME)); auto deletionList = QString(); if (!localDeletions.isEmpty()) { @@ -297,8 +298,14 @@ QDialog *browseRemoteFilesDialog(Data::SyncthingConnection &connection, const Da } messageBox.layout()->addLayout(buttonLayout); messageBox.setAttribute(Qt::WA_DeleteOnClose, false); - action->setParent(&messageBox); + // ensure action lives at least as long as message box (but don't remove it from the tool bar) + if (action->parent() != toolBar) { + action->setParent(&messageBox); + } if (messageBox.exec() != QDialog::Accepted) { + if (rejectableAction) { + rejectableAction->dismiss(); + } return; } if (!browser->isReadOnly()) { @@ -315,6 +322,9 @@ QDialog *browseRemoteFilesDialog(Data::SyncthingConnection &connection, const Da model->editLocalDeletions(editedLocalDeletions); } action->trigger(); + if (rejectableAction) { + rejectableAction->dismiss(); + } }); // setup layout diff --git a/syncthingwidgets/translations/syncthingwidgets_cs_CZ.ts b/syncthingwidgets/translations/syncthingwidgets_cs_CZ.ts index acff8700..2a414a30 100644 --- a/syncthingwidgets/translations/syncthingwidgets_cs_CZ.ts +++ b/syncthingwidgets/translations/syncthingwidgets_cs_CZ.ts @@ -1604,93 +1604,108 @@ QtGui::OtherDialogs - + Own device ID - + device ID is unknown - + Copy to clipboard - + Remote/global tree of folder "%1" - + + Ignore + + + + + Include + + + + + Other + + + + Manage ignore patterns (experimental) - - + + Deletion of the following local files: - + Edit patterns manually - + Changes to ignore patterns: - + Ignore patterns of folder "%1" - + Do you want to save the changes? - + Notifications/errors - + Copy - + Selected notification: - + Clear all notifications - + Ignore patterns have been changed. - + Apply - + No - + Unable to save ignore patterns: %1 diff --git a/syncthingwidgets/translations/syncthingwidgets_de_DE.ts b/syncthingwidgets/translations/syncthingwidgets_de_DE.ts index 5d322d73..e6a6298d 100644 --- a/syncthingwidgets/translations/syncthingwidgets_de_DE.ts +++ b/syncthingwidgets/translations/syncthingwidgets_de_DE.ts @@ -1800,78 +1800,93 @@ Die Weboberfläche wird stattdessen im Standardwebrowser geöffnet. QtGui::OtherDialogs - + Own device ID Eigene Geräte-ID - + device ID is unknown Geräte-ID ist unbekannt - + Copy to clipboard In Zwischenablage kopieren - + Remote/global tree of folder "%1" Globale Dateistruktur von "%1" - + + Ignore + Ignorieren + + + + Include + Einbeziehen + + + + Other + Weiteres + + + Manage ignore patterns (experimental) Ignoriermuster verwalten (experimentell) - - + + Deletion of the following local files: - + Löschung folgender lokalen Dateien: - + Edit patterns manually - + Muster manuell editieren - + Changes to ignore patterns: - + Änderung der Ignorier-Muster: - + Ignore patterns of folder "%1" Ignoriermuster des Ordners "%1" - + Do you want to save the changes? Sollen die Änderungen gespeichert werden? - + Notifications/errors Benachrichtigungen/Fehlermeldungen - + Copy Kopieren - + Selected notification: Ausgewählte Benachrichtigung: - + Clear all notifications Alle Benachrichtigungen löschen - + Ignore patterns have been changed. Die Ignoriermuster wurden gespeichert. @@ -1884,17 +1899,17 @@ Die Weboberfläche wird stattdessen im Standardwebrowser geöffnet.Manuell editieren - + Apply Anwenden - + No Nein - + Unable to save ignore patterns: %1 Ignoriermuster können nicht gespeichert werden: %1 diff --git a/syncthingwidgets/translations/syncthingwidgets_en_US.ts b/syncthingwidgets/translations/syncthingwidgets_en_US.ts index bdac1a7a..40f19e85 100644 --- a/syncthingwidgets/translations/syncthingwidgets_en_US.ts +++ b/syncthingwidgets/translations/syncthingwidgets_en_US.ts @@ -1602,93 +1602,108 @@ QtGui::OtherDialogs - + Own device ID - + device ID is unknown - + Copy to clipboard - + Remote/global tree of folder "%1" - + + Ignore + + + + + Include + + + + + Other + + + + Manage ignore patterns (experimental) - - + + Deletion of the following local files: - + Edit patterns manually - + Changes to ignore patterns: - + Ignore patterns of folder "%1" - + Do you want to save the changes? - + Notifications/errors - + Copy - + Selected notification: - + Clear all notifications - + Ignore patterns have been changed. - + Apply - + No - + Unable to save ignore patterns: %1 diff --git a/syncthingwidgets/translations/syncthingwidgets_zh_CN.ts b/syncthingwidgets/translations/syncthingwidgets_zh_CN.ts index cb6e5bd7..a0972984 100644 --- a/syncthingwidgets/translations/syncthingwidgets_zh_CN.ts +++ b/syncthingwidgets/translations/syncthingwidgets_zh_CN.ts @@ -1638,93 +1638,108 @@ The Web UI will be opened in the default web browser instead. QtGui::OtherDialogs - + Own device ID 本设备 ID - + device ID is unknown 设备 ID 未知 - + Copy to clipboard 复制至剪贴板 - + Remote/global tree of folder "%1" - + + Ignore + + + + + Include + + + + + Other + + + + Manage ignore patterns (experimental) - - + + Deletion of the following local files: - + Edit patterns manually - + Changes to ignore patterns: - + Ignore patterns of folder "%1" - + Do you want to save the changes? - + Notifications/errors - + Copy - + Selected notification: - + Clear all notifications - + Ignore patterns have been changed. - + Apply - + No - + Unable to save ignore patterns: %1