forked from qt-creator/qt-creator
QmlProfiler: Make TimeDisplay and TimeMarks declarative
Create the vertical elements only once and stretch them over the whole vertical space, but keep all horizontal elements distinct per model. Also, add horizontal elements to the labels group and rename that accordingly, so that we don't need a third Flickable or ListView Change-Id: I4fe3bd526767e8ff5d0ebcd70e9343f65fcc787f Reviewed-by: Kai Koehne <kai.koehne@theqtcompany.com>
This commit is contained in:
@@ -124,14 +124,6 @@ Item {
|
|||||||
renderType: Text.NativeRendering
|
renderType: Text.NativeRendering
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
height: 1
|
|
||||||
width: parent.width
|
|
||||||
color: "#999999"
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
z: 2
|
|
||||||
}
|
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
id: column
|
id: column
|
||||||
property QtObject parentModel: model
|
property QtObject parentModel: model
|
||||||
|
|||||||
@@ -105,7 +105,6 @@ Rectangle {
|
|||||||
zoomSlider.externalUpdate = true;
|
zoomSlider.externalUpdate = true;
|
||||||
zoomSlider.value = zoomSlider.minimumValue;
|
zoomSlider.value = zoomSlider.minimumValue;
|
||||||
overview.clear();
|
overview.clear();
|
||||||
timeDisplay.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function propagateSelection(newModel, newItem) {
|
function propagateSelection(newModel, newItem) {
|
||||||
@@ -190,46 +189,64 @@ Rectangle {
|
|||||||
Keys.onReleased: shiftPressed = false;
|
Keys.onReleased: shiftPressed = false;
|
||||||
|
|
||||||
Flickable {
|
Flickable {
|
||||||
id: labelsflick
|
id: categories
|
||||||
flickableDirection: Flickable.VerticalFlick
|
flickableDirection: Flickable.VerticalFlick
|
||||||
interactive: false
|
interactive: false
|
||||||
anchors.top: buttonsBar.bottom
|
anchors.top: buttonsBar.bottom
|
||||||
anchors.bottom: overview.top
|
anchors.bottom: overview.top
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
width: labels.width
|
anchors.right: parent.right
|
||||||
contentY: flick.contentY
|
contentY: flick.contentY
|
||||||
|
|
||||||
// reserve some more space than needed to prevent weird effects when resizing
|
// reserve some more space than needed to prevent weird effects when resizing
|
||||||
contentHeight: labels.height + height
|
contentHeight: categoryContent.height + height
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: labels
|
|
||||||
anchors.left: parent.left
|
|
||||||
width: 150
|
|
||||||
color: root.color
|
|
||||||
height: col.height
|
|
||||||
|
|
||||||
Column {
|
|
||||||
id: col
|
|
||||||
|
|
||||||
// Dispatch the cursor shape to all labels. When dragging the DropArea receiving
|
// Dispatch the cursor shape to all labels. When dragging the DropArea receiving
|
||||||
// the drag events is not necessarily related to the MouseArea receiving the mouse
|
// the drag events is not necessarily related to the MouseArea receiving the mouse
|
||||||
// events, so we can't use the drag events to determine the cursor shape.
|
// events, so we can't use the drag events to determine the cursor shape.
|
||||||
property bool dragging: false
|
property bool dragging: false
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: categoryContent
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
|
||||||
DelegateModel {
|
DelegateModel {
|
||||||
id: labelsModel
|
id: labelsModel
|
||||||
|
|
||||||
|
// As we cannot retrieve items by visible index we keep an array of row counts here,
|
||||||
|
// for the time marks to draw the row backgrounds in the right colors.
|
||||||
|
property var rowCounts: new Array(qmlProfilerModelProxy.models.length)
|
||||||
|
|
||||||
|
function updateRowCount(visualIndex, rowCount) {
|
||||||
|
if (rowCounts[visualIndex] !== rowCount) {
|
||||||
|
rowCounts[visualIndex] = rowCount;
|
||||||
|
// Array don't "change" if entries change. We have to signal manually.
|
||||||
|
rowCountsChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
model: qmlProfilerModelProxy.models
|
model: qmlProfilerModelProxy.models
|
||||||
delegate: CategoryLabel {
|
delegate: Rectangle {
|
||||||
|
color: root.color
|
||||||
|
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
property int visualIndex: DelegateModel.itemsIndex
|
||||||
|
height: label.visible ? label.height : 0
|
||||||
|
|
||||||
|
CategoryLabel {
|
||||||
|
id: label
|
||||||
model: modelData
|
model: modelData
|
||||||
mockup: qmlProfilerModelProxy.height == 0
|
mockup: qmlProfilerModelProxy.height === 0
|
||||||
visualIndex: DelegateModel.itemsIndex
|
visualIndex: parent.visualIndex
|
||||||
dragging: col.dragging
|
dragging: categories.dragging
|
||||||
reverseSelect: root.shiftPressed
|
reverseSelect: root.shiftPressed
|
||||||
onDragStarted: col.dragging = true
|
onDragStarted: categories.dragging = true
|
||||||
onDragStopped: col.dragging = false
|
onDragStopped: categories.dragging = false
|
||||||
draggerParent: labels
|
draggerParent: categories
|
||||||
dragOffset: y
|
width: 150
|
||||||
|
dragOffset: parent.y
|
||||||
|
|
||||||
onDropped: {
|
onDropped: {
|
||||||
timelineModel.items.move(sourceIndex, targetIndex);
|
timelineModel.items.move(sourceIndex, targetIndex);
|
||||||
@@ -251,8 +268,36 @@ Rectangle {
|
|||||||
zoomControl.rangeStart,
|
zoomControl.rangeStart,
|
||||||
root.selectedModel === index ? root.selectedItem : -1));
|
root.selectedModel === index ? root.selectedItem : -1));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TimeMarks {
|
||||||
|
id: timeMarks
|
||||||
|
model: modelData
|
||||||
|
mockup: qmlProfilerModelProxy.height === 0
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.left: label.right
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
property int visualIndex: parent.visualIndex
|
||||||
|
|
||||||
|
// Quite a mouthful, but works fine: Add up all the row counts up to the one
|
||||||
|
// for this visual index and check if the result is even or odd.
|
||||||
|
startOdd: (labelsModel.rowCounts.slice(0, visualIndex).reduce(
|
||||||
|
function(prev, rows) {return prev + rows}, 0) % 2) === 0
|
||||||
|
|
||||||
|
onRowCountChanged: labelsModel.updateRowCount(visualIndex, rowCount)
|
||||||
|
onVisualIndexChanged: labelsModel.updateRowCount(visualIndex, rowCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
visible: label.visible
|
||||||
|
opacity: parent.y == 0 ? 0 : 1
|
||||||
|
color: "#B0B0B0"
|
||||||
|
height: 1
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.top: parent.top
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -260,17 +305,25 @@ Rectangle {
|
|||||||
model: labelsModel
|
model: labelsModel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.top: categoryContent.bottom
|
||||||
|
height: 1
|
||||||
|
color: "#B0B0B0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// border between labels and timeline
|
TimeDisplay {
|
||||||
Rectangle {
|
id: timeDisplay
|
||||||
id: labelsborder
|
|
||||||
anchors.left: labelsflick.right
|
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
|
anchors.left: buttonsBar.right
|
||||||
|
anchors.right: parent.right
|
||||||
anchors.bottom: overview.top
|
anchors.bottom: overview.top
|
||||||
width: 1
|
zoomer: zoomControl
|
||||||
color: "#858585"
|
contentX: flick.contentX
|
||||||
|
clip: true
|
||||||
}
|
}
|
||||||
|
|
||||||
ButtonsBar {
|
ButtonsBar {
|
||||||
@@ -297,21 +350,12 @@ Rectangle {
|
|||||||
onLockChanged: selectionLocked = !lockButtonChecked();
|
onLockChanged: selectionLocked = !lockButtonChecked();
|
||||||
}
|
}
|
||||||
|
|
||||||
TimeDisplay {
|
|
||||||
id: timeDisplay
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.left: labelsborder.right
|
|
||||||
anchors.right: parent.right
|
|
||||||
height: 24
|
|
||||||
}
|
|
||||||
|
|
||||||
Flickable {
|
Flickable {
|
||||||
id: flick
|
id: flick
|
||||||
contentHeight: labels.height
|
contentHeight: categoryContent.height
|
||||||
contentWidth: zoomControl.windowDuration * width / Math.max(1, zoomControl.rangeDuration)
|
contentWidth: zoomControl.windowDuration * width / Math.max(1, zoomControl.rangeDuration)
|
||||||
flickableDirection: Flickable.HorizontalAndVerticalFlick
|
flickableDirection: Flickable.HorizontalAndVerticalFlick
|
||||||
boundsBehavior: Flickable.StopAtBounds
|
boundsBehavior: Flickable.StopAtBounds
|
||||||
clip:true
|
|
||||||
|
|
||||||
// ScrollView will try to deinteractivate it. We don't want that
|
// ScrollView will try to deinteractivate it. We don't want that
|
||||||
// as the horizontal flickable is interactive, too. We do occasionally
|
// as the horizontal flickable is interactive, too. We do occasionally
|
||||||
@@ -375,9 +419,6 @@ Rectangle {
|
|||||||
signal clearChildren
|
signal clearChildren
|
||||||
signal select(int modelIndex, int eventIndex)
|
signal select(int modelIndex, int eventIndex)
|
||||||
|
|
||||||
// As we cannot retrieve items by visible index we keep an array of row counts here,
|
|
||||||
// for the time marks to draw the row backgrounds in the right colors.
|
|
||||||
property var rowCounts: new Array(qmlProfilerModelProxy.models.length)
|
|
||||||
|
|
||||||
DelegateModel {
|
DelegateModel {
|
||||||
id: timelineModel
|
id: timelineModel
|
||||||
@@ -386,35 +427,8 @@ Rectangle {
|
|||||||
id: spacer
|
id: spacer
|
||||||
height: modelData.height
|
height: modelData.height
|
||||||
width: flick.contentWidth
|
width: flick.contentWidth
|
||||||
property int rowCount: (modelData.empty || modelData.hidden) ?
|
|
||||||
0 : modelData.rowCount
|
|
||||||
property int visualIndex: DelegateModel.itemsIndex
|
property int visualIndex: DelegateModel.itemsIndex
|
||||||
|
|
||||||
function updateRowParentCount() {
|
|
||||||
if (timelineView.rowCounts[visualIndex] !== rowCount) {
|
|
||||||
timelineView.rowCounts[visualIndex] = rowCount;
|
|
||||||
// Array don't "change" if entries change. We have to signal manually.
|
|
||||||
timelineView.rowCountsChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onRowCountChanged: updateRowParentCount()
|
|
||||||
onVisualIndexChanged: updateRowParentCount()
|
|
||||||
|
|
||||||
TimeMarks {
|
|
||||||
model: modelData
|
|
||||||
id: backgroundMarks
|
|
||||||
anchors.fill: renderer
|
|
||||||
startTime: zoomControl.rangeStart
|
|
||||||
endTime: zoomControl.rangeEnd
|
|
||||||
|
|
||||||
// Quite a mouthful, but works fine: Add up all the row counts up to the one
|
|
||||||
// for this visual index and check if the result is even or odd.
|
|
||||||
startOdd: (timelineView.rowCounts.slice(0, spacer.visualIndex).reduce(
|
|
||||||
function(prev, rows) {return prev + rows}, 0) % 2) === 0
|
|
||||||
onStartOddChanged: requestPaint()
|
|
||||||
}
|
|
||||||
|
|
||||||
TimelineRenderer {
|
TimelineRenderer {
|
||||||
id: renderer
|
id: renderer
|
||||||
model: modelData
|
model: modelData
|
||||||
@@ -543,8 +557,8 @@ Rectangle {
|
|||||||
ScrollView {
|
ScrollView {
|
||||||
id: scroller
|
id: scroller
|
||||||
contentItem: flick
|
contentItem: flick
|
||||||
anchors.left: labelsborder.right
|
anchors.left: buttonsBar.right
|
||||||
anchors.top: timeDisplay.bottom
|
anchors.top: categories.top
|
||||||
anchors.bottom: overview.top
|
anchors.bottom: overview.top
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
}
|
}
|
||||||
@@ -565,12 +579,20 @@ Rectangle {
|
|||||||
notes: qmlProfilerModelProxy.notes
|
notes: qmlProfilerModelProxy.notes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.left: buttonsBar.right
|
||||||
|
anchors.bottom: overview.top
|
||||||
|
anchors.top: parent.top
|
||||||
|
width: 1
|
||||||
|
color: "#B0B0B0"
|
||||||
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: filterMenu
|
id: filterMenu
|
||||||
color: "#9b9b9b"
|
color: "#9b9b9b"
|
||||||
enabled: buttonsBar.enabled
|
enabled: buttonsBar.enabled
|
||||||
visible: false
|
visible: false
|
||||||
width: labels.width
|
width: buttonsBar.width
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.top: buttonsBar.bottom
|
anchors.top: buttonsBar.bottom
|
||||||
height: qmlProfilerModelProxy.models.length * buttonsBar.height
|
height: qmlProfilerModelProxy.models.length * buttonsBar.height
|
||||||
@@ -598,7 +620,7 @@ Rectangle {
|
|||||||
color: "#9b9b9b"
|
color: "#9b9b9b"
|
||||||
enabled: buttonsBar.enabled
|
enabled: buttonsBar.enabled
|
||||||
visible: false
|
visible: false
|
||||||
width: labels.width
|
width: categoryContent.width
|
||||||
height: buttonsBar.height
|
height: buttonsBar.height
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.top: buttonsBar.bottom
|
anchors.top: buttonsBar.bottom
|
||||||
|
|||||||
@@ -29,87 +29,44 @@
|
|||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
import QtQuick 2.1
|
import QtQuick 2.1
|
||||||
import Monitor 1.0
|
|
||||||
|
|
||||||
Canvas {
|
Item {
|
||||||
id: timeDisplay
|
id: timeDisplay
|
||||||
objectName: "TimeDisplay"
|
|
||||||
contextType: "2d"
|
|
||||||
|
|
||||||
Connections {
|
property QtObject zoomer
|
||||||
target: zoomControl
|
|
||||||
onRangeChanged: requestPaint();
|
|
||||||
}
|
|
||||||
|
|
||||||
onPaint: {
|
readonly property int labelsHeight: 24
|
||||||
var context = (timeDisplay.context === null) ? getContext("2d") : timeDisplay.context;
|
|
||||||
|
|
||||||
context.reset();
|
readonly property int initialBlockLength: 120
|
||||||
context.fillStyle = "white";
|
|
||||||
context.fillRect(0, 0, width, height);
|
|
||||||
|
|
||||||
var totalTime = Math.max(1, zoomControl.rangeDuration);
|
property double spacing: width / rangeDuration
|
||||||
var spacing = width / totalTime;
|
|
||||||
|
|
||||||
var initialBlockLength = 120;
|
property double timePerBlock: Math.pow(2, Math.floor(Math.log(initialBlockLength / spacing) /
|
||||||
var timePerBlock = Math.pow(2, Math.floor( Math.log( totalTime / width * initialBlockLength ) / Math.LN2 ) );
|
Math.LN2))
|
||||||
var pixelsPerBlock = timePerBlock * spacing;
|
|
||||||
var pixelsPerSection = pixelsPerBlock / 5;
|
|
||||||
var blockCount = width / pixelsPerBlock;
|
|
||||||
|
|
||||||
var realStartTime = Math.floor(zoomControl.rangeStart / timePerBlock) * timePerBlock;
|
property double rangeDuration: Math.max(1, Math.round(zoomer.rangeDuration))
|
||||||
var startPos = (zoomControl.rangeStart - realStartTime) * spacing;
|
property double alignedWindowStart: Math.round(zoomer.windowStart - (zoomer.windowStart % timePerBlock))
|
||||||
|
property double pixelsPerBlock: timeDisplay.timePerBlock * timeDisplay.spacing
|
||||||
|
property double pixelsPerSection: pixelsPerBlock / 5
|
||||||
|
|
||||||
var timePerPixel = timePerBlock/pixelsPerBlock;
|
property int contentX
|
||||||
|
property int offsetX: contentX + Math.round((zoomer.windowStart % timePerBlock) * spacing)
|
||||||
|
|
||||||
var initialColor = Math.floor(realStartTime/timePerBlock) % 2;
|
readonly property var timeUnits: ["μs", "ms", "s"]
|
||||||
|
function prettyPrintTime(t, rangeDuration) {
|
||||||
context.fillStyle = "#000000";
|
|
||||||
context.font = "8px sans-serif";
|
|
||||||
for (var ii = 0; ii < blockCount+1; ii++) {
|
|
||||||
var x = Math.floor(ii*pixelsPerBlock - startPos);
|
|
||||||
|
|
||||||
context.fillStyle = (ii+initialColor)%2 ? "#E6E6E6":"white";
|
|
||||||
context.fillRect(x, 0, pixelsPerBlock, height);
|
|
||||||
|
|
||||||
context.strokeStyle = "#B0B0B0";
|
|
||||||
context.beginPath();
|
|
||||||
context.moveTo(x, 0);
|
|
||||||
context.lineTo(x, height);
|
|
||||||
context.stroke();
|
|
||||||
|
|
||||||
context.fillStyle = "#000000";
|
|
||||||
context.fillText(prettyPrintTime(ii*timePerBlock + realStartTime), x + 5, height/2 + 5);
|
|
||||||
}
|
|
||||||
|
|
||||||
context.strokeStyle = "#525252";
|
|
||||||
context.beginPath();
|
|
||||||
context.moveTo(0, height-1);
|
|
||||||
context.lineTo(width, height-1);
|
|
||||||
context.stroke();
|
|
||||||
}
|
|
||||||
|
|
||||||
function clear()
|
|
||||||
{
|
|
||||||
requestPaint();
|
|
||||||
}
|
|
||||||
|
|
||||||
function prettyPrintTime( t )
|
|
||||||
{
|
|
||||||
var round = 1;
|
var round = 1;
|
||||||
var barrier = 1;
|
var barrier = 1;
|
||||||
var units = ["μs", "ms", "s"];
|
|
||||||
|
|
||||||
for (var i = 0; i < units.length; ++i) {
|
for (var i = 0; i < timeUnits.length; ++i) {
|
||||||
barrier *= 1000;
|
barrier *= 1000;
|
||||||
if (zoomControl.rangeDuration < barrier)
|
if (rangeDuration < barrier)
|
||||||
round *= 1000;
|
round *= 1000;
|
||||||
else if (zoomControl.rangeDuration < barrier * 10)
|
else if (rangeDuration < barrier * 10)
|
||||||
round *= 100;
|
round *= 100;
|
||||||
else if (zoomControl.rangeDuration < barrier * 100)
|
else if (rangeDuration < barrier * 100)
|
||||||
round *= 10;
|
round *= 10;
|
||||||
if (t < barrier * 1000)
|
if (t < barrier * 1000)
|
||||||
return Math.floor(t / (barrier / round)) / round + units[i];
|
return Math.floor(t / (barrier / round)) / round + timeUnits[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
t /= barrier;
|
t /= barrier;
|
||||||
@@ -117,4 +74,111 @@ Canvas {
|
|||||||
var s = Math.floor((t - m * 60) * round) / round;
|
var s = Math.floor((t - m * 60) * round) / round;
|
||||||
return m + "m" + s + "s";
|
return m + "m" + s + "s";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
x: Math.floor(firstBlock * timeDisplay.pixelsPerBlock - timeDisplay.offsetX)
|
||||||
|
y: 0
|
||||||
|
id: row
|
||||||
|
|
||||||
|
property int firstBlock: timeDisplay.offsetX / timeDisplay.pixelsPerBlock
|
||||||
|
property int offset: firstBlock % repeater.model
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
id: repeater
|
||||||
|
model: Math.floor(timeDisplay.width / timeDisplay.initialBlockLength * 2 + 2)
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: column
|
||||||
|
|
||||||
|
// Changing the text in text nodes is expensive. We minimize the number of changes
|
||||||
|
// by rotating the nodes during scrolling.
|
||||||
|
property int stableIndex: row.offset > index ? repeater.model - row.offset + index :
|
||||||
|
index - row.offset
|
||||||
|
height: timeDisplay.height
|
||||||
|
y: 0
|
||||||
|
x: width * stableIndex
|
||||||
|
width: timeDisplay.pixelsPerBlock
|
||||||
|
|
||||||
|
// Manually control this. We don't want it to happen when firstBlock
|
||||||
|
// changes before stableIndex changes.
|
||||||
|
onStableIndexChanged: block = row.firstBlock + stableIndex
|
||||||
|
property int block: -1
|
||||||
|
property double blockStartTime: block * timeDisplay.timePerBlock +
|
||||||
|
timeDisplay.alignedWindowStart
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: timeLabel
|
||||||
|
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
height: timeDisplay.labelsHeight
|
||||||
|
|
||||||
|
color: (Math.round(column.block + timeDisplay.alignedWindowStart /
|
||||||
|
timeDisplay.timePerBlock) % 2) ?
|
||||||
|
"#E6E6E6" : "white";
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: labelText
|
||||||
|
renderType: Text.NativeRendering
|
||||||
|
font.pixelSize: 8
|
||||||
|
font.family: "sans-serif"
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.leftMargin: 5
|
||||||
|
anchors.bottomMargin: 5
|
||||||
|
verticalAlignment: Text.AlignBottom
|
||||||
|
text: prettyPrintTime(column.blockStartTime, timeDisplay.rangeDuration)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.top: timeLabel.bottom
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: 4
|
||||||
|
Item {
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
width: timeDisplay.pixelsPerSection
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
color: "#CCCCCC"
|
||||||
|
width: 1
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.right: parent.right
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
color: "#B0B0B0"
|
||||||
|
width: 1
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.right: parent.right
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
height: 2
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
y: labelsHeight - 2
|
||||||
|
color: "#B0B0B0"
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
height: 1
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.top: row.bottom
|
||||||
|
color: "#B0B0B0"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,72 +29,20 @@
|
|||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
import QtQuick 2.1
|
import QtQuick 2.1
|
||||||
import Monitor 1.0
|
|
||||||
|
|
||||||
Canvas {
|
Item {
|
||||||
id: timeMarks
|
id: timeMarks
|
||||||
objectName: "TimeMarks"
|
visible: model && (mockup || (!model.hidden && !model.empty))
|
||||||
contextType: "2d"
|
|
||||||
|
|
||||||
property QtObject model
|
property QtObject model
|
||||||
property bool startOdd
|
property bool startOdd
|
||||||
|
property bool mockup
|
||||||
|
|
||||||
readonly property int scaleMinHeight: 60
|
readonly property int scaleMinHeight: 60
|
||||||
readonly property int scaleStepping: 30
|
readonly property int scaleStepping: 30
|
||||||
readonly property string units: " kMGT"
|
readonly property string units: " kMGT"
|
||||||
|
|
||||||
property real startTime
|
|
||||||
property real endTime
|
|
||||||
property real timePerPixel
|
|
||||||
|
|
||||||
Connections {
|
property int rowCount: model ? model.rowCount : 0
|
||||||
target: model
|
|
||||||
onHeightChanged: requestPaint()
|
|
||||||
}
|
|
||||||
|
|
||||||
onStartTimeChanged: requestPaint()
|
|
||||||
onEndTimeChanged: requestPaint()
|
|
||||||
onYChanged: requestPaint()
|
|
||||||
onHeightChanged: requestPaint()
|
|
||||||
|
|
||||||
onPaint: {
|
|
||||||
var context = (timeMarks.context === null) ? getContext("2d") : timeMarks.context;
|
|
||||||
context.reset();
|
|
||||||
drawBackgroundBars( context, region );
|
|
||||||
|
|
||||||
var totalTime = endTime - startTime;
|
|
||||||
var spacing = width / totalTime;
|
|
||||||
|
|
||||||
var initialBlockLength = 120;
|
|
||||||
var timePerBlock = Math.pow(2, Math.floor( Math.log( totalTime / width * initialBlockLength ) / Math.LN2 ) );
|
|
||||||
var pixelsPerBlock = timePerBlock * spacing;
|
|
||||||
var pixelsPerSection = pixelsPerBlock / 5;
|
|
||||||
var blockCount = width / pixelsPerBlock;
|
|
||||||
|
|
||||||
var realStartTime = Math.floor(startTime/timePerBlock) * timePerBlock;
|
|
||||||
var realStartPos = (startTime-realStartTime) * spacing;
|
|
||||||
|
|
||||||
timePerPixel = timePerBlock/pixelsPerBlock;
|
|
||||||
|
|
||||||
|
|
||||||
for (var ii = 0; ii < blockCount+1; ii++) {
|
|
||||||
var x = Math.floor(ii*pixelsPerBlock - realStartPos);
|
|
||||||
context.strokeStyle = "#B0B0B0";
|
|
||||||
context.beginPath();
|
|
||||||
context.moveTo(x, 0);
|
|
||||||
context.lineTo(x, height);
|
|
||||||
context.stroke();
|
|
||||||
|
|
||||||
context.strokeStyle = "#CCCCCC";
|
|
||||||
for (var jj=1; jj < 5; jj++) {
|
|
||||||
var xx = Math.floor(ii*pixelsPerBlock + jj*pixelsPerSection - realStartPos);
|
|
||||||
context.beginPath();
|
|
||||||
context.moveTo(xx, 0);
|
|
||||||
context.lineTo(xx, height);
|
|
||||||
context.stroke();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function prettyPrintScale(amount) {
|
function prettyPrintScale(amount) {
|
||||||
var unitOffset = 0;
|
var unitOffset = 0;
|
||||||
@@ -112,66 +60,88 @@ Canvas {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function drawBackgroundBars( context, region ) {
|
Connections {
|
||||||
var colorIndex = startOdd;
|
target: model
|
||||||
|
onRowHeightChanged: {
|
||||||
context.font = "8px sans-serif";
|
if (row >= 0)
|
||||||
|
rowRepeater.itemAt(row).height = height;
|
||||||
// separators
|
|
||||||
var cumulatedHeight = 0;
|
|
||||||
|
|
||||||
for (var row = 0; row < model.rowCount; ++row) {
|
|
||||||
// row background
|
|
||||||
var rowHeight = model.rowHeight(row)
|
|
||||||
cumulatedHeight += rowHeight;
|
|
||||||
colorIndex = !colorIndex;
|
|
||||||
if (cumulatedHeight < y - rowHeight)
|
|
||||||
continue;
|
|
||||||
context.strokeStyle = context.fillStyle = colorIndex ? "#f0f0f0" : "white";
|
|
||||||
context.fillRect(0, cumulatedHeight - rowHeight - y, width, rowHeight);
|
|
||||||
|
|
||||||
if (rowHeight >= scaleMinHeight) {
|
|
||||||
var minVal = model.rowMinValue(row);
|
|
||||||
var maxVal = model.rowMaxValue(row);
|
|
||||||
if (minVal !== maxVal) {
|
|
||||||
context.strokeStyle = context.fillStyle = "#B0B0B0";
|
|
||||||
|
|
||||||
var stepValUgly = Math.ceil((maxVal - minVal) /
|
|
||||||
Math.floor(rowHeight / scaleStepping));
|
|
||||||
|
|
||||||
// align to clean 2**x
|
|
||||||
var stepVal = 1;
|
|
||||||
while (stepValUgly >>= 1)
|
|
||||||
stepVal <<= 1;
|
|
||||||
|
|
||||||
var stepHeight = rowHeight / (maxVal - minVal);
|
|
||||||
|
|
||||||
for (var step = minVal; step <= maxVal - stepVal; step += stepVal) {
|
|
||||||
var offset = cumulatedHeight - step * stepHeight - y;
|
|
||||||
context.beginPath();
|
|
||||||
context.moveTo(0, offset);
|
|
||||||
context.lineTo(width, offset);
|
|
||||||
context.stroke();
|
|
||||||
context.fillText(prettyPrintScale(step), 5, offset - 2);
|
|
||||||
}
|
|
||||||
context.beginPath();
|
|
||||||
context.moveTo(0, cumulatedHeight - rowHeight - y);
|
|
||||||
context.lineTo(width, cumulatedHeight - rowHeight - y);
|
|
||||||
context.stroke();
|
|
||||||
context.fillText(prettyPrintScale(maxVal), 5,
|
|
||||||
cumulatedHeight - rowHeight - y + 8);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cumulatedHeight > y + height)
|
|
||||||
return;
|
Column {
|
||||||
|
id: rows
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
Repeater {
|
||||||
|
id: rowRepeater
|
||||||
|
model: timeMarks.rowCount
|
||||||
|
Rectangle {
|
||||||
|
id: row
|
||||||
|
color: ((index + (startOdd ? 1 : 0)) % 2) ? "#f0f0f0" : "white"
|
||||||
|
anchors.left: rows.left
|
||||||
|
anchors.right: rows.right
|
||||||
|
height: timeMarks.model ? timeMarks.model.rowHeight(index) : 0
|
||||||
|
|
||||||
|
property int minVal: timeMarks.model ? timeMarks.model.rowMinValue(index) : 0
|
||||||
|
property int maxVal: timeMarks.model ? timeMarks.model.rowMaxValue(index) : 0
|
||||||
|
property int valDiff: maxVal - minVal
|
||||||
|
property bool scaleVisible: timeMarks.model && timeMarks.model.expanded &&
|
||||||
|
height > scaleMinHeight && valDiff > 0
|
||||||
|
|
||||||
|
property int stepVal: {
|
||||||
|
var ret = 1;
|
||||||
|
var ugly = Math.ceil(valDiff / Math.floor(height / scaleStepping));
|
||||||
|
while (isFinite(ugly) && ugly > 1) {
|
||||||
|
ugly >>= 1;
|
||||||
|
ret <<= 1;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
context.strokeStyle = "#B0B0B0";
|
Text {
|
||||||
context.beginPath();
|
id: scaleTopLabel
|
||||||
context.moveTo(0, cumulatedHeight - y);
|
renderType: Text.NativeRendering
|
||||||
context.lineTo(width, cumulatedHeight - y);
|
visible: parent.scaleVisible
|
||||||
context.stroke();
|
color: "#B0B0B0"
|
||||||
|
font.pixelSize: 8
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.leftMargin: 2
|
||||||
|
anchors.topMargin: 2
|
||||||
|
anchors.left: parent.left
|
||||||
|
text: prettyPrintScale(row.maxVal)
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: parent.scaleVisible ? row.valDiff / row.stepVal : 0
|
||||||
|
|
||||||
|
Item {
|
||||||
|
anchors.left: row.left
|
||||||
|
anchors.right: row.right
|
||||||
|
height: row.stepVal * row.height / row.valDiff
|
||||||
|
y: row.height - (index + 1) * height
|
||||||
|
visible: y > scaleTopLabel.height
|
||||||
|
Text {
|
||||||
|
renderType: Text.NativeRendering
|
||||||
|
color: "#B0B0B0"
|
||||||
|
font.pixelSize: 8
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.bottomMargin: 2
|
||||||
|
anchors.leftMargin: 2
|
||||||
|
anchors.left: parent.left
|
||||||
|
text: prettyPrintScale(index * row.stepVal)
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
height: 1
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
color: "#B0B0B0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user