Smooth convergence mode transitions

Animate dock item sizing and button opacity when toggling convergence
mode so the dock does not snap abruptly between states.

FavouritesBar: promote cell/icon/nav/pager/trash sizing properties to
writable and attach NumberAnimation Behaviors (InOutCubic, longDuration).
Home, Overview, and trash buttons fade in/out with an InOutQuad opacity
Behavior so they appear gradually rather than popping.

Dock overlay (main.qml): introduce an `active` guard property so the
window can be driven by opacity (InOutQuad, shortDuration) rather than
toggling visibility directly. Switch the dockOffset slide easing from
a directional InExpo/OutExpo pair to a single InOutCubic so the motion
feels symmetrical.

StatusBar: smooth the background colour and convergence affordance
transitions.
This commit is contained in:
Marco Allegretti 2026-05-06 13:33:25 +02:00
parent 3fba9798e4
commit daf6ec3fd6
3 changed files with 56 additions and 15 deletions

View file

@ -36,6 +36,10 @@ Item {
*/
property color backgroundColor: "transparent"
Behavior on backgroundColor {
ColorAnimation { duration: Kirigami.Units.shortDuration; easing.type: Easing.InOutQuad }
}
/**
* Whether to show a second row of the status bar, with more information.
*/
@ -93,7 +97,12 @@ Item {
Rectangle {
anchors.fill: parent
color: Qt.rgba(Kirigami.Theme.textColor.r, Kirigami.Theme.textColor.g, Kirigami.Theme.textColor.b, 0.1)
visible: ShellSettings.Settings.convergenceModeEnabled && statusBarHover.hovered
visible: opacity > 0
opacity: ShellSettings.Settings.convergenceModeEnabled && statusBarHover.hovered ? 1 : 0
Behavior on opacity {
NumberAnimation { duration: Kirigami.Units.shortDuration; easing.type: Easing.InOutQuad }
}
}
HoverHandler {
@ -223,8 +232,8 @@ Item {
anchors.bottom: parent.bottom
anchors.bottomMargin: Kirigami.Units.smallSpacing
visible: ShellSettings.Settings.convergenceModeEnabled
opacity: statusBarHover.hovered ? 0.6 : 0.2
visible: ShellSettings.Settings.convergenceModeEnabled || opacity > 0
opacity: ShellSettings.Settings.convergenceModeEnabled ? (statusBarHover.hovered ? 0.6 : 0.2) : 0
Behavior on opacity {
NumberAnimation { duration: Kirigami.Units.shortDuration }

View file

@ -36,13 +36,18 @@ MouseArea {
readonly property int totalItemCount: repeater.count + (showRunningTasks ? taskRepeater.count : 0)
// In convergence mode, size icons to fit the dock bar instead of using page grid cells
readonly property real dockCellWidth: convergenceMode ? root.height : folio.HomeScreenState.pageCellWidth
readonly property real dockCellHeight: convergenceMode ? root.height : folio.HomeScreenState.pageCellHeight
property real dockCellWidth: convergenceMode ? root.height : folio.HomeScreenState.pageCellWidth
property real dockCellHeight: convergenceMode ? root.height : folio.HomeScreenState.pageCellHeight
Behavior on dockCellWidth { NumberAnimation { duration: Kirigami.Units.longDuration; easing.type: Easing.InOutCubic } }
Behavior on dockCellHeight { NumberAnimation { duration: Kirigami.Units.longDuration; easing.type: Easing.InOutCubic } }
// Navigation buttons width (used to offset center positioning)
readonly property real navButtonWidth: convergenceMode ? root.height : 0
readonly property real dockItemInset: convergenceMode ? Math.max(2, Kirigami.Units.smallSpacing / 2) : 0
readonly property real dockIconSize: Math.min(root.height * 0.56, Kirigami.Units.iconSizes.large)
property real navButtonWidth: convergenceMode ? root.height : 0
property real dockItemInset: convergenceMode ? Math.max(2, Kirigami.Units.smallSpacing / 2) : 0
property real dockIconSize: Math.min(root.height * 0.56, Kirigami.Units.iconSizes.large)
Behavior on navButtonWidth { NumberAnimation { duration: Kirigami.Units.longDuration; easing.type: Easing.InOutCubic } }
Behavior on dockItemInset { NumberAnimation { duration: Kirigami.Units.longDuration; easing.type: Easing.InOutCubic } }
Behavior on dockIconSize { NumberAnimation { duration: Kirigami.Units.longDuration; easing.type: Easing.InOutCubic } }
function dockItemColor(pressed, hovered, active) {
if (pressed) {
@ -90,10 +95,12 @@ MouseArea {
// Virtual desktop pager (convergence mode, 2+ desktops)
readonly property bool showPager: convergenceMode && virtualDesktopInfo.numberOfDesktops > 1
readonly property real pagerButtonWidth: showPager ? Math.min(root.height, Kirigami.Units.gridUnit * 2.5) : 0
property real pagerButtonWidth: showPager ? Math.min(root.height, Kirigami.Units.gridUnit * 2.5) : 0
readonly property int pagerLeftCount: showPager ? Math.ceil(virtualDesktopInfo.numberOfDesktops / 2) : 0
readonly property int pagerRightCount: showPager ? virtualDesktopInfo.numberOfDesktops - pagerLeftCount : 0
readonly property real trashButtonWidth: convergenceMode ? root.height : 0
property real trashButtonWidth: convergenceMode ? root.height : 0
Behavior on pagerButtonWidth { NumberAnimation { duration: Kirigami.Units.longDuration; easing.type: Easing.InOutCubic } }
Behavior on trashButtonWidth { NumberAnimation { duration: Kirigami.Units.longDuration; easing.type: Easing.InOutCubic } }
function pagerDesktopName(index) {
let names = virtualDesktopInfo.desktopNames
@ -204,7 +211,9 @@ MouseArea {
// Home button (convergence mode, left end)
Rectangle {
id: homeButton
visible: root.convergenceMode
visible: root.convergenceMode || opacity > 0
enabled: root.convergenceMode
opacity: root.convergenceMode ? 1 : 0
activeFocusOnTab: root.convergenceMode
anchors.left: parent.left
anchors.top: parent.top
@ -212,6 +221,10 @@ MouseArea {
width: root.navButtonWidth
color: "transparent"
Behavior on opacity {
NumberAnimation { duration: Kirigami.Units.shortDuration; easing.type: Easing.InOutQuad }
}
Accessible.role: Accessible.Button
Accessible.name: i18n("Home")
Accessible.onPressAction: MobileShellState.ShellDBusClient.openHomeScreen()
@ -263,7 +276,9 @@ MouseArea {
// Overview button (convergence mode, right end)
Rectangle {
id: overviewButton
visible: root.convergenceMode
visible: root.convergenceMode || opacity > 0
enabled: root.convergenceMode
opacity: root.convergenceMode ? 1 : 0
activeFocusOnTab: root.convergenceMode
anchors.right: parent.right
anchors.top: parent.top
@ -271,6 +286,10 @@ MouseArea {
width: root.navButtonWidth
color: "transparent"
Behavior on opacity {
NumberAnimation { duration: Kirigami.Units.shortDuration; easing.type: Easing.InOutQuad }
}
Accessible.role: Accessible.Button
Accessible.name: i18n("Overview")
Accessible.onPressAction: root.folio.triggerOverview()
@ -480,7 +499,9 @@ MouseArea {
Rectangle {
id: trashButton
visible: root.convergenceMode
visible: root.convergenceMode || opacity > 0
enabled: root.convergenceMode
opacity: root.convergenceMode ? 1 : 0
activeFocusOnTab: root.convergenceMode
x: root.width - root.navButtonWidth - root.trashButtonWidth
y: 0
@ -488,6 +509,10 @@ MouseArea {
height: root.height
color: "transparent"
Behavior on opacity {
NumberAnimation { duration: Kirigami.Units.shortDuration; easing.type: Easing.InOutQuad }
}
Accessible.role: Accessible.Button
Accessible.name: i18n("Trash")
Accessible.onPressAction: Qt.openUrlExternally("trash:/")

View file

@ -276,7 +276,10 @@ ContainmentItem {
// task panel containment; this window only provides the visible dock.
Window {
id: dockOverlay
visible: ShellSettings.Settings.convergenceModeEnabled && !ShellSettings.Settings.gamingModeEnabled
readonly property bool active: ShellSettings.Settings.convergenceModeEnabled && !ShellSettings.Settings.gamingModeEnabled
visible: active
opacity: active ? 1 : 0
color: "transparent"
width: Screen.width
height: MobileShell.Constants.convergenceDockHeight
@ -346,11 +349,15 @@ ContainmentItem {
Behavior on dockOffset {
NumberAnimation {
easing.type: dockOverlay.shouldHide ? Easing.InExpo : Easing.OutExpo
easing.type: Easing.InOutCubic
duration: Kirigami.Units.longDuration
}
}
Behavior on opacity {
NumberAnimation { duration: Kirigami.Units.shortDuration; easing.type: Easing.InOutQuad }
}
Rectangle {
anchors.fill: parent
visible: !dockOverlay.shouldHide || dockOverlay.dockOffset < dockOverlay.dockHeight