Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reset splitter sizes on database unlock #11014

Merged
merged 2 commits into from
Oct 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions COPYING
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ Copyright: 2022 KeePassXC Team <[email protected]>
License: MIT

Files: share/icons/application/scalable/actions/application-exit.svg
share/icons/application/scalable/actions/arrow-collapse-down.svg
share/icons/application/scalable/actions/attributes-copy.svg
share/icons/application/scalable/actions/auto-type.svg
share/icons/application/scalable/actions/bitwarden.svg
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions share/icons/icons.qrc
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<file>application/256x256/apps/keepassxc.png</file>

<file>application/scalable/actions/application-exit.svg</file>
<file>application/scalable/actions/arrow-collapse-down.svg</file>
<file>application/scalable/actions/attributes-copy.svg</file>
<file>application/scalable/actions/auto-type.svg</file>
<file>application/scalable/actions/bitwarden.svg</file>
Expand Down
8 changes: 8 additions & 0 deletions share/translations/keepassxc_en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6179,6 +6179,14 @@ Expect some bugs and minor issues, this version is meant for testing purposes.</
<source>Setup Remote Sync…</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Show Group Panel</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Toggle Show Group Panel</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ManageDatabase</name>
Expand Down
1 change: 1 addition & 0 deletions src/core/Config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ static const QHash<Config::ConfigKey, ConfigDirective> configStrings = {
{Config::GUI_HideMenubar, {QS("GUI/HideMenubar"), Roaming, false}},
{Config::GUI_HideToolbar, {QS("GUI/HideToolbar"), Roaming, false}},
{Config::GUI_MovableToolbar, {QS("GUI/MovableToolbar"), Roaming, false}},
{Config::GUI_HideGroupPanel, {QS("GUI/HideGroupPanel"), Roaming, false}},
{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}},
Expand Down
1 change: 1 addition & 0 deletions src/core/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ class Config : public QObject
GUI_HideMenubar,
GUI_HideToolbar,
GUI_MovableToolbar,
GUI_HideGroupPanel,
GUI_HidePreviewPanel,
GUI_AlwaysOnTop,
GUI_ToolButtonStyle,
Expand Down
17 changes: 15 additions & 2 deletions src/gui/DatabaseWidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,8 @@ DatabaseWidget::DatabaseWidget(QSharedPointer<Database> db, QWidget* parent)
m_groupSplitter->setStretchFactor(0, 100);
m_groupSplitter->setStretchFactor(1, 0);
m_groupSplitter->setSizes({1, 1});
// Initial visibility based on config value
m_groupSplitter->setVisible(!config()->get(Config::GUI_HideGroupPanel).toBool());

auto rightHandSideWidget = new QWidget(m_mainSplitter);
auto rightHandSideVBox = new QVBoxLayout();
Expand All @@ -140,12 +142,11 @@ DatabaseWidget::DatabaseWidget(QSharedPointer<Database> db, QWidget* parent)
rightHandSideWidget->setLayout(rightHandSideVBox);
m_entryView = new EntryView(rightHandSideWidget);

m_mainSplitter->setChildrenCollapsible(true);
m_mainSplitter->setChildrenCollapsible(false);
m_mainSplitter->addWidget(m_groupSplitter);
m_mainSplitter->addWidget(rightHandSideWidget);
m_mainSplitter->setStretchFactor(0, 0);
m_mainSplitter->setStretchFactor(1, 100);
m_mainSplitter->setCollapsible(1, false);
m_mainSplitter->setSizes({1, 1});

m_previewSplitter->setOrientation(Qt::Vertical);
Expand Down Expand Up @@ -217,6 +218,7 @@ DatabaseWidget::DatabaseWidget(QSharedPointer<Database> db, QWidget* parent)
connect(m_databaseOpenWidget, SIGNAL(dialogFinished(bool)), SLOT(loadDatabase(bool)));
connect(this, SIGNAL(currentChanged(int)), SLOT(emitCurrentModeChanged()));
connect(this, SIGNAL(requestGlobalAutoType(const QString&)), parent, SLOT(performGlobalAutoType(const QString&)));
connect(config(), &Config::changed, this, &DatabaseWidget::onConfigChanged);
// clang-format on

