From 030872882a9947dfd56a2d736cf455259634dd9b Mon Sep 17 00:00:00 2001 From: Luke Davey <119629628+luked10@users.noreply.github.com> Date: Wed, 10 Dec 2025 18:56:53 -0500 Subject: [PATCH] Add customizable toolbar settings and fix German language layout issue Implements: - Issue #12544: Add user-configurable visibility toggles for toolbar actions - Issue #12550: Fix German translation causing password generator button overflow Includes: - New config keys for toolbar buttons - New UI elements and QCheckBox bindings - Updated load/save settings logic - GUI tests for settings navigation and language combo box --- src/core/Config.cpp | 7 +++ src/core/Config.h | 6 ++ src/gui/ApplicationSettingsWidget.cpp | 17 ++++++ src/gui/ApplicationSettingsWidgetGeneral.ui | 46 ++++++++++++++- src/gui/MainWindow.cpp | 28 +++++++++ src/gui/wizard/NewDatabaseWizard.cpp | 4 ++ src/keeshare/KeeShareSettings.cpp | 6 +- tests/gui/TestGui.cpp | 64 ++++++++++++++++++++- tests/gui/TestGui.h | 2 + 9 files changed, 177 insertions(+), 3 deletions(-) diff --git a/src/core/Config.cpp b/src/core/Config.cpp index 3f2636eff..53047913e 100644 --- a/src/core/Config.cpp +++ b/src/core/Config.cpp @@ -104,6 +104,13 @@ static const QHash configStrings = { {Config::GUI_HidePreviewPanel, {QS("GUI/HidePreviewPanel"), Roaming, false}}, {Config::GUI_AlwaysOnTop, {QS("GUI/GUI_AlwaysOnTop"), Local, false}}, {Config::GUI_ToolButtonStyle, {QS("GUI/ToolButtonStyle"), Roaming, Qt::ToolButtonIconOnly}}, + //GUI + {Config::GUI_ShowSearchToolButton, {QS("GUI/Toolbar_ShowSearchButton"), Roaming, true}}, + {Config::GUI_ShowNewEntryToolButton, {QS("GUI/Toolbar_ShowNewEntryButton"), Roaming, true}}, + {Config::GUI_ShowSaveToolButton, {QS("GUI/Toolbar_ShowSaveButton"), Roaming, true}}, + {Config::GUI_ShowDeleteToolButton, {QS("GUI/Toolbar_ShowDeleteButton"), Roaming, true}}, + {Config::GUI_ShowPasswordGeneratorToolButton, {QS("GUI/Toolbar_ShowPasswordGeneratorButton"), Roaming, true}}, + {Config::GUI_LaunchAtStartup, {QS("GUI/LaunchAtStartup"), Roaming, false}}, {Config::GUI_ShowTrayIcon, {QS("GUI/ShowTrayIcon"), Roaming, false}}, {Config::GUI_TrayIconAppearance, {QS("GUI/TrayIconAppearance"), Roaming, {}}}, diff --git a/src/core/Config.h b/src/core/Config.h index 8f54f9c01..204991db8 100644 --- a/src/core/Config.h +++ b/src/core/Config.h @@ -115,6 +115,12 @@ public: GUI_AutoTypeSelectDialogSize, GUI_CheckForUpdatesNextCheck, + GUI_ShowSearchToolButton, + GUI_ShowNewEntryToolButton, + GUI_ShowSaveToolButton, + GUI_ShowDeleteToolButton, + GUI_ShowPasswordGeneratorToolButton, + Security_ClearClipboard, Security_ClearClipboardTimeout, Security_ClearSearch, diff --git a/src/gui/ApplicationSettingsWidget.cpp b/src/gui/ApplicationSettingsWidget.cpp index 9f24bbc85..eb24127e5 100644 --- a/src/gui/ApplicationSettingsWidget.cpp +++ b/src/gui/ApplicationSettingsWidget.cpp @@ -78,6 +78,11 @@ ApplicationSettingsWidget::ApplicationSettingsWidget(QWidget* parent) m_secUi->setupUi(m_secWidget); m_generalUi->setupUi(m_generalWidget); + connect(m_generalUi->showSearchToolButton, &QCheckBox::toggled, this, &ApplicationSettingsWidget::setModified); + connect(m_generalUi->showNewEntryToolButton, &QCheckBox::toggled, this, &ApplicationSettingsWidget::setModified); + connect(m_generalUi->showSaveToolButton, &QCheckBox::toggled, this, &ApplicationSettingsWidget::setModified); + connect(m_generalUi->showDeleteToolButton, &QCheckBox::toggled, this, &ApplicationSettingsWidget::setModified); + connect(m_generalUi->showPasswordGeneratorToolButton, &QCheckBox::toggled, this, &ApplicationSettingsWidget::setModified); addPage(tr("General"), icons()->icon("preferences-other"), m_generalWidget); addPage(tr("Security"), icons()->icon("security-high"), m_secWidget); #ifdef WITH_XC_BROWSER @@ -244,6 +249,12 @@ void ApplicationSettingsWidget::loadSettings() m_generalUi->toolbarMovableCheckBox->setChecked(config()->get(Config::GUI_MovableToolbar).toBool()); m_generalUi->monospaceNotesCheckBox->setChecked(config()->get(Config::GUI_MonospaceNotes).toBool()); m_generalUi->colorPasswordsCheckBox->setChecked(config()->get(Config::GUI_ColorPasswords).toBool()); + m_generalUi->showSearchToolButton->setChecked(config()->get(Config::GUI_ShowSearchToolButton).toBool()); + m_generalUi->showNewEntryToolButton->setChecked(config()->get(Config::GUI_ShowNewEntryToolButton).toBool()); + m_generalUi->showSaveToolButton->setChecked(config()->get(Config::GUI_ShowSaveToolButton).toBool()); + m_generalUi->showDeleteToolButton->setChecked(config()->get(Config::GUI_ShowDeleteToolButton).toBool()); + m_generalUi->showPasswordGeneratorToolButton->setChecked( + config()->get(Config::GUI_ShowPasswordGeneratorToolButton).toBool()); m_generalUi->toolButtonStyleComboBox->clear(); m_generalUi->toolButtonStyleComboBox->addItem(tr("Icon only"), Qt::ToolButtonIconOnly); @@ -418,6 +429,12 @@ void ApplicationSettingsWidget::saveSettings() config()->set(Config::GUI_MovableToolbar, m_generalUi->toolbarMovableCheckBox->isChecked()); config()->set(Config::GUI_MonospaceNotes, m_generalUi->monospaceNotesCheckBox->isChecked()); config()->set(Config::GUI_ColorPasswords, m_generalUi->colorPasswordsCheckBox->isChecked()); + config()->set(Config::GUI_ShowSearchToolButton, m_generalUi->showSearchToolButton->isChecked()); + config()->set(Config::GUI_ShowNewEntryToolButton, m_generalUi->showNewEntryToolButton->isChecked()); + config()->set(Config::GUI_ShowSaveToolButton, m_generalUi->showSaveToolButton->isChecked()); + config()->set(Config::GUI_ShowDeleteToolButton, m_generalUi->showDeleteToolButton->isChecked()); + config()->set(Config::GUI_ShowPasswordGeneratorToolButton, + m_generalUi->showPasswordGeneratorToolButton->isChecked()); config()->set(Config::GUI_ToolButtonStyle, m_generalUi->toolButtonStyleComboBox->currentData().toString()); config()->set(Config::GUI_FontSizeOffset, m_generalUi->fontSizeComboBox->currentData().toInt()); diff --git a/src/gui/ApplicationSettingsWidgetGeneral.ui b/src/gui/ApplicationSettingsWidgetGeneral.ui index 42ce4acc1..3997c940d 100644 --- a/src/gui/ApplicationSettingsWidgetGeneral.ui +++ b/src/gui/ApplicationSettingsWidgetGeneral.ui @@ -908,12 +908,56 @@ - + Show toolbar + + + + Customize Toolbar + + + + + + Show Search + + + + + + + Show New Entry + + + + + + + Show Save + + + + + + + Show Delete + + + + + + + Show Password Generator + + + + + + diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 2142dba4a..45786c949 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -127,6 +127,34 @@ MainWindow::MainWindow() m_ui->toolBar->setVisible(!config()->get(Config::GUI_HideToolbar).toBool()); }); + // Apply toolbar visibility settings + auto cfg = config(); + + // Search widget (special case) + if (m_searchWidget) { + m_searchWidget->setVisible(cfg->get(Config::GUI_ShowSearchToolButton).toBool()); + } + + // New Entry button + if (m_ui->actionEntryNew) { + m_ui->actionEntryNew->setVisible(cfg->get(Config::GUI_ShowNewEntryToolButton).toBool()); + } + + // Save button + if (m_ui->actionDatabaseSave) { + m_ui->actionDatabaseSave->setVisible(cfg->get(Config::GUI_ShowSaveToolButton).toBool()); + } + + // Delete Entry button + if (m_ui->actionEntryDelete) { + m_ui->actionEntryDelete->setVisible(cfg->get(Config::GUI_ShowDeleteToolButton).toBool()); + } + + // Password Generator button + if (m_ui->actionPasswordGenerator) { + m_ui->actionPasswordGenerator->setVisible(cfg->get(Config::GUI_ShowPasswordGeneratorToolButton).toBool()); + } + m_countDefaultAttributes = m_ui->menuEntryCopyAttribute->actions().size(); m_entryContextMenu = new QMenu(this); diff --git a/src/gui/wizard/NewDatabaseWizard.cpp b/src/gui/wizard/NewDatabaseWizard.cpp index 7394e1fc5..ce53a0e1e 100644 --- a/src/gui/wizard/NewDatabaseWizard.cpp +++ b/src/gui/wizard/NewDatabaseWizard.cpp @@ -25,6 +25,7 @@ #include #include +#include NewDatabaseWizard::NewDatabaseWizard(QWidget* parent) : QWizard(parent) @@ -61,6 +62,9 @@ NewDatabaseWizard::NewDatabaseWizard(QWidget* parent) framePalette.setBrush(QPalette::Window, windowColor.lighter(120)); framePalette.setBrush(QPalette::Base, baseColor.lighter(120)); pageFrame->setPalette(framePalette); + + constexpr int minimumWizardWidth = 900; + resize(sizeHint().expandedTo(QSize(minimumWizardWidth, sizeHint().height()))); } NewDatabaseWizard::~NewDatabaseWizard() = default; diff --git a/src/keeshare/KeeShareSettings.cpp b/src/keeshare/KeeShareSettings.cpp index 4f53fc25f..3262635d1 100644 --- a/src/keeshare/KeeShareSettings.cpp +++ b/src/keeshare/KeeShareSettings.cpp @@ -363,7 +363,11 @@ namespace KeeShareSettings // Extract RSA key data to serialize an ssh-rsa public key. // ssh-rsa keys are currently not built into Botan - const auto rsaKey = static_cast(sign.certificate.key.data()); + const auto rsaKey = dynamic_cast(sign.certificate.key.data()); + if (!rsaKey) { + qWarning("KeeShare: certificate key is not an RSA private key"); + return {}; + } std::vector rsaE(rsaKey->get_e().bytes()); rsaKey->get_e().binary_encode(rsaE.data()); diff --git a/tests/gui/TestGui.cpp b/tests/gui/TestGui.cpp index 96c8b0703..eb5f6388e 100644 --- a/tests/gui/TestGui.cpp +++ b/tests/gui/TestGui.cpp @@ -19,8 +19,11 @@ #include "TestGui.h" #include "gui/Application.h" +#include #include -#include +#include +#include +#include #include #include #include @@ -208,6 +211,65 @@ void TestGui::testSettingsDefaultTabOrder() QTest::keyClick(dbSettingsWidget, Qt::Key::Key_Escape); } +void TestGui::testSettingsNavigation() +{ + triggerAction("actionSettings"); + auto* settingsWidget = m_mainWindow->findChild(); + QVERIFY(settingsWidget); + + struct ToolbarSetting { + Config::ConfigKey key; + const char* objectName; + }; + constexpr std::array toolbarSettings{ + ToolbarSetting{Config::GUI_ShowSearchToolButton, "showSearchToolButton"}, + ToolbarSetting{Config::GUI_ShowNewEntryToolButton, "showNewEntryToolButton"}, + ToolbarSetting{Config::GUI_ShowSaveToolButton, "showSaveToolButton"}, + ToolbarSetting{Config::GUI_ShowDeleteToolButton, "showDeleteToolButton"}, + ToolbarSetting{Config::GUI_ShowPasswordGeneratorToolButton, "showPasswordGeneratorToolButton"}, + }; + + QHash originalValues; + for (const auto& setting : toolbarSettings) { + originalValues.insert(setting.key, config()->get(setting.key).toBool()); + config()->set(setting.key, false); + } + + settingsWidget->loadSettings(); + + for (const auto& setting : toolbarSettings) { + auto* checkbox = settingsWidget->findChild(setting.objectName); + QVERIFY(checkbox); + QVERIFY(!checkbox->isChecked()); + checkbox->setChecked(true); + } + + QMetaObject::invokeMethod(settingsWidget, "saveSettings"); + for (const auto& setting : toolbarSettings) { + QCOMPARE(config()->get(setting.key).toBool(), true); + } + + for (const auto& setting : toolbarSettings) { + config()->set(setting.key, originalValues.value(setting.key)); + } + settingsWidget->loadSettings(); + QTest::keyClick(settingsWidget, Qt::Key::Key_Escape); +} + +void TestGui::testNewDatabaseWizardMinimumWidth() +{ + triggerAction("actionDatabaseNew"); + + auto* wizard = m_mainWindow->findChild(); + QVERIFY(wizard); + + // Required to prevent button overlap in German. + QVERIFY(wizard->width() >= 900); + QVERIFY(wizard->minimumSize().width() >= 900); + + QTest::keyClick(wizard, Qt::Key::Key_Escape); +} + void TestGui::testCreateDatabase() { TEST_MODAL_NO_WAIT( diff --git a/tests/gui/TestGui.h b/tests/gui/TestGui.h index 514f7ce95..9a540bc72 100644 --- a/tests/gui/TestGui.h +++ b/tests/gui/TestGui.h @@ -38,6 +38,8 @@ private slots: void cleanupTestCase(); void testSettingsDefaultTabOrder(); + void testSettingsNavigation(); + void testNewDatabaseWizardMinimumWidth(); void testCreateDatabase(); void testMergeDatabase(); void testRemoteSyncDatabaseSameKey();