From 0794f0a3fdc80f3316a206305a01a5356b1d270f Mon Sep 17 00:00:00 2001 From: Joseph Donofry Date: Mon, 25 Jan 2021 23:46:55 -0500 Subject: [PATCH 01/10] Initial commit for privacy screen Missing window focus event and knowing when room is encryption --- resources/qml/PrivacyScreen.qml | 74 +++++++++++++++++++++++++++++++++ resources/qml/TimelineRow.qml | 2 +- resources/qml/TimelineView.qml | 8 ++++ resources/res.qrc | 1 + src/UserSettingsPage.cpp | 63 +++++++++++++++++++++++++++- src/UserSettingsPage.h | 13 ++++++ 6 files changed, 159 insertions(+), 2 deletions(-) create mode 100644 resources/qml/PrivacyScreen.qml diff --git a/resources/qml/PrivacyScreen.qml b/resources/qml/PrivacyScreen.qml new file mode 100644 index 00000000..497630f1 --- /dev/null +++ b/resources/qml/PrivacyScreen.qml @@ -0,0 +1,74 @@ +import QtQuick 2.12 +import QtGraphicalEffects 1.0 + +Item { + property var timelineRoot + property var imageSource + property int screenTimeout + anchors.fill: parent + + Timer { + id: screenSaverTimer + interval: screenTimeout * 1000 + running: true + onTriggered: { + timelineRoot.grabToImage(function(result) { + imageSource = result.url; + screenSaver.visible = true + particles.resume() + }, Qt.size(width, height)) + } + } + + // Reset screensaver timer when clicks are received + MouseArea { + anchors.fill: parent + // Pass mouse events through + propagateComposedEvents: true + hoverEnabled: true + onClicked: { + screenSaverTimer.restart(); + mouse.accepted = false; + } + } + + Rectangle { + id: screenSaver + anchors.fill: parent + visible: false + color: "transparent" + + Image { + id: image + visible : screenSaver.visible + anchors.fill: parent + source: imageSource + } + + ShaderEffectSource { + id: effectSource + + sourceItem: image + anchors.fill: image + sourceRect: Qt.rect(0,0, width, height) + } + + FastBlur{ + id: blur + anchors.fill: effectSource + source: effectSource + radius: 50 + } + + MouseArea { + anchors.fill: parent + propagateComposedEvents: true + hoverEnabled: true + onClicked: { + screenSaver.visible = false; + screenSaverTimer.restart(); + mouse.accepted = false + } + } + } +} \ No newline at end of file diff --git a/resources/qml/TimelineRow.qml b/resources/qml/TimelineRow.qml index c4c18e0e..95a025cf 100644 --- a/resources/qml/TimelineRow.qml +++ b/resources/qml/TimelineRow.qml @@ -28,7 +28,7 @@ Item { if (mouse.button === Qt.RightButton) messageContextMenu.show(model.id, model.type, model.isEncrypted, row); else - event.accepted = false; + mouse.accepted = false; } onPressAndHold: { messageContextMenu.show(model.id, model.type, model.isEncrypted, row, mapToItem(timelineRoot, mouse.x, mouse.y)); diff --git a/resources/qml/TimelineView.qml b/resources/qml/TimelineView.qml index 38e3a928..5f43de55 100644 --- a/resources/qml/TimelineView.qml +++ b/resources/qml/TimelineView.qml @@ -1,3 +1,4 @@ +import "." import "./delegates" import "./device-verification" import "./emoji" @@ -187,6 +188,7 @@ Page { } ColumnLayout { + id: timelineLayout visible: TimelineManager.timeline != null anchors.fill: parent spacing: 0 @@ -271,6 +273,12 @@ Page { } + PrivacyScreen { + visible: Settings.privacyScreen + screenTimeout: Settings.privacyScreenTimeout + timelineRoot: timelineRoot + } + } systemInactive: SystemPalette { diff --git a/resources/res.qrc b/resources/res.qrc index e3998bd1..308d81a6 100644 --- a/resources/res.qrc +++ b/resources/res.qrc @@ -131,6 +131,7 @@ qml/MessageInput.qml qml/MessageView.qml qml/NhekoBusyIndicator.qml + qml/PrivacyScreen.qml qml/Reactions.qml qml/ReplyPopup.qml qml/ScrollHelper.qml diff --git a/src/UserSettingsPage.cpp b/src/UserSettingsPage.cpp index f90938c9..1875e4f9 100644 --- a/src/UserSettingsPage.cpp +++ b/src/UserSettingsPage.cpp @@ -95,6 +95,8 @@ UserSettings::load(std::optional profile) font_ = settings.value("user/font_family", "default").toString(); avatarCircles_ = settings.value("user/avatar_circles", true).toBool(); decryptSidebar_ = settings.value("user/decrypt_sidebar", true).toBool(); + privacyScreen_ = settings.value("user/privacy_screen", false).toBool(); + privacyScreenTimeout_ = settings.value("user/privacy_screen_timeout", 0).toInt(); shareKeysWithTrustedUsers_ = settings.value("user/share_keys_with_trusted_users", true).toBool(); mobileMode_ = settings.value("user/mobile_mode", false).toBool(); @@ -284,6 +286,28 @@ UserSettings::setDecryptSidebar(bool state) save(); } +void +UserSettings::setPrivacyScreen(bool state) +{ + if (state == privacyScreen_) { + return; + } + privacyScreen_ = state; + emit privacyScreenChanged(state); + save(); +} + +void +UserSettings::setPrivacyScreenTimeout(int state) +{ + if (state == privacyScreenTimeout_) { + return; + } + privacyScreenTimeout_ = state; + emit privacyScreenTimeoutChanged(state); + save(); +} + void UserSettings::setFontSize(double size) { @@ -531,6 +555,8 @@ UserSettings::save() settings.setValue("avatar_circles", avatarCircles_); settings.setValue("decrypt_sidebar", decryptSidebar_); + settings.setValue("privacy_screen", privacyScreen_); + settings.setValue("privacy_screen_timeout", privacyScreenTimeout_); settings.setValue("share_keys_with_trusted_users", shareKeysWithTrustedUsers_); settings.setValue("mobile_mode", mobileMode_); settings.setValue("font_size", baseFontSize_); @@ -619,6 +645,7 @@ UserSettingsPage::UserSettingsPage(QSharedPointer settings, QWidge startInTrayToggle_ = new Toggle{this}; avatarCircles_ = new Toggle{this}; decryptSidebar_ = new Toggle(this); + privacyScreen_ = new Toggle{this}; shareKeysWithTrustedUsers_ = new Toggle(this); groupViewToggle_ = new Toggle{this}; timelineButtonsToggle_ = new Toggle{this}; @@ -642,11 +669,13 @@ UserSettingsPage::UserSettingsPage(QSharedPointer settings, QWidge cameraResolutionCombo_ = new QComboBox{this}; cameraFrameRateCombo_ = new QComboBox{this}; timelineMaxWidthSpin_ = new QSpinBox{this}; + privacyScreenTimeout_ = new QSpinBox{this}; trayToggle_->setChecked(settings_->tray()); startInTrayToggle_->setChecked(settings_->startInTray()); avatarCircles_->setChecked(settings_->avatarCircles()); decryptSidebar_->setChecked(settings_->decryptSidebar()); + privacyScreen_->setChecked(settings_->privacyScreen()); shareKeysWithTrustedUsers_->setChecked(settings_->shareKeysWithTrustedUsers()); groupViewToggle_->setChecked(settings_->groupView()); timelineButtonsToggle_->setChecked(settings_->buttonsInTimeline()); @@ -666,6 +695,10 @@ UserSettingsPage::UserSettingsPage(QSharedPointer settings, QWidge startInTrayToggle_->setDisabled(true); } + if (!settings_->privacyScreen()) { + privacyScreenTimeout_->setDisabled(true); + } + avatarCircles_->setFixedSize(64, 48); auto uiLabel_ = new QLabel{tr("INTERFACE"), this}; @@ -706,6 +739,10 @@ UserSettingsPage::UserSettingsPage(QSharedPointer settings, QWidge timelineMaxWidthSpin_->setMaximum(100'000'000); timelineMaxWidthSpin_->setSingleStep(10); + privacyScreenTimeout_->setMinimum(0); + privacyScreenTimeout_->setMaximum(3600); + privacyScreenTimeout_->setSingleStep(10); + auto callsLabel = new QLabel{tr("CALLS"), this}; callsLabel->setFixedHeight(callsLabel->minimumHeight() + LayoutTopMargin); callsLabel->setAlignment(Qt::AlignBottom); @@ -799,6 +836,13 @@ UserSettingsPage::UserSettingsPage(QSharedPointer settings, QWidge decryptSidebar_, tr("Decrypt the messages shown in the sidebar.\nOnly affects messages in " "encrypted chats.")); + boxWrap(tr("Encrypted chat privacy screen"), + privacyScreen_, + tr("When the window loses focus, the timeline will\nbe blurred.")); + boxWrap(tr("Privacy screen timeout"), + privacyScreenTimeout_, + tr("Set timeout for how long after window loses\nfocus before the screen" + " will be blurred.\nSet to 0 to blur immediately after focus loss.")); boxWrap(tr("Show buttons in timeline"), timelineButtonsToggle_, tr("Show buttons to quickly reply, react or access additional options next to each " @@ -1057,7 +1101,15 @@ UserSettingsPage::UserSettingsPage(QSharedPointer settings, QWidge connect(decryptSidebar_, &Toggle::toggled, this, [this](bool enabled) { settings_->setDecryptSidebar(enabled); - emit decryptSidebarChanged(); + }); + + connect(privacyScreen_, &Toggle::toggled, this, [this](bool enabled) { + settings_->setPrivacyScreen(enabled); + if (enabled) { + privacyScreenTimeout_->setEnabled(true); + } else { + privacyScreenTimeout_->setDisabled(true); + } }); connect(shareKeysWithTrustedUsers_, &Toggle::toggled, this, [this](bool enabled) { @@ -1113,6 +1165,13 @@ UserSettingsPage::UserSettingsPage(QSharedPointer settings, QWidge this, [this](int newValue) { settings_->setTimelineMaxWidth(newValue); }); + connect(privacyScreenTimeout_, + qOverload(&QSpinBox::valueChanged), + this, + [this](int newValue) { + settings_->setPrivacyScreenTimeout(newValue); + }); + connect( sessionKeysImportBtn, &QPushButton::clicked, this, &UserSettingsPage::importSessionKeys); @@ -1146,6 +1205,7 @@ UserSettingsPage::showEvent(QShowEvent *) startInTrayToggle_->setState(settings_->startInTray()); groupViewToggle_->setState(settings_->groupView()); decryptSidebar_->setState(settings_->decryptSidebar()); + privacyScreen_->setState(settings_->privacyScreen()); shareKeysWithTrustedUsers_->setState(settings_->shareKeysWithTrustedUsers()); avatarCircles_->setState(settings_->avatarCircles()); typingNotifications_->setState(settings_->typingNotifications()); @@ -1160,6 +1220,7 @@ UserSettingsPage::showEvent(QShowEvent *) enlargeEmojiOnlyMessages_->setState(settings_->enlargeEmojiOnlyMessages()); deviceIdValue_->setText(QString::fromStdString(http::client()->device_id())); timelineMaxWidthSpin_->setValue(settings_->timelineMaxWidth()); + privacyScreenTimeout_->setValue(settings_->privacyScreenTimeout()); WebRTCSession::instance().refreshDevices(); auto mics = diff --git a/src/UserSettingsPage.h b/src/UserSettingsPage.h index 6744d101..7e475c3b 100644 --- a/src/UserSettingsPage.h +++ b/src/UserSettingsPage.h @@ -67,6 +67,9 @@ class UserSettings : public QObject bool avatarCircles READ avatarCircles WRITE setAvatarCircles NOTIFY avatarCirclesChanged) Q_PROPERTY(bool decryptSidebar READ decryptSidebar WRITE setDecryptSidebar NOTIFY decryptSidebarChanged) + Q_PROPERTY(bool privacyScreen READ privacyScreen WRITE setPrivacyScreen NOTIFY + privacyScreenChanged) + Q_PROPERTY(int privacyScreenTimeout READ privacyScreenTimeout WRITE setPrivacyScreenTimeout NOTIFY privacyScreenTimeoutChanged) Q_PROPERTY(int timelineMaxWidth READ timelineMaxWidth WRITE setTimelineMaxWidth NOTIFY timelineMaxWidthChanged) Q_PROPERTY(bool mobileMode READ mobileMode WRITE setMobileMode NOTIFY mobileModeChanged) @@ -131,6 +134,8 @@ public: void setAlertOnNotification(bool state); void setAvatarCircles(bool state); void setDecryptSidebar(bool state); + void setPrivacyScreen(bool state); + void setPrivacyScreenTimeout(int state); void setPresence(Presence state); void setRingtone(QString ringtone); void setMicrophone(QString microphone); @@ -153,6 +158,8 @@ public: bool groupView() const { return groupView_; } bool avatarCircles() const { return avatarCircles_; } bool decryptSidebar() const { return decryptSidebar_; } + bool privacyScreen() const { return privacyScreen_; } + int privacyScreenTimeout() const { return privacyScreenTimeout_; } bool markdown() const { return markdown_; } bool typingNotifications() const { return typingNotifications_; } bool sortByImportance() const { return sortByImportance_; } @@ -199,6 +206,8 @@ signals: void alertOnNotificationChanged(bool state); void avatarCirclesChanged(bool state); void decryptSidebarChanged(bool state); + void privacyScreenChanged(bool state); + void privacyScreenTimeoutChanged(int state); void timelineMaxWidthChanged(int state); void mobileModeChanged(bool mode); void fontSizeChanged(double state); @@ -239,6 +248,8 @@ private: bool hasAlertOnNotification_; bool avatarCircles_; bool decryptSidebar_; + bool privacyScreen_; + int privacyScreenTimeout_; bool shareKeysWithTrustedUsers_; bool mobileMode_; int timelineMaxWidth_; @@ -317,6 +328,8 @@ private: Toggle *avatarCircles_; Toggle *useStunServer_; Toggle *decryptSidebar_; + Toggle *privacyScreen_; + QSpinBox *privacyScreenTimeout_; Toggle *shareKeysWithTrustedUsers_; Toggle *mobileMode_; QLabel *deviceFingerprintValue_; From cb93ac3402a9b8d8862ce97d05386c52811166c5 Mon Sep 17 00:00:00 2001 From: Joseph Donofry Date: Tue, 26 Jan 2021 00:03:09 -0500 Subject: [PATCH 02/10] Fix formatting --- .ci/format.sh | 10 ++++++++++ resources/qml/PrivacyScreen.qml | 27 +++++++++++++++++---------- resources/qml/TimelineView.qml | 1 + src/UserSettingsPage.cpp | 26 ++++++++++++-------------- src/UserSettingsPage.h | 7 ++++--- 5 files changed, 44 insertions(+), 27 deletions(-) diff --git a/.ci/format.sh b/.ci/format.sh index e1e6c1e4..7f201bb8 100755 --- a/.ci/format.sh +++ b/.ci/format.sh @@ -14,4 +14,14 @@ do clang-format -i "$f" done; +QMLFORMAT_PATH=$(which qmlformat) +if [ ! -z "$QMLFORMAT_PATH" ]; then + QML_FILES=$(find resources -type f -iname "*.qml") + + for f in $QML_FILES + do + qmlformat -i "$f" + done; +fi + git diff --exit-code diff --git a/resources/qml/PrivacyScreen.qml b/resources/qml/PrivacyScreen.qml index 497630f1..9fdd35e0 100644 --- a/resources/qml/PrivacyScreen.qml +++ b/resources/qml/PrivacyScreen.qml @@ -1,22 +1,24 @@ -import QtQuick 2.12 import QtGraphicalEffects 1.0 +import QtQuick 2.12 Item { property var timelineRoot property var imageSource property int screenTimeout + anchors.fill: parent Timer { id: screenSaverTimer + interval: screenTimeout * 1000 running: true onTriggered: { timelineRoot.grabToImage(function(result) { imageSource = result.url; - screenSaver.visible = true - particles.resume() - }, Qt.size(width, height)) + screenSaver.visible = true; + particles.resume(); + }, Qt.size(width, height)); } } @@ -34,13 +36,15 @@ Item { Rectangle { id: screenSaver + anchors.fill: parent visible: false color: "transparent" Image { id: image - visible : screenSaver.visible + + visible: screenSaver.visible anchors.fill: parent source: imageSource } @@ -50,11 +54,12 @@ Item { sourceItem: image anchors.fill: image - sourceRect: Qt.rect(0,0, width, height) + sourceRect: Qt.rect(0, 0, width, height) } - FastBlur{ + FastBlur { id: blur + anchors.fill: effectSource source: effectSource radius: 50 @@ -64,11 +69,13 @@ Item { anchors.fill: parent propagateComposedEvents: true hoverEnabled: true - onClicked: { + onClicked: { screenSaver.visible = false; screenSaverTimer.restart(); - mouse.accepted = false + mouse.accepted = false; } } + } -} \ No newline at end of file + +} diff --git a/resources/qml/TimelineView.qml b/resources/qml/TimelineView.qml index 5f43de55..35d09d5a 100644 --- a/resources/qml/TimelineView.qml +++ b/resources/qml/TimelineView.qml @@ -189,6 +189,7 @@ Page { ColumnLayout { id: timelineLayout + visible: TimelineManager.timeline != null anchors.fill: parent spacing: 0 diff --git a/src/UserSettingsPage.cpp b/src/UserSettingsPage.cpp index 1875e4f9..a8558e95 100644 --- a/src/UserSettingsPage.cpp +++ b/src/UserSettingsPage.cpp @@ -87,16 +87,16 @@ UserSettings::load(std::optional profile) settings.value("user/timeline/message_hover_highlight", false).toBool(); enlargeEmojiOnlyMessages_ = settings.value("user/timeline/enlarge_emoji_only_msg", false).toBool(); - markdown_ = settings.value("user/markdown_enabled", true).toBool(); - typingNotifications_ = settings.value("user/typing_notifications", true).toBool(); - sortByImportance_ = settings.value("user/sort_by_unread", true).toBool(); - readReceipts_ = settings.value("user/read_receipts", true).toBool(); - theme_ = settings.value("user/theme", defaultTheme_).toString(); - font_ = settings.value("user/font_family", "default").toString(); - avatarCircles_ = settings.value("user/avatar_circles", true).toBool(); - decryptSidebar_ = settings.value("user/decrypt_sidebar", true).toBool(); - privacyScreen_ = settings.value("user/privacy_screen", false).toBool(); - privacyScreenTimeout_ = settings.value("user/privacy_screen_timeout", 0).toInt(); + markdown_ = settings.value("user/markdown_enabled", true).toBool(); + typingNotifications_ = settings.value("user/typing_notifications", true).toBool(); + sortByImportance_ = settings.value("user/sort_by_unread", true).toBool(); + readReceipts_ = settings.value("user/read_receipts", true).toBool(); + theme_ = settings.value("user/theme", defaultTheme_).toString(); + font_ = settings.value("user/font_family", "default").toString(); + avatarCircles_ = settings.value("user/avatar_circles", true).toBool(); + decryptSidebar_ = settings.value("user/decrypt_sidebar", true).toBool(); + privacyScreen_ = settings.value("user/privacy_screen", false).toBool(); + privacyScreenTimeout_ = settings.value("user/privacy_screen_timeout", 0).toInt(); shareKeysWithTrustedUsers_ = settings.value("user/share_keys_with_trusted_users", true).toBool(); mobileMode_ = settings.value("user/mobile_mode", false).toBool(); @@ -842,7 +842,7 @@ UserSettingsPage::UserSettingsPage(QSharedPointer settings, QWidge boxWrap(tr("Privacy screen timeout"), privacyScreenTimeout_, tr("Set timeout for how long after window loses\nfocus before the screen" - " will be blurred.\nSet to 0 to blur immediately after focus loss.")); + " will be blurred.\nSet to 0 to blur immediately after focus loss.")); boxWrap(tr("Show buttons in timeline"), timelineButtonsToggle_, tr("Show buttons to quickly reply, react or access additional options next to each " @@ -1168,9 +1168,7 @@ UserSettingsPage::UserSettingsPage(QSharedPointer settings, QWidge connect(privacyScreenTimeout_, qOverload(&QSpinBox::valueChanged), this, - [this](int newValue) { - settings_->setPrivacyScreenTimeout(newValue); - }); + [this](int newValue) { settings_->setPrivacyScreenTimeout(newValue); }); connect( sessionKeysImportBtn, &QPushButton::clicked, this, &UserSettingsPage::importSessionKeys); diff --git a/src/UserSettingsPage.h b/src/UserSettingsPage.h index 7e475c3b..8654afcf 100644 --- a/src/UserSettingsPage.h +++ b/src/UserSettingsPage.h @@ -67,9 +67,10 @@ class UserSettings : public QObject bool avatarCircles READ avatarCircles WRITE setAvatarCircles NOTIFY avatarCirclesChanged) Q_PROPERTY(bool decryptSidebar READ decryptSidebar WRITE setDecryptSidebar NOTIFY decryptSidebarChanged) - Q_PROPERTY(bool privacyScreen READ privacyScreen WRITE setPrivacyScreen NOTIFY - privacyScreenChanged) - Q_PROPERTY(int privacyScreenTimeout READ privacyScreenTimeout WRITE setPrivacyScreenTimeout NOTIFY privacyScreenTimeoutChanged) + Q_PROPERTY( + bool privacyScreen READ privacyScreen WRITE setPrivacyScreen NOTIFY privacyScreenChanged) + Q_PROPERTY(int privacyScreenTimeout READ privacyScreenTimeout WRITE setPrivacyScreenTimeout + NOTIFY privacyScreenTimeoutChanged) Q_PROPERTY(int timelineMaxWidth READ timelineMaxWidth WRITE setTimelineMaxWidth NOTIFY timelineMaxWidthChanged) Q_PROPERTY(bool mobileMode READ mobileMode WRITE setMobileMode NOTIFY mobileModeChanged) From bfeb766a91e259dce7924fb0bbcfcac85b91092b Mon Sep 17 00:00:00 2001 From: Joseph Donofry Date: Tue, 26 Jan 2021 17:23:28 -0500 Subject: [PATCH 03/10] Implement Privacy Screen * Add handles for window focus gained / focus lossed and connect to timer * Clean up some of the PrivacyScreen.qml code * Connect settings to PrivacyScreen visibility --- .ci/format.sh | 2 + resources/qml/PrivacyScreen.qml | 120 ++++++++++++++++++++++------- src/ChatPage.cpp | 2 + src/ChatPage.h | 2 +- src/MainWindow.cpp | 16 ++++ src/MainWindow.h | 4 + src/UserSettingsPage.cpp | 12 +-- src/timeline/TimelineViewManager.h | 14 +++- 8 files changed, 137 insertions(+), 35 deletions(-) diff --git a/.ci/format.sh b/.ci/format.sh index 7f201bb8..4df4282a 100755 --- a/.ci/format.sh +++ b/.ci/format.sh @@ -22,6 +22,8 @@ if [ ! -z "$QMLFORMAT_PATH" ]; then do qmlformat -i "$f" done; +else + echo "qmlformat not found; skipping qml formatting" fi git diff --exit-code diff --git a/resources/qml/PrivacyScreen.qml b/resources/qml/PrivacyScreen.qml index 9fdd35e0..45ae5cb5 100644 --- a/resources/qml/PrivacyScreen.qml +++ b/resources/qml/PrivacyScreen.qml @@ -1,13 +1,28 @@ import QtGraphicalEffects 1.0 import QtQuick 2.12 +import im.nheko 1.0 Item { + id: privacyScreen + property var timelineRoot - property var imageSource + property var imageSource: "" property int screenTimeout anchors.fill: parent + Connections { + target: TimelineManager + onFocusChanged: { + if (TimelineManager.isWindowFocused) { + screenSaverTimer.stop(); + screenSaver.state = "Invisible"; + } else { + screenSaverTimer.start(); + } + } + } + Timer { id: screenSaverTimer @@ -15,36 +30,98 @@ Item { running: true onTriggered: { timelineRoot.grabToImage(function(result) { + screenSaver.state = "Visible"; imageSource = result.url; - screenSaver.visible = true; - particles.resume(); }, Qt.size(width, height)); } } - // Reset screensaver timer when clicks are received - MouseArea { - anchors.fill: parent - // Pass mouse events through - propagateComposedEvents: true - hoverEnabled: true - onClicked: { - screenSaverTimer.restart(); - mouse.accepted = false; - } - } - Rectangle { id: screenSaver + state: "Invisible" anchors.fill: parent visible: false color: "transparent" + states: [ + State { + name: "Visible" + + PropertyChanges { + target: screenSaver + visible: true + } + + PropertyChanges { + target: screenSaver + opacity: 1 + } + + }, + State { + name: "Invisible" + + PropertyChanges { + target: screenSaver + opacity: 0 + } + + PropertyChanges { + target: screenSaver + visible: false + } + + } + ] + transitions: [ + Transition { + from: "Visible" + to: "Invisible" + + SequentialAnimation { + NumberAnimation { + target: screenSaver + property: "opacity" + duration: 250 + easing.type: Easing.InQuad + } + + NumberAnimation { + target: screenSaver + property: "visible" + duration: 0 + } + + } + + }, + Transition { + from: "Invisible" + to: "Visible" + + SequentialAnimation { + NumberAnimation { + target: screenSaver + property: "visible" + duration: 0 + } + + NumberAnimation { + target: screenSaver + property: "opacity" + duration: 500 + easing.type: Easing.InQuad + } + + } + + } + ] Image { id: image - visible: screenSaver.visible + cache: false anchors.fill: parent source: imageSource } @@ -65,17 +142,6 @@ Item { radius: 50 } - MouseArea { - anchors.fill: parent - propagateComposedEvents: true - hoverEnabled: true - onClicked: { - screenSaver.visible = false; - screenSaverTimer.restart(); - mouse.accepted = false; - } - } - } } diff --git a/src/ChatPage.cpp b/src/ChatPage.cpp index 0377ce30..5bbfa351 100644 --- a/src/ChatPage.cpp +++ b/src/ChatPage.cpp @@ -312,6 +312,8 @@ ChatPage::ChatPage(QSharedPointer userSettings, QWidget *parent) &ChatPage::initializeMentions, user_mentions_popup_, &popups::UserMentions::initializeMentions); + connect( + this, &ChatPage::chatFocusChanged, view_manager_, &TimelineViewManager::chatFocusChanged); connect(this, &ChatPage::syncUI, this, [this](const mtx::responses::Rooms &rooms) { try { room_list_->cleanupInvites(cache::invites()); diff --git a/src/ChatPage.h b/src/ChatPage.h index 0516f87d..917bd785 100644 --- a/src/ChatPage.h +++ b/src/ChatPage.h @@ -127,7 +127,6 @@ public slots: void receivedSessionKey(const std::string &room_id, const std::string &session_id); void decryptDownloadedSecrets(mtx::secret_storage::AesHmacSha2KeyDescription keyDesc, const SecretsToDecrypt &secrets); - signals: void connectionLost(); void connectionRestored(); @@ -176,6 +175,7 @@ signals: void retrievedPresence(const QString &statusMsg, mtx::presence::PresenceState state); void themeChanged(); void decryptSidebarChanged(); + void chatFocusChanged(const bool focused); //! Signals for device verificaiton void receivedDeviceVerificationAccept( diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index 77269008..8fd5c7e2 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -130,6 +130,9 @@ MainWindow::MainWindow(QWidget *parent) SLOT(iconActivated(QSystemTrayIcon::ActivationReason))); connect(chat_page_, SIGNAL(contentLoaded()), this, SLOT(removeOverlayProgressBar())); + + connect(this, &MainWindow::focusChanged, chat_page_, &ChatPage::chatFocusChanged); + connect( chat_page_, &ChatPage::showUserSettingsPage, this, &MainWindow::showUserSettingsPage); @@ -204,6 +207,19 @@ MainWindow::resizeEvent(QResizeEvent *event) QMainWindow::resizeEvent(event); } +bool +MainWindow::event(QEvent *event) +{ + auto type = event->type(); + if (type == QEvent::WindowActivate) { + emit focusChanged(true); + } else if (type == QEvent::WindowDeactivate) { + emit focusChanged(false); + } + + return QMainWindow::event(event); +} + void MainWindow::adjustSideBars() { diff --git a/src/MainWindow.h b/src/MainWindow.h index 0915a849..4560ec65 100644 --- a/src/MainWindow.h +++ b/src/MainWindow.h @@ -88,6 +88,7 @@ protected: void closeEvent(QCloseEvent *event) override; void resizeEvent(QResizeEvent *event) override; void showEvent(QShowEvent *event) override; + bool event(QEvent *event) override; private slots: //! Show or hide the sidebars based on window's size. @@ -115,6 +116,9 @@ private slots: virtual void setWindowTitle(int notificationCount); +signals: + void focusChanged(const bool focused); + private: bool loadJdenticonPlugin(); diff --git a/src/UserSettingsPage.cpp b/src/UserSettingsPage.cpp index a8558e95..c26bf2e9 100644 --- a/src/UserSettingsPage.cpp +++ b/src/UserSettingsPage.cpp @@ -836,13 +836,15 @@ UserSettingsPage::UserSettingsPage(QSharedPointer settings, QWidge decryptSidebar_, tr("Decrypt the messages shown in the sidebar.\nOnly affects messages in " "encrypted chats.")); - boxWrap(tr("Encrypted chat privacy screen"), + boxWrap(tr("Privacy Screen"), privacyScreen_, tr("When the window loses focus, the timeline will\nbe blurred.")); - boxWrap(tr("Privacy screen timeout"), - privacyScreenTimeout_, - tr("Set timeout for how long after window loses\nfocus before the screen" - " will be blurred.\nSet to 0 to blur immediately after focus loss.")); + boxWrap( + tr("Privacy screen timeout"), + privacyScreenTimeout_, + tr("Set timeout (in seconds) for how long after window loses\nfocus before the screen" + " will be blurred.\nSet to 0 to blur immediately after focus loss. Max value of 1 " + "hour (3600 seconds)")); boxWrap(tr("Show buttons in timeline"), timelineButtonsToggle_, tr("Show buttons to quickly reply, react or access additional options next to each " diff --git a/src/timeline/TimelineViewManager.h b/src/timeline/TimelineViewManager.h index 23a960b8..74128865 100644 --- a/src/timeline/TimelineViewManager.h +++ b/src/timeline/TimelineViewManager.h @@ -36,6 +36,8 @@ class TimelineViewManager : public QObject bool isInitialSync MEMBER isInitialSync_ READ isInitialSync NOTIFY initialSyncChanged) Q_PROPERTY( bool isNarrowView MEMBER isNarrowView_ READ isNarrowView NOTIFY narrowViewChanged) + Q_PROPERTY( + bool isWindowFocused MEMBER isWindowFocused_ READ isWindowFocused NOTIFY focusChanged) public: TimelineViewManager(CallManager *callManager, ChatPage *parent = nullptr); @@ -54,6 +56,7 @@ public: Q_INVOKABLE TimelineModel *activeTimeline() const { return timeline_; } Q_INVOKABLE bool isInitialSync() const { return isInitialSync_; } bool isNarrowView() const { return isNarrowView_; } + bool isWindowFocused() const { return isWindowFocused_; } Q_INVOKABLE void openImageOverlay(QString mxcUrl, QString eventId) const; Q_INVOKABLE QColor userColor(QString id, QColor background); Q_INVOKABLE QString escapeEmoji(QString str) const; @@ -83,11 +86,17 @@ signals: void inviteUsers(QStringList users); void showRoomList(); void narrowViewChanged(); + void focusChanged(); public slots: void updateReadReceipts(const QString &room_id, const std::vector &event_ids); void receivedSessionKey(const std::string &room_id, const std::string &session_id); void initWithMessages(const std::vector &roomIds); + void chatFocusChanged(bool focused) + { + isWindowFocused_ = focused; + emit focusChanged(); + } void setHistoryView(const QString &room_id); TimelineModel *getHistoryView(const QString &room_id) @@ -145,8 +154,9 @@ private: TimelineModel *timeline_ = nullptr; CallManager *callManager_ = nullptr; - bool isInitialSync_ = true; - bool isNarrowView_ = false; + bool isInitialSync_ = true; + bool isNarrowView_ = false; + bool isWindowFocused_ = false; QHash userColors; From d59910a8f298b13cc90e436a6f6efa325d863180 Mon Sep 17 00:00:00 2001 From: Joseph Donofry Date: Mon, 1 Feb 2021 18:42:18 -0500 Subject: [PATCH 04/10] Remove redundant import and fix visible warning --- resources/qml/PrivacyScreen.qml | 10 ++++++---- resources/qml/TimelineView.qml | 1 - 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/resources/qml/PrivacyScreen.qml b/resources/qml/PrivacyScreen.qml index 45ae5cb5..14a175f9 100644 --- a/resources/qml/PrivacyScreen.qml +++ b/resources/qml/PrivacyScreen.qml @@ -29,10 +29,12 @@ Item { interval: screenTimeout * 1000 running: true onTriggered: { - timelineRoot.grabToImage(function(result) { - screenSaver.state = "Visible"; - imageSource = result.url; - }, Qt.size(width, height)); + if (MainWindow.visible) { + timelineRoot.grabToImage(function(result) { + screenSaver.state = "Visible"; + imageSource = result.url; + }, Qt.size(width, height)); + } } } diff --git a/resources/qml/TimelineView.qml b/resources/qml/TimelineView.qml index 35d09d5a..379b19fb 100644 --- a/resources/qml/TimelineView.qml +++ b/resources/qml/TimelineView.qml @@ -1,4 +1,3 @@ -import "." import "./delegates" import "./device-verification" import "./emoji" From 1127aa7c91718ceec82ac0a775935b62b5220724 Mon Sep 17 00:00:00 2001 From: Joseph Donofry Date: Mon, 1 Feb 2021 18:57:59 -0500 Subject: [PATCH 05/10] Small UX fixes --- resources/qml/PrivacyScreen.qml | 4 +++- src/MainWindow.cpp | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/resources/qml/PrivacyScreen.qml b/resources/qml/PrivacyScreen.qml index 14a175f9..f3e388ce 100644 --- a/resources/qml/PrivacyScreen.qml +++ b/resources/qml/PrivacyScreen.qml @@ -18,7 +18,9 @@ Item { screenSaverTimer.stop(); screenSaver.state = "Invisible"; } else { - screenSaverTimer.start(); + if (timelineRoot.visible) { + screenSaverTimer.start(); + } } } } diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index 8fd5c7e2..ab3c2cf2 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -59,6 +59,8 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , userSettings_{UserSettings::instance()} { + instance_ = this; + setWindowTitle(0); setObjectName("MainWindow"); @@ -312,8 +314,6 @@ MainWindow::showChatPage() &Cache::secretChanged, userSettingsPage_, &UserSettingsPage::updateSecretStatus); - - instance_ = this; } void From 00885e41f87e954a8f0a1e4172821cf5e6ed9625 Mon Sep 17 00:00:00 2001 From: Joseph Donofry Date: Mon, 1 Feb 2021 19:07:04 -0500 Subject: [PATCH 06/10] Update wording on settings page for privacy timer --- src/UserSettingsPage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/UserSettingsPage.cpp b/src/UserSettingsPage.cpp index 61b04074..96c07d7c 100644 --- a/src/UserSettingsPage.cpp +++ b/src/UserSettingsPage.cpp @@ -849,7 +849,7 @@ UserSettingsPage::UserSettingsPage(QSharedPointer settings, QWidge privacyScreen_, tr("When the window loses focus, the timeline will\nbe blurred.")); boxWrap( - tr("Privacy screen timeout"), + tr("Privacy screen timeout (in seconds [0 - 3600])"), privacyScreenTimeout_, tr("Set timeout (in seconds) for how long after window loses\nfocus before the screen" " will be blurred.\nSet to 0 to blur immediately after focus loss. Max value of 1 " From 08dbdac3b747bd71f1c38c5f5d68cd443ae1f1b2 Mon Sep 17 00:00:00 2001 From: Joseph Donofry Date: Mon, 1 Feb 2021 21:02:27 -0500 Subject: [PATCH 07/10] Change bash variable check --- .ci/format.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/format.sh b/.ci/format.sh index 4df4282a..f2d01cec 100755 --- a/.ci/format.sh +++ b/.ci/format.sh @@ -15,7 +15,7 @@ do done; QMLFORMAT_PATH=$(which qmlformat) -if [ ! -z "$QMLFORMAT_PATH" ]; then +if [ -n "$QMLFORMAT_PATH" ]; then QML_FILES=$(find resources -type f -iname "*.qml") for f in $QML_FILES From 2bfd44755ea9c18ee9fc48e775b97f0363c9acd4 Mon Sep 17 00:00:00 2001 From: Joseph Donofry Date: Tue, 2 Feb 2021 11:37:10 -0500 Subject: [PATCH 08/10] Try to fix format script and fix linting --- .ci/format.sh | 4 ++-- resources/qml/PrivacyScreen.qml | 8 ++++---- resources/qml/UserProfile.qml | 17 ++++++++--------- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/.ci/format.sh b/.ci/format.sh index f2d01cec..31eba878 100755 --- a/.ci/format.sh +++ b/.ci/format.sh @@ -14,13 +14,13 @@ do clang-format -i "$f" done; -QMLFORMAT_PATH=$(which qmlformat) +QMLFORMAT_PATH=$(command -v qmlformat) if [ -n "$QMLFORMAT_PATH" ]; then QML_FILES=$(find resources -type f -iname "*.qml") for f in $QML_FILES do - qmlformat -i "$f" + $QMLFORMAT_PATH -i "$f" done; else echo "qmlformat not found; skipping qml formatting" diff --git a/resources/qml/PrivacyScreen.qml b/resources/qml/PrivacyScreen.qml index f3e388ce..98c802a0 100644 --- a/resources/qml/PrivacyScreen.qml +++ b/resources/qml/PrivacyScreen.qml @@ -18,9 +18,9 @@ Item { screenSaverTimer.stop(); screenSaver.state = "Invisible"; } else { - if (timelineRoot.visible) { + if (timelineRoot.visible) screenSaverTimer.start(); - } + } } } @@ -31,12 +31,12 @@ Item { interval: screenTimeout * 1000 running: true onTriggered: { - if (MainWindow.visible) { + if (MainWindow.visible) timelineRoot.grabToImage(function(result) { screenSaver.state = "Visible"; imageSource = result.url; }, Qt.size(width, height)); - } + } } diff --git a/resources/qml/UserProfile.qml b/resources/qml/UserProfile.qml index 4cb9eb10..4a402b69 100644 --- a/resources/qml/UserProfile.qml +++ b/resources/qml/UserProfile.qml @@ -53,10 +53,9 @@ ApplicationWindow { font.bold: true Layout.alignment: Qt.AlignHCenter selectByMouse: true - onAccepted: { - profile.changeUsername(displayUsername.text) - displayUsername.isUsernameEditingAllowed = false + profile.changeUsername(displayUsername.text); + displayUsername.isUsernameEditingAllowed = false; } ImageButton { @@ -65,18 +64,18 @@ ApplicationWindow { anchors.left: displayUsername.right anchors.verticalCenter: displayUsername.verticalCenter image: displayUsername.isUsernameEditingAllowed ? ":/icons/icons/ui/checkmark.png" : ":/icons/icons/ui/edit.png" - onClicked: { if (displayUsername.isUsernameEditingAllowed) { - profile.changeUsername(displayUsername.text) - displayUsername.isUsernameEditingAllowed = false + profile.changeUsername(displayUsername.text); + displayUsername.isUsernameEditingAllowed = false; } else { - displayUsername.isUsernameEditingAllowed = true - displayUsername.focus = true - displayUsername.selectAll() + displayUsername.isUsernameEditingAllowed = true; + displayUsername.focus = true; + displayUsername.selectAll(); } } } + } MatrixText { From 2a858d84e22c3813a380d4ff3e838cd0093955a0 Mon Sep 17 00:00:00 2001 From: Joseph Donofry Date: Tue, 2 Feb 2021 11:50:57 -0500 Subject: [PATCH 09/10] Update format script again --- .ci/format.sh | 3 ++- resources/qml/PrivacyScreen.qml | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.ci/format.sh b/.ci/format.sh index 31eba878..cdbc0464 100755 --- a/.ci/format.sh +++ b/.ci/format.sh @@ -14,7 +14,8 @@ do clang-format -i "$f" done; -QMLFORMAT_PATH=$(command -v qmlformat) +QMLFORMAT_PATH=$(command -v qmlformat || true) + if [ -n "$QMLFORMAT_PATH" ]; then QML_FILES=$(find resources -type f -iname "*.qml") diff --git a/resources/qml/PrivacyScreen.qml b/resources/qml/PrivacyScreen.qml index 98c802a0..2cfae471 100644 --- a/resources/qml/PrivacyScreen.qml +++ b/resources/qml/PrivacyScreen.qml @@ -33,9 +33,9 @@ Item { onTriggered: { if (MainWindow.visible) timelineRoot.grabToImage(function(result) { - screenSaver.state = "Visible"; - imageSource = result.url; - }, Qt.size(width, height)); + screenSaver.state = "Visible"; + imageSource = result.url; + }, Qt.size(width, height)); } } From 935abee62e7165debd05e521244934a648e0d7c9 Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Tue, 2 Feb 2021 12:57:21 -0500 Subject: [PATCH 10/10] Fix unused capture warning --- src/ui/UserProfile.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ui/UserProfile.cpp b/src/ui/UserProfile.cpp index 3872294a..de02bf5e 100644 --- a/src/ui/UserProfile.cpp +++ b/src/ui/UserProfile.cpp @@ -244,7 +244,7 @@ UserProfile::changeUsername(QString username) if (isGlobalUserProfile()) { // change global http::client()->set_displayname( - username.toStdString(), [this](mtx::http::RequestErr err) { + username.toStdString(), [](mtx::http::RequestErr err) { if (err) { nhlog::net()->warn("could not change username"); return; @@ -293,4 +293,4 @@ UserProfile::setGlobalUsername(const QString &globalUser) { globalUsername = globalUser; emit displayNameChanged(); -} \ No newline at end of file +}