Move shell and KWin surfaces to shared motion

Apply Motion tokens to startup, applet configuration, lockscreen controls, logout and splash transitions, KWin effects, and the mobile task switcher. Preserve timing-specific behavior where it drives runtime state.
This commit is contained in:
Marco Allegretti 2026-05-21 11:14:42 +02:00
parent 88a4f5883b
commit 753909a6ce
17 changed files with 181 additions and 132 deletions

View file

@ -7,6 +7,7 @@ import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15 import QtQuick.Layouts 1.15
import org.kde.kirigami as Kirigami import org.kde.kirigami as Kirigami
import org.kde.plasma.private.mobileshell as MobileShell
import org.kde.plasma.mobileinitialstart.initialstart import org.kde.plasma.mobileinitialstart.initialstart
import org.kde.plasma.mobileinitialstart.prepare 1.0 as Prepare import org.kde.plasma.mobileinitialstart.prepare 1.0 as Prepare
@ -14,9 +15,13 @@ import org.kde.plasma.mobileinitialstart.prepare 1.0 as Prepare
Item { Item {
id: root id: root
readonly property real scaleStart: 1.4 readonly property real scaleStart: 1.16
readonly property real scaleLanding: 1.2 readonly property real scaleLanding: 1.08
readonly property real scaleSteps: 1 readonly property real scaleSteps: 1
readonly property int initialContentFadeDuration: MobileShell.Motion.duration(MobileShell.Motion.EffectsSlow) * 2
readonly property int returnContentFadeDuration: MobileShell.Motion.duration(MobileShell.Motion.EffectsDefault)
readonly property int imageFadeDuration: MobileShell.Motion.duration(MobileShell.Motion.EffectsSlow)
readonly property int imageScaleDuration: MobileShell.Motion.duration(MobileShell.Motion.SpatialVerySlow)
signal requestNextPage() signal requestNextPage()
@ -27,14 +32,15 @@ Item {
} }
property real contentOpacity: 0 property real contentOpacity: 0
NumberAnimation on contentOpacity { MobileShell.MotionNumberAnimation on contentOpacity {
id: contentOpacityAnim id: contentOpacityAnim
type: MobileShell.Motion.EffectsSlow
running: true running: true
duration: 1000 duration: root.initialContentFadeDuration
to: 1 to: 1
// shorten animation after initial run // shorten animation after initial run
onFinished: duration = 200 onFinished: duration = root.returnContentFadeDuration
} }
Image { Image {
@ -62,11 +68,11 @@ Item {
opacity: 0 opacity: 0
NumberAnimation on opacity { MobileShell.MotionNumberAnimation on opacity {
type: MobileShell.Motion.EffectsSlow
running: true running: true
duration: 400 duration: root.imageFadeDuration
to: 1 to: 1
easing.type: Easing.InOutQuad
} }
// zoom animation // zoom animation
@ -74,9 +80,9 @@ Item {
Component.onCompleted: scale = scaleLanding Component.onCompleted: scale = scaleLanding
Behavior on scale { Behavior on scale {
NumberAnimation { MobileShell.MotionNumberAnimation {
duration: 2000 type: MobileShell.Motion.SpatialVerySlow
easing.type: Easing.OutExpo duration: root.imageScaleDuration
} }
} }

View file

@ -3,6 +3,7 @@
import QtQuick import QtQuick
import org.kde.kwin.decoration import org.kde.kwin.decoration
import org.kde.plasma.private.mobileshell as MobileShell
Decoration { Decoration {
id: root id: root
@ -19,6 +20,7 @@ Decoration {
readonly property int btnSpacing: 8 readonly property int btnSpacing: 8
readonly property int btnSideMargin: 12 readonly property int btnSideMargin: 12
readonly property int cornerRadius: decoration.client.maximized ? 0 : 8 readonly property int cornerRadius: decoration.client.maximized ? 0 : 8
readonly property int shortAnimationDuration: MobileShell.Motion.duration(MobileShell.Motion.EffectsFast)
Component.onCompleted: { Component.onCompleted: {
borders.top = barHeight; borders.top = barHeight;
@ -55,7 +57,7 @@ Decoration {
height: root.barHeight height: root.barHeight
radius: root.cornerRadius radius: root.cornerRadius
color: decoration.client.active ? root.activeBar : root.inactiveBar color: decoration.client.active ? root.activeBar : root.inactiveBar
Behavior on color { ColorAnimation { duration: 120 } } Behavior on color { MobileShell.MotionColorAnimation { type: MobileShell.Motion.EffectsFast; duration: root.shortAnimationDuration } }
// Square off bottom half only top corners are rounded // Square off bottom half only top corners are rounded
Rectangle { Rectangle {
@ -96,7 +98,7 @@ Decoration {
elide: Text.ElideMiddle elide: Text.ElideMiddle
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
renderType: Text.NativeRendering renderType: Text.NativeRendering
Behavior on color { ColorAnimation { duration: 120 } } Behavior on color { MobileShell.MotionColorAnimation { type: MobileShell.Motion.EffectsFast; duration: root.shortAnimationDuration } }
} }
Row { Row {
@ -195,7 +197,7 @@ Decoration {
color: parent.pressed ? Qt.darker(parent.hoverColor, 1.3) color: parent.pressed ? Qt.darker(parent.hoverColor, 1.3)
: parent.hovered ? parent.hoverColor : parent.hovered ? parent.hoverColor
: parent.normalColor : parent.normalColor
Behavior on color { ColorAnimation { duration: 100 } } Behavior on color { MobileShell.MotionColorAnimation { type: MobileShell.Motion.EffectsFast; duration: root.shortAnimationDuration } }
Text { Text {
anchors.centerIn: parent anchors.centerIn: parent
@ -204,7 +206,7 @@ Decoration {
font.pixelSize: Math.round(parent.width * 0.66) font.pixelSize: Math.round(parent.width * 0.66)
font.weight: Font.Bold font.weight: Font.Bold
opacity: 1.0 opacity: 1.0
Behavior on opacity { NumberAnimation { duration: 100 } } Behavior on opacity { MobileShell.MotionNumberAnimation { type: MobileShell.Motion.EffectsFast; duration: root.shortAnimationDuration } }
} }
} }
} }

View file

@ -14,6 +14,7 @@
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import org.kde.kwin as KWinComponents import org.kde.kwin as KWinComponents
import org.kde.plasma.private.mobileshell as MobileShell
import org.kde.plasma.private.mobileshell.shellsettingsplugin as ShellSettings import org.kde.plasma.private.mobileshell.shellsettingsplugin as ShellSettings
KWinComponents.SceneEffect { KWinComponents.SceneEffect {
@ -38,6 +39,7 @@ KWinComponents.SceneEffect {
readonly property int panelScreenMargin: 8 readonly property int panelScreenMargin: 8
readonly property int panelCursorGap: 12 readonly property int panelCursorGap: 12
readonly property int panelCursorRightBias: 34 readonly property int panelCursorRightBias: 34
readonly property int hoverAnimationDuration: MobileShell.Motion.duration(MobileShell.Motion.Press)
property var hoverWindowId: null property var hoverWindowId: null
property int hoverTicks: 0 property int hoverTicks: 0
property string hoverWindowStateKey: "" property string hoverWindowStateKey: ""
@ -649,7 +651,7 @@ KWinComponents.SceneEffect {
property bool hovered: false property bool hovered: false
Behavior on color { ColorAnimation { duration: 80 } } Behavior on color { MobileShell.MotionColorAnimation { type: MobileShell.Motion.Press; duration: effect.hoverAnimationDuration } }
Rectangle { Rectangle {
id: previewFrame id: previewFrame

View file

@ -3,6 +3,7 @@
import QtQuick import QtQuick
import org.kde.kwin as KWinComponents import org.kde.kwin as KWinComponents
import org.kde.plasma.private.mobileshell as MobileShell
import org.kde.plasma.private.mobileshell.shellsettingsplugin as ShellSettings import org.kde.plasma.private.mobileshell.shellsettingsplugin as ShellSettings
KWinComponents.SceneEffect { KWinComponents.SceneEffect {
@ -12,8 +13,8 @@ KWinComponents.SceneEffect {
readonly property int outerGap: 8 readonly property int outerGap: 8
readonly property int floatEscapeMargin: 32 readonly property int floatEscapeMargin: 32
readonly property int previewAnimationDuration: 180 readonly property int previewAnimationDuration: MobileShell.Motion.duration(MobileShell.Motion.SpatialDefault)
readonly property int previewFadeDuration: 110 readonly property int previewFadeDuration: MobileShell.Motion.duration(MobileShell.Motion.EffectsFast)
property var dragConnectedWindows: ({}) property var dragConnectedWindows: ({})
property var draggingWindow: null property var draggingWindow: null
@ -290,28 +291,28 @@ KWinComponents.SceneEffect {
Behavior on x { Behavior on x {
enabled: effect.animatePreview enabled: effect.animatePreview
NumberAnimation { duration: effect.previewAnimationDuration; easing.type: Easing.OutCubic } MobileShell.MotionNumberAnimation { type: MobileShell.Motion.SpatialDefault; duration: effect.previewAnimationDuration }
} }
Behavior on y { Behavior on y {
enabled: effect.animatePreview enabled: effect.animatePreview
NumberAnimation { duration: effect.previewAnimationDuration; easing.type: Easing.OutCubic } MobileShell.MotionNumberAnimation { type: MobileShell.Motion.SpatialDefault; duration: effect.previewAnimationDuration }
} }
Behavior on width { Behavior on width {
enabled: effect.animatePreview enabled: effect.animatePreview
NumberAnimation { duration: effect.previewAnimationDuration; easing.type: Easing.OutCubic } MobileShell.MotionNumberAnimation { type: MobileShell.Motion.SpatialDefault; duration: effect.previewAnimationDuration }
} }
Behavior on height { Behavior on height {
enabled: effect.animatePreview enabled: effect.animatePreview
NumberAnimation { duration: effect.previewAnimationDuration; easing.type: Easing.OutCubic } MobileShell.MotionNumberAnimation { type: MobileShell.Motion.SpatialDefault; duration: effect.previewAnimationDuration }
} }
Behavior on opacity { Behavior on opacity {
NumberAnimation { duration: effect.previewFadeDuration; easing.type: Easing.InOutQuad } MobileShell.MotionNumberAnimation { type: MobileShell.Motion.EffectsFast; duration: effect.previewFadeDuration }
} }
Behavior on color { Behavior on color {
ColorAnimation { duration: effect.previewFadeDuration; easing.type: Easing.OutCubic } MobileShell.MotionColorAnimation { type: MobileShell.Motion.EffectsFast; duration: effect.previewFadeDuration }
} }
Behavior on border.color { Behavior on border.color {
ColorAnimation { duration: effect.previewFadeDuration; easing.type: Easing.OutCubic } MobileShell.MotionColorAnimation { type: MobileShell.Motion.EffectsFast; duration: effect.previewFadeDuration }
} }
Rectangle { Rectangle {

View file

@ -12,6 +12,7 @@ import org.kde.kirigami as Kirigami
import org.kde.plasma.core as PlasmaCore import org.kde.plasma.core as PlasmaCore
import org.kde.plasma.components 3.0 as PlasmaComponents import org.kde.plasma.components 3.0 as PlasmaComponents
import org.kde.kwin 3.0 as KWinComponents import org.kde.kwin 3.0 as KWinComponents
import org.kde.plasma.private.mobileshell as MobileShell
import org.kde.plasma.private.mobileshell.shellsettingsplugin as ShellSettings import org.kde.plasma.private.mobileshell.shellsettingsplugin as ShellSettings
Item { Item {
@ -30,6 +31,8 @@ Item {
readonly property real dragOffset: -control.y readonly property real dragOffset: -control.y
readonly property int currentIndex: model.index readonly property int currentIndex: model.index
readonly property int shortAnimationDuration: MobileShell.Motion.duration(MobileShell.Motion.EffectsFast)
readonly property int longAnimationDuration: MobileShell.Motion.duration(MobileShell.Motion.SpatialDefault)
// whether this task is being interacted with // whether this task is being interacted with
readonly property bool interactingActive: control.pressed && control.passedDragThreshold readonly property bool interactingActive: control.pressed && control.passedDragThreshold
@ -137,11 +140,11 @@ Item {
} }
} }
NumberAnimation on y { MobileShell.MotionNumberAnimation on y {
id: yAnimator id: yAnimator
type: MobileShell.Motion.Standard
running: !control.pressed running: !control.pressed
duration: Kirigami.Units.longDuration duration: delegate.longAnimationDuration
easing.type: Easing.InOutQuad
to: 0 to: 0
onFinished: { onFinished: {
if (to != 0) { // close app if (to != 0) { // close app
@ -172,7 +175,7 @@ Item {
opacity: delegate.showHeader ? 1 : 0 opacity: delegate.showHeader ? 1 : 0
Behavior on opacity { Behavior on opacity {
NumberAnimation { duration: Kirigami.Units.shortDuration } MobileShell.MotionNumberAnimation { type: MobileShell.Motion.EffectsFast; duration: delegate.shortAnimationDuration }
} }
Kirigami.Icon { Kirigami.Icon {
@ -216,12 +219,9 @@ Item {
clip: true clip: true
// scale animation on press // scale animation on press
property real zoomScale: control.pressed ? 0.95 : 1 property real zoomScale: control.pressed ? MobileShell.Motion.previewPressScale : 1
Behavior on zoomScale { Behavior on zoomScale {
NumberAnimation { MobileShell.MotionNumberAnimation { type: MobileShell.Motion.SpatialDefault }
duration: Kirigami.Units.longDuration
easing.type: Easing.OutExpo
}
} }
transform: Scale { transform: Scale {

View file

@ -9,6 +9,7 @@ import QtQuick.Layouts
import org.kde.kirigami as Kirigami import org.kde.kirigami as Kirigami
import org.kde.plasma.components 3.0 as PlasmaComponents import org.kde.plasma.components 3.0 as PlasmaComponents
import org.kde.plasma.private.mobileshell as MobileShell
import org.kde.kwin 3.0 as KWinComponents import org.kde.kwin 3.0 as KWinComponents
@ -105,7 +106,7 @@ MouseArea {
// dynamic task offset animation duration based off of the touch position and task scale // dynamic task offset animation duration based off of the touch position and task scale
function dynamicDuration(left = true): int { function dynamicDuration(left = true): int {
// if the close animation is running, use the standard long duration time for consistency // if the close animation is running, use the standard long duration time for consistency
let duration = Kirigami.Units.longDuration * 1.75 let duration = root.taskSwitcherHelpers.longAnimationDuration * 1.75
if (!taskSwitcherHelpers.closeAnim.running && taskSwitcherHelpers.notHomeScreenState && taskSwitcherHelpers.gestureState != TaskSwitcherHelpers.GestureStates.HorizontalSwipe && !taskSwitcherHelpers.isInTaskScrubMode) { if (!taskSwitcherHelpers.closeAnim.running && taskSwitcherHelpers.notHomeScreenState && taskSwitcherHelpers.gestureState != TaskSwitcherHelpers.GestureStates.HorizontalSwipe && !taskSwitcherHelpers.isInTaskScrubMode) {
// max out the scale at 1 so it is not too fast when opening the task drawer with the button // max out the scale at 1 so it is not too fast when opening the task drawer with the button
let taskScale = Math.min(taskSwitcherHelpers.currentScale, 1) let taskScale = Math.min(taskSwitcherHelpers.currentScale, 1)
@ -170,16 +171,17 @@ MouseArea {
// this is the x-position with respect to the list // this is the x-position with respect to the list
property real listX: root.taskSwitcherHelpers.xPositionFromTaskIndex(currentIndex) property real listX: root.taskSwitcherHelpers.xPositionFromTaskIndex(currentIndex)
Behavior on listX { Behavior on listX {
NumberAnimation { MobileShell.MotionNumberAnimation {
duration: Kirigami.Units.longDuration type: MobileShell.Motion.Standard
easing.type: Easing.InOutQuad duration: root.taskSwitcherHelpers.longAnimationDuration
} }
} }
// the animated task offset value (always will be 0 if it is the current task in the task drawer) // the animated task offset value (always will be 0 if it is the current task in the task drawer)
property real taskOffsetNormalized: (root.baseTaskOffset * ((root.taskSwitcherHelpers.taskDrawerOpened && isCurrentTask) ? 0 : 1)) property real taskOffsetNormalized: (root.baseTaskOffset * ((root.taskSwitcherHelpers.taskDrawerOpened && isCurrentTask) ? 0 : 1))
Behavior on taskOffsetNormalized { Behavior on taskOffsetNormalized {
NumberAnimation { MobileShell.MotionNumberAnimation {
type: MobileShell.Motion.Standard
duration: root.taskSwitcherHelpers.currentDisplayTask > task.currentIndex ? root.taskOffsetDurationRight : root.taskOffsetDurationLeft duration: root.taskSwitcherHelpers.currentDisplayTask > task.currentIndex ? root.taskOffsetDurationRight : root.taskOffsetDurationLeft
easing.type: root.taskOffsetEasing easing.type: root.taskOffsetEasing
easing.overshoot: 0.85 easing.overshoot: 0.85

View file

@ -38,6 +38,9 @@ FocusScope {
readonly property real bottomMargin: ShellSettings.Settings.autoHidePanelsEnabled ? 0 : navBottomMargin readonly property real bottomMargin: ShellSettings.Settings.autoHidePanelsEnabled ? 0 : navBottomMargin
readonly property real leftMargin: 0 readonly property real leftMargin: 0
readonly property real rightMargin: ShellSettings.Settings.autoHidePanelsEnabled ? 0 : navRightMargin readonly property real rightMargin: ShellSettings.Settings.autoHidePanelsEnabled ? 0 : navRightMargin
readonly property int shortAnimationDuration: MobileShell.Motion.duration(MobileShell.Motion.EffectsFast)
readonly property int veryLongAnimationDuration: MobileShell.Motion.duration(MobileShell.Motion.EffectsSlow)
readonly property real disabledScrubArrowOpacity: 0.38
property var taskSwitcherHelpers: TaskSwitcherHelpers { property var taskSwitcherHelpers: TaskSwitcherHelpers {
taskSwitcher: root taskSwitcher: root
@ -64,7 +67,7 @@ FocusScope {
state.currentTaskIndex -= 1; state.currentTaskIndex -= 1;
// animated at the same speed as the task x position in the TaskList so that the task appears not to move from the perspective of the user. // animated at the same speed as the task x position in the TaskList so that the task appears not to move from the perspective of the user.
taskSwitcherHelpers.animateGoToTaskIndex(state.currentTaskIndex, Kirigami.Units.longDuration, Easing.InOutQuad); taskSwitcherHelpers.animateGoToTaskIndex(state.currentTaskIndex, taskSwitcherHelpers.longAnimationDuration, Easing.InOutQuad);
taskSwitcherHelpers.lastClosedTask = -1; taskSwitcherHelpers.lastClosedTask = -1;
} }
@ -73,10 +76,10 @@ FocusScope {
} else if (tasksCount < oldTasksCount) { } else if (tasksCount < oldTasksCount) {
if (state.currentTaskIndex < 0) { if (state.currentTaskIndex < 0) {
// if the user is on the first task, and it is closed, scroll right // if the user is on the first task, and it is closed, scroll right
taskSwitcherHelpers.animateGoToTaskIndex(0, Kirigami.Units.longDuration); taskSwitcherHelpers.animateGoToTaskIndex(0, taskSwitcherHelpers.longAnimationDuration);
} else if (state.currentTaskIndex >= tasksCount) { } else if (state.currentTaskIndex >= tasksCount) {
// if the user is on the last task, and it is closed, scroll left // if the user is on the last task, and it is closed, scroll left
taskSwitcherHelpers.animateGoToTaskIndex(tasksCount - 1, Kirigami.Units.longDuration); taskSwitcherHelpers.animateGoToTaskIndex(tasksCount - 1, taskSwitcherHelpers.longAnimationDuration);
} }
} }
@ -350,7 +353,7 @@ FocusScope {
if (!root.taskSwitcherHelpers.taskDrawerOpened && unmodifiedYposition < root.taskSwitcherHelpers.openedYPosition) { if (!root.taskSwitcherHelpers.taskDrawerOpened && unmodifiedYposition < root.taskSwitcherHelpers.openedYPosition) {
// if in a app, switch it to the new task when it is under the openedYPosition // if in a app, switch it to the new task when it is under the openedYPosition
taskList.setTaskOffsetValue(0, unmodifiedYposition < root.taskSwitcherHelpers.openedYPosition && root.taskSwitcherHelpers.notHomeScreenState); taskList.setTaskOffsetValue(0, unmodifiedYposition < root.taskSwitcherHelpers.openedYPosition && root.taskSwitcherHelpers.notHomeScreenState);
root.taskSwitcherHelpers.openApp(newIndex, Kirigami.Units.longDuration * 4, Easing.OutExpo); root.taskSwitcherHelpers.openApp(newIndex, root.taskSwitcherHelpers.extendedAnimationDuration, Easing.OutExpo);
} else { } else {
// if already in the task switcher or above the openedYPosition, only change the focus to the new task // if already in the task switcher or above the openedYPosition, only change the focus to the new task
root.taskSwitcherHelpers.animateGoToTaskIndex(newIndex); root.taskSwitcherHelpers.animateGoToTaskIndex(newIndex);
@ -463,8 +466,9 @@ FocusScope {
property real backgroundColorOpacity: 1 property real backgroundColorOpacity: 1
Behavior on backgroundColorOpacity { Behavior on backgroundColorOpacity {
id: backgroundColorOpacityAn id: backgroundColorOpacityAn
NumberAnimation { MobileShell.MotionNumberAnimation {
duration: Kirigami.Units.longDuration type: MobileShell.Motion.SpatialDefault
duration: root.taskSwitcherHelpers.longAnimationDuration
} }
} }
@ -643,12 +647,12 @@ FocusScope {
anchors.bottomMargin: root.bottomMargin anchors.bottomMargin: root.bottomMargin
anchors.topMargin: root.topMargin anchors.topMargin: root.topMargin
NumberAnimation on opacity { MobileShell.MotionNumberAnimation on opacity {
id: closeAnim id: closeAnim
type: MobileShell.Motion.Standard
running: false running: false
to: 0 to: 0
duration: Kirigami.Units.longDuration duration: root.taskSwitcherHelpers.longAnimationDuration
easing.type: Easing.InOutQuad
onFinished: { onFinished: {
closeAllButton.closeRequested = false; closeAllButton.closeRequested = false;
@ -664,7 +668,7 @@ FocusScope {
let baseOpacity = ((root.tasksCount === 0 && !root.taskSwitcherHelpers.currentlyBeingClosed) ? 0.9 : 0); let baseOpacity = ((root.tasksCount === 0 && !root.taskSwitcherHelpers.currentlyBeingClosed) ? 0.9 : 0);
return root.taskSwitcherHelpers.gestureState == TaskSwitcherHelpers.GestureStates.TaskSwitcher ? baseOpacity : 0; return root.taskSwitcherHelpers.gestureState == TaskSwitcherHelpers.GestureStates.TaskSwitcher ? baseOpacity : 0;
} }
Behavior on opacity { NumberAnimation { duration: Kirigami.Units.veryLongDuration } } Behavior on opacity { MobileShell.MotionNumberAnimation { type: MobileShell.Motion.EffectsSlow; duration: root.veryLongAnimationDuration } }
anchors.centerIn: container anchors.centerIn: container
@ -692,7 +696,7 @@ FocusScope {
RowLayout { RowLayout {
id: scrubIconList id: scrubIconList
opacity: root.taskSwitcherHelpers.isInTaskScrubMode ? 1 : 0 opacity: root.taskSwitcherHelpers.isInTaskScrubMode ? 1 : 0
Behavior on opacity { NumberAnimation { duration: Kirigami.Units.longDuration } } Behavior on opacity { MobileShell.MotionNumberAnimation { type: MobileShell.Motion.SpatialDefault; duration: root.taskSwitcherHelpers.longAnimationDuration } }
anchors.bottom: container.bottom anchors.bottom: container.bottom
anchors.right: container.horizontalCenter anchors.right: container.horizontalCenter
@ -704,7 +708,8 @@ FocusScope {
return -offset; return -offset;
} }
Behavior on anchors.rightMargin { Behavior on anchors.rightMargin {
NumberAnimation { MobileShell.MotionNumberAnimation {
type: MobileShell.Motion.SpatialDefault
duration: root.taskSwitcherHelpers.xAnimDuration; duration: root.taskSwitcherHelpers.xAnimDuration;
easing.type: root.taskSwitcherHelpers.xAnimEasingType; easing.type: root.taskSwitcherHelpers.xAnimEasingType;
} }
@ -735,7 +740,7 @@ FocusScope {
RowLayout { RowLayout {
id: scrubIndicator id: scrubIndicator
opacity: root.taskSwitcherHelpers.isInTaskScrubMode ? 1 : 0 opacity: root.taskSwitcherHelpers.isInTaskScrubMode ? 1 : 0
Behavior on opacity { NumberAnimation { duration: 200 } } Behavior on opacity { MobileShell.MotionNumberAnimation { type: MobileShell.Motion.SpatialDefault; duration: root.taskSwitcherHelpers.longAnimationDuration } }
anchors.bottom: container.bottom anchors.bottom: container.bottom
anchors.horizontalCenter: container.horizontalCenter anchors.horizontalCenter: container.horizontalCenter
@ -743,8 +748,8 @@ FocusScope {
Kirigami.Icon { Kirigami.Icon {
id: iconScrubBack id: iconScrubBack
opacity: root.state.currentTaskIndex == 0 ? 0.3 : 1 opacity: root.state.currentTaskIndex == 0 ? root.disabledScrubArrowOpacity : 1
Behavior on opacity { NumberAnimation { duration: Kirigami.Units.longDuration * 2; easing.type: Easing.OutExpo } } Behavior on opacity { MobileShell.MotionNumberAnimation { type: MobileShell.Motion.SpatialExtended; duration: root.taskSwitcherHelpers.taskScrubAnimationDuration } }
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
implicitWidth: Kirigami.Units.iconSizes.medium implicitWidth: Kirigami.Units.iconSizes.medium
implicitHeight: Kirigami.Units.iconSizes.medium implicitHeight: Kirigami.Units.iconSizes.medium
@ -758,8 +763,8 @@ FocusScope {
Kirigami.Icon { Kirigami.Icon {
id: iconScrubFront id: iconScrubFront
opacity: root.state.currentTaskIndex == root.tasksCount - 1 ? 0.3 : 1 opacity: root.state.currentTaskIndex == root.tasksCount - 1 ? root.disabledScrubArrowOpacity : 1
Behavior on opacity { NumberAnimation { duration: Kirigami.Units.longDuration * 2; easing.type: Easing.OutExpo } } Behavior on opacity { MobileShell.MotionNumberAnimation { type: MobileShell.Motion.SpatialExtended; duration: root.taskSwitcherHelpers.taskScrubAnimationDuration } }
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
implicitWidth: Kirigami.Units.iconSizes.medium implicitWidth: Kirigami.Units.iconSizes.medium
implicitHeight: Kirigami.Units.iconSizes.medium implicitHeight: Kirigami.Units.iconSizes.medium
@ -809,7 +814,7 @@ FocusScope {
} }
opacity: (root.taskSwitcherHelpers.currentlyBeingClosed || root.state.gestureInProgress || !root.taskSwitcherHelpers.taskDrawerOpened) ? 0.0 : 1.0 opacity: (root.taskSwitcherHelpers.currentlyBeingClosed || root.state.gestureInProgress || !root.taskSwitcherHelpers.taskDrawerOpened) ? 0.0 : 1.0
Behavior on opacity { NumberAnimation { duration: Kirigami.Units.shortDuration } } Behavior on opacity { MobileShell.MotionNumberAnimation { type: MobileShell.Motion.EffectsFast; duration: root.shortAnimationDuration } }
icon.name: "edit-clear-history" icon.name: "edit-clear-history"
font.bold: true font.bold: true

View file

@ -7,6 +7,7 @@ pragma ComponentBehavior: Bound
import QtQuick 2.15 import QtQuick 2.15
import org.kde.kirigami as Kirigami import org.kde.kirigami as Kirigami
import org.kde.plasma.private.mobileshell as MobileShell
import org.kde.plasma.private.mobileshell.taskswitcherplugin as TaskSwitcherPlugin import org.kde.plasma.private.mobileshell.taskswitcherplugin as TaskSwitcherPlugin
import org.kde.kwin 3.0 as KWinComponents import org.kde.kwin 3.0 as KWinComponents
@ -97,7 +98,13 @@ QtObject {
property bool reachedHeightThreshold: false property bool reachedHeightThreshold: false
// made as variables to keep x anim in task list and task scrub icon list in sync // made as variables to keep x anim in task list and task scrub icon list in sync
property int xAnimDuration: Kirigami.Units.longDuration * 2 readonly property int shortAnimationDuration: MobileShell.Motion.duration(MobileShell.Motion.SpatialFast)
readonly property int longAnimationDuration: MobileShell.Motion.duration(MobileShell.Motion.SpatialDefault)
readonly property int primaryAnimationDuration: MobileShell.Motion.duration(MobileShell.Motion.Emphasized)
readonly property int taskScrubAnimationDuration: MobileShell.Motion.duration(MobileShell.Motion.SpatialExtended)
readonly property int extendedAnimationDuration: MobileShell.Motion.duration(MobileShell.Motion.SpatialVerySlow)
readonly property real closeScaleOut: MobileShell.Motion.closeScaleOut
property int xAnimDuration: taskScrubAnimationDuration
property int xAnimEasingType: Easing.OutExpo property int xAnimEasingType: Easing.OutExpo
// ~~ measurement constants ~~ // ~~ measurement constants ~~
@ -232,12 +239,12 @@ QtObject {
closeFactorAnim.restart(); closeFactorAnim.restart();
} }
function openApp(index: int, duration = Kirigami.Units.shortDuration, horizontalEasing = Easing.OutBack): void { function openApp(index: int, duration = root.shortAnimationDuration, horizontalEasing = Easing.OutCubic): void {
// cancel any opening animations ongoing // cancel any opening animations ongoing
openAnim.stop(); openAnim.stop();
cancelAnimations(); cancelAnimations();
animateGoToTaskIndex(index, duration); animateGoToTaskIndex(index, duration, horizontalEasing);
openAppAnim.restart(); openAppAnim.restart();
KWinComponents.Workspace.activeWindow = taskList.getTaskAt(index).window; KWinComponents.Workspace.activeWindow = taskList.getTaskAt(index).window;
} }
@ -253,8 +260,8 @@ QtObject {
} }
// go to the task index, animated // go to the task index, animated
function animateGoToTaskIndex(index: int, duration = Kirigami.Units.longDuration * 2, easing = Easing.OutExpo): void { function animateGoToTaskIndex(index: int, duration = root.taskScrubAnimationDuration, easing = Easing.OutExpo): void {
xAnimDuration = duration; xAnimDuration = MobileShell.Motion.enabled ? duration : 0;
xAnimEasingType = easing; xAnimEasingType = easing;
xAnim.to = xPositionFromTaskIndex(index) - (gestureState == TaskSwitcherHelpers.GestureStates.HorizontalSwipe && !state.gestureInProgress && notHomeScreenState ? taskSpacing / 2 : 0); xAnim.to = xPositionFromTaskIndex(index) - (gestureState == TaskSwitcherHelpers.GestureStates.HorizontalSwipe && !state.gestureInProgress && notHomeScreenState ? taskSpacing / 2 : 0);
xAnim.restart(); xAnim.restart();
@ -309,19 +316,20 @@ QtObject {
// ~~ property animators ~~ // ~~ property animators ~~
property var xAnim: NumberAnimation { property var xAnim: MobileShell.MotionNumberAnimation {
target: root.state target: root.state
property: "xPosition" property: "xPosition"
type: MobileShell.Motion.SpatialDefault
duration: root.xAnimDuration duration: root.xAnimDuration
easing.type: root.xAnimEasingType easing.type: root.xAnimEasingType
} }
property var openAnim: NumberAnimation { property var openAnim: MobileShell.MotionNumberAnimation {
target: root.state target: root.state
property: "yPosition" property: "yPosition"
to: root.openedYPosition to: root.openedYPosition
duration: 250 type: MobileShell.Motion.EmphasizedDecel
easing.type: Easing.OutQuart duration: root.primaryAnimationDuration
onFinished: { onFinished: {
if (!root.isInTaskScrubMode) { if (!root.isInTaskScrubMode) {
@ -330,15 +338,13 @@ QtObject {
} }
} }
// TODO: This animation should ideally be replaced by some // Mirror the opening deceleration with an accelerating exit, without overshooting the gesture path.
// speed tracking to track finger movement better. Until then property var closeAnim: MobileShell.MotionNumberAnimation {
// InBack at least pretends to go in the finger move direction
property var closeAnim: NumberAnimation {
target: root.state target: root.state
property: "yPosition" property: "yPosition"
to: 0 to: 0
duration: Kirigami.Units.longDuration type: MobileShell.Motion.EmphasizedAccel
easing.type: Easing.InBack duration: root.primaryAnimationDuration
onFinished: { onFinished: {
root.inLastFrame = true; root.inLastFrame = true;
@ -347,32 +353,32 @@ QtObject {
} }
} }
property var closeScaleAnim: NumberAnimation { property var closeScaleAnim: MobileShell.MotionNumberAnimation {
target: root target: root
property: "closingScalingFactor" property: "closingScalingFactor"
to: 0.1 to: root.closeScaleOut
duration: Kirigami.Units.longDuration type: MobileShell.Motion.EmphasizedAccel
easing.type: Easing.InQuad duration: root.primaryAnimationDuration
onStopped: { onStopped: {
root.closingScalingFactor = 1; root.closingScalingFactor = 1;
} }
} }
property var closeFactorAnim: NumberAnimation { property var closeFactorAnim: MobileShell.MotionNumberAnimation {
target: root target: root
property: "closingFactor" property: "closingFactor"
to: 0 to: 0
duration: Kirigami.Units.longDuration type: MobileShell.Motion.EmphasizedAccel
easing.type: Easing.InQuad duration: root.primaryAnimationDuration
} }
property var openAppAnim: NumberAnimation { property var openAppAnim: MobileShell.MotionNumberAnimation {
target: root.state target: root.state
property: "yPosition" property: "yPosition"
to: 0 to: 0
duration: 300 type: MobileShell.Motion.Emphasized
easing.type: Easing.OutQuint duration: root.primaryAnimationDuration
onFinished: { onFinished: {
root.inLastFrame = true; root.inLastFrame = true;
root.state.status = TaskSwitcherPlugin.MobileTaskSwitcherState.Inactive; root.state.status = TaskSwitcherPlugin.MobileTaskSwitcherState.Inactive;

View file

@ -21,6 +21,7 @@ Item {
property alias circleOpacity: buttonRect.opacity property alias circleOpacity: buttonRect.opacity
property alias circleVisiblity: buttonRect.visible property alias circleVisiblity: buttonRect.visible
readonly property bool softwareRendering: GraphicsInfo.api === GraphicsInfo.Software readonly property bool softwareRendering: GraphicsInfo.api === GraphicsInfo.Software
readonly property color buttonBorderColor: Qt.rgba(Kirigami.Theme.textColor.r, Kirigami.Theme.textColor.g, Kirigami.Theme.textColor.b, 0.65)
signal clicked signal clicked
@ -39,7 +40,7 @@ Item {
color: Kirigami.Theme.backgroundColor color: Kirigami.Theme.backgroundColor
opacity: mouseArea.containsPress ? 1 : 0.6 opacity: mouseArea.containsPress ? 1 : 0.6
border { border {
color: Qt.rgba(255, 255, 255, 0.8) color: root.buttonBorderColor
width: 1 width: 1
} }
} }

View file

@ -15,10 +15,13 @@ import org.kde.kirigami as Kirigami
import org.kde.coreaddons 1.0 as KCoreAddons import org.kde.coreaddons 1.0 as KCoreAddons
import org.kde.plasma.private.sessions 2.0 import org.kde.plasma.private.sessions 2.0
import org.kde.plasma.private.mobileshell as MobileShell
import org.kde.plasma.private.mobileshell.shellsettingsplugin as ShellSettings import org.kde.plasma.private.mobileshell.shellsettingsplugin as ShellSettings
Item { Item {
id: root id: root
readonly property int longAnimationDuration: MobileShell.Motion.duration(MobileShell.Motion.EffectsDefault)
readonly property real backgroundDimOpacity: 0.6
Kirigami.Theme.colorSet: Kirigami.Theme.Complementary Kirigami.Theme.colorSet: Kirigami.Theme.Complementary
Kirigami.Theme.inherit: false Kirigami.Theme.inherit: false
@ -71,15 +74,15 @@ Item {
target: buttons target: buttons
from: 0 from: 0
to: 1 to: 1
duration: Kirigami.Units.longDuration duration: root.longAnimationDuration
easing.type: Easing.InOutQuad easing.type: MobileShell.Motion.easing(MobileShell.Motion.EffectsDefault)
} }
OpacityAnimator { OpacityAnimator {
target: background target: background
from: 0 from: 0
to: 0.6 to: root.backgroundDimOpacity
duration: Kirigami.Units.longDuration duration: root.longAnimationDuration
easing.type: Easing.InOutQuad easing.type: MobileShell.Motion.easing(MobileShell.Motion.EffectsDefault)
} }
} }
@ -98,22 +101,22 @@ Item {
target: buttons target: buttons
from: 1 from: 1
to: 0 to: 0
duration: Kirigami.Units.longDuration duration: root.longAnimationDuration
easing.type: Easing.InOutQuad easing.type: MobileShell.Motion.easing(MobileShell.Motion.EffectsDefault)
} }
OpacityAnimator { OpacityAnimator {
target: background target: background
from: 0.6 from: root.backgroundDimOpacity
to: 0 to: 0
duration: Kirigami.Units.longDuration duration: root.longAnimationDuration
easing.type: Easing.InOutQuad easing.type: MobileShell.Motion.easing(MobileShell.Motion.EffectsDefault)
} }
OpacityAnimator { OpacityAnimator {
target: blackOverlay target: blackOverlay
from: 0 from: 0
to: closeAnim.closeToBlack ? 1 : 0 to: closeAnim.closeToBlack ? 1 : 0
duration: Kirigami.Units.longDuration duration: root.longAnimationDuration
easing.type: Easing.InOutQuad easing.type: MobileShell.Motion.easing(MobileShell.Motion.EffectsDefault)
} }
} }
ScriptAction { ScriptAction {
@ -122,7 +125,7 @@ Item {
closeAnim.callback(); closeAnim.callback();
} }
buttons.opacity = 1; buttons.opacity = 1;
background.opacity = 0.6; background.opacity = root.backgroundDimOpacity;
} }
} }
} }

View file

@ -6,6 +6,7 @@
import QtQuick import QtQuick
import org.kde.kirigami as Kirigami import org.kde.kirigami as Kirigami
import org.kde.plasma.private.mobileshell as MobileShell
Rectangle { Rectangle {
id: root id: root
@ -15,6 +16,9 @@ Rectangle {
property int stage property int stage
readonly property bool busy: stage > 1 && stage < 6 readonly property bool busy: stage > 1 && stage < 6
readonly property int longAnimationDuration: MobileShell.Motion.duration(MobileShell.Motion.EffectsDefault)
readonly property int veryLongAnimationDuration: MobileShell.Motion.duration(MobileShell.Motion.EffectsSlow)
readonly property int spinnerAnimationDuration: Math.round(MobileShell.Motion.duration(MobileShell.Motion.EffectsSlow) * 3.5)
Item { Item {
id: content id: content
@ -25,8 +29,8 @@ Rectangle {
Behavior on opacity { Behavior on opacity {
OpacityAnimator { OpacityAnimator {
duration: Kirigami.Units.veryLongDuration duration: root.veryLongAnimationDuration
easing.type: Easing.InOutQuad easing.type: MobileShell.Motion.easing(MobileShell.Motion.EffectsSlow)
} }
} }
@ -63,8 +67,8 @@ Rectangle {
Behavior on opacity { Behavior on opacity {
OpacityAnimator { OpacityAnimator {
duration: Kirigami.Units.longDuration duration: root.longAnimationDuration
easing.type: Easing.InOutQuad easing.type: MobileShell.Motion.easing(MobileShell.Motion.EffectsDefault)
} }
} }
@ -89,9 +93,9 @@ Rectangle {
RotationAnimator on rotation { RotationAnimator on rotation {
from: 0 from: 0
to: 360 to: 360
duration: 1400 duration: root.spinnerAnimationDuration
loops: Animation.Infinite loops: Animation.Infinite
running: root.busy && Kirigami.Units.longDuration > 1 running: root.busy && MobileShell.Motion.enabled
} }
} }
} }

View file

@ -11,6 +11,7 @@ import org.kde.plasma.plasmoid
import org.kde.kirigami as Kirigami import org.kde.kirigami as Kirigami
import org.kde.plasma.configuration 2.0 import org.kde.plasma.configuration 2.0
import org.kde.kitemmodels 1.0 as KItemModels import org.kde.kitemmodels 1.0 as KItemModels
import org.kde.plasma.private.mobileshell as MobileShell
import './private' import './private'
@ -28,6 +29,7 @@ Item {
property bool isContainment: false property bool isContainment: false
property alias app: appLoader.item property alias app: appLoader.item
property bool loadApp: true property bool loadApp: true
readonly property int appAnimationDuration: MobileShell.Motion.duration(MobileShell.Motion.EffectsDefault)
signal appLoaded() signal appLoaded()
@ -132,10 +134,10 @@ Item {
opacity: 0 opacity: 0
scale: 0.7 + 0.3 * app.opacity scale: 0.7 + 0.3 * app.opacity
NumberAnimation on opacity { MobileShell.MotionNumberAnimation on opacity {
id: opacityAnim id: opacityAnim
duration: Kirigami.Units.longDuration type: MobileShell.Motion.EffectsDefault
easing.type: Easing.OutCubic duration: root.appAnimationDuration
onFinished: { onFinished: {
if (app.opacity === 0) { if (app.opacity === 0) {
root.Window.window.close(); root.Window.window.close();

View file

@ -47,10 +47,10 @@ MobileShell.SwipeArea {
} }
} }
NumberAnimation on position { MobileShell.MotionNumberAnimation on position {
id: positionAnim id: positionAnim
type: MobileShell.Motion.SpatialSlow
duration: root.animationDuration duration: root.animationDuration
easing.type: Easing.OutExpo
onFinished: { onFinished: {
if (root.position === keypadHeight) { if (root.position === keypadHeight) {

View file

@ -17,6 +17,8 @@ Item {
id: root id: root
required property real openProgress required property real openProgress
required property var lockScreenState required property var lockScreenState
readonly property int layoutAnimationDuration: openProgress > 0.5 ? MobileShell.Motion.duration(MobileShell.Motion.Emphasized) : 0
readonly property int shortAnimationDuration: MobileShell.Motion.duration(MobileShell.Motion.EffectsFast)
property alias passwordBar: passwordBar property alias passwordBar: passwordBar
@ -63,8 +65,8 @@ Item {
transitions: Transition { transitions: Transition {
AnchorAnimation { AnchorAnimation {
easing.type: Easing.OutCirc easing.type: MobileShell.Motion.easing(MobileShell.Motion.Emphasized)
duration: openProgress > 0.5 ? 300 : 0 duration: root.layoutAnimationDuration
} }
} }
} }
@ -102,8 +104,8 @@ Item {
transitions: Transition { transitions: Transition {
AnchorAnimation { AnchorAnimation {
easing.type: Easing.OutCirc easing.type: MobileShell.Motion.easing(MobileShell.Motion.Emphasized)
duration: openProgress > 0.5 ? 300 : 0 duration: root.layoutAnimationDuration
} }
} }
} }
@ -126,7 +128,7 @@ Item {
Layout.topMargin: keypadVerticalContainer.visible ? Kirigami.Units.gridUnit * 3 : 0 Layout.topMargin: keypadVerticalContainer.visible ? Kirigami.Units.gridUnit * 3 : 0
Behavior on opacity { Behavior on opacity {
NumberAnimation { duration: Kirigami.Units.shortDuration } MobileShell.MotionNumberAnimation { type: MobileShell.Motion.EffectsFast; duration: root.shortAnimationDuration }
} }
} }
@ -172,8 +174,8 @@ Item {
opacity: enabled opacity: enabled
Behavior on opacity { Behavior on opacity {
SequentialAnimation { SequentialAnimation {
PauseAnimation { duration: 20 * index } PauseAnimation { duration: MobileShell.Motion.enabled ? 20 * index : 0 }
NumberAnimation { duration: 300 } MobileShell.MotionNumberAnimation { type: MobileShell.Motion.Emphasized; duration: root.layoutAnimationDuration }
} }
} }

View file

@ -108,7 +108,7 @@ Item {
property alias passwordBar: keypad.passwordBar property alias passwordBar: keypad.passwordBar
// Speed up animation when passwordless // Speed up animation when passwordless
animationDuration: Kirigami.Units.veryLongDuration animationDuration: MobileShell.Motion.duration(MobileShell.Motion.SpatialSlow)
// Distance to swipe to fully open keypad // Distance to swipe to fully open keypad
keypadHeight: Kirigami.Units.gridUnit * 20 keypadHeight: Kirigami.Units.gridUnit * 20

View file

@ -7,6 +7,7 @@ import QtQuick.Layouts
import org.kde.plasma.workspace.keyboardlayout 1.0 import org.kde.plasma.workspace.keyboardlayout 1.0
import org.kde.plasma.workspace.keyboardlayout 1.0 as Keyboards import org.kde.plasma.workspace.keyboardlayout 1.0 as Keyboards
import org.kde.plasma.private.mobileshell as MobileShell
import org.kde.kirigami as Kirigami import org.kde.kirigami as Kirigami
@ -23,9 +24,12 @@ Rectangle {
readonly property color headerTextColor: Qt.rgba(255, 255, 255, 1) readonly property color headerTextColor: Qt.rgba(255, 255, 255, 1)
readonly property color headerTextInactiveColor: Qt.rgba(255, 255, 255, 0.4) readonly property color headerTextInactiveColor: Qt.rgba(255, 255, 255, 0.4)
readonly property color passwordBarSurfaceColor: Qt.rgba(Kirigami.Theme.textColor.r, Kirigami.Theme.textColor.g, Kirigami.Theme.textColor.b, 0.18)
readonly property int quickAnimationDuration: Math.round(MobileShell.Motion.duration(MobileShell.Motion.Press) * 2 / 3)
readonly property int previewAnimationDuration: MobileShell.Motion.duration(MobileShell.Motion.Press)
radius: Kirigami.Units.largeSpacing radius: Kirigami.Units.largeSpacing
color: Qt.rgba(255, 255, 255, 0.2) color: root.passwordBarSurfaceColor
// model for shown dots // model for shown dots
// we need to use a listmodel to avoid all delegates from reloading // we need to use a listmodel to avoid all delegates from reloading
@ -223,7 +227,7 @@ Rectangle {
model: dotDisplayModel model: dotDisplayModel
Behavior on implicitWidth { Behavior on implicitWidth {
NumberAnimation { duration: 50 } MobileShell.MotionNumberAnimation { type: MobileShell.Motion.Press; duration: root.quickAnimationDuration }
} }
onImplicitWidthChanged: { onImplicitWidthChanged: {
@ -242,7 +246,7 @@ Rectangle {
Component.onCompleted: { Component.onCompleted: {
if (showChar) { if (showChar) {
charAnimation.to = 1; charAnimation.to = 1;
charAnimation.duration = 75; charAnimation.duration = root.previewAnimationDuration;
charAnimation.restart(); charAnimation.restart();
} else { } else {
dotAnimation.to = 1; dotAnimation.to = 1;
@ -253,7 +257,7 @@ Rectangle {
onShowCharChanged: { onShowCharChanged: {
if (!showChar) { if (!showChar) {
charAnimation.to = 0; charAnimation.to = 0;
charAnimation.duration = 50; charAnimation.duration = root.quickAnimationDuration;
charAnimation.restart(); charAnimation.restart();
dotAnimation.to = 1; dotAnimation.to = 1;
dotAnimation.start(); dotAnimation.start();
@ -269,11 +273,12 @@ Rectangle {
radius: width radius: width
color: lockScreenState.waitingForAuth ? root.headerTextInactiveColor : root.headerTextColor // dim when waiting for auth color: lockScreenState.waitingForAuth ? root.headerTextInactiveColor : root.headerTextColor // dim when waiting for auth
PropertyAnimation { MobileShell.MotionNumberAnimation {
id: dotAnimation id: dotAnimation
target: dot; target: dot;
property: "scale"; property: "scale";
duration: 50 type: MobileShell.Motion.Press
duration: root.quickAnimationDuration
} }
} }
@ -285,10 +290,11 @@ Rectangle {
text: model.char text: model.char
font.pointSize: 12 font.pointSize: 12
PropertyAnimation { MobileShell.MotionNumberAnimation {
id: charAnimation id: charAnimation
target: charLabel; target: charLabel;
property: "scale"; property: "scale";
type: MobileShell.Motion.Press
} }
} }
} }

View file

@ -16,13 +16,18 @@ AbstractButton {
property int buttonAction property int buttonAction
property bool buttonHeld: false property bool buttonHeld: false
property double scale: pressed ? (buttonHeld ? 1.7 : 1.5) : 1 property double scale: pressed ? (buttonHeld ? heldScale : pressedScale) : 1
property real fillAmount: 0.0 property real fillAmount: 0.0
readonly property int visualAnimationDuration: MobileShell.Motion.duration(MobileShell.Motion.Standard)
readonly property real pressedScale: MobileShell.Motion.pressScaleOut
readonly property real heldScale: MobileShell.Motion.enabled ? 1.14 : 1
readonly property color actionSurfaceColor: Qt.rgba(Kirigami.Theme.textColor.r, Kirigami.Theme.textColor.g, Kirigami.Theme.textColor.b, 0.16)
readonly property color actionFillColor: Qt.rgba(Kirigami.Theme.highlightColor.r, Kirigami.Theme.highlightColor.g, Kirigami.Theme.highlightColor.b, 0.32)
Behavior on scale { Behavior on scale {
NumberAnimation { MobileShell.MotionNumberAnimation {
duration: Kirigami.Units.longDuration type: MobileShell.Motion.Standard
easing.type: Easing.OutBack duration: root.visualAnimationDuration
} }
} }
@ -42,14 +47,14 @@ AbstractButton {
background: Rectangle { background: Rectangle {
radius: width radius: width
color: Qt.rgba(255, 255, 255, 0.2) color: root.actionSurfaceColor
Rectangle { Rectangle {
anchors.centerIn: parent anchors.centerIn: parent
width: parent.width * root.fillAmount width: parent.width * root.fillAmount
height: parent.height * root.fillAmount height: parent.height * root.fillAmount
radius: Math.min(width, height) radius: Math.min(width, height)
color: Qt.rgba(255, 255, 255, 0.2) // Use the same background rectangle color color: root.actionFillColor
} }
} }
@ -117,19 +122,21 @@ AbstractButton {
} }
} }
PropertyAnimation { MobileShell.MotionNumberAnimation {
id: fillAnimation id: fillAnimation
target: root target: root
property: "fillAmount" property: "fillAmount"
duration: Kirigami.Units.longDuration type: MobileShell.Motion.Standard
duration: root.visualAnimationDuration
to: 1.0 to: 1.0
} }
PropertyAnimation { MobileShell.MotionNumberAnimation {
id: emptyAnimation id: emptyAnimation
target: root target: root
property: "fillAmount" property: "fillAmount"
duration: Kirigami.Units.longDuration type: MobileShell.Motion.Standard
duration: root.visualAnimationDuration
to: 0.0 to: 0.0
} }
} }