Skip to content

Instantly share code, notes, and snippets.

@JustLinuxUser
Last active December 31, 2025 06:14
Show Gist options
  • Select an option

  • Save JustLinuxUser/36541b38f8dcd0e2cf22cab392568fad to your computer and use it in GitHub Desktop.

Select an option

Save JustLinuxUser/36541b38f8dcd0e2cf22cab392568fad to your computer and use it in GitHub Desktop.
import QtQuick
import QtQuick.Layouts
/* This one is used like so:
AltScrollText {
NText {
pointSize: Style.fontSizeS
// here I can use any NText properties
}
maxWidth: 200
text: root.statusText
scrollMode: "always"
}
*/
// TODO: ScrollMode enum
Item {
id: root
required property string text
default property Component delegate
property real maxWidth: Infinity
property string scrollMode: "never"
// animation controls
// TODO: Take into account the animation speed
property real waitBeforeScrolling: 1000
property real scrollCycleDuration: Math.max(4000, root.text.length * 120)
property real resettingDuration: 300
clip: true
implicitHeight: titleText.height
enum ScrollState {
None = 0,
Scrolling = 1,
Resetting = 2
}
property int state: AltScrollText.ScrollState.None
onTextChanged: {
if (titleText.item)
titleText.item.text = text;
if (loopingText.item)
loopingText.item.text = text;
// reset state
root.implicitWidth = Math.min(root.maxWidth, titleText.width);
root.state = AltScrollText.ScrollState.None;
scrollContainer.x = 0;
scrollTimer.restart();
root.updateState();
}
Timer {
id: scrollTimer
interval: root.waitBeforeScrolling
onTriggered: {
root.state = AltScrollText.ScrollState.Scrolling;
root.updateState();
}
}
MouseArea {
id: hoverArea
anchors.fill: parent
hoverEnabled: true
acceptedButtons: Qt.NoButton
onEntered: root.updateState()
onExited: root.updateState()
}
function ensureReset() {
if (state === AltScrollText.ScrollState.Scrolling)
state = AltScrollText.ScrollState.Resetting;
}
function updateState() {
if (titleText.width <= root.maxWidth || scrollMode === "never") {
state = AltScrollText.ScrollState.None;
return;
}
if (scrollMode === "always") {
if (hoverArea.containsMouse) {
ensureReset();
} else {
scrollTimer.restart();
}
} else if (scrollMode === "hover") {
if (hoverArea.containsMouse)
state = AltScrollText.ScrollState.Scrolling;
else
ensureReset();
}
}
RowLayout {
id: scrollContainer
height: parent.height
x: 0
spacing: 50
Loader {
id: titleText
sourceComponent: root.delegate
onLoaded: this.item.text = root.text
}
Loader {
id: loopingText
sourceComponent: root.delegate
visible: root.state === AltScrollText.ScrollState.Scrolling
onLoaded: this.item.text = root.text
}
NumberAnimation on x {
running: root.state === AltScrollText.ScrollState.Resetting
to: 0
duration: root.resettingDuration
easing.type: Easing.OutQuad
onFinished: {
root.state = AltScrollText.ScrollState.None;
root.updateState();
}
}
NumberAnimation on x {
running: root.state === AltScrollText.ScrollState.Scrolling
from: 0
to: -(titleText.width + scrollContainer.spacing)
duration: root.scrollCycleDuration
loops: Animation.Infinite
easing.type: Easing.Linear
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment