mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2024-12-23 22:39:43 -05:00
Add search help pop-up
* Support ! modifier (same as '-') * Create reusable PopupHelpWidget as self-contained popup that can be positioned around a parent widget and will follow the movement and sizing of the window * Eliminated KEEPASSXC_MAIN_WINDOW macro and replaced with getMainWindow() function * Add tests to cover search help show/hide
This commit is contained in:
parent
d6ffee5e99
commit
880c3aeb34
BIN
share/icons/application/16x16/actions/system-help.png
Normal file
BIN
share/icons/application/16x16/actions/system-help.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 897 B |
BIN
share/icons/application/22x22/actions/system-help.png
Normal file
BIN
share/icons/application/22x22/actions/system-help.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.2 KiB |
BIN
share/icons/application/32x32/actions/system-help.png
Normal file
BIN
share/icons/application/32x32/actions/system-help.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.1 KiB |
@ -126,7 +126,6 @@ set(keepassx_SOURCES
|
||||
gui/UnlockDatabaseWidget.cpp
|
||||
gui/UnlockDatabaseDialog.cpp
|
||||
gui/WelcomeWidget.cpp
|
||||
gui/widgets/ElidedLabel.cpp
|
||||
gui/csvImport/CsvImportWidget.cpp
|
||||
gui/csvImport/CsvImportWizard.cpp
|
||||
gui/csvImport/CsvParserModel.cpp
|
||||
@ -154,6 +153,8 @@ set(keepassx_SOURCES
|
||||
gui/dbsettings/DatabaseSettingsWidgetEncryption.cpp
|
||||
gui/dbsettings/DatabaseSettingsWidgetMasterKey.cpp
|
||||
gui/settings/SettingsWidget.cpp
|
||||
gui/widgets/ElidedLabel.cpp
|
||||
gui/widgets/PopupHelpWidget.cpp
|
||||
gui/wizard/NewDatabaseWizard.cpp
|
||||
gui/wizard/NewDatabaseWizardPage.cpp
|
||||
gui/wizard/NewDatabaseWizardPageMetaData.cpp
|
||||
|
@ -88,7 +88,7 @@ bool BrowserService::openDatabase(bool triggerUnlock)
|
||||
}
|
||||
|
||||
if (triggerUnlock) {
|
||||
KEEPASSXC_MAIN_WINDOW->bringToFront();
|
||||
getMainWindow()->bringToFront();
|
||||
m_bringToFrontRequested = true;
|
||||
}
|
||||
|
||||
@ -901,7 +901,7 @@ void BrowserService::databaseUnlocked(DatabaseWidget* dbWidget)
|
||||
{
|
||||
if (dbWidget) {
|
||||
if (m_bringToFrontRequested) {
|
||||
KEEPASSXC_MAIN_WINDOW->lower();
|
||||
getMainWindow()->lower();
|
||||
m_bringToFrontRequested = false;
|
||||
}
|
||||
emit databaseUnlocked();
|
||||
|
@ -96,6 +96,7 @@ bool EntrySearcher::searchEntryImpl(const QString& searchString, Entry* entry)
|
||||
found = !attachments.filter(term->regex).empty();
|
||||
break;
|
||||
default:
|
||||
// Terms without a specific field try to match title, username, url, and notes
|
||||
found = term->regex.match(entry->resolvePlaceholder(entry->title())).hasMatch() ||
|
||||
term->regex.match(entry->resolvePlaceholder(entry->username())).hasMatch() ||
|
||||
term->regex.match(entry->resolvePlaceholder(entry->url())).hasMatch() ||
|
||||
@ -139,7 +140,7 @@ QList<QSharedPointer<EntrySearcher::SearchTerm> > EntrySearcher::parseSearchTerm
|
||||
term->regex = Tools::convertToRegex(term->word, !mods.contains("*"), mods.contains("+"), m_caseSensitive);
|
||||
|
||||
// Exclude modifier
|
||||
term->exclude = mods.contains("-");
|
||||
term->exclude = mods.contains("-") || mods.contains("!");
|
||||
|
||||
// Determine the field to search
|
||||
QString field = result.captured(2);
|
||||
|
@ -49,7 +49,6 @@ namespace
|
||||
|
||||
Application::Application(int& argc, char** argv)
|
||||
: QApplication(argc, argv)
|
||||
, m_mainWindow(nullptr)
|
||||
#ifdef Q_OS_UNIX
|
||||
, m_unixSignalNotifier(nullptr)
|
||||
#endif
|
||||
@ -143,16 +142,6 @@ Application::~Application()
|
||||
}
|
||||
}
|
||||
|
||||
QWidget* Application::mainWindow() const
|
||||
{
|
||||
return m_mainWindow;
|
||||
}
|
||||
|
||||
void Application::setMainWindow(QWidget* mainWindow)
|
||||
{
|
||||
m_mainWindow = mainWindow;
|
||||
}
|
||||
|
||||
bool Application::event(QEvent* event)
|
||||
{
|
||||
// Handle Apple QFileOpenEvent from finder (double click on .kdbx file)
|
||||
|
@ -37,9 +37,7 @@ class Application : public QApplication
|
||||
|
||||
public:
|
||||
Application(int& argc, char** argv);
|
||||
QWidget* mainWindow() const;
|
||||
~Application() override;
|
||||
void setMainWindow(QWidget* mainWindow);
|
||||
|
||||
bool event(QEvent* event) override;
|
||||
bool isAlreadyRunning() const;
|
||||
@ -60,8 +58,6 @@ private slots:
|
||||
void socketReadyRead();
|
||||
|
||||
private:
|
||||
QWidget* m_mainWindow;
|
||||
|
||||
#if defined(Q_OS_UNIX)
|
||||
/**
|
||||
* Register Unix signals such as SIGINT and SIGTERM for clean shutdown.
|
||||
|
@ -109,12 +109,17 @@ public:
|
||||
|
||||
const QString MainWindow::BaseWindowTitle = "KeePassXC";
|
||||
|
||||
MainWindow* g_MainWindow = nullptr;
|
||||
MainWindow* getMainWindow() { return g_MainWindow; }
|
||||
|
||||
MainWindow::MainWindow()
|
||||
: m_ui(new Ui::MainWindow())
|
||||
, m_trayIcon(nullptr)
|
||||
, m_appExitCalled(false)
|
||||
, m_appExiting(false)
|
||||
{
|
||||
g_MainWindow = this;
|
||||
|
||||
m_ui->setupUi(this);
|
||||
|
||||
#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS) && !defined(QT_NO_DBUS)
|
||||
|
@ -148,8 +148,12 @@ private:
|
||||
bool m_appExiting;
|
||||
};
|
||||
|
||||
#define KEEPASSXC_MAIN_WINDOW \
|
||||
(qobject_cast<Application*>(qApp) ? qobject_cast<MainWindow*>(qobject_cast<Application*>(qApp)->mainWindow()) \
|
||||
: nullptr)
|
||||
/**
|
||||
* Return instance of MainWindow created on app load
|
||||
* non-gui instances will return nullptr
|
||||
*
|
||||
* @return MainWindow instance or nullptr
|
||||
*/
|
||||
MainWindow* getMainWindow();
|
||||
|
||||
#endif // KEEPASSX_MAINWINDOW_H
|
||||
|
458
src/gui/SearchHelpWidget.ui
Normal file
458
src/gui/SearchHelpWidget.ui
Normal file
@ -0,0 +1,458 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>SearchHelpWidget</class>
|
||||
<widget class="QFrame" name="SearchHelpWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>334</width>
|
||||
<height>249</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Search Help</string>
|
||||
</property>
|
||||
<property name="autoFillBackground">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">#SearchHelpWidget { background-color: #ffffff }</string>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::Box</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Plain</enum>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetDefaultConstraint</enum>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Search terms are as follows: [modifiers][field:]["]term["]</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_24">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Every search term must match (ie, logical AND)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2" stretch="0,0">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_2">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>50</weight>
|
||||
<bold>false</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Modifiers</string>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<property name="horizontalSpacing">
|
||||
<number>8</number>
|
||||
</property>
|
||||
<property name="verticalSpacing">
|
||||
<number>8</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>10</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true">!</string>
|
||||
</property>
|
||||
<property name="scaledContents">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="label_7">
|
||||
<property name="text">
|
||||
<string>exclude term from results</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLabel" name="label_8">
|
||||
<property name="text">
|
||||
<string>match term exactly</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_16">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>10</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true">*</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLabel" name="label_15">
|
||||
<property name="text">
|
||||
<string>use regex in term</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>10</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true">+</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>50</weight>
|
||||
<bold>false</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Fields</string>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<property name="leftMargin">
|
||||
<number>15</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>15</number>
|
||||
</property>
|
||||
<property name="horizontalSpacing">
|
||||
<number>8</number>
|
||||
</property>
|
||||
<property name="verticalSpacing">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string notr="true">username</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_11">
|
||||
<property name="text">
|
||||
<string notr="true">password</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string notr="true">title</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_13">
|
||||
<property name="text">
|
||||
<string notr="true">url</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string notr="true">notes</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLabel" name="label_12">
|
||||
<property name="text">
|
||||
<string notr="true">attribute</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLabel" name="label_14">
|
||||
<property name="text">
|
||||
<string notr="true">attachment</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_4">
|
||||
<property name="title">
|
||||
<string>Term Wildcards</string>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout_2">
|
||||
<property name="horizontalSpacing">
|
||||
<number>8</number>
|
||||
</property>
|
||||
<property name="verticalSpacing">
|
||||
<number>8</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_18">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>10</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true">*</string>
|
||||
</property>
|
||||
<property name="scaledContents">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_19">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>10</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true">?</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_20">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>10</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true">|</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="label_21">
|
||||
<property name="text">
|
||||
<string>match anything</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLabel" name="label_22">
|
||||
<property name="text">
|
||||
<string>match one</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLabel" name="label_23">
|
||||
<property name="text">
|
||||
<string>logical OR</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_3">
|
||||
<property name="title">
|
||||
<string>Examples</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<property name="spacing">
|
||||
<number>8</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_9">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true">user:name1 url:google</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_10">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true">user:"name1|name2"</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_17">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true">+user:name1 *notes:"secret \d"</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
@ -18,6 +18,7 @@
|
||||
|
||||
#include "SearchWidget.h"
|
||||
#include "ui_SearchWidget.h"
|
||||
#include "ui_SearchHelpWidget.h"
|
||||
|
||||
#include <QKeyEvent>
|
||||
#include <QMenu>
|
||||
@ -26,6 +27,7 @@
|
||||
|
||||
#include "core/Config.h"
|
||||
#include "core/FilePath.h"
|
||||
#include "gui/widgets/PopupHelpWidget.h"
|
||||
|
||||
SearchWidget::SearchWidget(QWidget* parent)
|
||||
: QWidget(parent)
|
||||
@ -35,11 +37,17 @@ SearchWidget::SearchWidget(QWidget* parent)
|
||||
{
|
||||
m_ui->setupUi(this);
|
||||
|
||||
m_helpWidget = new PopupHelpWidget(m_ui->searchEdit);
|
||||
m_helpWidget->setOffset(QPoint(0,1));
|
||||
Ui::SearchHelpWidget helpUi;
|
||||
helpUi.setupUi(m_helpWidget);
|
||||
|
||||
m_searchTimer->setSingleShot(true);
|
||||
m_clearSearchTimer->setSingleShot(true);
|
||||
|
||||
connect(m_ui->searchEdit, SIGNAL(textChanged(QString)), SLOT(startSearchTimer()));
|
||||
connect(m_ui->clearIcon, SIGNAL(triggered(bool)), m_ui->searchEdit, SLOT(clear()));
|
||||
connect(m_ui->helpIcon, SIGNAL(triggered()), SLOT(toggleHelp()));
|
||||
connect(m_searchTimer, SIGNAL(timeout()), this, SLOT(startSearch()));
|
||||
connect(m_clearSearchTimer, SIGNAL(timeout()), m_ui->searchEdit, SLOT(clear()));
|
||||
connect(this, SIGNAL(escapePressed()), m_ui->searchEdit, SLOT(clear()));
|
||||
@ -65,6 +73,9 @@ SearchWidget::SearchWidget(QWidget* parent)
|
||||
m_ui->searchIcon->setMenu(searchMenu);
|
||||
m_ui->searchEdit->addAction(m_ui->searchIcon, QLineEdit::LeadingPosition);
|
||||
|
||||
m_ui->helpIcon->setIcon(filePath()->icon("actions", "system-help"));
|
||||
m_ui->searchEdit->addAction(m_ui->helpIcon, QLineEdit::TrailingPosition);
|
||||
|
||||
m_ui->clearIcon->setIcon(filePath()->icon("actions", "edit-clear-locationbar-rtl"));
|
||||
m_ui->clearIcon->setVisible(false);
|
||||
m_ui->searchEdit->addAction(m_ui->clearIcon, QLineEdit::TrailingPosition);
|
||||
@ -86,13 +97,6 @@ bool SearchWidget::eventFilter(QObject* obj, QEvent* event)
|
||||
if (keyEvent->key() == Qt::Key_Escape) {
|
||||
emit escapePressed();
|
||||
return true;
|
||||
} else if (keyEvent->matches(QKeySequence::Copy)) {
|
||||
// If Control+C is pressed in the search edit when no text
|
||||
// is selected, copy the password of the current entry
|
||||
if (!m_ui->searchEdit->hasSelectedText()) {
|
||||
emit copyPressed();
|
||||
return true;
|
||||
}
|
||||
} else if (keyEvent->matches(QKeySequence::MoveToNextLine)) {
|
||||
if (m_ui->searchEdit->cursorPosition() == m_ui->searchEdit->text().length()) {
|
||||
// If down is pressed at EOL, move the focus to the entry view
|
||||
@ -111,7 +115,7 @@ bool SearchWidget::eventFilter(QObject* obj, QEvent* event)
|
||||
m_clearSearchTimer->stop();
|
||||
}
|
||||
|
||||
return QObject::eventFilter(obj, event);
|
||||
return QWidget::eventFilter(obj, event);
|
||||
}
|
||||
|
||||
void SearchWidget::connectSignals(SignalMultiplexer& mx)
|
||||
@ -188,3 +192,12 @@ void SearchWidget::searchFocus()
|
||||
m_ui->searchEdit->setFocus();
|
||||
m_ui->searchEdit->selectAll();
|
||||
}
|
||||
|
||||
void SearchWidget::toggleHelp()
|
||||
{
|
||||
if (m_helpWidget->isVisible()) {
|
||||
m_helpWidget->hide();
|
||||
} else {
|
||||
m_helpWidget->show();
|
||||
}
|
||||
}
|
||||
|
@ -30,6 +30,8 @@ namespace Ui
|
||||
class SearchWidget;
|
||||
}
|
||||
|
||||
class PopupHelpWidget;
|
||||
|
||||
class SearchWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
@ -45,6 +47,7 @@ public:
|
||||
void setLimitGroup(bool state);
|
||||
|
||||
protected:
|
||||
// Filter key presses in the search field
|
||||
bool eventFilter(QObject* obj, QEvent* event) override;
|
||||
|
||||
signals:
|
||||
@ -65,9 +68,11 @@ private slots:
|
||||
void updateCaseSensitive();
|
||||
void updateLimitGroup();
|
||||
void searchFocus();
|
||||
void toggleHelp();
|
||||
|
||||
private:
|
||||
const QScopedPointer<Ui::SearchWidget> m_ui;
|
||||
PopupHelpWidget* m_helpWidget;
|
||||
QTimer* m_searchTimer;
|
||||
QTimer* m_clearSearchTimer;
|
||||
QAction* m_actionCaseSensitive;
|
||||
|
@ -81,6 +81,11 @@
|
||||
<string>Clear</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="helpIcon">
|
||||
<property name="text">
|
||||
<string>Search Help</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<tabstops>
|
||||
<tabstop>searchEdit</tabstop>
|
||||
|
@ -45,7 +45,7 @@ bool KeyFileEditWidget::addToCompositeKey(QSharedPointer<CompositeKey> key)
|
||||
}
|
||||
|
||||
if (fileKey->type() != FileKey::Hashed) {
|
||||
QMessageBox::warning(KEEPASSXC_MAIN_WINDOW,
|
||||
QMessageBox::warning(getMainWindow(),
|
||||
tr("Legacy key file format"),
|
||||
tr("You are using a legacy key file format which may become\n"
|
||||
"unsupported in the future.\n\n"
|
||||
@ -100,7 +100,7 @@ void KeyFileEditWidget::createKeyFile()
|
||||
QString errorMsg;
|
||||
bool created = FileKey::create(fileName, &errorMsg);
|
||||
if (!created) {
|
||||
MessageBox::critical(KEEPASSXC_MAIN_WINDOW, tr("Error creating key file"),
|
||||
MessageBox::critical(getMainWindow(), tr("Error creating key file"),
|
||||
tr("Unable to create key file: %1").arg(errorMsg), QMessageBox::Button::Ok);
|
||||
} else {
|
||||
m_compUi->keyFileCombo->setEditText(fileName);
|
||||
|
99
src/gui/widgets/PopupHelpWidget.cpp
Normal file
99
src/gui/widgets/PopupHelpWidget.cpp
Normal file
@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright (C) 2018 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 or (at your option)
|
||||
* version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "PopupHelpWidget.h"
|
||||
|
||||
#include <QEvent>
|
||||
|
||||
#include "gui/MainWindow.h"
|
||||
|
||||
PopupHelpWidget::PopupHelpWidget(QWidget* parent)
|
||||
: QFrame(parent)
|
||||
, m_parentWindow(parent->window())
|
||||
, m_appWindow(getMainWindow())
|
||||
, m_offset({0, 0})
|
||||
, m_corner(Qt::BottomLeftCorner)
|
||||
{
|
||||
Q_ASSERT(parent);
|
||||
|
||||
setWindowFlags(Qt::FramelessWindowHint | Qt::Tool);
|
||||
hide();
|
||||
|
||||
m_appWindow->installEventFilter(this);
|
||||
parent->installEventFilter(this);
|
||||
}
|
||||
|
||||
PopupHelpWidget::~PopupHelpWidget()
|
||||
{
|
||||
m_parentWindow->removeEventFilter(this);
|
||||
parentWidget()->removeEventFilter(this);
|
||||
}
|
||||
|
||||
void PopupHelpWidget::setOffset(const QPoint& offset)
|
||||
{
|
||||
m_offset = offset;
|
||||
if (isVisible()) {
|
||||
alignWithParent();
|
||||
}
|
||||
}
|
||||
|
||||
void PopupHelpWidget::setPosition(Qt::Corner corner)
|
||||
{
|
||||
m_corner = corner;
|
||||
if (isVisible()) {
|
||||
alignWithParent();
|
||||
}
|
||||
}
|
||||
|
||||
bool PopupHelpWidget::eventFilter(QObject* obj, QEvent* event)
|
||||
{
|
||||
if (obj == parentWidget() && event->type() == QEvent::FocusOut) {
|
||||
hide();
|
||||
} else if (obj == m_appWindow && (event->type() == QEvent::Move || event->type() == QEvent::Resize)) {
|
||||
if (isVisible()) {
|
||||
alignWithParent();
|
||||
}
|
||||
}
|
||||
return QFrame::eventFilter(obj, event);
|
||||
}
|
||||
|
||||
void PopupHelpWidget::showEvent(QShowEvent* event)
|
||||
{
|
||||
alignWithParent();
|
||||
QFrame::showEvent(event);
|
||||
}
|
||||
|
||||
void PopupHelpWidget::alignWithParent()
|
||||
{
|
||||
QPoint pos;
|
||||
switch (m_corner) {
|
||||
case Qt::TopLeftCorner:
|
||||
pos = parentWidget()->geometry().topLeft() + m_offset - QPoint(0, height());
|
||||
break;
|
||||
case Qt::TopRightCorner:
|
||||
pos = parentWidget()->geometry().topRight() + m_offset - QPoint(width(), height());
|
||||
break;
|
||||
case Qt::BottomRightCorner:
|
||||
pos = parentWidget()->geometry().bottomRight() + m_offset - QPoint(width(), 0);
|
||||
break;
|
||||
default:
|
||||
pos = parentWidget()->geometry().bottomLeft() + m_offset;
|
||||
break;
|
||||
}
|
||||
|
||||
move(m_parentWindow->mapToGlobal(pos));
|
||||
}
|
48
src/gui/widgets/PopupHelpWidget.h
Normal file
48
src/gui/widgets/PopupHelpWidget.h
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (C) 2018 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 or (at your option)
|
||||
* version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef KEEPASSXC_POPUPHELPWIDGET_H
|
||||
#define KEEPASSXC_POPUPHELPWIDGET_H
|
||||
|
||||
#include <QPointer>
|
||||
#include <QFrame>
|
||||
|
||||
class PopupHelpWidget : public QFrame
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit PopupHelpWidget(QWidget* parent);
|
||||
~PopupHelpWidget() override;
|
||||
|
||||
void setOffset(const QPoint& offset);
|
||||
void setPosition(Qt::Corner corner);
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject* obj, QEvent* event) override;
|
||||
void showEvent(QShowEvent* event) override;
|
||||
|
||||
private:
|
||||
void alignWithParent();
|
||||
QPointer<QWidget> m_parentWindow;
|
||||
QPointer<QWidget> m_appWindow;
|
||||
|
||||
QPoint m_offset;
|
||||
Qt::Corner m_corner;
|
||||
};
|
||||
|
||||
|
||||
#endif //KEEPASSXC_POPUPHELPWIDGET_H
|
@ -37,9 +37,9 @@ YkChallengeResponseKey::YkChallengeResponseKey(int slot, bool blocking)
|
||||
, m_slot(slot)
|
||||
, m_blocking(blocking)
|
||||
{
|
||||
if (KEEPASSXC_MAIN_WINDOW) {
|
||||
connect(this, SIGNAL(userInteractionRequired()), KEEPASSXC_MAIN_WINDOW, SLOT(showYubiKeyPopup()));
|
||||
connect(this, SIGNAL(userConfirmed()), KEEPASSXC_MAIN_WINDOW, SLOT(hideYubiKeyPopup()));
|
||||
if (getMainWindow()) {
|
||||
connect(this, SIGNAL(userInteractionRequired()), getMainWindow(), SLOT(showYubiKeyPopup()));
|
||||
connect(this, SIGNAL(userConfirmed()), getMainWindow(), SLOT(hideYubiKeyPopup()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -111,7 +111,6 @@ int main(int argc, char** argv)
|
||||
}
|
||||
|
||||
MainWindow mainWindow;
|
||||
app.setMainWindow(&mainWindow);
|
||||
QObject::connect(&app, SIGNAL(anotherInstanceStarted()), &mainWindow, SLOT(bringToFront()));
|
||||
QObject::connect(&app, SIGNAL(applicationActivated()), &mainWindow, SLOT(bringToFront()));
|
||||
QObject::connect(&app, SIGNAL(openFile(QString)), &mainWindow, SLOT(openDatabase(QString)));
|
||||
|
@ -802,10 +802,22 @@ void TestGui::testSearch()
|
||||
auto* clearButton = searchWidget->findChild<QAction*>("clearIcon");
|
||||
QVERIFY(!clearButton->isVisible());
|
||||
|
||||
auto* helpButton = searchWidget->findChild<QAction*>("helpIcon");
|
||||
auto* helpPanel = searchWidget->findChild<QWidget*>("SearchHelpWidget");
|
||||
QVERIFY(helpButton->isVisible());
|
||||
QVERIFY(!helpPanel->isVisible());
|
||||
|
||||
// Enter search
|
||||
QTest::mouseClick(searchTextEdit, Qt::LeftButton);
|
||||
QTRY_VERIFY(searchTextEdit->hasFocus());
|
||||
QTRY_VERIFY(!clearButton->isVisible());
|
||||
// Show/Hide search help
|
||||
helpButton->trigger();
|
||||
QTRY_VERIFY(helpPanel->isVisible());
|
||||
QTest::mouseClick(searchTextEdit, Qt::LeftButton);
|
||||
QTRY_VERIFY(helpPanel->isVisible());
|
||||
helpButton->trigger();
|
||||
QTRY_VERIFY(!helpPanel->isVisible());
|
||||
// Search for "ZZZ"
|
||||
QTest::keyClicks(searchTextEdit, "ZZZ");
|
||||
QTRY_COMPARE(searchTextEdit->text(), QString("ZZZ"));
|
||||
|
Loading…
Reference in New Issue
Block a user