fix(ui): smooth label hover animation

This commit is contained in:
2026-06-07 00:15:04 +00:00
parent 50cfc9e66e
commit bd85a4fd4f
2 changed files with 53 additions and 45 deletions
+2 -2
View File
@@ -7,13 +7,13 @@ Item {
required property string artworkSource required property string artworkSource
required property var labelLines required property var labelLines
required property string fontFamily required property string fontFamily
required property int fontPixelSize required property real fontPixelSize
required property string textColor required property string textColor
required property bool textShadowEnabled required property bool textShadowEnabled
required property int verticalSpacing required property int verticalSpacing
required property int horizontalAlignment required property int horizontalAlignment
required property int borderRadius required property int borderRadius
required property int artworkSize required property real artworkSize
property string currentArtworkSource: "" property string currentArtworkSource: ""
property string incomingArtworkSource: "" property string incomingArtworkSource: ""
property real currentArtworkOpacity: 0 property real currentArtworkOpacity: 0
+51 -43
View File
@@ -35,9 +35,17 @@ MouseArea {
readonly property int hideModeHorizontalPadding: 8 readonly property int hideModeHorizontalPadding: 8
readonly property int contentInset: 8 readonly property int contentInset: 8
readonly property int labelRailSize: Math.max(0, height - contentInset * 2) readonly property int labelRailSize: Math.max(0, height - contentInset * 2)
readonly property bool mediaControlsShown: mediaControlsEnabled && controlsHoverActive && nowPlayingLabelsVisible && !hideNowPlayingArea readonly property bool mediaControlsTargetShown: mediaControlsEnabled && controlsHoverActive && nowPlayingLabelsVisible && !hideNowPlayingArea
readonly property int labelControlGap: 8 readonly property real labelShrunkScale: labelRailSize > 0 ? Math.max(0, (labelRailSize - buttonSize) / labelRailSize) : 1
readonly property int labelContentSize: Math.max(0, labelRailSize - (mediaControlsShown ? buttonSize + labelControlGap : 0)) property real labelContentScale: mediaControlsTargetShown ? labelShrunkScale : 1
readonly property real controlsReveal: {
const range = 1 - labelShrunkScale;
if (range <= 0)
return mediaControlsTargetShown ? 1 : 0;
return Math.max(0, Math.min(1, (1 - labelContentScale) / range));
}
readonly property real labelContentSize: labelRailSize * labelContentScale
readonly property int effectiveImageBorderRadius: Math.max(0, configuredImageBorderRadius) readonly property int effectiveImageBorderRadius: Math.max(0, configuredImageBorderRadius)
readonly property string effectiveBackgroundColor: configuredBackgroundColor || "transparent" readonly property string effectiveBackgroundColor: configuredBackgroundColor || "transparent"
readonly property int effectiveBackgroundRadius: Math.max(0, configuredBackgroundRadius) readonly property int effectiveBackgroundRadius: Math.max(0, configuredBackgroundRadius)
@@ -71,7 +79,7 @@ MouseArea {
onRawHoverActiveChanged: { onRawHoverActiveChanged: {
if (!mediaControlsMouseArea.mediaControlsEnabled) { if (!mediaControlsMouseArea.mediaControlsEnabled) {
hoverExitTimer.stop(); hoverExitTimer.stop();
return ; return;
} }
if (rawHoverActive) if (rawHoverActive)
hoverExitTimer.stop(); hoverExitTimer.stop();
@@ -81,12 +89,11 @@ MouseArea {
onMediaControlsEnabledChanged: { onMediaControlsEnabledChanged: {
if (!mediaControlsMouseArea.mediaControlsEnabled) if (!mediaControlsMouseArea.mediaControlsEnabled)
hoverExitTimer.stop(); hoverExitTimer.stop();
} }
Keys.onPressed: (event) => { Keys.onPressed: event => {
if (!mediaControlsMouseArea.mediaControlsEnabled) { if (!mediaControlsMouseArea.mediaControlsEnabled) {
event.accepted = false; event.accepted = false;
return ; return;
} }
if (!event.modifiers) { if (!event.modifiers) {
event.accepted = true; event.accepted = true;
@@ -114,6 +121,13 @@ MouseArea {
interval: 300 interval: 300
} }
Behavior on labelContentScale {
NumberAnimation {
duration: 250
easing.type: Easing.InOutQuad
}
}
Rectangle { Rectangle {
anchors.fill: parent anchors.fill: parent
visible: mediaControlsMouseArea.usesCustomBackground visible: mediaControlsMouseArea.usesCustomBackground
@@ -126,7 +140,7 @@ MouseArea {
anchors.margins: mediaControlsMouseArea.contentInset anchors.margins: mediaControlsMouseArea.contentInset
layoutDirection: mediaControlsMouseArea.labelsOnRight ? Qt.RightToLeft : Qt.LeftToRight layoutDirection: mediaControlsMouseArea.labelsOnRight ? Qt.RightToLeft : Qt.LeftToRight
ColumnLayout { Item {
id: leftColumn id: leftColumn
visible: !mediaControlsMouseArea.hideNowPlayingArea visible: !mediaControlsMouseArea.hideNowPlayingArea
@@ -134,43 +148,54 @@ MouseArea {
Layout.minimumWidth: visible ? mediaControlsMouseArea.labelRailSize : 0 Layout.minimumWidth: visible ? mediaControlsMouseArea.labelRailSize : 0
Layout.preferredWidth: visible ? mediaControlsMouseArea.labelRailSize : 0 Layout.preferredWidth: visible ? mediaControlsMouseArea.labelRailSize : 0
Layout.maximumWidth: visible ? mediaControlsMouseArea.labelRailSize : 0 Layout.maximumWidth: visible ? mediaControlsMouseArea.labelRailSize : 0
spacing: 0
ColumnLayout { Item {
id: nowPlayingLabels id: labelSlot
Layout.alignment: mediaControlsMouseArea.labelsOnRight ? Qt.AlignLeft : Qt.AlignRight readonly property bool showsArtwork: mediaControlsMouseArea.configuredUseLabelArtwork && player.artUrl.length > 0
Layout.fillWidth: true // Text is rasterized at whole pixel sizes, then lightly scaled between steps for smoother motion.
readonly property real textTargetFontSize: Math.max(1, mediaControlsMouseArea.effectiveLabelFontSize * mediaControlsMouseArea.labelContentScale)
readonly property int textRasterFontSize: Math.max(1, Math.round(textTargetFontSize))
readonly property real textResidualScale: textTargetFontSize / textRasterFontSize
x: mediaControlsMouseArea.labelsOnRight ? 0 : leftColumn.width - width
y: 0
width: mediaControlsMouseArea.labelContentSize
height: mediaControlsMouseArea.labelContentSize
clip: true
visible: mediaControlsMouseArea.nowPlayingLabelsVisible visible: mediaControlsMouseArea.nowPlayingLabelsVisible
LabelContent { LabelContent {
Layout.alignment: mediaControlsMouseArea.labelsOnRight ? Qt.AlignLeft : Qt.AlignRight anchors.left: parent.left
Layout.preferredWidth: mediaControlsMouseArea.labelContentSize anchors.top: parent.top
Layout.preferredHeight: mediaControlsMouseArea.labelContentSize width: labelSlot.showsArtwork ? mediaControlsMouseArea.labelRailSize : mediaControlsMouseArea.labelContentSize / labelSlot.textResidualScale
Layout.maximumWidth: mediaControlsMouseArea.labelRailSize height: labelSlot.showsArtwork ? mediaControlsMouseArea.labelRailSize : mediaControlsMouseArea.labelContentSize / labelSlot.textResidualScale
Layout.maximumHeight: mediaControlsMouseArea.labelRailSize scale: labelSlot.showsArtwork ? mediaControlsMouseArea.labelContentScale : labelSlot.textResidualScale
transformOrigin: Item.TopLeft
artworkSource: mediaControlsMouseArea.configuredUseLabelArtwork ? player.artUrl : "" artworkSource: mediaControlsMouseArea.configuredUseLabelArtwork ? player.artUrl : ""
labelLines: mediaControlsMouseArea.effectiveLabelLines labelLines: mediaControlsMouseArea.effectiveLabelLines
fontFamily: mediaControlsMouseArea.configuredFontFamily fontFamily: mediaControlsMouseArea.configuredFontFamily
fontPixelSize: mediaControlsMouseArea.effectiveLabelFontSize fontPixelSize: labelSlot.showsArtwork ? mediaControlsMouseArea.effectiveLabelFontSize : labelSlot.textRasterFontSize
textColor: mediaControlsMouseArea.effectiveForegroundColor textColor: mediaControlsMouseArea.effectiveForegroundColor
textShadowEnabled: mediaControlsMouseArea.configuredTextShadowEnabled textShadowEnabled: mediaControlsMouseArea.configuredTextShadowEnabled
verticalSpacing: mediaControlsMouseArea.effectiveLabelVerticalSpacing verticalSpacing: mediaControlsMouseArea.effectiveLabelVerticalSpacing
horizontalAlignment: mediaControlsMouseArea.labelsOnRight ? Text.AlignLeft : Text.AlignRight horizontalAlignment: mediaControlsMouseArea.labelsOnRight ? Text.AlignLeft : Text.AlignRight
borderRadius: mediaControlsMouseArea.effectiveImageBorderRadius borderRadius: mediaControlsMouseArea.effectiveImageBorderRadius
artworkSize: mediaControlsMouseArea.labelContentSize artworkSize: labelSlot.showsArtwork ? mediaControlsMouseArea.labelRailSize : mediaControlsMouseArea.labelContentSize / labelSlot.textResidualScale
} }
} }
RowLayout { RowLayout {
id: mediaControls id: mediaControls
Layout.alignment: mediaControlsMouseArea.labelsOnRight ? Qt.AlignLeft : Qt.AlignRight x: mediaControlsMouseArea.labelsOnRight ? 0 : leftColumn.width - width
Layout.topMargin: visible ? mediaControlsMouseArea.labelControlGap : 0 y: mediaControlsMouseArea.labelContentSize
enabled: mediaControlsMouseArea.mediaControlsShown width: implicitWidth
opacity: mediaControlsMouseArea.mediaControlsShown ? 1 : 0 height: mediaControlsMouseArea.buttonSize
visible: mediaControlsMouseArea.mediaControlsShown clip: true
enabled: mediaControlsMouseArea.mediaControlsTargetShown
opacity: mediaControlsMouseArea.controlsReveal
visible: mediaControlsMouseArea.mediaControlsTargetShown || opacity > 0
QQC2.Button { QQC2.Button {
Layout.preferredWidth: buttonSize Layout.preferredWidth: buttonSize
@@ -186,7 +211,6 @@ MouseArea {
source: "media-skip-backward" source: "media-skip-backward"
color: mediaControlsMouseArea.effectiveForegroundColor color: mediaControlsMouseArea.effectiveForegroundColor
} }
} }
QQC2.Button { QQC2.Button {
@@ -205,7 +229,6 @@ MouseArea {
source: player.playbackStatus === 2 ? "media-playback-pause" : "media-playback-start" source: player.playbackStatus === 2 ? "media-playback-pause" : "media-playback-start"
color: mediaControlsMouseArea.effectiveForegroundColor color: mediaControlsMouseArea.effectiveForegroundColor
} }
} }
QQC2.Button { QQC2.Button {
@@ -219,19 +242,8 @@ MouseArea {
source: "media-skip-forward" source: "media-skip-forward"
color: mediaControlsMouseArea.effectiveForegroundColor color: mediaControlsMouseArea.effectiveForegroundColor
} }
} }
Behavior on opacity {
PropertyAnimation {
duration: 250
easing.type: Easing.InOutQuad
}
}
} }
} }
Item { Item {
@@ -252,7 +264,6 @@ MouseArea {
height: Math.round(parent.height * mediaControlsMouseArea.effectiveSeparatorHeight / 100) height: Math.round(parent.height * mediaControlsMouseArea.effectiveSeparatorHeight / 100)
color: mediaControlsMouseArea.configuredHideSeparator ? "transparent" : mediaControlsMouseArea.effectiveForegroundColor color: mediaControlsMouseArea.configuredHideSeparator ? "transparent" : mediaControlsMouseArea.effectiveForegroundColor
} }
} }
ColumnLayout { ColumnLayout {
@@ -289,9 +300,6 @@ MouseArea {
elide: Text.ElideRight elide: Text.ElideRight
horizontalAlignment: mediaControlsMouseArea.labelsOnRight ? Text.AlignRight : Text.AlignLeft horizontalAlignment: mediaControlsMouseArea.labelsOnRight ? Text.AlignRight : Text.AlignLeft
} }
} }
} }
} }