diff --git a/Assets/Translations/de.json b/Assets/Translations/de.json index 34dd4840..8432af14 100644 --- a/Assets/Translations/de.json +++ b/Assets/Translations/de.json @@ -140,6 +140,14 @@ "frame-rate": { "label": "Bildrate", "description": "Höhere Raten sind flüssiger, verbrauchen aber mehr Ressourcen." + }, + "scrolling-title": { + "label": "Scrollender Titel", + "description": "Kontinuierliches Scrollen für lange Medientitel aktivieren" + }, + "scrolling-speed": { + "label": "Scrollgeschwindigkeit", + "description": "Zeit in Sekunden, die der Titel zum Scrollen vom Anfang bis zum Ende benötigt" } } }, diff --git a/Assets/Translations/en.json b/Assets/Translations/en.json index 4a787dbf..b2ae51d3 100644 --- a/Assets/Translations/en.json +++ b/Assets/Translations/en.json @@ -140,6 +140,14 @@ "frame-rate": { "label": "Frame rate", "description": "Higher rates are smoother but use more resources." + }, + "scrolling-title": { + "label": "Scrolling title", + "description": "Enable continuous scrolling for long media titles" + }, + "scrolling-speed": { + "label": "Scrolling speed", + "description": "Time in seconds for the title to scroll from start to end" } } }, diff --git a/Assets/Translations/es.json b/Assets/Translations/es.json index bad22775..f9a3f742 100644 --- a/Assets/Translations/es.json +++ b/Assets/Translations/es.json @@ -138,6 +138,14 @@ "frame-rate": { "label": "Tasa de fotogramas", "description": "Tasas más altas son más fluidas pero usan más recursos." + }, + "scrolling-title": { + "label": "Título con desplazamiento", + "description": "Activar desplazamiento continuo para títulos de medios largos" + }, + "scrolling-speed": { + "label": "Velocidad de desplazamiento", + "description": "Tiempo en segundos para que el título se desplace del inicio al final" } } }, diff --git a/Assets/Translations/fr.json b/Assets/Translations/fr.json index 2915fed1..cdb38c08 100644 --- a/Assets/Translations/fr.json +++ b/Assets/Translations/fr.json @@ -138,6 +138,14 @@ "frame-rate": { "label": "Fréquence d'images", "description": "Des fréquences plus élevées sont plus fluides mais utilisent plus de ressources." + }, + "scrolling-title": { + "label": "Titre défilant", + "description": "Activer le défilement continu pour les titres multimédias longs" + }, + "scrolling-speed": { + "label": "Vitesse de défilement", + "description": "Temps en secondes pour que le titre défile du début à la fin" } } }, diff --git a/Assets/Translations/pt.json b/Assets/Translations/pt.json index 86101c2b..6cd104f8 100644 --- a/Assets/Translations/pt.json +++ b/Assets/Translations/pt.json @@ -138,6 +138,14 @@ "frame-rate": { "label": "Taxa de quadros", "description": "Taxas mais altas são mais suaves, mas consomem mais recursos." + }, + "scrolling-title": { + "label": "Título com rolagem", + "description": "Ativar rolagem contínua para títulos de mídia longos" + }, + "scrolling-speed": { + "label": "Velocidade de rolagem", + "description": "Tempo em segundos para o título rolar do início ao fim" } } }, diff --git a/Assets/settings-default.json b/Assets/settings-default.json index 5ba03f2e..ab2ae437 100644 --- a/Assets/settings-default.json +++ b/Assets/settings-default.json @@ -141,7 +141,7 @@ "cavaFrameRate": 60, "visualizerType": "linear", "mprisBlacklist": [], - "preferredPlayer": "" + "preferredPlayer": "", }, "ui": { "fontDefault": "Roboto", diff --git a/Commons/Settings.qml b/Commons/Settings.qml index 7f378ed9..82ca10c4 100644 --- a/Commons/Settings.qml +++ b/Commons/Settings.qml @@ -266,6 +266,8 @@ Singleton { property string visualizerType: "linear" property list mprisBlacklist: [] property string preferredPlayer: "" + property bool scrollingTitle: true + property int scrollingSpeed: 18 } // ui diff --git a/Modules/Bar/Widgets/MediaMini.qml b/Modules/Bar/Widgets/MediaMini.qml index e8b3fd90..52e235e9 100644 --- a/Modules/Bar/Widgets/MediaMini.qml +++ b/Modules/Bar/Widgets/MediaMini.qml @@ -36,6 +36,8 @@ Item { readonly property bool showAlbumArt: (widgetSettings.showAlbumArt !== undefined) ? widgetSettings.showAlbumArt : widgetMetadata.showAlbumArt readonly property bool showVisualizer: (widgetSettings.showVisualizer !== undefined) ? widgetSettings.showVisualizer : widgetMetadata.showVisualizer readonly property string visualizerType: (widgetSettings.visualizerType !== undefined && widgetSettings.visualizerType !== "") ? widgetSettings.visualizerType : widgetMetadata.visualizerType + readonly property bool scrollingTitle: Settings.data.audio.scrollingTitle + readonly property int scrollingSpeed: Settings.data.audio.scrollingSpeed // 6% of total width readonly property real minWidth: Math.max(1, screen.width * 0.06) @@ -180,8 +182,8 @@ Item { } } - NText { - id: titleText + Item { + id: titleContainer Layout.preferredWidth: { if (mouseArea.containsMouse) { @@ -191,13 +193,73 @@ Item { } } Layout.alignment: Qt.AlignVCenter + Layout.preferredHeight: titleText.height - text: getTitle() - font.pointSize: Style.fontSizeS * scaling - font.weight: Style.fontWeightMedium - elide: Text.ElideRight - verticalAlignment: Text.AlignVCenter - color: Color.mSecondary + clip: true + + property bool shouldScroll: scrollingTitle && fullTitleMetrics.contentWidth > titleContainer.width + property bool isScrolling: false + + // Start scrolling when text is too long and not hovering + Timer { + id: scrollStartTimer + interval: 2000 // Wait 2 seconds before starting scroll + repeat: false + onTriggered: { + if (titleContainer.shouldScroll && !mouseArea.containsMouse) { + titleContainer.isScrolling = true + } + } + } + + // Reset scroll position when text changes or on hover + onShouldScrollChanged: { + if (shouldScroll && !mouseArea.containsMouse) { + scrollStartTimer.restart() + } else { + scrollStartTimer.stop() + isScrolling = false + } + } + + Connections { + target: mouseArea + function onContainsMouseChanged() { + if (mouseArea.containsMouse) { + scrollStartTimer.stop() + titleContainer.isScrolling = false + } else if (titleContainer.shouldScroll) { + scrollStartTimer.restart() + } + } + } + + NText { + id: titleText + + text: getTitle() + font.pointSize: Style.fontSizeS * scaling + font.weight: Style.fontWeightMedium + verticalAlignment: Text.AlignVCenter + color: Color.mSecondary + + property real scrollPosition: 0 + + x: scrollPosition + + // Continuous scrolling animation + SequentialAnimation on scrollPosition { + running: titleContainer.isScrolling + loops: Animation.Infinite + + NumberAnimation { + from: 0 + to: -(fullTitleMetrics.contentWidth - titleContainer.width) + duration: scrollingSpeed * 1000 // Convert seconds to milliseconds + easing.type: Easing.Linear + } + } + } Behavior on Layout.preferredWidth { NumberAnimation { diff --git a/Modules/Settings/Tabs/AudioTab.qml b/Modules/Settings/Tabs/AudioTab.qml index 64919045..252fdb56 100644 --- a/Modules/Settings/Tabs/AudioTab.qml +++ b/Modules/Settings/Tabs/AudioTab.qml @@ -385,6 +385,27 @@ ColumnLayout { currentKey: Settings.data.audio.cavaFrameRate onSelected: key => Settings.data.audio.cavaFrameRate = key } + + // Scrolling Title Settings + NToggle { + label: I18n.tr("settings.audio.media.scrolling-title.label") + description: I18n.tr("settings.audio.media.scrolling-title.description") + checked: Settings.data.audio.scrollingTitle + onToggled: checked => Settings.data.audio.scrollingTitle = checked + } + + NSpinBox { + Layout.fillWidth: true + label: I18n.tr("settings.audio.media.scrolling-speed.label") + description: I18n.tr("settings.audio.media.scrolling-speed.description") + minimum: 5 + maximum: 60 + value: Settings.data.audio.scrollingSpeed + stepSize: 1 + suffix: "s" + enabled: Settings.data.audio.scrollingTitle + onValueChanged: Settings.data.audio.scrollingSpeed = value + } } NDivider {