Skip to content

Commit c72d769

Browse files
Copilotfabiencastan
andcommitted
Replace basic Slider with TimelineSlider in SequencePlayer
Agent-Logs-Url: https://github.com/alicevision/Meshroom/sessions/995f742e-a390-4c02-9248-78f308576f07 Co-authored-by: fabiencastan <153585+fabiencastan@users.noreply.github.com>
1 parent c8eaf8f commit c72d769

3 files changed

Lines changed: 163 additions & 27 deletions

File tree

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
import QtQuick
2+
import QtQuick.Controls
3+
4+
import Utils 1.0
5+
6+
/**
7+
* A Slider styled as a timeline: a ruler with tick marks and frame number
8+
* labels sits above a track that highlights cached frame intervals in blue.
9+
* The handle is a "playhead" — a downward-pointing triangle connected to a
10+
* thin vertical line — rather than the default circular thumb.
11+
*/
12+
Slider {
13+
id: root
14+
15+
// Array of {x: startFrameIndex, y: endFrameIndex} intervals
16+
property var cachedFrames: []
17+
18+
readonly property int _trackHeight: 8
19+
readonly property int _rulerHeight: 20
20+
21+
implicitHeight: _rulerHeight + _trackHeight + topPadding + bottomPadding
22+
23+
// ── Playhead handle ──────────────────────────────────────────────────────
24+
handle: Item {
25+
// Center the playhead on the logical slider position
26+
x: root.leftPadding + root.visualPosition * root.availableWidth - width / 2
27+
y: root.topPadding
28+
width: 10
29+
height: root.availableHeight
30+
31+
// Downward-pointing triangle marker
32+
Canvas {
33+
id: playheadMarker
34+
35+
anchors.top: parent.top
36+
anchors.horizontalCenter: parent.horizontalCenter
37+
width: parent.width
38+
height: 7
39+
40+
property color markerColor: root.palette.highlight
41+
42+
onMarkerColorChanged: requestPaint()
43+
Component.onCompleted: requestPaint()
44+
45+
onPaint: {
46+
var ctx = getContext("2d")
47+
ctx.reset()
48+
ctx.fillStyle = markerColor.toString()
49+
ctx.beginPath()
50+
ctx.moveTo(0, 0)
51+
ctx.lineTo(width, 0)
52+
ctx.lineTo(width / 2, height)
53+
ctx.closePath()
54+
ctx.fill()
55+
}
56+
}
57+
58+
// Vertical playhead line below the triangle
59+
Rectangle {
60+
anchors.top: playheadMarker.bottom
61+
anchors.bottom: parent.bottom
62+
anchors.horizontalCenter: parent.horizontalCenter
63+
width: 2
64+
color: root.palette.highlight
65+
opacity: 0.9
66+
}
67+
}
68+
69+
// ── Background: ruler + track ────────────────────────────────────────────
70+
background: Item {
71+
x: root.leftPadding
72+
y: root.topPadding
73+
width: root.availableWidth
74+
height: root.availableHeight
75+
76+
// Ruler: tick marks and frame-number labels
77+
Item {
78+
id: ruler
79+
80+
anchors.top: parent.top
81+
width: parent.width
82+
height: root._rulerHeight
83+
84+
readonly property int range: root.to - root.from
85+
86+
// Pick a "nice" interval so there are roughly 10-15 major ticks
87+
readonly property int majorInterval: {
88+
if (range <= 0) return 1
89+
if (range <= 10) return 1
90+
if (range <= 25) return 5
91+
if (range <= 50) return 10
92+
if (range <= 250) return 50
93+
if (range <= 500) return 100
94+
return 250
95+
}
96+
97+
Repeater {
98+
model: ruler.range > 0 ? Math.floor(ruler.range / ruler.majorInterval) + 1 : 1
99+
100+
Item {
101+
readonly property int frameNum: root.from + index * ruler.majorInterval
102+
readonly property real xPos: ruler.range > 0
103+
? (frameNum - root.from) / ruler.range * ruler.width
104+
: 0
105+
106+
x: xPos - width / 2
107+
width: Math.max(1, frameLabel.implicitWidth)
108+
height: ruler.height
109+
110+
Text {
111+
id: frameLabel
112+
anchors.top: parent.top
113+
anchors.horizontalCenter: parent.horizontalCenter
114+
text: frameNum
115+
font.pixelSize: 9
116+
color: Colors.lightgrey
117+
}
118+
119+
Rectangle {
120+
anchors.bottom: parent.bottom
121+
anchors.horizontalCenter: parent.horizontalCenter
122+
width: 1
123+
height: 5
124+
color: Colors.lightgrey
125+
}
126+
}
127+
}
128+
}
129+
130+
// Track: grey base with blue cached-frame segments
131+
Rectangle {
132+
id: track
133+
134+
anchors.bottom: parent.bottom
135+
width: parent.width
136+
height: root._trackHeight
137+
radius: height / 2
138+
color: Colors.grey
139+
140+
Repeater {
141+
id: cacheView
142+
143+
model: root.cachedFrames
144+
property real frameLength: (root.to - root.from + 1) > 0
145+
? track.width / (root.to - root.from + 1)
146+
: 0
147+
148+
Rectangle {
149+
x: modelData.x * cacheView.frameLength
150+
y: 0
151+
width: cacheView.frameLength * (modelData.y - modelData.x + 1)
152+
height: track.height
153+
radius: track.radius
154+
color: Colors.blue
155+
}
156+
}
157+
}
158+
}
159+
}

meshroom/ui/qml/Controls/qmldir

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,4 @@ DelegateSelectionBox 1.0 DelegateSelectionBox.qml
2323
DelegateSelectionLine 1.0 DelegateSelectionLine.qml
2424
StatusBar 1.0 StatusBar.qml
2525
NodeActions 1.0 NodeActions.qml
26+
TimelineSlider 1.0 TimelineSlider.qml

meshroom/ui/qml/Viewer/SequencePlayer.qml

Lines changed: 3 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ FloatingPane {
186186
}
187187

188188

189-
Slider {
189+
TimelineSlider {
190190
id: frameSlider
191191

192192
Layout.fillWidth: true
@@ -200,6 +200,8 @@ FloatingPane {
200200
from: frameRange.min
201201
to: frameRange.max
202202

203+
cachedFrames: viewer ? viewer.cachedFrames : []
204+
203205
onValueChanged: {
204206
m.frame = value
205207
}
@@ -215,32 +217,6 @@ FloatingPane {
215217
visible: frameSlider.hovered
216218
text: m.frame
217219
}
218-
219-
220-
background: Rectangle {
221-
x: frameSlider.leftPadding
222-
y: frameSlider.topPadding + frameSlider.height / 2 - height / 2
223-
width: frameSlider.availableWidth
224-
height: 4
225-
radius: 2
226-
color: Colors.grey
227-
228-
Repeater {
229-
id: cacheView
230-
231-
model: viewer ? viewer.cachedFrames : []
232-
property real frameLength: sortedViewIds.length > 0 ? frameSlider.width / (frameRange.max - frameRange.min + 1) : 0
233-
234-
Rectangle {
235-
x: modelData.x * cacheView.frameLength
236-
y: 0
237-
width: cacheView.frameLength * (modelData.y - modelData.x + 1)
238-
height: 4
239-
radius: 2
240-
color: Colors.blue
241-
}
242-
}
243-
}
244220
}
245221

246222
RowLayout {

0 commit comments

Comments
 (0)