Ruler.qml 10.4 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/*
 * Copyright (c) 2013 Meltytech, LLC
 * Author: Dan Dennedy <dan@dennedy.org>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

19
20
import QtQuick 2.11
import QtQuick.Controls 2.4
21

22
Item {
23
    id: rulerRoot
24
    // The standard width for labels. Depends on format used (frame number or full timecode)
25
    property int labelSize: fontMetrics.boundingRect(timeline.timecode(36000)).width
26
27
28
29
    // The spacing between labels. Depends on labelSize
    property real labelSpacing: labelSize
    // The space we want between each ticks in the ruler
    property real tickSpacing: timeline.scaleFactor
30
    property alias rulerZone : zone
31
    property int workingPreview : timeline.workingPreview
32
    property int labelMod: 1
33
    property bool useTimelineRuler : timeline.useRuler
34
    property int zoneHeight: Math.ceil(root.baseUnit / 2) + 1
35
    property bool showZoneLabels: false
36
    property bool resizeActive: false // Used to decide which mouse cursor we should display
37
38
    property bool hoverGuide: false
    property int cursorShape: resizeActive ? Qt.SizeHorCursor : hoverGuide ? Qt.PointingHandCursor : Qt.ArrowCursor
39
    property var effectZones: timeline.masterEffectZones
40
    property int guideLabelHeight: timeline.showMarkers ? Math.round(fontMetrics.height) : 0
41
    property int previewHeight: Math.ceil(timecodeContainer.height / 5)
42
    
43
    function adjustStepSize() {
44
45
46
47
48
        if (timeline.scaleFactor > 19) {
            // Frame size >= 20 pixels
            rulerRoot.tickSpacing = timeline.scaleFactor
            // labelSpacing cannot be smaller than 1 frame
            rulerRoot.labelSpacing = timeline.scaleFactor > rulerRoot.labelSize * 1.3 ? timeline.scaleFactor : Math.floor(rulerRoot.labelSize/timeline.scaleFactor) * timeline.scaleFactor
49
        } else {
50
            rulerRoot.tickSpacing = Math.floor(3 * root.baseUnit / timeline.scaleFactor) * timeline.scaleFactor
51
            rulerRoot.labelSpacing = (Math.floor(rulerRoot.labelSize/rulerRoot.tickSpacing) + 1) * rulerRoot.tickSpacing
52
        }
53
        rulerRoot.labelMod = Math.max(1, Math.ceil((rulerRoot.labelSize + root.baseUnit) / rulerRoot.tickSpacing))
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
54
        //console.log('LABELMOD: ', Math.ceil((rulerRoot.labelSize + root.fontUnit) / rulerRoot.tickSpacing)))
55
        tickRepeater.model = Math.ceil(rulercontainer.width / rulerRoot.tickSpacing) + 2
56
57
    }

Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
58
    function adjustFormat() {
59
        rulerRoot.labelSize = fontMetrics.boundingRect(timeline.timecode(36000)).width
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
60
        adjustStepSize()
61
        repaintRuler()
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
62
    }
63

64
65
66
    function repaintRuler() {
        // Enforce repaint
        tickRepeater.model = 0
67
        tickRepeater.model = Math.ceil(rulercontainer.width / rulerRoot.tickSpacing) + 2
68
    }
69

70
    // Timeline preview stuff
71
72
    Repeater {
        model: timeline.dirtyChunks
73
        anchors.fill: parent
74
        delegate: Rectangle {
75
            x: modelData * timeline.scaleFactor
76
77
            anchors.bottom: parent.bottom
            anchors.bottomMargin: zoneHeight
78
            width: 25 * timeline.scaleFactor
79
            height: previewHeight
80
81
82
83
84
85
            color: 'darkred'
        }
    }

    Repeater {
        model: timeline.renderedChunks
86
        anchors.fill: parent
87
        delegate: Rectangle {
88
            x: modelData * timeline.scaleFactor
89
90
            anchors.bottom: parent.bottom
            anchors.bottomMargin: zoneHeight
91
            width: 25 * timeline.scaleFactor
92
            height: previewHeight
93
94
95
96
97
            color: 'darkgreen'
        }
    }
    Rectangle {
        id: working
98
        x: rulerRoot.workingPreview * timeline.scaleFactor
99
100
        anchors.bottom: parent.bottom
        anchors.bottomMargin: zoneHeight
101
        width: 25 * timeline.scaleFactor
102
        height: previewHeight
103
        color: 'orange'
104
        visible: rulerRoot.workingPreview > -1
105
    }
106
    
107
108
109
110
111
112
    // Guides
    Repeater {
        model: guidesModel
        delegate:
        Item {
            id: guideRoot
113
            z: proxy.position == model.frame ? 20 : 10
114
115
116
117
118
119
            Rectangle {
                id: markerBase
                width: 1
                height: rulerRoot.height
                x: model.frame * timeline.scaleFactor
                color: model.color
120
                property int markerId: model.id
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
                Rectangle {
                    visible: timeline.showMarkers
                    width: mlabel.contentWidth + 4
                    height: guideLabelHeight
                    anchors {
                        top: parent.top
                        left: parent.left
                    }
                    color: model.color
                    Text {
                        id: mlabel
                        text: model.comment
                        topPadding: -2
                        leftPadding: 2
                        rightPadding: 2
                        font: miniFont
137
                        color: '#FFF'
138
139
140
141
                    }
                    MouseArea {
                        z: 10
                        id: guideArea
142
143
144
145
                        anchors.left: parent.left
                        anchors.top: parent.top
                        width: parent.width
                        height: parent.height
146
147
148
                        acceptedButtons: Qt.LeftButton
                        cursorShape: Qt.PointingHandCursor
                        hoverEnabled: true
149
150
                        property int prevFrame
                        property int xOffset: 0
151
152
                        drag.axis: Drag.XAxis
                        onPressed: {
153
154
155
                            prevFrame = model.frame
                            xOffset = mouseX
                            anchors.left = undefined
156
157
                        }
                        onReleased: {
158
159
160
161
                            if (prevFrame != model.frame) {
                                var newFrame = model.frame
                                timeline.moveGuideWithoutUndo(markerBase.markerId,  prevFrame)
                                timeline.moveGuide(prevFrame, newFrame)
162
                            }
163
                            anchors.left = parent.left
164
165
166
                        }
                        onPositionChanged: {
                            if (pressed) {
167
168
169
                                var newFrame = Math.round(model.frame + (mouseX - xOffset) / timeline.scaleFactor)
                                newFrame = controller.suggestSnapPoint(newFrame, root.snapping)
                                timeline.moveGuideWithoutUndo(markerBase.markerId,  newFrame)
170
171
172
173
174
175
176
                            }
                        }
                        drag.smoothed: false
                        onDoubleClicked: timeline.editGuide(model.frame)
                        onClicked: {
                            proxy.position = model.frame
                        }
177
178
179
180
181
182
                        onEntered: {
                            rulerRoot.hoverGuide = true
                        }
                        onExited: {
                            rulerRoot.hoverGuide = false
                        }
183
184
185
186
187
188
                    }
                }
            }
        }
    }
    
189
    // Ruler marks
190
    Item {
191
        id: timecodeContainer
192
193
194
195
196
197
        anchors.top: parent.top
        anchors.topMargin: guideLabelHeight
        anchors.bottom: parent.bottom
        anchors.bottomMargin: zoneHeight
        anchors.left: parent.left
        anchors.right: parent.right
198
    Repeater {
199
        id: tickRepeater
200
        model: Math.ceil(rulercontainer.width / rulerRoot.tickSpacing) + 2
201
        property int offset: Math.floor(scrollView.contentX /rulerRoot.tickSpacing)
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
        Item {
            property int realPos: (tickRepeater.offset + index) * rulerRoot.tickSpacing / timeline.scaleFactor
            x: realPos * timeline.scaleFactor
            height: parent.height
            property bool showText: (tickRepeater.offset + index)%rulerRoot.labelMod == 0
            Rectangle {
                anchors.bottom: parent.bottom
                height: parent.showText ? 8 : 4
                width: 1
                color: activePalette.windowText
                opacity: 0.5
            }
            Label {
                visible: parent.showText
                anchors.top: parent.top
217
                opacity: 0.7
218
                text: timeline.timecode(parent.realPos)
219
                font: miniFont
220
221
                color: activePalette.windowText
            }
222
223
        }
    }
224
225
    }
    
226
    RulerZone {
227
        id: zone
228
229
230
231
232
233
234
235
236
237
        Binding {
            target: zone
            property: "frameIn"
            value: timeline.zoneIn
        }
        Binding {
            target: zone
            property: "frameOut"
            value: timeline.zoneOut
        }
238
239
        color: useTimelineRuler ? Qt.rgba(activePalette.highlight.r,activePalette.highlight.g,activePalette.highlight.b,0.9) :
        Qt.rgba(activePalette.highlight.r,activePalette.highlight.g,activePalette.highlight.b,0.5)
240
        anchors.bottom: parent.bottom
241
        height: zoneHeight
242
243
244
        function updateZone(start, end, update)
        {
            timeline.updateZone(start, end, update)
245
        }
246
    }
247
248
249
250
251
252

    // Master effect zones
    Repeater {
        model: effectZones
        Rectangle {
            x: effectZones[index].x * timeline.scaleFactor
253
            height: zoneHeight - 1
254
255
256
            width: (effectZones[index].y - effectZones[index].x) * timeline.scaleFactor
            color: "blueviolet"
            anchors.bottom: parent.bottom
257
            opacity: 0.4
258
259
260
        }
    }

261
262
263
264
265
266
267
268
269
270
271
272
273
    // Effect zone
    RulerZone {
        id: effectZone
        Binding {
            target: effectZone
            property: "frameIn"
            value: timeline.effectZone.x
        }
        Binding {
            target: effectZone
            property: "frameOut"
            value: timeline.effectZone.y
        }
274
        color: "orchid"
275
        anchors.bottom: parent.bottom
276
        height: zoneHeight - 1
277
278
279
280
281
        function updateZone(start, end, update)
        {
            timeline.updateEffectZone(start, end, update)
        }
    }
282
283
}