mirror of
https://github.com/noctalia-dev/noctalia-shell.git
synced 2026-05-11 17:08:27 +08:00
b18d33742a
Replace the leftGradient and rightGradient Rectangles in NScrollText with a MultiEffect mask approach. Old solution caused visual bugs.
219 lines
5.1 KiB
QML
219 lines
5.1 KiB
QML
import QtQuick
|
|
import QtQuick.Effects
|
|
import QtQuick.Layouts
|
|
import qs.Commons
|
|
|
|
/*
|
|
NScrollText {
|
|
NText {
|
|
pointSize: Style.fontSizeS
|
|
// here any NText properties can be used
|
|
}
|
|
maxWidth: 200
|
|
text: "Some long long long text"
|
|
scrollMode: NScrollText.ScrollMode.Always
|
|
}
|
|
*/
|
|
|
|
Item {
|
|
id: root
|
|
|
|
required property string text
|
|
default property Component delegate: NText {
|
|
pointSize: Style.fontSizeS
|
|
}
|
|
|
|
property real maxWidth: Infinity
|
|
|
|
enum ScrollMode {
|
|
Never = 0,
|
|
Always = 1,
|
|
Hover = 2
|
|
}
|
|
|
|
property int scrollMode: NScrollText.ScrollMode.Never
|
|
property bool alwaysMaxWidth: false
|
|
property bool forcedHover: false
|
|
property int cursorShape: Qt.ArrowCursor
|
|
|
|
property real waitBeforeScrolling: 1000
|
|
property real scrollCycleDuration: Math.max(4000, root.text.length * 120)
|
|
property real resettingDuration: 300
|
|
|
|
// Fade controls
|
|
property real fadeExtent: 0.1
|
|
property real fadeCornerRadius: 0
|
|
property bool fadeRoundLeftCorners: true
|
|
|
|
readonly property real contentWidth: {
|
|
if (!titleText.item)
|
|
return 0;
|
|
const implicit = titleText.item.implicitWidth;
|
|
return implicit > 0 ? implicit : titleText.item.width;
|
|
}
|
|
readonly property real measuredWidth: scrollContainer.width
|
|
|
|
implicitWidth: alwaysMaxWidth ? maxWidth : Math.min(maxWidth, contentWidth)
|
|
implicitHeight: titleText.height
|
|
|
|
layer.enabled: true
|
|
layer.effect: MultiEffect {
|
|
maskEnabled: true
|
|
maskThresholdMin: 0.5
|
|
maskSpreadAtMin: 1.0
|
|
maskSource: fadeMask
|
|
}
|
|
|
|
enum ScrollState {
|
|
None = 0,
|
|
Scrolling = 1,
|
|
Resetting = 2
|
|
}
|
|
|
|
property int state: NScrollText.ScrollState.None
|
|
|
|
onTextChanged: {
|
|
if (titleText.item)
|
|
titleText.item.text = text;
|
|
if (loopingText.item)
|
|
loopingText.item.text = text;
|
|
|
|
// reset state
|
|
resetState();
|
|
}
|
|
onMaxWidthChanged: resetState()
|
|
onContentWidthChanged: root.updateState()
|
|
onForcedHoverChanged: updateState()
|
|
|
|
function resetState() {
|
|
root.state = NScrollText.ScrollState.None;
|
|
scrollContainer.x = 0;
|
|
scrollTimer.restart();
|
|
root.updateState();
|
|
}
|
|
|
|
Timer {
|
|
id: scrollTimer
|
|
interval: root.waitBeforeScrolling
|
|
onTriggered: {
|
|
root.state = NScrollText.ScrollState.Scrolling;
|
|
root.updateState();
|
|
}
|
|
}
|
|
|
|
MouseArea {
|
|
id: hoverArea
|
|
anchors.fill: parent
|
|
hoverEnabled: true
|
|
acceptedButtons: Qt.NoButton
|
|
onEntered: root.updateState()
|
|
onExited: root.updateState()
|
|
cursorShape: root.cursorShape
|
|
}
|
|
|
|
function ensureReset() {
|
|
if (state === NScrollText.ScrollState.Scrolling)
|
|
state = NScrollText.ScrollState.Resetting;
|
|
}
|
|
|
|
function updateState() {
|
|
if (contentWidth <= root.maxWidth || scrollMode === NScrollText.ScrollMode.Never) {
|
|
state = NScrollText.ScrollState.None;
|
|
return;
|
|
}
|
|
if (scrollMode === NScrollText.ScrollMode.Always) {
|
|
if (hoverArea.containsMouse) {
|
|
ensureReset();
|
|
} else {
|
|
scrollTimer.restart();
|
|
}
|
|
} else if (scrollMode === NScrollText.ScrollMode.Hover) {
|
|
if (hoverArea.containsMouse || forcedHover)
|
|
state = NScrollText.ScrollState.Scrolling;
|
|
else
|
|
ensureReset();
|
|
}
|
|
}
|
|
|
|
RowLayout {
|
|
id: scrollContainer
|
|
height: parent.height
|
|
x: 0
|
|
spacing: 50
|
|
|
|
Loader {
|
|
id: titleText
|
|
sourceComponent: root.delegate
|
|
Layout.fillHeight: true
|
|
onLoaded: {
|
|
this.item.text = root.text;
|
|
// Bind height to container to enable vertical centering of overly high text
|
|
this.item.height = Qt.binding(() => titleText.height);
|
|
}
|
|
}
|
|
|
|
Loader {
|
|
id: loopingText
|
|
sourceComponent: root.delegate
|
|
Layout.fillHeight: true
|
|
visible: root.state !== NScrollText.ScrollState.None
|
|
onLoaded: {
|
|
this.item.text = root.text;
|
|
this.item.height = Qt.binding(() => loopingText.height);
|
|
}
|
|
}
|
|
|
|
NumberAnimation on x {
|
|
running: root.state === NScrollText.ScrollState.Resetting
|
|
to: 0
|
|
duration: root.resettingDuration
|
|
easing.type: Easing.OutQuad
|
|
onFinished: {
|
|
root.state = NScrollText.ScrollState.None;
|
|
root.updateState();
|
|
}
|
|
}
|
|
|
|
NumberAnimation on x {
|
|
running: root.state === NScrollText.ScrollState.Scrolling
|
|
to: -(titleText.width + scrollContainer.spacing)
|
|
duration: root.scrollCycleDuration
|
|
loops: Animation.Infinite
|
|
easing.type: Easing.Linear
|
|
}
|
|
}
|
|
|
|
// Transparency Fade Rectangle
|
|
Rectangle {
|
|
id: fadeMask
|
|
width: root.width
|
|
height: root.height
|
|
topLeftRadius: fadeRoundLeftCorners ? fadeCornerRadius : 0
|
|
bottomLeftRadius: fadeRoundLeftCorners ? fadeCornerRadius : 0
|
|
topRightRadius: fadeCornerRadius
|
|
bottomRightRadius: fadeCornerRadius
|
|
gradient: Gradient {
|
|
GradientStop {
|
|
position: 0.0
|
|
color: "transparent"
|
|
}
|
|
GradientStop {
|
|
position: fadeExtent
|
|
color: "white"
|
|
}
|
|
GradientStop {
|
|
position: 1 - fadeExtent
|
|
color: "white"
|
|
}
|
|
GradientStop {
|
|
position: 1.0
|
|
color: "transparent"
|
|
}
|
|
orientation: Gradient.Horizontal
|
|
}
|
|
layer.enabled: true
|
|
layer.smooth: true
|
|
opacity: 0 // Great for debugging! Will show the white masks
|
|
}
|
|
}
|