diff --git a/debian/control b/debian/control index 2cc47da58..e25d4c52a 100644 --- a/debian/control +++ b/debian/control @@ -22,6 +22,7 @@ Build-Depends: libxcb-res0-dev, libxcb-util-dev, libxcb1-dev, + libxcb-shape0-dev, libxtst-dev, libyaml-cpp-dev, qml6-module-qtquick-controls2-styles-chameleon, diff --git a/frame/CMakeLists.txt b/frame/CMakeLists.txt index 195a1dc3e..1bfc03365 100644 --- a/frame/CMakeLists.txt +++ b/frame/CMakeLists.txt @@ -118,7 +118,7 @@ target_link_directories(dde-shell-frame INTERFACE if (BUILD_WITH_X11) target_compile_definitions(dde-shell-frame PRIVATE BUILD_WITH_X11) - pkg_check_modules(XCB REQUIRED IMPORTED_TARGET xcb-ewmh xcb-icccm xtst x11) + pkg_check_modules(XCB REQUIRED IMPORTED_TARGET xcb-ewmh xcb-icccm xtst xcb-shape x11) target_sources(dde-shell-frame PRIVATE layershell/x11dlayershellemulation.h layershell/x11dlayershellemulation.cpp) target_link_libraries(dde-shell-frame PRIVATE PkgConfig::XCB) endif(BUILD_WITH_X11) diff --git a/frame/layershell/dlayershellwindow.cpp b/frame/layershell/dlayershellwindow.cpp index 3149e005c..934e91133 100644 --- a/frame/layershell/dlayershellwindow.cpp +++ b/frame/layershell/dlayershellwindow.cpp @@ -43,6 +43,7 @@ class DLayerShellWindowPrivate int preferredWidth = -1; int preferredHeight = -1; bool closeOnDismissed = true; + QRegion inputRegion; }; void DLayerShellWindow::setAnchors(DLayerShellWindow::Anchors anchors) @@ -192,6 +193,29 @@ int DLayerShellWindow::preferredHeight() const return d->preferredHeight; } +void DLayerShellWindow::setInputRegion(const QRegion ®ion) +{ + if (d->inputRegion != region) { + d->inputRegion = region; + Q_EMIT inputRegionChanged(); + } +} + +void DLayerShellWindow::resetInputRegion() +{ + setInputRegion(QRegion()); +} + +QRegion DLayerShellWindow::inputRegion() const +{ + return d->inputRegion; +} + +void DLayerShellWindow::setInputRegionRect(int x, int y, int width, int height) +{ + setInputRegion(QRegion(x, y, width, height)); +} + bool DLayerShellWindow::closeOnDismissed() const { return d->closeOnDismissed; diff --git a/frame/layershell/dlayershellwindow.h b/frame/layershell/dlayershellwindow.h index 1b77ac777..578e94ed2 100644 --- a/frame/layershell/dlayershellwindow.h +++ b/frame/layershell/dlayershellwindow.h @@ -30,6 +30,7 @@ class DS_SHARE DLayerShellWindow : public QObject Q_PROPERTY(ScreenConfiguration screenConfiguration READ screenConfiguration WRITE setScreenConfiguration) Q_PROPERTY(int preferredWidth READ preferredWidth WRITE setPreferredWidth RESET resetPreferredWidth NOTIFY geometryHintsChanged) Q_PROPERTY(int preferredHeight READ preferredHeight WRITE setPreferredHeight RESET resetPreferredHeight NOTIFY geometryHintsChanged) + Q_PROPERTY(QRegion inputRegion READ inputRegion WRITE setInputRegion RESET resetInputRegion NOTIFY inputRegionChanged) Q_PROPERTY(bool closeOnDismissed READ closeOnDismissed WRITE setCloseOnDismissed) @@ -121,6 +122,12 @@ class DS_SHARE DLayerShellWindow : public QObject void resetPreferredHeight(); int preferredHeight() const; + void setInputRegion(const QRegion ®ion); + void resetInputRegion(); + QRegion inputRegion() const; + + Q_INVOKABLE void setInputRegionRect(int x, int y, int width, int height); + /** * Sets a string based identifier for this window. * This may be used by a compositor to determine stacking @@ -156,6 +163,7 @@ class DS_SHARE DLayerShellWindow : public QObject void layerChanged(); void scopeChanged(); void geometryHintsChanged(); + void inputRegionChanged(); private: DLayerShellWindow(QWindow* window); diff --git a/frame/layershell/qwaylandlayershellsurface.cpp b/frame/layershell/qwaylandlayershellsurface.cpp index 8731d7d15..2de637ba8 100644 --- a/frame/layershell/qwaylandlayershellsurface.cpp +++ b/frame/layershell/qwaylandlayershellsurface.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2023 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -75,6 +75,13 @@ QWaylandLayerShellSurface::QWaylandLayerShellSurface(QtWayland::zwlr_layer_shell window->waylandSurface()->commit(); }); + auto applyInputRegion = [this, window]() { + window->window()->setMask(m_dlayerShellWindow->inputRegion()); + window->waylandSurface()->commit(); + }; + + connect(m_dlayerShellWindow, &DLayerShellWindow::inputRegionChanged, this, applyInputRegion); + calcAndSetRequestSize(window->surfaceSize()); if (m_requestSize.isValid()) { diff --git a/frame/layershell/x11dlayershellemulation.cpp b/frame/layershell/x11dlayershellemulation.cpp index c7ae84ce4..08d6f76ba 100644 --- a/frame/layershell/x11dlayershellemulation.cpp +++ b/frame/layershell/x11dlayershellemulation.cpp @@ -17,6 +17,7 @@ #include #include #include +#include DS_BEGIN_NAMESPACE @@ -71,6 +72,9 @@ LayerShellEmulation::LayerShellEmulation(QWindow* window, QObject *parent) onScopeChanged(); connect(m_dlayerShellWindow, &DLayerShellWindow::scopeChanged, this, &LayerShellEmulation::onScopeChanged); + onInputRegionChanged(); + connect(m_dlayerShellWindow, &DLayerShellWindow::inputRegionChanged, this, &LayerShellEmulation::onInputRegionChanged); + // connect(m_dlayerShellWindow, &DS_NAMESPACE::DLayerShellWindow::keyboardInteractivityChanged, this, &LayerShellEmulation::onKeyboardInteractivityChanged); } @@ -321,6 +325,41 @@ void LayerShellEmulation::onScopeChanged() qCDebug(layershell) << "Set WM_CLASS for window" << m_window->winId() << " wm_class:" << wmClassData; } +void LayerShellEmulation::onInputRegionChanged() +{ + auto *x11Application = qGuiApp->nativeInterface(); + if (!x11Application || !m_window->winId() || !m_dlayerShellWindow) { + return; + } + + if (m_dlayerShellWindow->inputRegion().isNull()) { + // Reset input region (no shape constraint) + xcb_shape_mask(x11Application->connection(), XCB_SHAPE_SO_SET, XCB_SHAPE_SK_INPUT, m_window->winId(), 0, 0, XCB_NONE); + xcb_flush(x11Application->connection()); + return; + } + + QRegion region = m_dlayerShellWindow->inputRegion(); + qreal scaleFactor = qGuiApp->devicePixelRatio(); + + QVector rects; + for (const QRect &r : region) { + xcb_rectangle_t rect; + rect.x = r.x() * scaleFactor; + rect.y = r.y() * scaleFactor; + rect.width = r.width() * scaleFactor; + rect.height = r.height() * scaleFactor; + rects.append(rect); + } + + // Set the input shape via XCB + // If rects vector is empty, the window will become completely transparent to input clicks (unclickable) + xcb_shape_rectangles(x11Application->connection(), XCB_SHAPE_SO_SET, XCB_SHAPE_SK_INPUT, + XCB_CLIP_ORDERING_UNSORTED, m_window->winId(), 0, 0, + rects.size(), rects.data()); + xcb_flush(x11Application->connection()); +} + // void X11Emulation::onKeyboardInteractivityChanged() // { // // kwin no implentation on wayland diff --git a/frame/layershell/x11dlayershellemulation.h b/frame/layershell/x11dlayershellemulation.h index 72aec4d9e..5814f7b8b 100644 --- a/frame/layershell/x11dlayershellemulation.h +++ b/frame/layershell/x11dlayershellemulation.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2023 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -28,6 +28,7 @@ private slots: void onPositionChanged(); void onExclusionZoneChanged(); void onScopeChanged(); + void onInputRegionChanged(); // void onKeyboardInteractivityChanged(); private: diff --git a/panels/notification/bubble/bubbleitem.cpp b/panels/notification/bubble/bubbleitem.cpp index a70693c0e..530db1ddc 100644 --- a/panels/notification/bubble/bubbleitem.cpp +++ b/panels/notification/bubble/bubbleitem.cpp @@ -282,19 +282,6 @@ void BubbleItem::updateActions() m_actions = array; } -int BubbleItem::level() const -{ - return m_level; -} - -void BubbleItem::setLevel(int level) -{ - if (m_level == level) - return; - - m_level = level; - emit levelChanged(); -} QString BubbleItem::timeTip() const { diff --git a/panels/notification/bubble/bubbleitem.h b/panels/notification/bubble/bubbleitem.h index ac4cb8d38..1b28f0aa8 100644 --- a/panels/notification/bubble/bubbleitem.h +++ b/panels/notification/bubble/bubbleitem.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2024 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -39,8 +39,6 @@ class BubbleItem : public QObject QVariantList actions() const; void updateActions(); - int level() const; - void setLevel(int level); QString timeTip() const; void setTimeTip(const QString &timeTip); @@ -51,7 +49,6 @@ class BubbleItem : public QObject bool isValid() const; signals: - void levelChanged(); void timeTipChanged(); private: @@ -59,7 +56,6 @@ class BubbleItem : public QObject private: NotifyEntity m_entity; - int m_level = 0; int m_urgency = NotifyEntity::Normal; QString m_timeTip; bool m_enablePreview = true; diff --git a/panels/notification/bubble/bubblemodel.cpp b/panels/notification/bubble/bubblemodel.cpp index 455161497..fa0714f42 100644 --- a/panels/notification/bubble/bubblemodel.cpp +++ b/panels/notification/bubble/bubblemodel.cpp @@ -23,18 +23,33 @@ namespace notification { BubbleModel::BubbleModel(QObject *parent) : QAbstractListModel(parent) , m_updateTimeTipTimer(new QTimer(this)) + , m_processPendingTimer(new QTimer(this)) { m_updateTimeTipTimer->setInterval(1000); m_updateTimeTipTimer->setSingleShot(false); - BubbleMaxCount = NotifySetting::instance()->bubbleCount(); + + m_processPendingTimer->setInterval(300); + m_processPendingTimer->setSingleShot(true); + + m_maxKeep = NotifySetting::instance()->bubbleCount() + 2; // max keep folds. connect(m_updateTimeTipTimer, &QTimer::timeout, this, &BubbleModel::updateBubbleTimeTip); + connect(m_processPendingTimer, &QTimer::timeout, this, [this] { + if (!m_pendingBubbles.isEmpty()) { + auto bubble = m_pendingBubbles.dequeue(); + insertBubble(bubble); + m_processPendingTimer->start(); + } + }); + connect(NotifySetting::instance(), &NotifySetting::contentRowCountChanged, this, &BubbleModel::updateContentRowCount); connect(NotifySetting::instance(), &NotifySetting::bubbleCountChanged, this, &BubbleModel::updateBubbleCount); } BubbleModel::~BubbleModel() { + qDeleteAll(m_pendingBubbles); + m_pendingBubbles.clear(); qDeleteAll(m_bubbles); m_bubbles.clear(); } @@ -45,16 +60,28 @@ void BubbleModel::push(BubbleItem *bubble) m_updateTimeTipTimer->start(); } - bool more = displayRowCount() >= BubbleMaxCount; - if (more) { - beginRemoveRows(QModelIndex(), BubbleMaxCount - 1, BubbleMaxCount - 1); + if (m_processPendingTimer->isActive()) { + m_pendingBubbles.enqueue(bubble); + } else { + insertBubble(bubble); + m_processPendingTimer->start(); + } +} + +void BubbleModel::insertBubble(BubbleItem *bubble) +{ + // Retain only enough bubbles to show requested number + folded overlay space. + // This offloads disappearance smoothly to the QML remove transition. + if (m_bubbles.size() >= m_maxKeep) { + beginRemoveRows(QModelIndex(), m_bubbles.size() - 1, m_bubbles.size() - 1); + auto old = m_bubbles.takeLast(); + old->deleteLater(); endRemoveRows(); } + beginInsertRows(QModelIndex(), 0, 0); m_bubbles.prepend(bubble); endInsertRows(); - - updateLevel(); } bool BubbleModel::isReplaceBubble(const BubbleItem *bubble) const @@ -75,6 +102,12 @@ BubbleItem *BubbleModel::replaceBubble(BubbleItem *bubble) void BubbleModel::clear() { + if (m_processPendingTimer) { + m_processPendingTimer->stop(); + } + qDeleteAll(m_pendingBubbles); + m_pendingBubbles.clear(); + if (m_bubbles.count() <= 0) return; beginResetModel(); @@ -82,7 +115,6 @@ void BubbleModel::clear() m_bubbles.clear(); endResetModel(); - updateLevel(); m_updateTimeTipTimer->stop(); } @@ -96,22 +128,11 @@ void BubbleModel::remove(int index) if (index < 0 || index >= m_bubbles.size()) return; - if (index >= rowCount(QModelIndex())) { - auto bubble = m_bubbles.takeAt(index); - bubble->deleteLater(); - return; - } - beginRemoveRows(QModelIndex(), index, index); auto bubble = m_bubbles.takeAt(index); bubble->deleteLater(); endRemoveRows(); - if (m_bubbles.count() >= BubbleMaxCount) { - beginInsertRows(QModelIndex(), displayRowCount() - 1, displayRowCount() - 1); - endInsertRows(); - } - updateLevel(); } void BubbleModel::remove(const BubbleItem *bubble) @@ -165,16 +186,12 @@ QVariant BubbleModel::data(const QModelIndex &index, int role) const return m_bubbles[row]->summary(); case BubbleModel::IconName: return m_bubbles[row]->appIcon(); - case BubbleModel::Level: - return m_bubbles[row]->level(); case BubbleModel::CTime: return m_bubbles[row]->ctime(); case BubbleModel::TimeTip: return m_bubbles[row]->timeTip(); case BubbleModel::BodyImagePath: return m_bubbles[row]->bodyImagePath(); - case BubbleModel::OverlayCount: - return overlayCount(); case BubbleModel::DefaultAction: return m_bubbles[row]->defaultAction(); case BubbleModel::Actions: @@ -183,6 +200,8 @@ QVariant BubbleModel::data(const QModelIndex &index, int role) const return m_bubbles[row]->urgency(); case BubbleModel::ContentRowCount: return NotifySetting::instance()->contentRowCount(); + case BubbleModel::BubbleCount: + return NotifySetting::instance()->bubbleCount(); default: break; } @@ -197,48 +216,31 @@ QHash BubbleModel::roleNames() const mapRoleNames[BubbleModel::Body] = "body"; mapRoleNames[BubbleModel::Summary] = "summary"; mapRoleNames[BubbleModel::IconName] = "iconName"; - mapRoleNames[BubbleModel::Level] = "level"; mapRoleNames[BubbleModel::CTime] = "ctime"; mapRoleNames[BubbleModel::TimeTip] = "timeTip"; mapRoleNames[BubbleModel::Urgency] = "urgency"; mapRoleNames[BubbleModel::BodyImagePath] = "bodyImagePath"; - mapRoleNames[BubbleModel::OverlayCount] = "overlayCount"; mapRoleNames[BubbleModel::DefaultAction] = "defaultAction"; mapRoleNames[BubbleModel::Actions] = "actions"; mapRoleNames[BubbleModel::ContentRowCount] = "contentRowCount"; + mapRoleNames[BubbleModel::BubbleCount] = "bubbleCount"; return mapRoleNames; } int BubbleModel::displayRowCount() const { - return qMin(m_bubbles.count(), BubbleMaxCount); -} - -int BubbleModel::overlayCount() const -{ - return qMin(m_bubbles.count() - displayRowCount(), OverlayMaxCount); + return m_bubbles.count(); } void BubbleModel::updateBubbleCount(int count) { - if (count == BubbleMaxCount) - return; + m_maxKeep = count + 2; + // We don't dynamically add/remove based on setting here anymore + // to let QML handle Repeater logic fully mapped to the model size - int currentRowCount = rowCount(QModelIndex()); - - if (count < currentRowCount) { - beginRemoveRows(QModelIndex(), count, currentRowCount - 1); - endRemoveRows(); - } else if (count > currentRowCount) { - int maxInsertCount = std::min(count, (int)m_bubbles.size()); - beginInsertRows(QModelIndex(), currentRowCount, maxInsertCount - 1); - endInsertRows(); + if (!m_bubbles.isEmpty()) { + Q_EMIT dataChanged(index(0), index(m_bubbles.size() - 1), {BubbleModel::BubbleCount}); } - - BubbleMaxCount = count; - - layoutChanged(); - updateLevel(); } void BubbleModel::clearInvalidBubbles() @@ -267,19 +269,6 @@ int BubbleModel::replaceBubbleIndex(const BubbleItem *bubble) const return -1; } -void BubbleModel::updateLevel() -{ - if (m_bubbles.isEmpty()) - return; - - int lastBubbleMaxIndex = BubbleMaxCount - 1; - for (int i = 0; i < displayRowCount(); i++) { - auto item = m_bubbles.at(i); - item->setLevel(i == lastBubbleMaxIndex ? 1 + overlayCount() : 1); - } - Q_EMIT dataChanged(index(0), index(displayRowCount() - 1), {BubbleModel::Level}); -} - void BubbleModel::updateBubbleTimeTip() { if (m_bubbles.isEmpty()) { diff --git a/panels/notification/bubble/bubblemodel.h b/panels/notification/bubble/bubblemodel.h index 24891c97f..b9b8f6203 100644 --- a/panels/notification/bubble/bubblemodel.h +++ b/panels/notification/bubble/bubblemodel.h @@ -8,6 +8,7 @@ #include "notifyentity.h" #include +#include class QTimer; @@ -24,15 +25,14 @@ class BubbleModel : public QAbstractListModel Body, Summary, IconName, - Level, CTime, TimeTip, BodyImagePath, - OverlayCount, DefaultAction, Actions, Urgency, - ContentRowCount + ContentRowCount, + BubbleCount } BubbleRole; explicit BubbleModel(QObject *parent = nullptr); @@ -58,23 +58,23 @@ class BubbleModel : public QAbstractListModel QHash roleNames() const override; int displayRowCount() const; - int overlayCount() const; void clearInvalidBubbles(); private: + void insertBubble(BubbleItem *bubble); void updateBubbleCount(int count); int replaceBubbleIndex(const BubbleItem *bubble) const; - void updateLevel(); void updateBubbleTimeTip(); void updateContentRowCount(int rowCount); private: QTimer *m_updateTimeTipTimer = nullptr; + QTimer *m_processPendingTimer = nullptr; QList m_bubbles; - int BubbleMaxCount{3}; + QQueue m_pendingBubbles; + int m_maxKeep{5}; int m_contentRowCount{6}; - const int OverlayMaxCount{2}; }; } diff --git a/panels/notification/bubble/bubblepanel.cpp b/panels/notification/bubble/bubblepanel.cpp index df180214b..469fe67f7 100644 --- a/panels/notification/bubble/bubblepanel.cpp +++ b/panels/notification/bubble/bubblepanel.cpp @@ -111,7 +111,16 @@ void BubblePanel::onNotificationStateChanged(qint64 id, int processedType) void BubblePanel::onBubbleCountChanged() { bool isEmpty = m_bubbles->items().isEmpty(); - setVisible(!isEmpty && enabled()); + if (isEmpty) { + // 延迟发送 false ,好让 QML 中的 remove: Transition 有足够的时间播放完动画 + QTimer::singleShot(500, this, [this]() { + if (m_bubbles->items().isEmpty()) { + setVisible(false); + } + }); + } else { + setVisible(enabled()); + } } void BubblePanel::addBubble(qint64 id) diff --git a/panels/notification/bubble/package/Bubble.qml b/panels/notification/bubble/package/Bubble.qml index bad3bb46e..2019262f3 100644 --- a/panels/notification/bubble/package/Bubble.qml +++ b/panels/notification/bubble/package/Bubble.qml @@ -6,27 +6,34 @@ import QtQuick 2.15 import QtQuick.Controls 2.15 import org.deepin.ds 1.0 +import org.deepin.ds.notification 1.0 import org.deepin.dtk 1.0 as D -Control { +NotifyItemContent { id: control - height: loader.height property var bubble - Loader { - id: loader - width: control.width - sourceComponent: bubble.level <= 1 ? normalCom : overlayCom + + width: 360 + appName: bubble.appName + iconName: bubble.iconName + date: bubble.timeTip + actions: bubble.actions + defaultAction: bubble.defaultAction + title: bubble.summary + content: bubble.body + strongInteractive: bubble.urgency === 2 + contentIcon: bubble.bodyImagePath + contentRowCount: bubble.contentRowCount + onRemove: function () { + console.log("remove notify", bubble.appName) + Applet.close(bubble.index, NotifyItem.Closed) } - Component { - id: normalCom - NormalBubble { - bubble: control.bubble - } + onDismiss: function () { + console.log("dismiss notify", bubble.appName) + Applet.close(bubble.index, NotifyItem.Dismissed) } - Component { - id: overlayCom - OverlayBubble { - bubble: control.bubble - } + onActionInvoked: function (actionId) { + console.log("action notify", bubble.appName, actionId) + Applet.invokeAction(bubble.index, actionId) } } diff --git a/panels/notification/bubble/package/BubbleDelegate.qml b/panels/notification/bubble/package/BubbleDelegate.qml new file mode 100644 index 000000000..6c4ba6484 --- /dev/null +++ b/panels/notification/bubble/package/BubbleDelegate.qml @@ -0,0 +1,52 @@ +// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +import QtQuick 2.15 +import QtQuick.Controls 2.15 + +Item { + id: delegateRoot + width: 360 + property var bubble: model + property int maxCount: 3 + // ListView 的 remove 动画执行的时候,remove Item的index会以负数的方式出现 + property int realIndex: index < 0 ? ListView.view.count + index : index; + + height: bubbleContent.height + z: -realIndex + Bubble { + id: bubbleContent + width: 360 + bubble: delegateRoot.bubble + + transformOrigin: Item.Top + + y: { + // normal bubble dont need to move + if (realIndex < delegateRoot.maxCount) + return 0 + + let spacing = 10 + let peekAmount = 8 + // 根据 realIndex 计算出超出部分的折叠层数(最多折叠3层,再多层保留为了动画淡出) + let levelsFolded = Math.min(realIndex - (delegateRoot.maxCount - 1), 3) + let ret = levelsFolded * (delegateRoot.height + spacing - peekAmount) + return ret + } + + scale: { + if (realIndex < delegateRoot.maxCount) + return 1.0 + + let levelsFolded = Math.min(realIndex - (delegateRoot.maxCount - 1), 3) + return 1.0 - levelsFolded * 0.05 + } + + opacity: realIndex >= (delegateRoot.maxCount + 2) ? 0 : 1.0 + + Behavior on y { NumberAnimation { duration: 600; easing.type: Easing.OutExpo } } + Behavior on scale { NumberAnimation { duration: 600; easing.type: Easing.OutExpo } } + Behavior on opacity { NumberAnimation { duration: 600; easing.type: Easing.OutExpo } } + } +} diff --git a/panels/notification/bubble/package/NormalBubble.qml b/panels/notification/bubble/package/NormalBubble.qml deleted file mode 100644 index 2cbfe81b3..000000000 --- a/panels/notification/bubble/package/NormalBubble.qml +++ /dev/null @@ -1,39 +0,0 @@ -// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. -// -// SPDX-License-Identifier: LGPL-3.0-or-later - -import QtQuick 2.15 -import QtQuick.Controls 2.15 - -import org.deepin.ds 1.0 -import org.deepin.ds.notification 1.0 -import org.deepin.dtk 1.0 as D - -NotifyItemContent { - id: control - property var bubble - - width: 360 - appName: bubble.appName - iconName: bubble.iconName - date: bubble.timeTip - actions: bubble.actions - defaultAction: bubble.defaultAction - title: bubble.summary - content: bubble.body - strongInteractive: bubble.urgency === 2 - contentIcon: bubble.bodyImagePath - contentRowCount: bubble.contentRowCount - onRemove: function () { - console.log("remove notify", bubble.appName) - Applet.close(bubble.index, NotifyItem.Closed) - } - onDismiss: function () { - console.log("dismiss notify", bubble.appName) - Applet.close(bubble.index, NotifyItem.Dismissed) - } - onActionInvoked: function (actionId) { - console.log("action notify", bubble.appName, actionId) - Applet.invokeAction(bubble.index, actionId) - } -} diff --git a/panels/notification/bubble/package/OverlayBubble.qml b/panels/notification/bubble/package/OverlayBubble.qml deleted file mode 100644 index 500066be5..000000000 --- a/panels/notification/bubble/package/OverlayBubble.qml +++ /dev/null @@ -1,41 +0,0 @@ -// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. -// -// SPDX-License-Identifier: LGPL-3.0-or-later - -import QtQuick 2.15 -import QtQuick.Controls 2.15 -import QtQuick.Layouts 1.15 - -import org.deepin.ds 1.0 -import org.deepin.ds.notification 1.0 -import org.deepin.dtk 1.0 as D - -Item { - id: control - property var bubble - readonly property int radius: 12 - - height: bubbleContent.height + indicator.height - OverlapIndicator { - id: indicator - width: parent.width - count: bubble.level - 1 - revert: true - anchors { - top: parent.top - left: parent.left - leftMargin: radius - right: parent.right - rightMargin: radius - } - } - - NormalBubble { - id: bubbleContent - width: parent.width - bubble: control.bubble - anchors { - top: indicator.bottom - } - } -} diff --git a/panels/notification/bubble/package/main.qml b/panels/notification/bubble/package/main.qml index 18dd17787..426576743 100644 --- a/panels/notification/bubble/package/main.qml +++ b/panels/notification/bubble/package/main.qml @@ -68,8 +68,8 @@ Window { } visible: Applet.visible - DLayerShellWindow.preferredWidth: 390 - DLayerShellWindow.preferredHeight: Math.max(10, bubbleView.height + bubbleView.anchors.topMargin + bubbleView.anchors.bottomMargin) + width: 390 + height: root.screen.height DLayerShellWindow.layer: DLayerShellWindow.LayerOverlay DLayerShellWindow.anchors: DLayerShellWindow.AnchorBottom | DLayerShellWindow.AnchorRight DLayerShellWindow.topMargin: windowMargin(0) @@ -96,11 +96,22 @@ Window { anchors { right: parent.right bottom: parent.bottom - bottomMargin: 10 rightMargin: 10 - margins: 30 + bottomMargin: 10 } + function updateInputRegion() { + root.DLayerShellWindow.setInputRegionRect( + Math.ceil(bubbleView.x), + Math.ceil(bubbleView.y), + Math.ceil(bubbleView.width), + Math.ceil(Math.max(10, bubbleView.contentHeight)) + ) + } + onContentHeightChanged: updateInputRegion() + onHeightChanged: updateInputRegion() + onYChanged: updateInputRegion() + spacing: 10 model: Applet.bubbles interactive: false @@ -126,11 +137,37 @@ Window { duration: 600 easing.type: Easing.OutExpo } + PropertyAnimation { + target: addDisplacedTrans.ViewTransition.item + properties: "y" + duration: 600 + easing.type: Easing.OutExpo + } + } + + remove: Transition { + id: removeTrans + ParallelAnimation { + PropertyAnimation { + target: removeTrans.ViewTransition.item + property: "opacity" + to: 0 + duration: 400 + easing.type: Easing.OutCubic + } + } + } + + removeDisplaced: Transition { + PropertyAnimation { + properties: "opacity,y" + duration: 400 + easing.type: Easing.OutExpo + } } - delegate: Bubble { - width: 360 - bubble: model + delegate: BubbleDelegate { + maxCount: model.bubbleCount } HoverHandler {