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
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
height: 1
|
||||
width: parent.width
|
||||
color: "#999999"
|
||||
anchors.bottom: parent.bottom
|
||||
z: 2
|
||||
}
|
||||
|
||||
Column {
|
||||
id: column
|
||||
property QtObject parentModel: model
|
||||
|
||||
@@ -105,7 +105,6 @@ Rectangle {
|
||||
zoomSlider.externalUpdate = true;
|
||||
zoomSlider.value = zoomSlider.minimumValue;
|
||||
overview.clear();
|
||||
timeDisplay.clear();
|
||||
}
|
||||
|
||||
function propagateSelection(newModel, newItem) {
|
||||
@@ -190,46 +189,64 @@ Rectangle {
|
||||
Keys.onReleased: shiftPressed = false;
|
||||
|
||||
Flickable {
|
||||
id: labelsflick
|
||||
id: categories
|
||||
flickableDirection: Flickable.VerticalFlick
|
||||
interactive: false
|
||||
anchors.top: buttonsBar.bottom
|
||||
anchors.bottom: overview.top
|
||||
anchors.left: parent.left
|
||||
width: labels.width
|
||||
anchors.right: parent.right
|
||||
contentY: flick.contentY
|
||||
|
||||
// reserve some more space than needed to prevent weird effects when resizing
|
||||
contentHeight: labels.height + height
|
||||
|
||||
Rectangle {
|
||||
id: labels
|
||||
anchors.left: parent.left
|
||||
width: 150
|
||||
color: root.color
|
||||
height: col.height
|
||||
|
||||
Column {
|
||||
id: col
|
||||
contentHeight: categoryContent.height + height
|
||||
|
||||
// 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
|
||||
// events, so we can't use the drag events to determine the cursor shape.
|
||||
property bool dragging: false
|
||||
|
||||
Column {
|
||||
id: categoryContent
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
DelegateModel {
|
||||
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
|
||||
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
|
||||
mockup: qmlProfilerModelProxy.height == 0
|
||||
visualIndex: DelegateModel.itemsIndex
|
||||
dragging: col.dragging
|
||||
mockup: qmlProfilerModelProxy.height === 0
|
||||
visualIndex: parent.visualIndex
|
||||
dragging: categories.dragging
|
||||
reverseSelect: root.shiftPressed
|
||||
onDragStarted: col.dragging = true
|
||||
onDragStopped: col.dragging = false
|
||||
draggerParent: labels
|
||||
dragOffset: y
|
||||
onDragStarted: categories.dragging = true
|
||||
onDragStopped: categories.dragging = false
|
||||
draggerParent: categories
|
||||
width: 150
|
||||
dragOffset: parent.y
|
||||
|
||||
onDropped: {
|
||||
timelineModel.items.move(sourceIndex, targetIndex);
|
||||
@@ -251,8 +268,36 @@ Rectangle {
|
||||
zoomControl.rangeStart,
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: categoryContent.bottom
|
||||
height: 1
|
||||
color: "#B0B0B0"
|
||||
}
|
||||
}
|
||||
|
||||
// border between labels and timeline
|
||||
Rectangle {
|
||||
id: labelsborder
|
||||
anchors.left: labelsflick.right
|
||||
TimeDisplay {
|
||||
id: timeDisplay
|
||||
anchors.top: parent.top
|
||||
anchors.left: buttonsBar.right
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: overview.top
|
||||
width: 1
|
||||
color: "#858585"
|
||||
zoomer: zoomControl
|
||||
contentX: flick.contentX
|
||||
clip: true
|
||||
}
|
||||
|
||||
ButtonsBar {
|
||||
@@ -297,21 +350,12 @@ Rectangle {
|
||||
onLockChanged: selectionLocked = !lockButtonChecked();
|
||||
}
|
||||
|
||||
TimeDisplay {
|
||||
id: timeDisplay
|
||||
anchors.top: parent.top
|
||||
anchors.left: labelsborder.right
|
||||
anchors.right: parent.right
|
||||
height: 24
|
||||
}
|
||||
|
||||
Flickable {
|
||||
id: flick
|
||||
contentHeight: labels.height
|
||||
contentHeight: categoryContent.height
|
||||
contentWidth: zoomControl.windowDuration * width / Math.max(1, zoomControl.rangeDuration)
|
||||
flickableDirection: Flickable.HorizontalAndVerticalFlick
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
clip:true
|
||||
|
||||
// ScrollView will try to deinteractivate it. We don't want that
|
||||
// as the horizontal flickable is interactive, too. We do occasionally
|
||||
@@ -375,9 +419,6 @@ Rectangle {
|
||||
signal clearChildren
|
||||
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 {
|
||||
id: timelineModel
|
||||
@@ -386,35 +427,8 @@ Rectangle {
|
||||
id: spacer
|
||||
height: modelData.height
|
||||
width: flick.contentWidth
|
||||
property int rowCount: (modelData.empty || modelData.hidden) ?
|
||||
0 : modelData.rowCount
|
||||
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 {
|
||||
id: renderer
|
||||
model: modelData
|
||||
@@ -543,8 +557,8 @@ Rectangle {
|
||||
ScrollView {
|
||||
id: scroller
|
||||
contentItem: flick
|
||||
anchors.left: labelsborder.right
|
||||
anchors.top: timeDisplay.bottom
|
||||
anchors.left: buttonsBar.right
|
||||
anchors.top: categories.top
|
||||
anchors.bottom: overview.top
|
||||
anchors.right: parent.right
|
||||
}
|
||||
@@ -565,12 +579,20 @@ Rectangle {
|
||||
notes: qmlProfilerModelProxy.notes
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.left: buttonsBar.right
|
||||
anchors.bottom: overview.top
|
||||
anchors.top: parent.top
|
||||
width: 1
|
||||
color: "#B0B0B0"
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: filterMenu
|
||||
color: "#9b9b9b"
|
||||
enabled: buttonsBar.enabled
|
||||
visible: false
|
||||
width: labels.width
|
||||
width: buttonsBar.width
|
||||
anchors.left: parent.left
|
||||
anchors.top: buttonsBar.bottom
|
||||
height: qmlProfilerModelProxy.models.length * buttonsBar.height
|
||||
@@ -598,7 +620,7 @@ Rectangle {
|
||||
color: "#9b9b9b"
|
||||
enabled: buttonsBar.enabled
|
||||
visible: false
|
||||
width: labels.width
|
||||
width: categoryContent.width
|
||||
height: buttonsBar.height
|
||||
anchors.left: parent.left
|
||||
anchors.top: buttonsBar.bottom
|
||||
|
||||
@@ -29,87 +29,44 @@
|
||||
****************************************************************************/
|
||||
|
||||
import QtQuick 2.1
|
||||
import Monitor 1.0
|
||||
|
||||
Canvas {
|
||||
Item {
|
||||
id: timeDisplay
|
||||
objectName: "TimeDisplay"
|
||||
contextType: "2d"
|
||||
|
||||
Connections {
|
||||
target: zoomControl
|
||||
onRangeChanged: requestPaint();
|
||||
}
|
||||
property QtObject zoomer
|
||||
|
||||
onPaint: {
|
||||
var context = (timeDisplay.context === null) ? getContext("2d") : timeDisplay.context;
|
||||
readonly property int labelsHeight: 24
|
||||
|
||||
context.reset();
|
||||
context.fillStyle = "white";
|
||||
context.fillRect(0, 0, width, height);
|
||||
readonly property int initialBlockLength: 120
|
||||
|
||||
var totalTime = Math.max(1, zoomControl.rangeDuration);
|
||||
var spacing = width / totalTime;
|
||||
property double spacing: width / rangeDuration
|
||||
|
||||
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;
|
||||
property double timePerBlock: Math.pow(2, Math.floor(Math.log(initialBlockLength / spacing) /
|
||||
Math.LN2))
|
||||
|
||||
var realStartTime = Math.floor(zoomControl.rangeStart / timePerBlock) * timePerBlock;
|
||||
var startPos = (zoomControl.rangeStart - realStartTime) * spacing;
|
||||
property double rangeDuration: Math.max(1, Math.round(zoomer.rangeDuration))
|
||||
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;
|
||||
|
||||
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 )
|
||||
{
|
||||
readonly property var timeUnits: ["μs", "ms", "s"]
|
||||
function prettyPrintTime(t, rangeDuration) {
|
||||
var round = 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;
|
||||
if (zoomControl.rangeDuration < barrier)
|
||||
if (rangeDuration < barrier)
|
||||
round *= 1000;
|
||||
else if (zoomControl.rangeDuration < barrier * 10)
|
||||
else if (rangeDuration < barrier * 10)
|
||||
round *= 100;
|
||||
else if (zoomControl.rangeDuration < barrier * 100)
|
||||
else if (rangeDuration < barrier * 100)
|
||||
round *= 10;
|
||||
if (t < barrier * 1000)
|
||||
return Math.floor(t / (barrier / round)) / round + units[i];
|
||||
return Math.floor(t / (barrier / round)) / round + timeUnits[i];
|
||||
}
|
||||
|
||||
t /= barrier;
|
||||
@@ -117,4 +74,111 @@ Canvas {
|
||||
var s = Math.floor((t - m * 60) * round) / round;
|
||||
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 Monitor 1.0
|
||||
|
||||
Canvas {
|
||||
Item {
|
||||
id: timeMarks
|
||||
objectName: "TimeMarks"
|
||||
contextType: "2d"
|
||||
|
||||
visible: model && (mockup || (!model.hidden && !model.empty))
|
||||
property QtObject model
|
||||
property bool startOdd
|
||||
property bool mockup
|
||||
|
||||
readonly property int scaleMinHeight: 60
|
||||
readonly property int scaleStepping: 30
|
||||
readonly property string units: " kMGT"
|
||||
|
||||
property real startTime
|
||||
property real endTime
|
||||
property real timePerPixel
|
||||
|
||||
Connections {
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
property int rowCount: model ? model.rowCount : 0
|
||||
|
||||
function prettyPrintScale(amount) {
|
||||
var unitOffset = 0;
|
||||
@@ -112,66 +60,88 @@ Canvas {
|
||||
}
|
||||
}
|
||||
|
||||
function drawBackgroundBars( context, region ) {
|
||||
var colorIndex = startOdd;
|
||||
|
||||
context.font = "8px sans-serif";
|
||||
|
||||
// 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);
|
||||
|
||||
Connections {
|
||||
target: model
|
||||
onRowHeightChanged: {
|
||||
if (row >= 0)
|
||||
rowRepeater.itemAt(row).height = height;
|
||||
}
|
||||
}
|
||||
|
||||
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";
|
||||
context.beginPath();
|
||||
context.moveTo(0, cumulatedHeight - y);
|
||||
context.lineTo(width, cumulatedHeight - y);
|
||||
context.stroke();
|
||||
Text {
|
||||
id: scaleTopLabel
|
||||
renderType: Text.NativeRendering
|
||||
visible: parent.scaleVisible
|
||||
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