Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
159 changes: 159 additions & 0 deletions meshroom/ui/qml/Controls/TimelineSlider.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
import QtQuick
import QtQuick.Controls

import Utils 1.0

/**
* A Slider styled as a timeline: a ruler with tick marks and frame number
* labels sits above a track that highlights cached frame intervals in blue.
* The handle is a "playhead" — a downward-pointing triangle connected to a
* thin vertical line — rather than the default circular thumb.
*/
Slider {
id: root

// Array of {x: startFrameIndex, y: endFrameIndex} intervals
property var cachedFrames: []

readonly property int _trackHeight: 8
readonly property int _rulerHeight: 20

implicitHeight: _rulerHeight + _trackHeight + topPadding + bottomPadding

// ── Playhead handle ──────────────────────────────────────────────────────
handle: Item {
// Center the playhead on the logical slider position
x: root.leftPadding + root.visualPosition * root.availableWidth - width / 2
y: root.topPadding
width: 10
height: root.availableHeight

// Downward-pointing triangle marker
Canvas {
id: playheadMarker

anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
width: parent.width
height: 7

property color markerColor: root.palette.highlight

onMarkerColorChanged: requestPaint()
Component.onCompleted: requestPaint()

onPaint: {
var ctx = getContext("2d")
ctx.reset()
ctx.fillStyle = markerColor.toString()
ctx.beginPath()
ctx.moveTo(0, 0)
ctx.lineTo(width, 0)
ctx.lineTo(width / 2, height)
ctx.closePath()
ctx.fill()
}
}

// Vertical playhead line below the triangle
Rectangle {
anchors.top: playheadMarker.bottom
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
width: 2
color: root.palette.highlight
opacity: 0.9
}
}

// ── Background: ruler + track ────────────────────────────────────────────
background: Item {
x: root.leftPadding
y: root.topPadding
width: root.availableWidth
height: root.availableHeight

// Ruler: tick marks and frame-number labels
Item {
id: ruler

anchors.top: parent.top
width: parent.width
height: root._rulerHeight

readonly property int range: root.to - root.from

// Pick a "nice" interval so there are roughly 10-15 major ticks
readonly property int majorInterval: {
if (range <= 0) return 1
if (range <= 10) return 1
if (range <= 25) return 5
if (range <= 50) return 10
if (range <= 250) return 50
if (range <= 500) return 100
return 250
}

Repeater {
model: ruler.range > 0 ? Math.floor(ruler.range / ruler.majorInterval) + 1 : 1

Item {
readonly property int frameNum: root.from + index * ruler.majorInterval
readonly property real xPos: ruler.range > 0
? (frameNum - root.from) / ruler.range * ruler.width
: 0

x: xPos - width / 2
width: Math.max(1, frameLabel.implicitWidth)
height: ruler.height

Text {
id: frameLabel
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
text: frameNum
font.pixelSize: 9
color: Colors.lightgrey
}

Rectangle {
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
width: 1
height: 5
color: Colors.lightgrey
}
}
}
}

// Track: grey base with blue cached-frame segments
Rectangle {
id: track

anchors.bottom: parent.bottom
width: parent.width
height: root._trackHeight
radius: height / 2
color: Colors.grey

Repeater {
id: cacheView

model: root.cachedFrames
property real frameLength: (root.to - root.from + 1) > 0
? track.width / (root.to - root.from + 1)
: 0

Rectangle {
x: modelData.x * cacheView.frameLength
y: 0
width: cacheView.frameLength * (modelData.y - modelData.x + 1)
height: track.height
radius: track.radius
color: Colors.blue
}
}
}
}
}
1 change: 1 addition & 0 deletions meshroom/ui/qml/Controls/qmldir
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,4 @@ DelegateSelectionLine 1.0 DelegateSelectionLine.qml
StatusBar 1.0 StatusBar.qml
NodeActions 1.0 NodeActions.qml
ThinSlider 1.0 ThinSlider.qml
TimelineSlider 1.0 TimelineSlider.qml
30 changes: 3 additions & 27 deletions meshroom/ui/qml/Viewer/SequencePlayer.qml
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ FloatingPane {
}


Slider {
TimelineSlider {
id: frameSlider

Layout.fillWidth: true
Expand All @@ -200,6 +200,8 @@ FloatingPane {
from: frameRange.min
to: frameRange.max

cachedFrames: viewer ? viewer.cachedFrames : []

onValueChanged: {
m.frame = value
}
Expand All @@ -215,32 +217,6 @@ FloatingPane {
visible: frameSlider.hovered
text: m.frame
}


background: Rectangle {
x: frameSlider.leftPadding
y: frameSlider.topPadding + frameSlider.height / 2 - height / 2
width: frameSlider.availableWidth
height: 4
radius: 2
color: Colors.grey

Repeater {
id: cacheView

model: viewer ? viewer.cachedFrames : []
property real frameLength: sortedViewIds.length > 0 ? frameSlider.width / (frameRange.max - frameRange.min + 1) : 0

Rectangle {
x: modelData.x * cacheView.frameLength
y: 0
width: cacheView.frameLength * (modelData.y - modelData.x + 1)
height: 4
radius: 2
color: Colors.blue
}
}
}
}

RowLayout {
Expand Down
Loading