connectDatabaseSignals();
Expand Down Expand Up @@ -408,6 +410,15 @@ void DatabaseWidget::setSplitterSizes(const QHash<Config::ConfigKey, QList<int>>
}
}

void DatabaseWidget::onConfigChanged(Config::ConfigKey key)
{
if (key == Config::GUI_HideGroupPanel) {
// Toggle the group splitter visibility and reset the size
m_groupSplitter->setVisible(!config()->get(Config::GUI_HideGroupPanel).toBool());
setSplitterSizes({{Config::GUI_SplitterState, QList<int>({})}});
}
}

void DatabaseWidget::setSearchStringForAutoType(const QString& search)
{
m_searchStringForAutoType = search;
Expand Down Expand Up @@ -1284,6 +1295,7 @@ void DatabaseWidget::loadDatabase(bool accepted)
}

if (accepted) {
emit databaseAboutToUnlock();
replaceDatabase(openWidget->database());
switchToMainView();
processAutoOpen();
Expand Down Expand Up @@ -1440,6 +1452,7 @@ void DatabaseWidget::unlockDatabase(bool accepted)
}
}

emit databaseAboutToUnlock();
QSharedPointer<Database> db;
if (senderDialog) {
db = senderDialog->database();
Expand Down
2 changes: 2 additions & 0 deletions src/gui/DatabaseWidget.h
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ class DatabaseWidget : public QStackedWidget
void databaseModified();
void databaseNonDataChanged();
void databaseSaved();
void databaseAboutToUnlock();
void databaseUnlocked();
void databaseLockRequested();
void databaseLocked();
Expand Down Expand Up @@ -287,6 +288,7 @@ private slots:
// Database autoreload slots
void reloadDatabaseFile();
void restoreGroupEntryFocus(const QUuid& groupUuid, const QUuid& EntryUuid);
void onConfigChanged(Config::ConfigKey key);

private:
int addChildWidget(QWidget* w);
Expand Down
107 changes: 54 additions & 53 deletions src/gui/DatabaseWidgetStateSync.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ DatabaseWidgetStateSync::DatabaseWidgetStateSync(QObject* parent)
m_listViewState = config()->get(Config::GUI_ListViewState).toByteArray();
m_searchViewState = config()->get(Config::GUI_SearchViewState).toByteArray();

connect(qApp, &QCoreApplication::aboutToQuit, this, &DatabaseWidgetStateSync::sync);
m_syncTimer.setSingleShot(true);
m_syncTimer.setInterval(100);
connect(&m_syncTimer, &QTimer::timeout, this, &DatabaseWidgetStateSync::sync);
}

