diff --git a/frame/layershell/dlayershellwindow.cpp b/frame/layershell/dlayershellwindow.cpp index 25a184c9c..e23f09325 100644 --- a/frame/layershell/dlayershellwindow.cpp +++ b/frame/layershell/dlayershellwindow.cpp @@ -231,6 +231,11 @@ DLayerShellWindow* DLayerShellWindow::get(QWindow* window) return new DLayerShellWindow(window); } +void DLayerShellWindow::requestPositionUpdate(int width, int height) +{ + Q_EMIT positionUpdateRequested(width, height); +} + DLayerShellWindow* DLayerShellWindow::qmlAttachedProperties(QObject *object) { auto window = qobject_cast(object); diff --git a/frame/layershell/dlayershellwindow.h b/frame/layershell/dlayershellwindow.h index c62497357..075c15ffa 100644 --- a/frame/layershell/dlayershellwindow.h +++ b/frame/layershell/dlayershellwindow.h @@ -130,6 +130,14 @@ class DS_SHARE DLayerShellWindow : public QObject void setCloseOnDismissed(bool close); bool closeOnDismissed() const; + /** + * Request position update with specified width and height + * This can be called from QML to trigger a position recalculation + * @param width The window width to use for position calculation + * @param height The window height to use for position calculation + */ + Q_INVOKABLE void requestPositionUpdate(int width, int height); + /** * Gets the LayerShell Window for a given Qt Window * Ownership is not transferred @@ -145,6 +153,7 @@ class DS_SHARE DLayerShellWindow : public QObject void keyboardInteractivityChanged(); void layerChanged(); void scopeChanged(); + void positionUpdateRequested(int width, int height); private: DLayerShellWindow(QWindow* window); diff --git a/frame/layershell/x11dlayershellemulation.cpp b/frame/layershell/x11dlayershellemulation.cpp index c38280967..a3326211f 100644 --- a/frame/layershell/x11dlayershellemulation.cpp +++ b/frame/layershell/x11dlayershellemulation.cpp @@ -33,6 +33,7 @@ LayerShellEmulation::LayerShellEmulation(QWindow* window, QObject *parent) onPositionChanged(); connect(m_dlayerShellWindow, &DLayerShellWindow::anchorsChanged, this, &LayerShellEmulation::onPositionChanged); connect(m_dlayerShellWindow, &DLayerShellWindow::marginsChanged, this, &LayerShellEmulation::onPositionChanged); + connect(m_dlayerShellWindow, &DLayerShellWindow::positionUpdateRequested, this, &LayerShellEmulation::onPositionUpdateRequested); onExclusionZoneChanged(); m_exclusionZoneChangedTimer.setSingleShot(true); @@ -120,6 +121,9 @@ void LayerShellEmulation::onPositionChanged() auto screenRect = screen->geometry(); auto x = screenRect.left() + (screenRect.width() - m_window->width()) / 2; auto y = screenRect.top() + (screenRect.height() - m_window->height()) / 2; + + qWarning() << "caimengci position x=" << x << "y=" << y << "window width=" << m_window->width() << "window height=" << m_window->height(); + if (anchors & DLayerShellWindow::AnchorRight) { // https://doc.qt.io/qt-6/qrect.html#right x = (screen->geometry().right() + 1 - m_window->width() - m_dlayerShellWindow->rightMargin()); @@ -153,6 +157,45 @@ void LayerShellEmulation::onPositionChanged() m_window->setGeometry(rect); } +void LayerShellEmulation::onPositionUpdateRequested(int width, int height) +{ + auto anchors = m_dlayerShellWindow->anchors(); + auto screen = m_window->screen(); + auto screenRect = screen->geometry(); + auto x = screenRect.left() + (screenRect.width() - width) / 2; + auto y = screenRect.top() + (screenRect.height() - height) / 2; + + if (anchors & DLayerShellWindow::AnchorRight) { + x = (screen->geometry().right() + 1 - width - m_dlayerShellWindow->rightMargin()); + } + + if (anchors & DLayerShellWindow::AnchorBottom) { + y = (screen->geometry().bottom() + 1 - height - m_dlayerShellWindow->bottomMargin()); + } + if (anchors & DLayerShellWindow::AnchorLeft) { + x = (screen->geometry().left() + m_dlayerShellWindow->leftMargin()); + } + if (anchors & DLayerShellWindow::AnchorTop) { + y = (screen->geometry().top() + m_dlayerShellWindow->topMargin()); + } + + QRect rect(x, y, width, height); + + const bool horizontallyConstrained = anchors.testFlags({DLayerShellWindow::AnchorLeft, DLayerShellWindow::AnchorRight}); + const bool verticallyConstrained = anchors.testFlags({DLayerShellWindow::AnchorTop, DLayerShellWindow::AnchorBottom}); + + if (horizontallyConstrained) { + rect.setX(screen->geometry().left() + m_dlayerShellWindow->leftMargin()); + rect.setWidth(screen->geometry().width() - m_dlayerShellWindow->leftMargin() - m_dlayerShellWindow->rightMargin()); + } + if (verticallyConstrained) { + rect.setY(screen->geometry().top() + m_dlayerShellWindow->topMargin()); + rect.setHeight(screen->geometry().height() - m_dlayerShellWindow->topMargin() - m_dlayerShellWindow->bottomMargin()); + } + + m_window->setGeometry(rect); +} + /** * https://specifications.freedesktop.org/wm-spec/wm-spec-1.4.html#idm45649101327728 */ diff --git a/frame/layershell/x11dlayershellemulation.h b/frame/layershell/x11dlayershellemulation.h index 72aec4d9e..ee713722b 100644 --- a/frame/layershell/x11dlayershellemulation.h +++ b/frame/layershell/x11dlayershellemulation.h @@ -26,6 +26,7 @@ private slots: void onLayerChanged(); // margins or anchor changed void onPositionChanged(); + void onPositionUpdateRequested(int width, int height); void onExclusionZoneChanged(); void onScopeChanged(); // void onKeyboardInteractivityChanged(); diff --git a/panels/notification/bubble/bubblepanel.cpp b/panels/notification/bubble/bubblepanel.cpp index 8c0edc912..949bfbb3f 100644 --- a/panels/notification/bubble/bubblepanel.cpp +++ b/panels/notification/bubble/bubblepanel.cpp @@ -7,6 +7,7 @@ #include "bubblemodel.h" #include "dataaccessorproxy.h" #include "pluginfactory.h" +#include #include #include @@ -54,6 +55,7 @@ bool BubblePanel::init() connect(m_bubbles, &BubbleModel::rowsInserted, this, &BubblePanel::onBubbleCountChanged); connect(m_bubbles, &BubbleModel::rowsRemoved, this, &BubblePanel::onBubbleCountChanged); + setVisible(true); return true; } @@ -111,7 +113,14 @@ void BubblePanel::onNotificationStateChanged(qint64 id, int processedType) void BubblePanel::onBubbleCountChanged() { bool isEmpty = m_bubbles->items().isEmpty(); - setVisible(!isEmpty && enabled()); + + if (isEmpty) { + QTimer::singleShot(400, this, [this]() { + setVisible(false); + }); + } else { + setVisible(!isEmpty && enabled()); + } } void BubblePanel::addBubble(qint64 id) diff --git a/panels/notification/bubble/package/main.qml b/panels/notification/bubble/package/main.qml index 47a0e7de7..872e8e7af 100644 --- a/panels/notification/bubble/package/main.qml +++ b/panels/notification/bubble/package/main.qml @@ -69,8 +69,8 @@ Window { visible: Applet.visible width: 390 - height: Math.max(10, bubbleView.height + bubbleView.anchors.topMargin + bubbleView.anchors.bottomMargin) - DLayerShellWindow.layer: DLayerShellWindow.LayerOverlay + height: 0 + DLayerShellWindow.layer: DLayerShellWindow.LayerTop DLayerShellWindow.anchors: DLayerShellWindow.AnchorBottom | DLayerShellWindow.AnchorRight DLayerShellWindow.topMargin: windowMargin(0) DLayerShellWindow.rightMargin: windowMargin(1) @@ -89,6 +89,11 @@ Window { root.screen = Qt.binding(function () { return Qt.application.screens[0]}) } + // Function to trigger position update with custom width and height + function updatePosition(width, height) { + DLayerShellWindow.requestPositionUpdate(width, height) + } + ListView { id: bubbleView width: 360 @@ -98,13 +103,18 @@ Window { bottom: parent.bottom bottomMargin: 10 rightMargin: 10 - margins: 30 + topMargin: 10 } spacing: 10 model: Applet.bubbles interactive: false verticalLayoutDirection: ListView.BottomToTop + + // Monitor height changes and update position + onHeightChanged: { + updatePosition(390, bubbleView.height + bubbleView.anchors.topMargin + bubbleView.anchors.bottomMargin) + } add: Transition { id: addTrans // Before starting the new animation, forcibly complete the previous notification bubble's animation @@ -129,8 +139,31 @@ Window { } } delegate: Bubble { + id: delegateItem width: 360 bubble: model + + ListView.onRemove: SequentialAnimation { + PropertyAction { + target: delegateItem + property: "ListView.delayRemove" + value: true + } + ParallelAnimation { + NumberAnimation { + target: delegateItem + property: "x" + to: 360 + duration: 400 + easing.type: Easing.InExpo + } + } + PropertyAction { + target: delegateItem + property: "ListView.delayRemove" + value: false + } + } } } }