diff --git a/internal/frontend/bridge-gui/bridge-gui/CMakeLists.txt b/internal/frontend/bridge-gui/bridge-gui/CMakeLists.txt index 2cadd2bd..93a9f499 100644 --- a/internal/frontend/bridge-gui/bridge-gui/CMakeLists.txt +++ b/internal/frontend/bridge-gui/bridge-gui/CMakeLists.txt @@ -75,7 +75,7 @@ if(NOT UNIX) set(CMAKE_INSTALL_BINDIR ".") endif(NOT UNIX) -find_package(Qt6 COMPONENTS Core Quick Qml QuickControls2 Widgets Svg REQUIRED) +find_package(Qt6 COMPONENTS Core Quick Qml QuickControls2 Widgets Svg Gui REQUIRED) qt_standard_project_setup() set(CMAKE_AUTORCC ON) message(STATUS "Using Qt ${Qt6_VERSION}") @@ -120,6 +120,7 @@ add_executable(bridge-gui UserList.cpp UserList.h SentryUtils.cpp SentryUtils.h Settings.cpp Settings.h + ClipboardProxy.cpp ClipboardProxy.h ${DOCK_ICON_SRC_FILE} MacOS/DockIcon.h ) @@ -148,6 +149,7 @@ target_link_libraries(bridge-gui Qt6::Qml Qt6::QuickControls2 Qt6::Svg + Qt6::Gui sentry::sentry bridgepp ) diff --git a/internal/frontend/bridge-gui/bridge-gui/ClipboardProxy.cpp b/internal/frontend/bridge-gui/bridge-gui/ClipboardProxy.cpp new file mode 100644 index 00000000..4f73f601 --- /dev/null +++ b/internal/frontend/bridge-gui/bridge-gui/ClipboardProxy.cpp @@ -0,0 +1,25 @@ +// Copyright (c) 2024 Proton AG +// This file is part of Proton Mail Bridge. +// Proton Mail Bridge 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 3 of the License, or +// (at your option) any later version. +// Proton Mail Bridge 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 Proton Mail Bridge. If not, see . +#include "ClipboardProxy.h" + +// The following definitions were taken and adapted from: +// https://stackoverflow.com/questions/40092352/passing-qclipboard-to-qml +// Author: krzaq + +ClipboardProxy::ClipboardProxy(QClipboard* c) : clipboard(c) { + connect(clipboard, &QClipboard::dataChanged, this, &ClipboardProxy::textChanged); +} + +QString ClipboardProxy::text() const { + return clipboard->text(); +} \ No newline at end of file diff --git a/internal/frontend/bridge-gui/bridge-gui/ClipboardProxy.h b/internal/frontend/bridge-gui/bridge-gui/ClipboardProxy.h new file mode 100644 index 00000000..fa0d0614 --- /dev/null +++ b/internal/frontend/bridge-gui/bridge-gui/ClipboardProxy.h @@ -0,0 +1,38 @@ +// Copyright (c) 2024 Proton AG +// This file is part of Proton Mail Bridge. +// Proton Mail Bridge 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 3 of the License, or +// (at your option) any later version. +// Proton Mail Bridge 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 Proton Mail Bridge. If not, see . +#ifndef BRIDGE_GUI_CLIPBOARDPROXY_H +#define BRIDGE_GUI_CLIPBOARDPROXY_H + +// The following class declarations were taken and adapted from: +// https://stackoverflow.com/questions/40092352/passing-qclipboard-to-qml +// Author: krzaq + + +class ClipboardProxy : public QObject +{ +Q_OBJECT + Q_PROPERTY(QString text READ text NOTIFY textChanged) +public: + explicit ClipboardProxy(QClipboard*); + + QString text() const; + +signals: + void textChanged(); + +private: + QClipboard* clipboard; +}; + + +#endif //BRIDGE_GUI_CLIPBOARDPROXY_H diff --git a/internal/frontend/bridge-gui/bridge-gui/Pch.h b/internal/frontend/bridge-gui/bridge-gui/Pch.h index 58b4d7ea..93282a61 100644 --- a/internal/frontend/bridge-gui/bridge-gui/Pch.h +++ b/internal/frontend/bridge-gui/bridge-gui/Pch.h @@ -26,6 +26,7 @@ #include #include #include +#include #include diff --git a/internal/frontend/bridge-gui/bridge-gui/Resources.qrc b/internal/frontend/bridge-gui/bridge-gui/Resources.qrc index 0e0d0d9f..696fb942 100644 --- a/internal/frontend/bridge-gui/bridge-gui/Resources.qrc +++ b/internal/frontend/bridge-gui/bridge-gui/Resources.qrc @@ -132,5 +132,6 @@ qml/ConnectionModeSettings.qml qml/SplashScreen.qml qml/Status.qml + qml/Proton/ContextMenu.qml diff --git a/internal/frontend/bridge-gui/bridge-gui/main.cpp b/internal/frontend/bridge-gui/bridge-gui/main.cpp index 5b364532..dcb6025d 100644 --- a/internal/frontend/bridge-gui/bridge-gui/main.cpp +++ b/internal/frontend/bridge-gui/bridge-gui/main.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include "bridgepp/CLI/CLIUtils.h" @@ -347,6 +348,8 @@ int main(int argc, char *argv[]) { log.info(QString("Qt Quick renderer: %1").arg(QQuickWindow::sceneGraphBackend())); QQmlApplicationEngine engine; + // Set up clipboard + engine.rootContext()->setContextProperty("clipboard", new ClipboardProxy(QGuiApplication::clipboard())); std::unique_ptr rootComponent(createRootQmlComponent(engine)); std::unique_ptr rootObject(rootComponent->create(engine.rootContext())); if (!rootObject) { diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/Proton/ContextMenu.qml b/internal/frontend/bridge-gui/bridge-gui/qml/Proton/ContextMenu.qml new file mode 100644 index 00000000..8c6cf21b --- /dev/null +++ b/internal/frontend/bridge-gui/bridge-gui/qml/Proton/ContextMenu.qml @@ -0,0 +1,79 @@ +// Copyright (c) 2024 Proton AG +// This file is part of Proton Mail Bridge. +// Proton Mail Bridge 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 3 of the License, or +// (at your option) any later version. +// Proton Mail Bridge 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 Proton Mail Bridge. If not, see . +import QtQuick +import QtQuick.Controls + +Item { + property var parentObject: null + property var colorScheme: null + property bool readOnly: false + property bool isPassword: false + + MouseArea { + id: controlMouseArea + width: parentObject ? parentObject.width : 0 + height: parentObject ? parentObject.height : 0 + acceptedButtons: Qt.RightButton + onClicked: controlContextMenu.popup() + + propagateComposedEvents: true + } + + Menu { + id: controlContextMenu + colorScheme: root.colorScheme + onVisibleChanged: { + if (controlContextMenu.visible) { + const hasSelectedText = parentObject.selectedText.length > 0; + const hasClipboardText = clipboard.text.length > 0; + + copyMenuItem.visible = hasSelectedText && !isPassword; + cutMenuItem.visible = hasSelectedText && !readOnly && !isPassword; + pasteMenuItem.visible = hasClipboardText && !readOnly; + controlContextMenu.visible = copyMenuItem.visible || cutMenuItem.visible || pasteMenuItem.visible; + } + } + + MenuItem { + id: cutMenuItem + colorScheme: root.colorScheme + height: visible ? implicitHeight : 0 + text: qsTr("Cut") + + onClicked: { + parentObject.cut() + } + } + MenuItem { + id: copyMenuItem + colorScheme: root.colorScheme + height: visible ? implicitHeight : 0 + text: qsTr("Copy") + + onTriggered: { + parentObject.copy() + } + } + MenuItem { + id: pasteMenuItem + colorScheme: root.colorScheme + height: visible ? implicitHeight : 0 + + text: qsTr("Paste") + onTriggered: { + parentObject.paste() + } + } + } + +} diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/Proton/TextArea.qml b/internal/frontend/bridge-gui/bridge-gui/qml/Proton/TextArea.qml index 161dee1a..f67b3da1 100644 --- a/internal/frontend/bridge-gui/bridge-gui/qml/Proton/TextArea.qml +++ b/internal/frontend/bridge-gui/bridge-gui/qml/Proton/TextArea.qml @@ -362,4 +362,9 @@ FocusScope { } } } + + Proton.ContextMenu { + parentObject: root + colorScheme: root.colorScheme + } } diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/Proton/TextField.qml b/internal/frontend/bridge-gui/bridge-gui/qml/Proton/TextField.qml index c3b3685d..753d3129 100644 --- a/internal/frontend/bridge-gui/bridge-gui/qml/Proton/TextField.qml +++ b/internal/frontend/bridge-gui/bridge-gui/qml/Proton/TextField.qml @@ -331,6 +331,15 @@ FocusScope { x: control.leftPadding y: control.topPadding } + + Proton.ContextMenu { + parentObject: control + colorScheme: root.colorScheme + isPassword: control.echoMode === TextInput.Password + readOnly: control.readOnly + } + + } Proton.Button { id: eyeButton diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/Proton/qmldir b/internal/frontend/bridge-gui/bridge-gui/qml/Proton/qmldir index 15a2af26..a00cb1b6 100644 --- a/internal/frontend/bridge-gui/bridge-gui/qml/Proton/qmldir +++ b/internal/frontend/bridge-gui/bridge-gui/qml/Proton/qmldir @@ -39,3 +39,4 @@ TextArea 4.0 TextArea.qml TextField 4.0 TextField.qml Toggle 4.0 Toggle.qml WebFrame 4.0 WebFrame.qml +ContextMenu 4.0 ContextMenu.qml