DatabaseWidgetStateSync::~DatabaseWidgetStateSync() = default;
Expand All @@ -43,6 +45,7 @@ DatabaseWidgetStateSync::~DatabaseWidgetStateSync() = default;
*/
void DatabaseWidgetStateSync::sync()
{
m_syncTimer.stop();
config()->set(Config::GUI_SplitterState, intListToVariant(m_splitterSizes.value(Config::GUI_SplitterState)));
config()->set(Config::GUI_PreviewSplitterState,
intListToVariant(m_splitterSizes.value(Config::GUI_PreviewSplitterState)));
Expand All @@ -56,79 +59,71 @@ void DatabaseWidgetStateSync::sync()
void DatabaseWidgetStateSync::setActive(DatabaseWidget* dbWidget)
{
if (m_activeDbWidget) {
if (m_activeDbWidget->currentMode() != DatabaseWidget::Mode::LockedMode) {
// Update settings from previously active database if unlocked
updateAll();
}
disconnect(m_activeDbWidget, nullptr, this, nullptr);
}

m_activeDbWidget = dbWidget;

if (m_activeDbWidget) {
// Give the database widget a chance to render itself before restoring the state
QTimer::singleShot(0, this, [this] {
if (!m_activeDbWidget) {
return;
}

m_blockUpdates = true;

m_activeDbWidget->setSplitterSizes(m_splitterSizes);

if (m_activeDbWidget->isSearchActive()) {
restoreSearchView();
} else {
restoreListView();
}

m_blockUpdates = false;
});
if (m_activeDbWidget->currentMode() != DatabaseWidget::Mode::LockedMode) {
// Immediately apply settings to active database if already unlocked
applySplitterSizes();
applyViewState();
}

connect(m_activeDbWidget, SIGNAL(databaseAboutToUnlock()), SLOT(blockUpdates()));
connect(m_activeDbWidget, SIGNAL(databaseUnlocked()), SLOT(applySplitterSizes()));
connect(m_activeDbWidget, SIGNAL(databaseUnlocked()), SLOT(applyViewState()));
connect(m_activeDbWidget, &DatabaseWidget::databaseLocked, this, [this] { updateAll(true); });
connect(m_activeDbWidget, SIGNAL(splitterSizesChanged()), SLOT(updateSplitterSizes()));
connect(m_activeDbWidget, SIGNAL(entryViewStateChanged()), SLOT(updateViewState()));
connect(m_activeDbWidget, SIGNAL(listModeActivated()), SLOT(restoreListView()));
connect(m_activeDbWidget, SIGNAL(searchModeActivated()), SLOT(restoreSearchView()));
connect(m_activeDbWidget, SIGNAL(listModeActivated()), SLOT(applyViewState()));
connect(m_activeDbWidget, SIGNAL(searchModeActivated()), SLOT(applyViewState()));
connect(m_activeDbWidget, SIGNAL(listModeAboutToActivate()), SLOT(blockUpdates()));
connect(m_activeDbWidget, SIGNAL(searchModeAboutToActivate()), SLOT(blockUpdates()));
}
}

/**
* Restore entry view list view state
*
* NOTE:
* States of entry view 'Hide Usernames'/'Hide Passwords' settings are global,
* i.e. they are the same for both list and search mode
*
* NOTE:
* If m_listViewState is empty, the list view has been activated for the first
* time after starting with a clean (or invalid) config.
*/
void DatabaseWidgetStateSync::restoreListView()
void DatabaseWidgetStateSync::applySplitterSizes()
{
if (!m_listViewState.isEmpty()) {
m_activeDbWidget->setEntryViewState(m_listViewState);
if (!m_activeDbWidget) {
return;
}

m_blockUpdates = true;

m_activeDbWidget->setSplitterSizes(m_splitterSizes);

m_blockUpdates = false;
}

/**
* Restore entry view search view state
*
* NOTE:
* States of entry view 'Hide Usernames'/'Hide Passwords' settings are global,
* i.e. they are the same for both list and search mode
* Restore entry view list view state
*
* NOTE:
* If m_searchViewState is empty, the search view has been activated for the
* first time after starting with a clean (or invalid) config. Thus, save the
* current state. Without this, m_searchViewState would remain empty until
* there is an actual view state change (e.g. column is resized)
* If m_listViewState is empty, the list view has been activated for the first
* time after starting with a clean (or invalid) config.
*/
void DatabaseWidgetStateSync::restoreSearchView()
void DatabaseWidgetStateSync::applyViewState()
{
if (!m_searchViewState.isEmpty()) {
m_activeDbWidget->setEntryViewState(m_searchViewState);
if (!m_activeDbWidget) {
return;
}

m_blockUpdates = true;

if (m_activeDbWidget->isSearchActive()) {
if (!m_searchViewState.isEmpty()) {
m_activeDbWidget->setEntryViewState(m_searchViewState);
}
} else {
m_searchViewState = m_activeDbWidget->entryViewState();
if (!m_listViewState.isEmpty()) {
m_activeDbWidget->setEntryViewState(m_listViewState);
}
}

m_blockUpdates = false;
Expand All @@ -139,19 +134,25 @@ void DatabaseWidgetStateSync::blockUpdates()
m_blockUpdates = true;
}

void DatabaseWidgetStateSync::updateAll(bool forceSync)
{
updateSplitterSizes();
updateViewState();
if (forceSync) {
sync();
}
}

void DatabaseWidgetStateSync::updateSplitterSizes()
{
if (!m_blockUpdates) {
m_splitterSizes = m_activeDbWidget->splitterSizes();
m_syncTimer.start();
}
}

/**
* Update entry view list/search view state
*
* NOTE:
* States of entry view 'Hide Usernames'/'Hide Passwords' settings are global,
* i.e. they are the same for both list and search mode
*/
void DatabaseWidgetStateSync::updateViewState()
{
Expand All @@ -165,7 +166,7 @@ void DatabaseWidgetStateSync::updateViewState()
m_listViewState = m_activeDbWidget->entryViewState();
}

sync();
m_syncTimer.start();
}

QList<int> DatabaseWidgetStateSync::variantToIntList(const QVariant& variant)
Expand Down
7 changes: 5 additions & 2 deletions src/gui/DatabaseWidgetStateSync.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,14 @@ class DatabaseWidgetStateSync : public QObject

public slots:
void setActive(DatabaseWidget* dbWidget);
void restoreListView();
void restoreSearchView();
void applySplitterSizes();
void applyViewState();

private slots:
void blockUpdates();
void updateSplitterSizes();
void updateViewState();
void updateAll(bool forceSync = false);
void sync();

private:
Expand All @@ -48,6 +49,8 @@ private slots:
QPointer<DatabaseWidget> m_activeDbWidget;

bool m_blockUpdates;
QTimer m_syncTimer;

QHash<Config::ConfigKey, QList<int>> m_splitterSizes;

QByteArray m_listViewState;
Expand Down
2 changes: 1 addition & 1 deletion src/gui/EntryPreviewWidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ EntryPreviewWidget::EntryPreviewWidget(QWidget* parent)

// Entry
m_ui->entryTotpButton->setIcon(icons()->icon("totp"));
m_ui->entryCloseButton->setIcon(icons()->icon("dialog-close"));
m_ui->entryCloseButton->setIcon(icons()->icon("arrow-collapse-down"));
m_ui->toggleUsernameButton->setIcon(icons()->onOffIcon("password-show", true));
m_ui->togglePasswordButton->setIcon(icons()->onOffIcon("password-show", true));
m_ui->toggleEntryNotesButton->setIcon(icons()->onOffIcon("password-show", true));
Expand Down
6 changes: 6 additions & 0 deletions src/gui/MainWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1968,6 +1968,11 @@ void MainWindow::initViewMenu()
applySettingsChanges();
});

