mirror of
https://gitlab.linphone.org/BC/public/linphone-desktop.git
synced 2026-01-29 17:59:21 +00:00
Show notifications on all screens
- Fix windows flags for DesktopPopup - Merge notification creation and show macros - Store height offset for each screens (key: screen name) - Create a Visual Root Window for each notifications - Put each Visual Root Windows behind its notification to get visually only one entity - Bind each Visual Root Window to a Screen - Propagate events between visual objects in order to use only one - Add open/close events - Add missing anchors - Avoid using window as a keyword
This commit is contained in:
parent
e412a0466b
commit
ca500317ca
5 changed files with 97 additions and 109 deletions
|
|
@ -21,6 +21,8 @@
|
|||
#include <QQmlApplicationEngine>
|
||||
#include <QQmlComponent>
|
||||
#include <QQuickWindow>
|
||||
#include <QQuickItem>
|
||||
#include <QQuickView>
|
||||
#include <QScreen>
|
||||
#include <QTimer>
|
||||
|
||||
|
|
@ -115,49 +117,61 @@ Notifier::~Notifier () {
|
|||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
QObject *Notifier::createNotification (Notifier::NotificationType type) {
|
||||
mMutex->lock();
|
||||
QObject *Notifier::createNotification (Notifier::NotificationType type, QVariantMap data) {
|
||||
QQuickItem *wrapperItem = nullptr;
|
||||
mMutex->lock();
|
||||
Q_ASSERT(mInstancesNumber <= MaxNotificationsNumber);
|
||||
if (mInstancesNumber == MaxNotificationsNumber) { // Check existing instances.
|
||||
qWarning() << QStringLiteral("Unable to create another notification.");
|
||||
mMutex->unlock();
|
||||
return nullptr;
|
||||
}
|
||||
QList<QScreen *> allScreens = QGuiApplication::screens();
|
||||
if(allScreens.size() > 0){ // Ensure to have a screen to avoid errors
|
||||
|
||||
Q_ASSERT(mInstancesNumber <= MaxNotificationsNumber);
|
||||
QQuickItem * previousWrapper = nullptr;
|
||||
++mInstancesNumber;
|
||||
for(int i = 0 ; i < allScreens.size() ; ++i){
|
||||
QQuickView *view = new QQuickView(App::getInstance()->getEngine(), nullptr); // Use QQuickView to create a visual root object that is independant from current application Window
|
||||
QScreen *screen = allScreens[i];
|
||||
|
||||
// Check existing instances.
|
||||
if (mInstancesNumber == MaxNotificationsNumber) {
|
||||
qWarning() << QStringLiteral("Unable to create another notification.");
|
||||
mMutex->unlock();
|
||||
return nullptr;
|
||||
}
|
||||
view->setScreen(screen); // Bind the visual root object to the screen
|
||||
view->setProperty("flags", QVariant(Qt::BypassWindowManagerHint | Qt::WindowStaysOnBottomHint | Qt::CustomizeWindowHint | Qt::X11BypassWindowManagerHint)); // Set the visual ghost window
|
||||
view->setSource(QString(NotificationsPath)+Notifier::Notifications[type].filename);
|
||||
QQuickWindow *subWindow = view->findChild<QQuickWindow *>("__internalWindow");
|
||||
QObject::connect(subWindow, &QObject::destroyed, view, &QObject::deleteLater); // When destroying window, detroy visual root object too
|
||||
|
||||
// Create instance and set attributes.
|
||||
QObject *instance = mComponents[type]->create();
|
||||
qInfo() << QStringLiteral("Create notification:") << instance;
|
||||
int * screenHeightOffset = &mScreenHeightOffset[screen->name()]; // Access optimization
|
||||
QRect availableGeometry = screen->availableGeometry();
|
||||
int heightOffset = availableGeometry.y() + screen->devicePixelRatio()*(availableGeometry.height() - subWindow->height());
|
||||
|
||||
mInstancesNumber++;
|
||||
subWindow->setX(availableGeometry.x()+availableGeometry.width()*screen->devicePixelRatio()-subWindow->property("width").toInt()*screen->devicePixelRatio());
|
||||
subWindow->setY(heightOffset-(*screenHeightOffset % heightOffset));
|
||||
|
||||
{
|
||||
QQuickWindow *window = instance->findChild<QQuickWindow *>(NotificationPropertyWindow);
|
||||
Q_CHECK_PTR(window);
|
||||
*screenHeightOffset = (subWindow->height() + *screenHeightOffset) + NotificationSpacing;
|
||||
if (*screenHeightOffset - heightOffset + availableGeometry.y() >= 0)
|
||||
*screenHeightOffset = 0;
|
||||
|
||||
QScreen *screen = window->screen();
|
||||
Q_CHECK_PTR(screen);
|
||||
// if(primaryScreen != screen){ //Useful when doing manual scaling jobs. Need to implement scaler in GUI objects
|
||||
// //subwindow->setProperty("xScale", (double)screen->availableVirtualGeometry().width()/availableGeometry.width() );
|
||||
// //subwindow->setProperty("yScale", (double)screen->availableVirtualGeometry().height()/availableGeometry.height());
|
||||
// }
|
||||
wrapperItem = view->findChild<QQuickItem *>("__internalWrapper");
|
||||
::setProperty(*wrapperItem, NotificationPropertyData,data);
|
||||
view->setGeometry(subWindow->geometry()); // Ensure to have sufficient space to both let painter do job without error, and stay behind popup
|
||||
|
||||
QRect geometry = screen->availableGeometry();
|
||||
|
||||
// Set X/Y. (Not Pokémon games.)
|
||||
int windowHeight = window->height();
|
||||
int offset = geometry.y() + geometry.height() - windowHeight;
|
||||
|
||||
::setProperty(*instance, NotificationPropertyX, geometry.x() + geometry.width() - window->width());
|
||||
::setProperty(*instance, NotificationPropertyY, offset - (mOffset % offset));
|
||||
|
||||
// Update offset.
|
||||
mOffset = (windowHeight + mOffset) + NotificationSpacing;
|
||||
if (mOffset - offset + geometry.y() >= 0)
|
||||
mOffset = 0;
|
||||
}
|
||||
|
||||
mMutex->unlock();
|
||||
|
||||
return instance;
|
||||
if(previousWrapper!=nullptr){ // Link objects in order to propagate events without having to store them
|
||||
QObject::connect(wrapperItem, SIGNAL(isOpened()), previousWrapper,SLOT(open()));
|
||||
QObject::connect(wrapperItem, SIGNAL(isClosed()), previousWrapper,SLOT(close()));
|
||||
QObject::connect(wrapperItem, &QObject::destroyed, previousWrapper, &QObject::deleteLater);
|
||||
}
|
||||
previousWrapper = wrapperItem; // The last one is used as a point of start when deleting and openning
|
||||
view->show();
|
||||
}
|
||||
qInfo() << QStringLiteral("Create notifications:") << wrapperItem;
|
||||
}
|
||||
mMutex->unlock();
|
||||
return wrapperItem;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
|
@ -204,7 +218,7 @@ void Notifier::deleteNotification (QVariant notification) {
|
|||
Q_ASSERT(mInstancesNumber >= 0);
|
||||
|
||||
if (mInstancesNumber == 0)
|
||||
mOffset = 0;
|
||||
mScreenHeightOffset.clear();
|
||||
|
||||
mMutex->unlock();
|
||||
|
||||
|
|
@ -213,14 +227,11 @@ void Notifier::deleteNotification (QVariant notification) {
|
|||
|
||||
// =============================================================================
|
||||
|
||||
#define CREATE_NOTIFICATION(TYPE) \
|
||||
QObject * notification = createNotification(TYPE); \
|
||||
#define CREATE_NOTIFICATION(TYPE, DATA) \
|
||||
QObject * notification = createNotification(TYPE, DATA); \
|
||||
if (!notification) \
|
||||
return; \
|
||||
const int timeout = Notifications[TYPE].timeout * 1000;
|
||||
|
||||
#define SHOW_NOTIFICATION(DATA) \
|
||||
::setProperty(*notification, NotificationPropertyData, DATA); \
|
||||
const int timeout = Notifications[TYPE].timeout * 1000; \
|
||||
showNotification(notification, timeout);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
|
@ -228,8 +239,6 @@ void Notifier::deleteNotification (QVariant notification) {
|
|||
// -----------------------------------------------------------------------------
|
||||
|
||||
void Notifier::notifyReceivedMessage (const shared_ptr<linphone::ChatMessage> &message) {
|
||||
CREATE_NOTIFICATION(Notifier::ReceivedMessage)
|
||||
|
||||
QVariantMap map;
|
||||
map["message"] = message->getFileTransferInformation()
|
||||
? tr("newFileMessage")
|
||||
|
|
@ -239,62 +248,46 @@ void Notifier::notifyReceivedMessage (const shared_ptr<linphone::ChatMessage> &m
|
|||
map["peerAddress"] = Utils::coreStringToAppString(chatRoom->getPeerAddress()->asStringUriOnly());
|
||||
map["localAddress"] = Utils::coreStringToAppString(chatRoom->getLocalAddress()->asStringUriOnly());
|
||||
map["window"].setValue(App::getInstance()->getMainWindow());
|
||||
|
||||
SHOW_NOTIFICATION(map)
|
||||
CREATE_NOTIFICATION(Notifier::ReceivedMessage, map)
|
||||
}
|
||||
|
||||
void Notifier::notifyReceivedFileMessage (const shared_ptr<linphone::ChatMessage> &message) {
|
||||
CREATE_NOTIFICATION(Notifier::ReceivedFileMessage)
|
||||
|
||||
QVariantMap map;
|
||||
map["fileUri"] = Utils::coreStringToAppString(message->getFileTransferInformation()->getFilePath());
|
||||
map["fileSize"] = quint64(message->getFileTransferInformation()->getSize() +message->getFileTransferInformation()->getFileSize());
|
||||
|
||||
SHOW_NOTIFICATION(map)
|
||||
CREATE_NOTIFICATION(Notifier::ReceivedFileMessage, map)
|
||||
}
|
||||
|
||||
void Notifier::notifyReceivedCall (const shared_ptr<linphone::Call> &call) {
|
||||
CREATE_NOTIFICATION(Notifier::ReceivedCall)
|
||||
|
||||
CallModel *callModel = &call->getData<CallModel>("call-model");
|
||||
QVariantMap map;
|
||||
map["call"].setValue(callModel);
|
||||
CREATE_NOTIFICATION(Notifier::ReceivedCall, map)
|
||||
|
||||
QObject::connect(callModel, &CallModel::statusChanged, notification, [this, notification](CallModel::CallStatus status) {
|
||||
if (status == CallModel::CallStatusEnded || status == CallModel::CallStatusConnected)
|
||||
deleteNotification(QVariant::fromValue(notification));
|
||||
});
|
||||
|
||||
QVariantMap map;
|
||||
map["call"].setValue(callModel);
|
||||
|
||||
SHOW_NOTIFICATION(map)
|
||||
}
|
||||
|
||||
void Notifier::notifyNewVersionAvailable (const QString &version, const QString &url) {
|
||||
CREATE_NOTIFICATION(Notifier::NewVersionAvailable)
|
||||
|
||||
QVariantMap map;
|
||||
map["message"] = tr("newVersionAvailable").arg(version);
|
||||
map["url"] = url;
|
||||
|
||||
SHOW_NOTIFICATION(map)
|
||||
CREATE_NOTIFICATION(Notifier::NewVersionAvailable, map)
|
||||
}
|
||||
|
||||
void Notifier::notifySnapshotWasTaken (const QString &filePath) {
|
||||
CREATE_NOTIFICATION(Notifier::SnapshotWasTaken)
|
||||
|
||||
QVariantMap map;
|
||||
map["filePath"] = filePath;
|
||||
|
||||
SHOW_NOTIFICATION(map)
|
||||
CREATE_NOTIFICATION(Notifier::SnapshotWasTaken, map)
|
||||
}
|
||||
|
||||
void Notifier::notifyRecordingCompleted (const QString &filePath) {
|
||||
CREATE_NOTIFICATION(Notifier::RecordingCompleted)
|
||||
|
||||
QVariantMap map;
|
||||
map["filePath"] = filePath;
|
||||
|
||||
SHOW_NOTIFICATION(map)
|
||||
CREATE_NOTIFICATION(Notifier::RecordingCompleted, map)
|
||||
}
|
||||
|
||||
#undef SHOW_NOTIFICATION
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
#include <memory>
|
||||
|
||||
#include <QObject>
|
||||
#include <QHash>
|
||||
|
||||
// =============================================================================
|
||||
|
||||
|
|
@ -72,10 +73,10 @@ private:
|
|||
int timeout;
|
||||
};
|
||||
|
||||
QObject *createNotification (NotificationType type);
|
||||
QObject *createNotification (NotificationType type, QVariantMap data);
|
||||
void showNotification (QObject *notification, int timeout);
|
||||
|
||||
int mOffset = 0;
|
||||
QHash<QString,int> mScreenHeightOffset;
|
||||
int mInstancesNumber = 0;
|
||||
|
||||
QMutex *mMutex = nullptr;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
import QtQuick 2.7
|
||||
import QtQuick.Window 2.2
|
||||
import QtQuick.Window 2.12
|
||||
|
||||
import QtQuick.Controls 2.2
|
||||
import QtQuick.Layouts 1.3
|
||||
import Qt.labs.platform 1.0
|
||||
|
||||
import Common.Styles 1.0
|
||||
|
||||
|
|
@ -7,56 +11,57 @@ import Common.Styles 1.0
|
|||
|
||||
Item {
|
||||
id: wrapper
|
||||
objectName: '__internalWrapper'
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
property alias popupX: window.x
|
||||
property alias popupY: window.y
|
||||
property alias popupX: windowId.x
|
||||
property alias popupY: windowId.y
|
||||
property bool requestActivate: false
|
||||
property int flags: Qt.SplashScreen
|
||||
|
||||
readonly property alias popupWidth: window.width
|
||||
readonly property alias popupHeight: window.height
|
||||
readonly property alias popupWidth: windowId.width
|
||||
readonly property alias popupHeight: windowId.height
|
||||
|
||||
default property alias _content: content.data
|
||||
property bool _isOpen: false
|
||||
signal isOpened()
|
||||
signal isClosed()
|
||||
signal dataChanged()
|
||||
|
||||
on_ContentChanged: dataChanged(_content)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function open () {
|
||||
_isOpen = true
|
||||
_isOpen = true;
|
||||
isOpened();
|
||||
}
|
||||
|
||||
function close () {
|
||||
_isOpen = false
|
||||
isClosed()
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// DO NOT TOUCH THESE PROPERTIES.
|
||||
|
||||
// No visible.
|
||||
visible: false
|
||||
|
||||
// No size, no position.
|
||||
height: 0
|
||||
width: 0
|
||||
x: 0
|
||||
y: 0
|
||||
visible:true
|
||||
|
||||
Window {
|
||||
id: window
|
||||
|
||||
// Used for internal purposes only. Like Notifications.
|
||||
id: windowId
|
||||
objectName: '__internalWindow'
|
||||
|
||||
flags: wrapper.flags
|
||||
opacity: 0
|
||||
property bool isFrameLess : false;
|
||||
// Don't use Popup for flags : it could lead to error in geometry
|
||||
flags: Qt.BypassWindowManagerHint | Qt.WindowStaysOnTopHint | Qt.Window | Qt.FramelessWindowHint;
|
||||
opacity: 1.0
|
||||
height: _content[0] != null ? _content[0].height : 0
|
||||
width: _content[0] != null ? _content[0].width : 0
|
||||
|
||||
visible:true
|
||||
Item {
|
||||
id: content
|
||||
anchors.fill:parent
|
||||
|
||||
property var $parent: wrapper
|
||||
}
|
||||
|
|
@ -65,37 +70,32 @@ Item {
|
|||
// ---------------------------------------------------------------------------
|
||||
|
||||
states: State {
|
||||
name: 'opened'
|
||||
name: 'opening'
|
||||
when: _isOpen
|
||||
|
||||
PropertyChanges {
|
||||
opacity: 1.0
|
||||
target: window
|
||||
target: windowId
|
||||
}
|
||||
}
|
||||
|
||||
transitions: [
|
||||
Transition {
|
||||
from: ''
|
||||
to: 'opened'
|
||||
|
||||
to: 'opening'
|
||||
ScriptAction {
|
||||
script: {
|
||||
window.showNormal()
|
||||
|
||||
if (wrapper.requestActivate) {
|
||||
window.requestActivate()
|
||||
windowId.requestActivate()
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
Transition {
|
||||
from: 'opened'
|
||||
from: '*'
|
||||
to: ''
|
||||
|
||||
ScriptAction {
|
||||
script: window.hide()
|
||||
script: windowId.hide()
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -23,11 +23,6 @@ DesktopPopup {
|
|||
deleteNotification(notification)
|
||||
}
|
||||
|
||||
flags: {
|
||||
return (Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint) |
|
||||
(Qt.platform.os === 'osx' ? Qt.Window : Qt.Popup)
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
color: NotificationStyle.color
|
||||
height: overrodeHeight || NotificationStyle.height
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ Notification {
|
|||
}
|
||||
|
||||
sourceComponent: ColumnLayout {
|
||||
spacing: NotificationReceivedCallStyle.spacing
|
||||
spacing: NotificationReceivedCallStyle.spacing
|
||||
|
||||
Contact {
|
||||
Layout.fillWidth: true
|
||||
|
|
@ -77,7 +77,6 @@ Notification {
|
|||
|
||||
ActionButton {
|
||||
icon: 'hangup'
|
||||
|
||||
onClicked: notification._close(notification.call.terminate)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue