diff options
author | Janek Bevendorff <janek@jbev.net> | 2019-12-30 02:15:11 +0300 |
---|---|---|
committer | Janek Bevendorff <janek@jbev.net> | 2019-12-30 02:15:11 +0300 |
commit | 8637054c319759f4d5aa11c9108f08ed4797a326 (patch) | |
tree | 4c9e00e014f1e2e3a924239fcb5d6daa6b9e74e3 | |
parent | 2fab4d576a35c28cdee38ed88f2fb3b6d8901fdf (diff) |
Implement initial prototype UIfeature/qtquick-ui
-rw-r--r-- | CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/CMakeLists.txt | 5 | ||||
-rw-r--r-- | src/main.cpp | 61 | ||||
-rw-r--r-- | src/qml.qrc | 47 | ||||
-rw-r--r-- | src/qml/classic/main.qml | 423 |
5 files changed, 510 insertions, 28 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 643e2f093..b697ce746 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -368,7 +368,7 @@ endif() include(CLangFormat) -set(QT_COMPONENTS Core Network Concurrent Gui Svg Widgets Test LinguistTools) +set(QT_COMPONENTS Core Network Concurrent Gui Qml Quick QuickControls2 Svg Widgets Test LinguistTools) if(UNIX AND NOT APPLE) find_package(Qt5 COMPONENTS ${QT_COMPONENTS} DBus REQUIRED) elseif(APPLE) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index af9b9bb58..ab67688b0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -197,7 +197,7 @@ if(MINGW OR (UNIX AND NOT APPLE)) core/OSEventFilter.cpp) endif() -set(keepassx_SOURCES_MAINEXE main.cpp) +set(keepassx_SOURCES_MAINEXE main.cpp qml.qrc) add_feature_info(Auto-Type WITH_XC_AUTOTYPE "Automatic password typing") add_feature_info(Networking WITH_XC_NETWORKING "Compile KeePassXC with network access code (e.g. for downloading website icons)") @@ -300,6 +300,9 @@ target_link_libraries(keepassx_core Qt5::Core Qt5::Concurrent Qt5::Network + Qt5::Qml + Qt5::Quick + Qt5::QuickControls2 Qt5::Widgets ${sodium_LIBRARY_RELEASE} ${YUBIKEY_LIBRARIES} diff --git a/src/main.cpp b/src/main.cpp index dd503d957..f9ef0f39b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -16,6 +16,8 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <QQmlApplicationEngine> +#include <QQuickStyle> #include <QCommandLineParser> #include <QFile> #include <QTextStream> @@ -49,10 +51,8 @@ int main(int argc, char** argv) QT_REQUIRE_VERSION(argc, argv, QT_VERSION_STR) #if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) - QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); -#ifdef Q_OS_LINUX - QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); -#endif + QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); #endif #if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0) @@ -129,29 +129,38 @@ int main(int argc, char** argv) Config::createConfigFromFile(parser.value(configOption)); } - MainWindow 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))); - QObject::connect(&app, SIGNAL(quitSignalReceived()), &mainWindow, SLOT(appExit()), Qt::DirectConnection); - - Bootstrap::restoreMainWindowState(mainWindow); - - const bool pwstdin = parser.isSet(pwstdinOption); - for (const QString& filename : fileNames) { - QString password; - if (pwstdin) { - // we always need consume a line of STDIN if --pw-stdin is set to clear out the - // buffer for native messaging, even if the specified file does not exist - QTextStream out(stdout, QIODevice::WriteOnly); - out << QObject::tr("Database password: ") << flush; - password = Utils::getPassword(); - } - - if (!filename.isEmpty() && QFile::exists(filename) && !filename.endsWith(".json", Qt::CaseInsensitive)) { - mainWindow.openDatabase(filename, password, parser.value(keyfileOption)); + QQmlApplicationEngine engine; + const QUrl url("qrc:/qml/classic/main.qml"); + QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, &app, [url](QObject *obj, const QUrl &objUrl) { + if (!obj && url == objUrl) { + QCoreApplication::exit(-1); } - } + }, Qt::QueuedConnection); + engine.load(url); + +// MainWindow 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))); +// QObject::connect(&app, SIGNAL(quitSignalReceived()), &mainWindow, SLOT(appExit()), Qt::DirectConnection); +// +// Bootstrap::restoreMainWindowState(mainWindow); +// +// const bool pwstdin = parser.isSet(pwstdinOption); +// for (const QString& filename : fileNames) { +// QString password; +// if (pwstdin) { +// // we always need consume a line of STDIN if --pw-stdin is set to clear out the +// // buffer for native messaging, even if the specified file does not exist +// QTextStream out(stdout, QIODevice::WriteOnly); +// out << QObject::tr("Database password: ") << flush; +// password = Utils::getPassword(); +// } +// +// if (!filename.isEmpty() && QFile::exists(filename) && !filename.endsWith(".json", Qt::CaseInsensitive)) { +// mainWindow.openDatabase(filename, password, parser.value(keyfileOption)); +// } +// } int exitCode = Application::exec(); diff --git a/src/qml.qrc b/src/qml.qrc new file mode 100644 index 000000000..f16b8ea5e --- /dev/null +++ b/src/qml.qrc @@ -0,0 +1,47 @@ +<RCC> + <qresource prefix="/"> + <file>qml/classic/main.qml</file> + </qresource> + + <qresource prefix="/icons"> + <file alias="application-exit">../share/icons/application/32x32/actions/application-exit.png</file> + <file alias="auto-type">../share/icons/application/32x32/actions/auto-type.png</file> + <file alias="chronometer">../share/icons/application/32x32/actions/chronometer.png</file> + <file alias="configure">../share/icons/application/32x32/actions/configure.png</file> + <file alias="database-change-key">../share/icons/application/32x32/actions/database-change-key.png</file> + <file alias="database-lock">../share/icons/application/32x32/actions/database-lock.png</file> + <file alias="dialog-close">../share/icons/application/32x32/actions/dialog-close.png</file> + <file alias="dialog-ok">../share/icons/application/32x32/actions/dialog-ok.png</file> + <file alias="document-close">../share/icons/application/32x32/actions/document-close.png</file> + <file alias="document-edit">../share/icons/application/32x32/actions/document-edit.png</file> + <file alias="document-new">../share/icons/application/32x32/actions/document-new.png</file> + <file alias="document-open">../share/icons/application/32x32/actions/document-open.png</file> + <file alias="document-properties">../share/icons/application/32x32/actions/document-properties.png</file> + <file alias="document-save">../share/icons/application/32x32/actions/document-save.png</file> + <file alias="edit-clear-locationbar-ltr">../share/icons/application/32x32/actions/edit-clear-locationbar-ltr.png</file> + <file alias="edit-clear-locationbar-rtl">../share/icons/application/32x32/actions/edit-clear-locationbar-rtl.png</file> + <file alias="entry-clone">../share/icons/application/32x32/actions/entry-clone.png</file> + <file alias="entry-delete">../share/icons/application/32x32/actions/entry-delete.png</file> + <file alias="entry-edit">../share/icons/application/32x32/actions/entry-edit.png</file> + <file alias="entry-new">../share/icons/application/32x32/actions/entry-new.png</file> + <file alias="favicon-download">../share/icons/application/32x32/actions/favicon-download.png</file> + <file alias="group-empty-trash">../share/icons/application/32x32/actions/group-empty-trash.png</file> + <file alias="group-delete">../share/icons/application/16x16/actions/group-delete.png</file> + <file alias="group-new">../share/icons/application/16x16/actions/group-new.png</file> + <file alias="group-edit">../share/icons/application/16x16/actions/group-edit.png</file> + <file alias="help-about">../share/icons/application/32x32/actions/help-about.png</file> + <file alias="key-enter">../share/icons/application/32x32/actions/key-enter.png</file> + <file alias="paperclip">../share/icons/application/32x32/actions/paperclip.png</file> + <file alias="password-copy">../share/icons/application/32x32/actions/password-copy.png</file> + <file alias="password-generate">../share/icons/application/32x32/actions/password-generate.png</file> + <file alias="password-generator">../share/icons/application/32x32/actions/password-generator.png</file> + <file alias="password-show-off">../share/icons/application/32x32/actions/password-show-off.png</file> + <file alias="password-show-on">../share/icons/application/32x32/actions/password-show-on.png</file> + <file alias="statistics">../share/icons/application/32x32/actions/statistics.png</file> + <file alias="system-help">../share/icons/application/32x32/actions/system-help.png</file> + <file alias="system-search">../share/icons/application/32x32/actions/system-search.png</file> + <file alias="url-copy">../share/icons/application/32x32/actions/url-copy.png</file> + <file alias="username-copy">../share/icons/application/32x32/actions/username-copy.png</file> + <file alias="view-history">../share/icons/application/32x32/actions/view-history.png</file> + </qresource> +</RCC> diff --git a/src/qml/classic/main.qml b/src/qml/classic/main.qml new file mode 100644 index 000000000..d548fa823 --- /dev/null +++ b/src/qml/classic/main.qml @@ -0,0 +1,423 @@ +import QtQuick 2.7 +import QtQuick.Controls 2.7 +import QtQuick.Layouts 1.7 + +ApplicationWindow { + title: "KeePassXC" + width: 900 + height: 600 + visible: true + + menuBar: MenuBar { + Menu { + title: qsTr("&Database") + MenuItem { action: actionDatabaseNew; hoverEnabled: true } + MenuItem { action: actionDatabaseOpen; hoverEnabled: true } + Menu { + title: qsTr("&Recent databases") + } + MenuItem { action: actionDatabaseSave; hoverEnabled: true } + MenuItem { action: actionDatabaseSaveAs; hoverEnabled: true } + MenuItem { action: actionDatabaseClose; hoverEnabled: true } + MenuSeparator { } + MenuItem { action: actionChangeMasterKey; hoverEnabled: true } + MenuItem { action: actionChangeDatabaseSettings; hoverEnabled: true } + MenuSeparator { } + MenuItem { action: actionDatabaseMerge; hoverEnabled: true } + Menu { + title: qsTr("&Import") + MenuItem { action: actionImportKeePass1; hoverEnabled: true } + MenuItem { action: actionImportOpVault; hoverEnabled: true } + MenuItem { action: actionImportCsv; hoverEnabled: true } + } + Menu { + title: qsTr("&Export") + enabled: false + MenuItem { action: actionExportCsv; hoverEnabled: true } + MenuItem { action: actionExportHtml; hoverEnabled: true } + } + MenuItem { action: actionQuit } + } + + Menu { + title: qsTr("E&ntries") + MenuItem { action: actionEntryNew; hoverEnabled: true } + MenuItem { action: actionEntryEdit; hoverEnabled: true } + MenuItem { action: actionEntryClone; hoverEnabled: true } + MenuItem { action: actionEntryDelete; hoverEnabled: true } + MenuSeparator { } + MenuItem { action: actionEntryCopyUsername; hoverEnabled: true } + MenuItem { action: actionEntryCopyPassword; hoverEnabled: true } + Menu { + title: qsTr("Copy att&ribute") + MenuItem { action: actionEntryCopyTitle; hoverEnabled: true } + MenuItem { action: actionEntryCopyUrl; hoverEnabled: true } + MenuItem { action: actionEntryCopyNotes; hoverEnabled: true } + } + Menu { + title: qsTr("TOTP") + MenuItem { action: actionEntryCopyTotp; hoverEnabled: true } + MenuItem { action: actionEntryTotp; hoverEnabled: true } + MenuItem { action: actionEntryTotpQRCode; hoverEnabled: true } + MenuItem { action: actionEntrySetupTotp; hoverEnabled: true } + } + MenuSeparator { } + MenuItem { action: actionEntryAutoType; hoverEnabled: true } + MenuSeparator { } + MenuItem { action: actionEntryOpenUrl; hoverEnabled: true } + MenuItem { action: actionEntryDownloadIcon; hoverEnabled: true } + + } + + Menu { + title: qsTr("&Groups") + MenuItem { action: actionGroupNew; hoverEnabled: true } + MenuItem { action: actionGroupEdit; hoverEnabled: true } + MenuItem { action: actionGroupDelete; hoverEnabled: true } + MenuSeparator { } + MenuItem { action: actionGroupDownloadFavicons; hoverEnabled: true } + MenuSeparator { } + MenuItem { action: actionGroupSortAsc; hoverEnabled: true } + MenuItem { action: actionGroupSortDesc; hoverEnabled: true } + } + + Menu { + title: qsTr("&Tools") + MenuItem { action: actionLockDatabases; hoverEnabled: true } + MenuItem { action: actionPasswordGenerator; hoverEnabled: true } + MenuItem { action: actionSettings; hoverEnabled: true } + } + + Menu { + title: qsTr("&Help") + MenuItem { action: actionGettingStarted; hoverEnabled: true } + MenuItem { action: actionUserGuide; hoverEnabled: true } + MenuItem { action: actionKeyboardShortcuts; hoverEnabled: true } + MenuItem { action: actionOnlineHelp; hoverEnabled: true } + MenuSeparator { } + MenuItem { action: actionCheckForUpdates; hoverEnabled: true } + MenuItem { action: actionDonate; hoverEnabled: true } + MenuItem { action: actionBugReport; hoverEnabled: true } + MenuSeparator { } + MenuItem { action: actionAbout; hoverEnabled: true } + } + } + + header: ToolBar { + RowLayout { + anchors.fill: parent + ToolButton { action: actionDatabaseNew; display: AbstractButton.IconOnly } + ToolButton { action: actionDatabaseOpen; display: AbstractButton.IconOnly } + ToolButton { action: actionDatabaseSave; display: AbstractButton.IconOnly } + ToolSeparator { } + ToolButton { action: actionEntryNew; display: AbstractButton.IconOnly } + ToolButton { action: actionEntryEdit; display: AbstractButton.IconOnly } + ToolButton { action: actionEntryDelete; display: AbstractButton.IconOnly } + ToolSeparator { } + ToolButton { action: actionEntryCopyUsername; display: AbstractButton.IconOnly } + ToolButton { action: actionEntryCopyPassword; display: AbstractButton.IconOnly } + ToolButton { action: actionEntryCopyUrl; display: AbstractButton.IconOnly } + ToolButton { action: actionEntryAutoType; display: AbstractButton.IconOnly } + ToolSeparator { } + ToolButton { action: actionPasswordGenerator; display: AbstractButton.IconOnly } + ToolButton { action: actionLockDatabases; display: AbstractButton.IconOnly } + ToolButton { action: actionSettings; display: AbstractButton.IconOnly } + ToolSeparator { } + Item { Layout.fillWidth: true } + TextField { + placeholderText: qsTr("Search (Ctrl+F)") + Layout.preferredWidth: 300 + leftPadding: 36 + ToolButton { action: actionSearch; display: AbstractButton.IconOnly } + } + } + } + + TabBar { + width: parent.width + TabButton { + text: "Database.kdbx" + } + } + + // Database menu actions + Action { + id: actionDatabaseNew + text: qsTr("&New Database...") + shortcut: "Ctrl+Shift+N" + icon.name: "document-new" + icon.source: "qrc:/icons/document-new" + } + Action { + id: actionDatabaseOpen + text: qsTr("&Open Database...") + shortcut: StandardKey.Open + icon.name: "document-open" + icon.source: "qrc:/icons/document-open" + } + Action { + id: actionDatabaseSave + text: qsTr("&Save Database") + shortcut: StandardKey.Save + icon.name: "document-save" + icon.source: "qrc:/icons/document-save" + } + Action { + id: actionDatabaseSaveAs + text: qsTr("Sa&ve Database As...") + shortcut: StandardKey.SaveAs + icon.name: "document-save-as" + icon.source: "qrc:/icons/document-new" + } + Action { + id: actionDatabaseClose + text: qsTr("&Close Database") + shortcut: StandardKey.Close + icon.name: "document-close" + icon.source: "qrc:/icons/document-close" + } + Action { + id: actionChangeMasterKey + text: qsTr("Change Master &Key...") + icon.name: "database-change-key" + icon.source: "qrc:/icons/database-change-key" + } + Action { + id: actionChangeDatabaseSettings + text: qsTr("&Database Settings...") + icon.name: "document-edit" + icon.source: "qrc:/icons/document-edit" + } + Action { + id: actionDatabaseMerge + text: qsTr("&Merge From Database...") + icon.name: "document-edit" + icon.source: "qrc:/icons/document-edit" + } + Action { + id: actionImportKeePass1 + text: qsTr("KeePass 1 Database...") + } + Action { + id: actionImportOpVault + text: qsTr("1Password OpVault...") + } + Action { + id: actionImportCsv + text: qsTr("CSV...") + } + Action { + id: actionExportCsv + text: qsTr("Export to CSV File...") + } + Action { + id: actionExportHtml + text: qsTr("Export to HTML File...") + } + Action { + id: actionQuit + text: qsTr("&Quit") + shortcut: StandardKey.Quit + icon.name: "application-exit" + icon.source: "qrc:/icons/application-exit" + } + + // Edit menu actions + Action { + id: actionEntryNew + text: qsTr("&New Entry...") + shortcut: StandardKey.New + icon.name: "entry-new" + icon.source: "qrc:/icons/entry-new" + } + Action { + id: actionEntryEdit + text: qsTr("&Edit Entry...") + shortcut: "Ctrl+E" + icon.name: "entry-edit" + icon.source: "qrc:/icons/entry-edit" + } + Action { + id: actionEntryClone + text: qsTr("&Clone Entry...") + shortcut: "Ctrl+K" + icon.name: "entry-clone" + icon.source: "qrc:/icons/entry-clone" + } + Action { + id: actionEntryDelete + text: qsTr("&Delete Entry...") + shortcut: StandardKey.Delete + icon.name: "entry-delete" + icon.source: "qrc:/icons/entry-delete" + } + Action { + id: actionEntryCopyUsername + text: qsTr("Copy &Username") + shortcut: "Ctrl+B" + icon.name: "username-copy" + icon.source: "qrc:/icons/username-copy" + } + Action { + id: actionEntryCopyPassword + text: qsTr("Copy &Password") + shortcut: StandardKey.Copy + icon.name: "password-copy" + icon.source: "qrc:/icons/password-copy" + } + Action { + id: actionEntryCopyTitle + text: qsTr("&Title") + } + Action { + id: actionEntryCopyUrl + text: qsTr("&URL") + shortcut: "Ctr+U" + icon.name: "url-copy" + icon.source: "qrc:/icons/url-copy" + } + Action { + id: actionEntryCopyNotes + text: qsTr("&Notes") + } + Action { + id: actionEntryCopyTotp + text: qsTr("Copy &TOTP") + shortcut: "Ctrl+T" + } + Action { + id: actionEntryTotp + text: qsTr("Show TOTP...") + shortcut: "Ctrl+Shift+T" + } + Action { + id: actionEntryTotpQRCode + text: qsTr("Show TOTP QR Code...") + } + Action { + id: actionEntrySetupTotp + text: qsTr("Set up TOTP...") + } + Action { + id: actionEntryAutoType + text: qsTr("Perform &Auto-Type") + shortcut: "Ctrl+Shift+V" + icon.name: "auto-type" + icon.source: "qrc:/icons/auto-type" + } + Action { + id: actionEntryOpenUrl + text: qsTr("Open &URL") + shortcut: "Ctrl+Shift+U" + } + Action { + id: actionEntryDownloadIcon + text: qsTr("Download Favicon") + shortcut: "Ctrl+Shift+D" + icon.name: "favicon-download" + icon.source: "qrc:/icons/favicon-download" + } + + // Groups menu actions + Action { + id: actionGroupNew + text: qsTr("&New Group...") + icon.name: "group-new" + icon.source: "qrc:/icons/group-new" + } + Action { + id: actionGroupEdit + text: qsTr("&Edit Group...") + icon.name: "group-edit" + icon.source: "qrc:/icons/group-edit" + } + Action { + id: actionGroupDelete + text: qsTr("&Delete Group...") + icon.name: "group-delete" + icon.source: "qrc:/icons/group-delete" + } + Action { + id: actionGroupDownloadFavicons + text: qsTr("Downlo&ad All Favicons...") + icon.name: "favicon-download" + icon.source: "qrc:/icons/favicon-download" + } + Action { + id: actionGroupSortAsc + text: qsTr("Sort &A-Z") + } + Action { + id: actionGroupSortDesc + text: qsTr("Sort &Z-A") + } + + // Tools menu actions + Action { + id: actionLockDatabases + text: qsTr("&Lock Databases") + shortcut: "Ctrl+L" + icon.name: "database-lock" + icon.source: "qrc:/icons/database-lock" + } + Action { + id: actionPasswordGenerator + text: qsTr("&Password Generator...") + icon.name: "password-generator" + icon.source: "qrc:/icons/password-generator" + checkable: true + } + Action { + id: actionSettings + text: qsTr("&Settings...") + icon.name: "configure" + icon.source: "qrc:/icons/configure" + checkable: true + } + + // Help menu actions + Action { + id: actionGettingStarted + text: qsTr("&Getting Started...") + } + Action { + id: actionUserGuide + text: qsTr("&User Guide...") + } + Action { + id: actionKeyboardShortcuts + text: qsTr("&Keyboard Shortcuts...") + shortcut: "Ctrl+/" + } + Action { + id: actionOnlineHelp + text: qsTr("&Online Help...") + } + Action { + id: actionCheckForUpdates + text: qsTr("&Check for Updates...") + icon.name: "system-software-update" + icon.source: "qrc:/icons/system-software-update" + } + Action { + id: actionDonate + text: qsTr("&Donate...") + } + Action { + id: actionBugReport + text: qsTr("&Report a Bug...") + } + Action { + id: actionAbout + text: qsTr("&About KeePassXC...") + icon.name: "help-about" + icon.source: "qrc:/icons/help-about" + } + + // Search action + Action { + id: actionSearch + icon.name: "system-search" + icon.source: "qrc:/icons/system-search" + } +} |