m_ui->actionShowGroupPanel->setChecked(!config()->get(Config::GUI_HideGroupPanel).toBool());
connect(m_ui->actionShowGroupPanel, &QAction::toggled, this, [](bool checked) {
config()->set(Config::GUI_HideGroupPanel, !checked);
});

m_ui->actionShowPreviewPanel->setChecked(!config()->get(Config::GUI_HidePreviewPanel).toBool());
connect(m_ui->actionShowPreviewPanel, &QAction::toggled, this, [](bool checked) {
config()->set(Config::GUI_HidePreviewPanel, !checked);
Expand Down Expand Up @@ -2071,6 +2076,7 @@ void MainWindow::initActionCollection()
m_ui->actionShowMenubar,
#endif
m_ui->actionShowToolbar,
m_ui->actionShowGroupPanel,
m_ui->actionShowPreviewPanel,
m_ui->actionAllowScreenCapture,
m_ui->actionAlwaysOnTop,
Expand Down
18 changes: 18 additions & 0 deletions src/gui/MainWindow.ui
Original file line number Diff line number Diff line change
Expand Up @@ -387,9 +387,13 @@
<addaction name="actionCompactMode"/>
<addaction name="actionAlwaysOnTop"/>
<addaction name="actionAllowScreenCapture"/>
<addaction name="separator"/>
<addaction name="actionShowPreviewPanel"/>
<addaction name="actionShowGroupPanel"/>
<addaction name="separator"/>
<addaction name="actionShowMenubar"/>
<addaction name="actionShowToolbar"/>
<addaction name="separator"/>
<addaction name="actionHideUsernames"/>
<addaction name="actionHidePasswords"/>
</widget>
Expand Down Expand Up @@ -1285,6 +1289,20 @@
<string>Import…</string>
</property>
</action>
<action name="actionShowGroupPanel">
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>true</bool>
</property>
<property name="text">
<string>Show Group Panel</string>
</property>
<property name="toolTip">
<string>Toggle Show Group Panel</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>
Expand Down