-
Notifications
You must be signed in to change notification settings - Fork 61
feat: add dynamic position updates for notification bubbles #1500
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -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) { | ||||||||||||||||||||||
|
Comment on lines
122
to
127
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion (performance): Avoid hard-coded debug logging left in production code This
Suggested change
|
||||||||||||||||||||||
| // 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); | ||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion: Factor out shared positioning logic to avoid duplication
Suggested implementation: QRect LayerShellEmulation::computeGeometry(const QSize &size)
{
auto anchors = m_dlayerShellWindow->anchors();
auto screen = m_window->screen();
auto screenRect = screen->geometry();
int x = screenRect.left() + (screenRect.width() - size.width()) / 2;
int y = screenRect.top() + (screenRect.height() - size.height()) / 2;
if (anchors & DLayerShellWindow::AnchorRight) {
// https://doc.qt.io/qt-6/qrect.html#right
x = (screen->geometry().right() + 1 - size.width() - m_dlayerShellWindow->rightMargin());
}
return QRect(x, y, size.width(), size.height());
}
void LayerShellEmulation::onPositionUpdateRequested(int width, int height)
{
const QSize size(width, height);
const QRect rect = computeGeometry(size);
// Keep behavior consistent with other positioning code: move/resize the window.
m_window->setGeometry(rect);
Here are the changes: <file_operations>
|
||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| 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 | ||||||||||||||||||||||
| */ | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,11 +5,12 @@ | |
| #include "bubblepanel.h" | ||
| #include "bubbleitem.h" | ||
| #include "bubblemodel.h" | ||
| #include "dataaccessorproxy.h" | ||
| #include "pluginfactory.h" | ||
| #include <qtimer.h> | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No description provided. |
||
|
|
||
| #include <QTimer> | ||
| #include <QLoggingCategory> | ||
| #include <QQueue> | ||
|
|
||
| #include <appletbridge.h> | ||
|
|
@@ -54,6 +55,7 @@ | |
| 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::onBubbleCountChanged() | ||
| { | ||
| bool isEmpty = m_bubbles->items().isEmpty(); | ||
| setVisible(!isEmpty && enabled()); | ||
|
|
||
| if (isEmpty) { | ||
| QTimer::singleShot(400, this, [this]() { | ||
|
Comment on lines
+117
to
+118
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. issue (bug_risk): Pending hide timer can fire even after bubbles are added Using |
||
| setVisible(false); | ||
| }); | ||
| } else { | ||
| setVisible(!isEmpty && enabled()); | ||
| } | ||
| } | ||
|
|
||
| void BubblePanel::addBubble(qint64 id) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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 | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. question (bug_risk): Clarify relationship between QML height and geometry set from C++
|
||
| 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 | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
这个日志还有用吗?甚至是warning