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