MainView.qml 16.1 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
Kai Koehne's avatar
Kai Koehne committed
2
**
3
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
hjk's avatar
hjk committed
4
** Contact: http://www.qt-project.org/legal
Kai Koehne's avatar
Kai Koehne committed
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
Kai Koehne's avatar
Kai Koehne committed
7
**
hjk's avatar
hjk committed
8
9
10
11
12
13
14
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
Kai Koehne's avatar
Kai Koehne committed
15
16
**
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17
18
19
20
21
22
23
24
25
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
Kai Koehne's avatar
Kai Koehne committed
26
27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
28
****************************************************************************/
Kai Koehne's avatar
Kai Koehne committed
29

30
import QtQuick 2.1
Christiaan Janssen's avatar
Christiaan Janssen committed
31
import Monitor 1.0
32
import QtQuick.Controls 1.0
Christiaan Janssen's avatar
Christiaan Janssen committed
33
34
35
36

Rectangle {
    id: root

37
38
    // ***** properties

39
    property int singleRowHeight: 30
40

41
42
    property alias selectionLocked : view.selectionLocked
    signal updateLockButton
43
    property bool lockItemSelection : false
44

45
    property real mainviewTimePerPixel : 0
46

Christiaan Janssen's avatar
Christiaan Janssen committed
47
48
49
    signal updateCursorPosition
    property string fileName: ""
    property int lineNumber: -1
50
    property int columnNumber: 0
51

52
53
54
    signal updateRangeButton
    property bool selectionRangeMode: false

55
    property bool selectionRangeReady: selectionRange.ready
56
57
    property real selectionRangeStart: selectionRange.startTime
    property real selectionRangeEnd: selectionRange.startTime + selectionRange.duration
58

59
60
    signal changeToolTip(string text)

61
62
    color: "#dcdcdc"

63
64
65
66
67
68
69
    // ***** connections with external objects
    Connections {
        target: zoomControl
        onRangeChanged: {
            var startTime = zoomControl.startTime();
            var endTime = zoomControl.endTime();

70
            mainviewTimePerPixel = Math.abs(endTime - startTime) / root.width;
71
72
73
74
75
76

            backgroundMarks.updateMarks(startTime, endTime);
            view.updateFlickRange(startTime, endTime);
        }
    }

Christiaan Janssen's avatar
Christiaan Janssen committed
77

78
    Connections {
Christiaan Janssen's avatar
Christiaan Janssen committed
79
        target: qmlProfilerModelProxy
80
        onStateChanged: {
81
82
            // Clear if model is empty.
            if (qmlProfilerModelProxy.getState() === 0)
83
                root.clear();
84
        }
Christiaan Janssen's avatar
Christiaan Janssen committed
85
86
87
88
89
        onDataAvailable: {
            view.clearData();
            zoomControl.setRange(qmlProfilerModelProxy.traceStartTime(),
                                 qmlProfilerModelProxy.traceStartTime() +
                                 qmlProfilerModelProxy.traceDuration()/10);
90
            view.requestPaint();
Christiaan Janssen's avatar
Christiaan Janssen committed
91
        }
92
93
    }

Christiaan Janssen's avatar
Christiaan Janssen committed
94

95
    // ***** functions
96
    function gotoSourceLocation(file,line,column) {
Christiaan Janssen's avatar
Christiaan Janssen committed
97
98
99
100
101
102
        if (file !== undefined) {
            root.fileName = file;
            root.lineNumber = line;
            root.columnNumber = column;
            root.updateCursorPosition();
        }
Christiaan Janssen's avatar
Christiaan Janssen committed
103
104
    }

105
106
107
108
    function clear() {
        flick.contentY = 0;
        flick.contentX = 0;
        flick.contentWidth = 0;
109
        view.clearData();
110
        view.startTime = view.endTime = 0;
111
        hideRangeDetails();
112
113
114
        selectionRangeMode = false;
        updateRangeButton();
        zoomControl.setRange(0,0);
115
116
        zoomSlider.externalUpdate = true;
        zoomSlider.value = zoomSlider.minimumValue;
117
118
    }

119
    function nextEvent() {
120
        view.selectNext();
121
122
123
    }

    function prevEvent() {
124
        view.selectPrev();
125
126
    }

127
128
129
130
131
    function recenter( centerPoint ) {
        var windowLength = view.endTime - view.startTime;
        var newStart = Math.floor(centerPoint - windowLength/2);
        if (newStart < 0)
            newStart = 0;
Christiaan Janssen's avatar
Christiaan Janssen committed
132
133
        if (newStart + windowLength > qmlProfilerModelProxy.traceEndTime())
            newStart = qmlProfilerModelProxy.traceEndTime() - windowLength;
134
135
136
        zoomControl.setRange(newStart, newStart + windowLength);
    }

137
    function recenterOnItem(modelIndex, itemIndex)
138
    {
139
140
141
        if (itemIndex === -1)
            return;

142
        // if item is outside of the view, jump back to its position
Christiaan Janssen's avatar
Christiaan Janssen committed
143
144
145
146
        if (qmlProfilerModelProxy.getEndTime(modelIndex, itemIndex) < view.startTime ||
                qmlProfilerModelProxy.getStartTime(modelIndex, itemIndex) > view.endTime) {
            recenter((qmlProfilerModelProxy.getStartTime(modelIndex, itemIndex) +
                      qmlProfilerModelProxy.getEndTime(modelIndex, itemIndex)) / 2);
147
148
149
        }
    }

150
151
152
153
154
155
    function hideRangeDetails() {
        rangeDetails.visible = false;
        rangeDetails.duration = "";
        rangeDetails.label = "";
        rangeDetails.file = "";
        rangeDetails.line = -1;
156
        rangeDetails.column = 0;
157
        rangeDetails.isBindingLoop = false;
158
159
    }

Christiaan Janssen's avatar
Christiaan Janssen committed
160
161
    function selectNextByHash(hash) {
        var eventId = qmlProfilerModelProxy.getEventIdForHash(hash);
162
        if (eventId !== -1)
Christiaan Janssen's avatar
Christiaan Janssen committed
163
164
165
166
            selectNextById(eventId);
    }

    function selectNextById(eventId)
167
    {
Christiaan Janssen's avatar
Christiaan Janssen committed
168
169
        // this is a slot responding to events from the other pane
        // which tracks only events from the basic model
170
171
        if (!lockItemSelection) {
            lockItemSelection = true;
Christiaan Janssen's avatar
Christiaan Janssen committed
172
            var modelIndex = qmlProfilerModelProxy.basicModelIndex();
173
            var itemIndex = view.nextItemFromId(modelIndex, eventId);
174
            // select an item, lock to it, and recenter if necessary
Christiaan Janssen's avatar
Christiaan Janssen committed
175
176
            if (view.selectedItem != itemIndex || view.selectedModel != modelIndex) {
                view.selectedModel = modelIndex;
177
178
179
180
181
182
183
184
185
                view.selectedItem = itemIndex;
                if (itemIndex !== -1) {
                    view.selectionLocked = true;
                }
            }
            lockItemSelection = false;
        }
    }

186
    // ***** slots
187
188
    onSelectionRangeModeChanged: {
        selectionRangeControl.enabled = selectionRangeMode;
Ulf Hermann's avatar
Ulf Hermann committed
189
        selectionRange.reset();
190
191
    }

192
193
194
195
    onSelectionLockedChanged: {
        updateLockButton();
    }

196
    Flickable {
197
        id: labelsflick
198
        flickableDirection: Flickable.VerticalFlick
199
200
201
202
203
204
205
        interactive: false
        anchors.top: parent.top
        anchors.bottom: parent.bottom
        anchors.left: parent.left
        width: labels.width
        contentY: flick.contentY

206
207
208
        // reserve some more space than needed to prevent weird effects when resizing
        contentHeight: labels.height + height

209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
        Rectangle {
            id: labels
            anchors.left: parent.left
            width: 150
            color: root.color
            height: col.height

            property int rowCount: qmlProfilerModelProxy.categoryCount();

            Column {
                id: col
                Repeater {
                    model: labels.rowCount
                    delegate: CategoryLabel { }
                }
            }
        }
    }

    // border between labels and timeline
    Rectangle {
        id: labelsborder
        anchors.left: labelsflick.right
        anchors.top: parent.top
        anchors.bottom: parent.bottom
        width: 1
        color: "#858585"
    }

    Flickable {
        id: flick
240
        contentHeight: labels.height
241
242
        contentWidth: 0
        flickableDirection: Flickable.HorizontalAndVerticalFlick
243
        boundsBehavior: Flickable.StopAtBounds
244
        clip:true
245
246

        // ScrollView will try to deinteractivate it. We don't want that
247
248
249
250
251
        // as the horizontal flickable is interactive, too. We do occasionally
        // switch to non-interactive ourselves, though.
        property bool stayInteractive: true
        onInteractiveChanged: interactive = stayInteractive
        onStayInteractiveChanged: interactive = stayInteractive
Christiaan Janssen's avatar
Christiaan Janssen committed
252

253
254
        onContentXChanged: view.updateZoomControl()
        onWidthChanged: {
255
256
257
258
259
            var duration = Math.abs(zoomControl.endTime() - zoomControl.startTime());
            if (duration > 0)
                contentWidth = qmlProfilerModelProxy.traceDuration() * width / duration;
        }

260
261
262
        // ***** child items
        TimeMarks {
            id: backgroundMarks
263
264
265
266
            y: flick.contentY
            x: flick.contentX
            height: flick.height
            width: scroller.width
267
268
        }

269
270
271
272
273
        SelectionRange {
            id: selectionRange
            visible: root.selectionRangeMode && creationState !== 0
            z: 2
        }
274

275
276
        TimelineRenderer {
            id: view
277

278
            profilerModelProxy: qmlProfilerModelProxy
279

280
281
            x: flick.contentX
            y: flick.contentY
282

283
284
285
            // paint "under" the vertical scrollbar, so that it always matches with the timemarks
            width: scroller.width
            height: flick.height
Christiaan Janssen's avatar
Christiaan Janssen committed
286

287
288
289
            onEndTimeChanged: requestPaint()
            onYChanged: requestPaint()
            onHeightChanged: requestPaint()
290
            property bool recursionGuard: false
291

292
            function updateZoomControl() {
293
294
295
296
297
298
                // Don't updateZoomControl if we're just updating the flick range, _from_
                // zoomControl. The other way round is OK. We _want_ the flick range to be updated
                // on external changes to zoomControl.
                if (recursionGuard)
                    return;

299
300
301
302
303
304
305
306
                var newStartTime = Math.round(flick.contentX * (endTime - startTime) / flick.width) +
                        qmlProfilerModelProxy.traceStartTime();
                if (Math.abs(newStartTime - startTime) > 1) {
                    var newEndTime = Math.round((flick.contentX + flick.width) *
                                                (endTime - startTime) /
                                                flick.width) +
                                                qmlProfilerModelProxy.traceStartTime();
                    zoomControl.setRange(newStartTime, newEndTime);
307
                }
308
            }
309
310

            function updateFlickRange(start, end) {
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
                var duration = end - start;
                if (recursionGuard || duration <= 0 || (start === startTime && end === endTime))
                    return;

                recursionGuard = true;

                startTime = start;
                endTime = end;
                if (!flick.flickingHorizontally) {
                    // This triggers an unwanted automatic change in contentX. We ignore that by
                    // checking recursionGuard in this function and in updateZoomControl.
                    flick.contentWidth = qmlProfilerModelProxy.traceDuration() * flick.width /
                            duration;

                    var newStartX = (startTime - qmlProfilerModelProxy.traceStartTime()) *
                            flick.width / duration;

                    if (isFinite(newStartX) && Math.abs(newStartX - flick.contentX) >= 1)
                        flick.contentX = newStartX;
330
                }
331
                recursionGuard = false;
332
            }
Christiaan Janssen's avatar
Christiaan Janssen committed
333

334
335
336
337
338
            onSelectedItemChanged: {
                if (selectedItem !== -1) {
                    // display details
                    rangeDetails.showInfo(qmlProfilerModelProxy.getEventDetails(selectedModel, selectedItem));
                    rangeDetails.setLocation(qmlProfilerModelProxy.getEventLocation(selectedModel, selectedItem));
339

340
                    // center view (horizontally)
341
                    recenterOnItem(selectedModel, selectedItem);
Ulf Hermann's avatar
Ulf Hermann committed
342
343
344
345
346
347
348
349
                    if (!lockItemSelection) {
                        lockItemSelection = true;
                        // update in other views
                        var eventLocation = qmlProfilerModelProxy.getEventLocation(
                                    view.selectedModel, view.selectedItem);
                        gotoSourceLocation(eventLocation.file, eventLocation.line,
                                           eventLocation.column);
                        lockItemSelection = false;
350
351
352
                    }
                } else {
                    root.hideRangeDetails();
353
                }
354
            }
355

356
357
358
359
360
            onItemPressed: {
                var location = qmlProfilerModelProxy.getEventLocation(modelIndex, pressedItem);
                if (location.hasOwnProperty("file")) // not empty
                    root.gotoSourceLocation(location.file, location.line, location.column);
            }
361

362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
         // hack to pass mouse events to the other mousearea if enabled
            startDragArea: selectionRange.ready ? selectionRange.getLeft() : -flick.contentX
            endDragArea: selectionRange.ready ? selectionRange.getRight() : -flick.contentX-1
        }
        MouseArea {
            id: selectionRangeControl
            enabled: false
            width: flick.width
            height: flick.height
            x: flick.contentX
            y: flick.contentY
            hoverEnabled: enabled
            z: 2

            onReleased:  {
                selectionRange.releasedOnCreation();
            }
            onPressed:  {
                selectionRange.pressedOnCreation();
            }
            onCanceled: {
                selectionRange.releasedOnCreation();
            }
            onPositionChanged: {
                selectionRange.movedOnCreation();
            }
388
389
        }
    }
390

391
    ScrollView {
392
393
394
395
396
397
        id: scroller
        contentItem: flick
        anchors.left: labelsborder.right
        anchors.top: parent.top
        anchors.bottom: parent.bottom
        anchors.right: parent.right
398
399
    }

400
401
    SelectionRangeDetails {
        id: selectionRangeDetails
Ulf Hermann's avatar
Ulf Hermann committed
402
        visible: selectionRange.visible
403
404
405
        startTime: selectionRange.startTimeString
        duration: selectionRange.durationString
        endTime: selectionRange.endTimeString
406
        showDuration: selectionRange.getWidth() > 1
407
408
409
410
411
    }

    RangeDetails {
        id: rangeDetails
    }
412

413
414
415
416
417
418
419
420
421
422
    Rectangle {
        objectName: "zoomSliderToolBar"
        color: "#9b9b9b"
        enabled: false
        visible: false
        width: labels.width
        height: 24
        x: 0
        y: 0

423
424
425
426
427
        function updateZoomLevel() {
            zoomSlider.externalUpdate = true;
            zoomSlider.value = Math.pow((view.endTime - view.startTime) / qmlProfilerModelProxy.traceDuration(), 1 / zoomSlider.exponent) * zoomSlider.maximumValue;
        }

428
429
430
431
432
433
434
435

        Slider {
            id: zoomSlider
            anchors.fill: parent
            minimumValue: 1
            maximumValue: 10000
            stepSize: 100

436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
            property int exponent: 3
            property bool externalUpdate: false
            property int minWindowLength: 1e5 // 0.1 ms

            onValueChanged: {
                if (externalUpdate || qmlProfilerModelProxy.traceEndTime() <= qmlProfilerModelProxy.traceStartTime()) {
                    // Zoom range is independently updated. We shouldn't mess
                    // with it here as otherwise we might introduce rounding
                    // or arithmetic errors.
                    externalUpdate = false;
                    return;
                }

                var windowLength = Math.max(
                            Math.pow(value / maximumValue, exponent) * qmlProfilerModelProxy.traceDuration(),
                            minWindowLength);

                var fixedPoint = (view.startTime + view.endTime) / 2;
                if (view.selectedItem !== -1) {
                    // center on selected item if it's inside the current screen
                    var newFixedPoint = qmlProfilerModelProxy.getStartTime(view.selectedModel, view.selectedItem);
                    if (newFixedPoint >= view.startTime && newFixedPoint < view.endTime)
                        fixedPoint = newFixedPoint;
                }

                var startTime = Math.max(qmlProfilerModelProxy.traceStartTime(), fixedPoint - windowLength / 2)
                zoomControl.setRange(startTime, startTime + windowLength);
            }
464
465
        }
    }
Christiaan Janssen's avatar
Christiaan Janssen committed
466
}