mirror of
https://invent.kde.org/marcoa/shift-shell.git
synced 2026-04-26 14:23:09 +00:00
VolumeOSD/NotificationPopup: Animation Adjustments
This merge request adjust animations in the VolumeOSD and NotificationPopup to bring consistency and to make it feel better when swiping up a notification.
This commit is contained in:
parent
0f1c0d86e7
commit
d39bdbedb8
4 changed files with 130 additions and 61 deletions
|
|
@ -33,6 +33,9 @@ Item {
|
|||
property real aboveNotificationFullOffset: 0
|
||||
property int aboveNotificationHeight: 0
|
||||
|
||||
// set to true when notification is swiped up by user
|
||||
property bool closedWithSwipe: false
|
||||
|
||||
// the drag offset on the current popup notification - used to position notification when stacked underneath
|
||||
property real currentDragOffset: {
|
||||
let current = popupNotifications.currentPopupIndex == notificationPopup.popupIndex;
|
||||
|
|
@ -257,7 +260,7 @@ Item {
|
|||
if (notificationPopup.popupDrawerOpened && notificationItem.state != "inDrawerClosed" && notificationItem.state != "open") {
|
||||
notificationItem.offset = openOffset;
|
||||
notificationItem.scale = 0.75;
|
||||
notificationItem.opacity = 0.0;
|
||||
notificationItem.popupOpacity = 0.0;
|
||||
}
|
||||
notificationItem.state = "inDrawerClosed";
|
||||
notificationPopup.removeKeyboardFocus();
|
||||
|
|
@ -268,7 +271,7 @@ Item {
|
|||
if (notificationPopup.popupDrawerOpened && notificationItem.state != "open" && notificationItem.state != "inDrawerClosed") {
|
||||
notificationItem.offset = openOffset;
|
||||
notificationItem.scale = 0.75;
|
||||
notificationItem.opacity = 0.0;
|
||||
notificationItem.popupOpacity = 0.0;
|
||||
}
|
||||
notificationItem.state = "open";
|
||||
notificationPopup.removeKeyboardFocus();
|
||||
|
|
@ -353,29 +356,53 @@ Item {
|
|||
|
||||
property real offset: closedOffset
|
||||
property real scale: 1.0
|
||||
property real drawerScale: 1 - Math.max(Math.min(notificationPopup.popupIndex - popupNotifications.currentPopupIndex, 3), 1) * 0.075
|
||||
property real popupOpacity: 1.0 // controls the opacity of the notification popup when outside the popup drawer
|
||||
property real drawerScale: {
|
||||
if (notificationPopup.popupDrawerOpened) {
|
||||
return 0; // when popup drawer is opened, reset scale to 0
|
||||
}
|
||||
let index = notificationPopup.popupIndex - popupNotifications.currentPopupIndex;
|
||||
// clamp the index value to avoid scaling too much with animations
|
||||
let indexClamped = Math.max(Math.min(index, 2), 0);
|
||||
return indexClamped * 0.075;
|
||||
}
|
||||
property real drawerAddedOffset: {
|
||||
if (notificationPopup.popupDrawerOpened) {
|
||||
return 0; // when popup drawer is opened, reset any added height to 0
|
||||
}
|
||||
let index = notificationPopup.popupIndex - popupNotifications.currentPopupIndex;
|
||||
// clamp the index value to avoid moving too much with animations
|
||||
let indexClamped = Math.max(Math.min(index, 2), -1);
|
||||
return Kirigami.Units.gridUnit * 0.5 * indexClamped;
|
||||
}
|
||||
property real drawerOpacity: {
|
||||
let index = notificationPopup.popupIndex - popupNotifications.currentPopupIndex;
|
||||
if (index > 2 && !notificationPopup.popupDrawerOpened) {
|
||||
return 0; // make this popup invisible if it is below 3 other popups
|
||||
} else {
|
||||
return 1; // when popup drawer is opened, reset opacity to 1
|
||||
}
|
||||
}
|
||||
Behavior on drawerScale {
|
||||
NumberAnimation {
|
||||
duration: Kirigami.Units.veryLongDuration
|
||||
easing.type: Easing.OutExpo
|
||||
duration: Kirigami.Units.veryLongDuration * 1.25
|
||||
easing.type: Easing.OutQuint
|
||||
}
|
||||
}
|
||||
property real drawerAddedOffset: Kirigami.Units.gridUnit * 0.5 * Math.max(Math.min(notificationPopup.popupIndex - popupNotifications.currentPopupIndex, 3), 1)
|
||||
Behavior on drawerAddedOffset {
|
||||
NumberAnimation {
|
||||
duration: Kirigami.Units.veryLongDuration
|
||||
easing.type: Easing.OutExpo
|
||||
duration: Kirigami.Units.veryLongDuration * 1.25
|
||||
easing.type: Easing.OutQuint
|
||||
}
|
||||
}
|
||||
property real drawerOpacity: (Math.max(Math.min(notificationPopup.popupIndex - popupNotifications.currentPopupIndex, 3), 1) > 2) ? 0 : 1
|
||||
Behavior on drawerOpacity {
|
||||
NumberAnimation {
|
||||
duration: Kirigami.Units.veryLongDuration
|
||||
easing.type: Easing.OutExpo
|
||||
duration: Kirigami.Units.veryLongDuration * 1.25
|
||||
easing.type: Easing.OutQuint
|
||||
}
|
||||
}
|
||||
|
||||
opacity: 1.0
|
||||
opacity: Math.min(popupOpacity, drawerOpacity)
|
||||
|
||||
state: ""
|
||||
|
||||
|
|
@ -389,7 +416,7 @@ Item {
|
|||
target: notificationItem; scale: 1.0
|
||||
}
|
||||
PropertyChanges {
|
||||
target: notificationItem; opacity: 1.0
|
||||
target: notificationItem; popupOpacity: 1.0
|
||||
}
|
||||
},
|
||||
State {
|
||||
|
|
@ -401,7 +428,7 @@ Item {
|
|||
target: notificationItem; scale: 1.0
|
||||
}
|
||||
PropertyChanges {
|
||||
target: notificationItem; opacity: 1.0
|
||||
target: notificationItem; popupOpacity: 1.0
|
||||
}
|
||||
},
|
||||
State {
|
||||
|
|
@ -413,47 +440,88 @@ Item {
|
|||
target: notificationItem; scale: 0.75
|
||||
}
|
||||
PropertyChanges {
|
||||
target: notificationItem; opacity: 0.0
|
||||
target: notificationItem; popupOpacity: 0.0
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "inDrawerClosed"
|
||||
PropertyChanges {
|
||||
target: notificationItem; offset: notificationPopup.openOffset + (notificationPopup.popupDrawerOpened ? 0 : drawerAddedOffset)
|
||||
target: notificationItem; offset: notificationPopup.openOffset
|
||||
}
|
||||
PropertyChanges {
|
||||
target: notificationItem; scale: notificationPopup.popupDrawerOpened ? 1 : drawerScale
|
||||
target: notificationItem; scale: 1
|
||||
}
|
||||
PropertyChanges {
|
||||
target: notificationItem; opacity: notificationPopup.popupDrawerOpened ? 1 : drawerOpacity
|
||||
target: notificationItem; popupOpacity: 1
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
readonly property int notificationEasing: {
|
||||
// check whether the popup is the current one or above it
|
||||
let topPopup = popupNotifications.currentPopupIndex >= notificationPopup.popupIndex;
|
||||
// check whether the popup has any popups below it
|
||||
let popupBelow = notificationPopup.popupCount - notificationPopup.popupIndex > 1;
|
||||
let popupOpening = notificationItem.state == "open" || notificationItem.state == "inDrawerClosed";
|
||||
let popupClosing = notificationItem.state == "closeWithMove" || notificationItem.state == "closeWithScale"
|
||||
if (notificationPopup.closedWithSwipe || (topPopup && popupClosing && popupBelow)) {
|
||||
// set the easing type to linear when closed with a swipe or if a popup is below when closing
|
||||
// as to make sure the popup feels like it is keeping it's momentum
|
||||
return Easing.Linear;
|
||||
} else if (popupOpening) {
|
||||
// set the easing type to 'Out' when opening so the popup will have a gentle landing
|
||||
return Easing.OutQuint;
|
||||
} else {
|
||||
// if above conditions fail, set the easing type to 'In' so the popup will build up speed for it's exit
|
||||
return Easing.InQuint;
|
||||
}
|
||||
}
|
||||
|
||||
readonly property real notificationDuration: {
|
||||
// check whether the popup is the current one or above it
|
||||
let topPopup = popupNotifications.currentPopupIndex >= notificationPopup.popupIndex;
|
||||
// check whether the popup has any popups below it
|
||||
let popupBelow = notificationPopup.popupCount - notificationPopup.popupIndex > 1;
|
||||
let popupClosing = notificationItem.state == "closeWithMove" || notificationItem.state == "closeWithScale"
|
||||
if (notificationPopup.closedWithSwipe || (topPopup && popupClosing && popupBelow)) {
|
||||
// make sure the speed it faster when closed with a swipe or if there is a popup below when closing
|
||||
// as to make sure the speed feels comparable with the easing type is set to linear
|
||||
return Kirigami.Units.veryLongDuration * 0.5;
|
||||
} else {
|
||||
return Kirigami.Units.veryLongDuration * 1.25;
|
||||
}
|
||||
}
|
||||
|
||||
transitions: Transition {
|
||||
SequentialAnimation {
|
||||
ParallelAnimation {
|
||||
PropertyAnimation {
|
||||
properties: "offset"; easing.type: Easing.OutExpo; duration: Kirigami.Units.veryLongDuration * 1.5
|
||||
properties: "offset"
|
||||
easing.type: notificationItem.notificationEasing
|
||||
duration: notificationItem.notificationDuration
|
||||
}
|
||||
PropertyAnimation {
|
||||
properties: "scale"; easing.type: Easing.OutExpo; duration: Kirigami.Units.veryLongDuration * 1.5
|
||||
properties: "scale"
|
||||
easing.type: notificationItem.notificationEasing
|
||||
duration: notificationItem.notificationDuration
|
||||
}
|
||||
PropertyAnimation {
|
||||
properties: "opacity"; easing.type: Easing.OutExpo; duration: Kirigami.Units.veryLongDuration * 1.5
|
||||
properties: "popupOpacity"
|
||||
easing.type: notificationItem.notificationEasing
|
||||
duration: notificationItem.notificationDuration
|
||||
}
|
||||
}
|
||||
ScriptAction {
|
||||
script: {
|
||||
if (notificationItem.state == "open") {
|
||||
preventDismissTimeout = false;
|
||||
notificationPopup.preventDismissTimeout = false;
|
||||
notificationPopup.updateTouchArea();
|
||||
} else if (notificationItem.state == "closeWithMove" || notificationItem.state == "closeWithScale") {
|
||||
preventDismissTimeout = true;
|
||||
if (dismissTimeout) {
|
||||
notificationPopup.preventDismissTimeout = true;
|
||||
if (notificationPopup.dismissTimeout) {
|
||||
notificationPopup.dismissClicked();
|
||||
} else if (!isActionDrawerOpen) {
|
||||
notificationPopup.expired();
|
||||
} else if (!notificationPopup.isActionDrawerOpen) {
|
||||
notificationPopup.expired();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -465,15 +533,15 @@ Item {
|
|||
Scale {
|
||||
origin.x: Math.round(notificationPopup.popupWidth / 2)
|
||||
origin.y: notificationPopup.scaleOriginY
|
||||
xScale: notificationItem.scale
|
||||
yScale: notificationItem.scale
|
||||
xScale: notificationItem.scale - notificationItem.drawerScale
|
||||
yScale: notificationItem.scale - notificationItem.drawerScale
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
transform: [
|
||||
Translate {
|
||||
y: notificationItem.offset + notificationPopup.fullOffsetAn + notificationPopup.dragOffset + notificationPopup.currentDragOffset
|
||||
y: notificationItem.offset + notificationPopup.fullOffsetAn + notificationPopup.dragOffset + notificationPopup.currentDragOffset + notificationItem.drawerAddedOffset
|
||||
}
|
||||
]
|
||||
|
||||
|
|
@ -506,12 +574,14 @@ Item {
|
|||
startActive = active;
|
||||
notificationPopup.preventDismissTimeout = true;
|
||||
if (!active && !(notificationItem.state == "closeWithScale" || notificationItem.state == "closeWithMove")) {
|
||||
dragOffsetAn.running = true;
|
||||
if ((lastOffset - notificationPopup.dragOffset > 1.0 && notificationPopup.dragOffset < 0) || (-(notificationPopup.openOffset - notificationPopup.closedOffset) / 4 > notificationPopup.dragOffset)) {
|
||||
// this code is called when the notifition is swiped or draged to the top.
|
||||
notificationPopup.closedWithSwipe = true;
|
||||
notificationPopup.closePopup(popupIndex);
|
||||
return;
|
||||
} else if (notificationPopup.dragOffset - lastOffset > 1.0 || Kirigami.Units.gridUnit * 3 < notificationPopup.dragOffset) {
|
||||
}
|
||||
dragOffsetAn.running = true;
|
||||
if (notificationPopup.dragOffset - lastOffset > 1.0 || Kirigami.Units.gridUnit * 3 < notificationPopup.dragOffset) {
|
||||
// this code is called when the notifition is swiped or draged down.
|
||||
}
|
||||
notificationPopup.preventDismissTimeout = (keyboardInteractivity == LayerShell.Window.KeyboardInteractivityOnDemand);
|
||||
|
|
|
|||
|
|
@ -74,6 +74,14 @@ Window {
|
|||
value: popupDrawerOpened
|
||||
}
|
||||
|
||||
// hide on timeout to give time to finish animations
|
||||
Timer {
|
||||
id: hideTimeout
|
||||
interval: Kirigami.Units.veryLongDuration * 1.5
|
||||
repeat: false
|
||||
onTriggered: if (notifications.count == 0) notificationPopupManager.visible = false;
|
||||
}
|
||||
|
||||
// Update the window touch region to encapsulate the notification area or the whole screen depending on the 'popupDrawerOpened' state
|
||||
function updateTouchArea() {
|
||||
ShellUtil.setInputTransparent(notificationPopupManager, false);
|
||||
|
|
@ -166,7 +174,7 @@ Window {
|
|||
onCountChanged: {
|
||||
if (count == 0) {
|
||||
ShellUtil.setInputTransparent(notificationPopupManager, true);
|
||||
notificationPopupManager.visible = false;
|
||||
hideTimeout.restart();
|
||||
notificationPopupManager.popupDrawerOpened = false;
|
||||
fullHeight = 0;
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -23,17 +23,16 @@ Window {
|
|||
id: window
|
||||
|
||||
width: osd.width + 6
|
||||
height: cards.implicitHeight + 6
|
||||
height: cards.implicitHeight + 6 + cards.openOffset
|
||||
|
||||
onWidthChanged: if (visible) window.updateTouchRegion()
|
||||
|
||||
visible: false
|
||||
|
||||
readonly property real offsetMargins: Math.max(cards.offset, 0)
|
||||
|
||||
LayerShell.Window.scope: "overlay"
|
||||
LayerShell.Window.anchors: LayerShell.Window.AnchorTop
|
||||
LayerShell.Window.layer: LayerShell.Window.LayerOverlay
|
||||
LayerShell.Window.exclusionZone: -1
|
||||
LayerShell.Window.margins.top: offsetMargins
|
||||
LayerShell.Window.keyboardInteractivity: LayerShell.Window.KeyboardInteractivityNone
|
||||
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.View
|
||||
|
|
@ -51,10 +50,11 @@ Window {
|
|||
}
|
||||
|
||||
function open() {
|
||||
// set window input transparency to allow touches to pass through while the opening animation is playing
|
||||
ShellUtil.setInputTransparent(window, true);
|
||||
|
||||
window.visible = true;
|
||||
cards.state = "open";
|
||||
// set window input transparency to accept touches
|
||||
ShellUtil.setInputTransparent(window, false);
|
||||
}
|
||||
|
||||
function close() {
|
||||
|
|
@ -63,12 +63,16 @@ Window {
|
|||
ShellUtil.setInputTransparent(window, true);
|
||||
}
|
||||
|
||||
function updateTouchRegion() {
|
||||
ShellUtil.setInputRegion(window, Qt.rect(0, cards.openOffset, window.width, cards.implicitHeight + 6));
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: hideTimer
|
||||
interval: 2000
|
||||
running: false
|
||||
onTriggered: {
|
||||
window.close();
|
||||
window.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -87,7 +91,6 @@ Window {
|
|||
readonly property real closedOffset: -(cards.implicitHeight + Kirigami.Units.smallSpacing)
|
||||
readonly property real openOffset: Kirigami.Units.gridUnit + Kirigami.Units.smallSpacing * 3
|
||||
property real offset: closedOffset
|
||||
property real scale: 0.95
|
||||
|
||||
state: "closed"
|
||||
|
||||
|
|
@ -97,18 +100,12 @@ Window {
|
|||
PropertyChanges {
|
||||
target: cards; offset: openOffset
|
||||
}
|
||||
PropertyChanges {
|
||||
target: cards; scale: 1.0
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "closed"
|
||||
PropertyChanges {
|
||||
target: cards; offset: closedOffset
|
||||
}
|
||||
PropertyChanges {
|
||||
target: cards; scale: 0.95
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
|
|
@ -116,16 +113,16 @@ Window {
|
|||
SequentialAnimation {
|
||||
ParallelAnimation {
|
||||
PropertyAnimation {
|
||||
properties: "offset"; easing.type: Easing.OutExpo; duration: Kirigami.Units.veryLongDuration * 1.25
|
||||
}
|
||||
PropertyAnimation {
|
||||
properties: "scale"; easing.type: Easing.OutExpo; duration: Kirigami.Units.veryLongDuration * 1.25
|
||||
properties: "offset"; easing.type: cards.state == "open" ? Easing.OutQuint : Easing.InQuint; duration: Kirigami.Units.veryLongDuration * 1.25
|
||||
}
|
||||
}
|
||||
ScriptAction {
|
||||
script: {
|
||||
if (cards.state == "open") {
|
||||
hideTimer.restart();
|
||||
// set window input transparency to accept touches
|
||||
ShellUtil.setInputTransparent(window, false);
|
||||
window.updateTouchRegion();
|
||||
} else {
|
||||
hideTimer.stop();
|
||||
window.visible = false;
|
||||
|
|
@ -142,13 +139,7 @@ Window {
|
|||
|
||||
transform: [
|
||||
Translate {
|
||||
y: cards.offset - window.offsetMargins + 1
|
||||
},
|
||||
Scale {
|
||||
origin.x: Math.round(width / 2)
|
||||
origin.y: Math.round(height / 2)
|
||||
xScale: cards.scale
|
||||
yScale: cards.scale
|
||||
y: cards.offset + 1
|
||||
}
|
||||
]
|
||||
|
||||
|
|
|
|||
|
|
@ -121,16 +121,16 @@ Window {
|
|||
SequentialAnimation {
|
||||
ParallelAnimation {
|
||||
PropertyAnimation {
|
||||
properties: "offset"; easing.type: Easing.OutExpo; duration: Kirigami.Units.veryLongDuration * 1.25
|
||||
properties: "offset"; easing.type: Easing.OutQuint; duration: Kirigami.Units.veryLongDuration * 1.25
|
||||
}
|
||||
PropertyAnimation {
|
||||
properties: "scale"; easing.type: Easing.OutExpo; duration: Kirigami.Units.veryLongDuration * 1.25
|
||||
properties: "scale"; easing.type: Easing.OutQuint; duration: Kirigami.Units.veryLongDuration * 1.25
|
||||
}
|
||||
PropertyAnimation {
|
||||
properties: "opacity"; easing.type: Easing.OutExpo; duration: Kirigami.Units.veryLongDuration * 1.25
|
||||
properties: "opacity"; easing.type: Easing.OutQuint; duration: Kirigami.Units.veryLongDuration * 1.25
|
||||
}
|
||||
PropertyAnimation {
|
||||
properties: "color"; easing.type: Easing.OutExpo; duration: Kirigami.Units.veryLongDuration * 1.25
|
||||
properties: "color"; easing.type: Easing.OutQuint; duration: Kirigami.Units.veryLongDuration * 1.25
|
||||
}
|
||||
}
|
||||
ScriptAction {
|
||||
|
|
|
|||
Loading…
Reference in a new issue