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
This commit is contained in:
Luke Davey 2025-12-10 18:56:53 -05:00
parent 592d553ff8
commit 030872882a
9 changed files with 177 additions and 3 deletions

View file

@ -104,6 +104,13 @@ static const QHash<Config::ConfigKey, ConfigDirective> 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, {}}},

View file

@ -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,

View file

@ -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());

View file

@ -908,12 +908,56 @@
</layout>
</item>
<item>
<widget class="QCheckBox" name="toolbarShowCheckBox">
<widget class="QCheckBox" name="toolbarShowCheckBox">
<property name="text">
<string>Show toolbar</string>
</property>
</widget>
</item>
<item>
<widget class="QGroupBox" name="toolbarCustomizeGroupBox">
<property name="title">
<string>Customize Toolbar</string>
</property>
<layout class="QVBoxLayout" name="verticalLayoutCustomizeToolbar">
<item>
<widget class="QCheckBox" name="showSearchToolButton">
<property name="text">
<string>Show Search</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="showNewEntryToolButton">
<property name="text">
<string>Show New Entry</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="showSaveToolButton">
<property name="text">
<string>Show Save</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="showDeleteToolButton">
<property name="text">
<string>Show Delete</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="showPasswordGeneratorToolButton">
<property name="text">
<string>Show Password Generator</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QCheckBox" name="menubarShowCheckBox">
<property name="toolTip">

View file

@ -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);

View file

@ -25,6 +25,7 @@
#include <QFrame>
#include <QPalette>
#include <QSize>
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;

View file

@ -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<Botan::RSA_PrivateKey*>(sign.certificate.key.data());
const auto rsaKey = dynamic_cast<Botan::RSA_PrivateKey*>(sign.certificate.key.data());
if (!rsaKey) {
qWarning("KeeShare: certificate key is not an RSA private key");
return {};
}
std::vector<uint8_t> rsaE(rsaKey->get_e().bytes());
rsaKey->get_e().binary_encode(rsaE.data());

View file

@ -19,8 +19,11 @@
#include "TestGui.h"
#include "gui/Application.h"
#include <array>
#include <QCheckBox>
#include <QClipboard>
#include <QComboBox>
#include <QHash>
#include <QMetaObject>
#include <QListWidget>
#include <QMenu>
#include <QMenuBar>
@ -208,6 +211,65 @@ void TestGui::testSettingsDefaultTabOrder()
QTest::keyClick(dbSettingsWidget, Qt::Key::Key_Escape);
}
void TestGui::testSettingsNavigation()
{
triggerAction("actionSettings");
auto* settingsWidget = m_mainWindow->findChild<ApplicationSettingsWidget*>();
QVERIFY(settingsWidget);
struct ToolbarSetting {
Config::ConfigKey key;
const char* objectName;
};
constexpr std::array<ToolbarSetting, 5> 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<Config::ConfigKey, bool> 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<QCheckBox*>(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<NewDatabaseWizard*>();
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(

View file

@ -38,6 +38,8 @@ private slots:
void cleanupTestCase();
void testSettingsDefaultTabOrder();
void testSettingsNavigation();
void testNewDatabaseWizardMinimumWidth();
void testCreateDatabase();
void testMergeDatabase();
void testRemoteSyncDatabaseSameKey();