Is there any DatePicker control for Qt 5? - datepicker

I'm writing my first QML/Javascript app for QtQuick 2.0.
I need to place a DatePicker control, but I haven't found any control like that under QtQuick.Controls -and nowhere, in fact-.
I'm starting to believe there is no way to call a 'native' DatePicker in QML. Do I have to implement one or there is exist one?

Just in case anyone else stumbles upon this, there is a Calendar element in QtQuick.Controls by now. This should make the implementation of such a Datepicker a lot easier: http://qt-project.org/doc/qt-5/qml-qtquick-controls-calendar.html

Well, I had to make my own control. It is called Datepicker.
It is intented to used in this way:
import QtQuick.Controls 1.1
ApplicationWindow {
id: main
Datepicker {
id: myDate
activeWindow: main
width: 200
}
}
It asumes you are using it from a Window object, and needs the parent reference to show the datepicker in the correct position (it shows a calendar in a new window).
You can download the source code from:
https://bitbucket.org/camolin3/datepicker
This is the first version and need a lot of polish to be ready, but is a start point.

**Code for DatePicker. Try this one **
*TextField {
id: textDate
x: 10
y: 42
width: 175
height: 33
placeholderText: qsTr("Text Field")
text:Qt.formatDate(cal.selectedDate, "dd-MM-yyyy")
font.bold: true
font.family:"times new roman"
font.pointSize: 12
}
Button {
id: button
x: 198
y: 42
width: 25
height: 29
Image {
x: -4
y: -4
id: img
width: 36
height: 44
source: "/Images/calendar-512.png"
}
onClicked:{
cal.visible=true
}
}
Calendar{
id:cal
x: 10
y: 82
width: 220
height: 205
visible: false
selectedDate: new Date()
onClicked: {
textDate.text=Qt.formatDate(cal.selectedDate, "dd-MM-yyyy");
cal.visible=false
}
}*

I wrote another date picker, relying purely on the JavaScript Date object as follows. It adheres to material design.
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Controls.Material 2.15
import QtQuick.Controls.Material.impl 2.15
Pane {
id: control
property int radius: 2
background: Rectangle {
color: control.Material.background
radius: control.Material.elevation > 0 ? control.radius : 0
layer.enabled: control.enabled && control.Material.elevation > 0
layer.effect: ElevationEffect {
elevation: control.Material.elevation
}
}
}
And then, here is it
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import QtQuick.Controls.Material 2.15
Rectangle {
id: root
property int month: selectedDate.getMonth()
property int year: selectedDate.getFullYear()
property int day: selectedDate.getDate()
property int weekday: selectedDate.getDay()
property int daysInMonth: new Date(year, month + 1, 0).getDate()
property date selectedDate: new Date()
property int _start_weekday: new Date(year, month, 1).getDay()
implicitWidth: layout.width
implicitHeight: layout.height
color: "transparent"
ColumnLayout {
id: layout
Rectangle {
id: title
implicitHeight: 40
Layout.fillWidth: true
color: "transparent"
Text {
anchors.fill: parent
anchors.leftMargin: 8
text: selectedDate.toLocaleDateString(Qt.locale(), "ddd, MMM d")
font.pixelSize: 40
}
}
Rectangle {
color: "transparent"
implicitHeight: 28
Layout.fillWidth: true
}
Rectangle {
id: controls
color: "transparent"
implicitHeight: 24
RowLayout {
anchors.fill: parent
anchors.leftMargin: 8
spacing: 16
Layout.alignment: Qt.AlignHCenter
Text {
text: selectedDate.toLocaleDateString(Qt.locale(), "MMMM yyyy")
font.pixelSize: 16
}
RoundButton {
id: monthButton
contentItem: MIcon {
icon: "expand_more"
color: "black"
size: 16
}
Material.elevation: 0
onClicked: {
monthMenu.open()
}
Menu {
id: monthMenu
Repeater {
model: 12
MenuItem {
text: new Date(2020, index, 1).toLocaleDateString(Qt.locale(), "MMMM")
onTriggered: {
set_month(index)
monthMenu.close()
}
}
}
}
}
// 2 button to change month <>
RowLayout {
Layout.alignment: Qt.AlignRight
spacing: 8
RoundButton {
Material.background: 'transparent'
contentItem: MIcon {
icon: 'navigate_before'
color: 'black'
size: 16
}
onClicked: {
set_month(month - 1)
}
}
RoundButton {
Material.background: 'transparent'
contentItem: MIcon {
icon: 'navigate_next'
color: 'black'
size: 16
}
onClicked: {
set_month(month + 1)
}
}
}
}
}
Rectangle {
color: "transparent"
implicitHeight: 16
Layout.fillWidth: true
}
Rectangle {
color: Material.accent
implicitHeight: 1
Layout.fillWidth: true
}
// Sunday - Saturday
RowLayout {
Layout.alignment: Qt.AlignHCenter
Layout.fillWidth: true
spacing: 4
Repeater {
model: 7
Rectangle { // just for spacing
width: 40
height: 40
color: "transparent"
Text {
anchors.centerIn: parent
Layout.fillWidth: true
text: new Date(2020, 0, index - 2).toLocaleDateString(Qt.locale(), "ddd").slice(0, 1)
font.pixelSize: 16
horizontalAlignment: Text.AlignHCenter
}
}
}
}
// calendar
GridLayout {
id: grid
columns: 7
rows: 6
columnSpacing: 4
rowSpacing: 4
Repeater {
model: 42
delegate: Rectangle {
color: default_color()
radius: 20
border.width: 1
border.color: is_selected() ? Material.accent : "transparent"
width: 40
height: 40
Text {
anchors.centerIn: parent
text: get_day()
color: in_current_month() ? 'black' : 'gray'
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
onEntered: {
cursorShape = Qt.PointingHandCursor
color = Material.color(Material.accent, Material.Shade200)
}
onExited: {
cursorShape = Qt.ArrowCursor
color = default_color()
}
onClicked: {
var _day = get_day()
if (!in_current_month()){
if (index < _start_weekday) {
set_month(month - 1)
} else {
set_month(month + 1)
}
}
set_day(_day)
}
}
function default_color() {
return 'transparent'
}
function in_current_month() {
return index >= _start_weekday && (index - _start_weekday) < daysInMonth
}
function is_selected() {
return day == get_day() && in_current_month()
}
function get_day() {
var this_day = index - _start_weekday + 1
if (this_day > daysInMonth) {
return this_day - daysInMonth
} else if (this_day < 1) {
return new Date(year, month, 0).getDate() + this_day
} else {
return this_day
}
}
}
}
}
}
function set_month(month) {
var days_in = new Date(year, month + 1, 0).getDate()
var new_day = Math.min(day, days_in)
selectedDate = new Date(year, month, new_day)
}
function set_day(day) {
day = Math.min(day, daysInMonth)
selectedDate = new Date(year, month, day)
}
}

Related

While horizontal scrolling the datalabels of charjs is overlapping to y-axis

To show all bars I have set the horizontal scrolling to chartjs in Ionic angular project , and i have used the DataLabelsPlugin constant for bars label. and while scrolling the datalabels is overlapping with y-axis its not hiding before y-axis like bars.
and also horizontal scroll is not happening smoothly.
graph working fine as a expected output
marked with issue about - after scrolling the datalabels went over the y-axis not hide below y-axis like bars
I have tried to add and used the custom datalabels but same issue i am getting and i didnt find any css or attribute on 'https://www.chartjs.org/docs/latest/' official site or not on any other sites -> to hide the datalabels from over the y-axis.
ts file code:
createBarChart() {
const footer = (tooltipItems) => {
let sum = 0;
tooltipItems.forEach(function(tooltipItem) {
sum += tooltipItem.parsed.y;
});
return this.util.getFormatValue(sum)+'%';
};
const toolLabel = (tooltipItems) => {
return "";
};
const toolTitle = (tooltipItems) => {
var string_to_array = function (str) {
return str.trim().split("#$#$");
};
var ss;
tooltipItems.forEach(function(tooltipItem) {
ss = string_to_array(tooltipItem.label.replace(/(.{40})/g, "$1#$#$"))
});
return ss;
};
let graphSize = Math.max(...this.daywise_occupancy);
if(graphSize == 0){
graphSize =1;
}
const plugin = {
id: 'customCanvasBackgroundColor',
beforeDraw: (chart, args, options) => {
const {ctx} = chart;
ctx.save();
ctx.globalCompositeOperation = 'destination-over';
ctx.fillStyle = '#ffffff';
ctx.fillRect(0, 0, chart.width, chart.height);
ctx.restore();
}
};
this.bars = new Chart(this.barchart6.nativeElement, {
type: 'bar',
data: {
labels: this.daywise_date,
datasets: [{
data: this.daywise_occupancy,
backgroundColor: function(context) {
var value = context.dataset.data[context.dataIndex];
return value <= 15 ? '#f95959'
: value > 15 && value <=60 ? '#F5A623'
: '#00ADB5'
},
borderColor: function(context) {
var value = context.dataset.data[context.dataIndex];
return value <= 15 ? '#f95959'
: value > 15 && value <=60 ? '#F5A623'
: '#00ADB5'
},
borderWidth: 1,
barThickness:30,
}]
},
plugins: [DataLabelsPlugin,plugin],
options: {
animations: {
tension: {
duration: 1000,
easing: 'linear',
from: 1,
to: 0,
loop: true
}
},
scales: {
x: {
min:0,
max:5,
ticks : {
maxRotation: 70,
minRotation: 70,
font:{
size:10,
},
callback: function(value : any, index, ticks_array) {
let characterLimit = 12;
let label = this.getLabelForValue(value);
if ( label.length >= characterLimit) {
return label.slice(0, label.length).substring(0, characterLimit -1).trim() + '..';
}
return label;
}
}
},
y: { // defining min and max so hiding the dataset does not change scale range
min: 0,
max: this.loader.getGraphsizeRound(graphSize),
title: { display: true, text: (this.titleSet)? '% of Branches Contribution' : '% of Seat Occupancy' },
beginAtZero: true,
display: true,
position: 'left',
// ticks: {
// stepSize: 6,
// },
}
},
plugins: {
legend: {
display: false
},
datalabels:{
anchor: 'end',
align: 'end',labels: {
value: {
color: '#2C3A45;',
formatter: function (value) {
// return Math.round(value) + '%';
return value + '%';
},
font:{
weight:700,
size:14
}
}
}
},
tooltip: {
callbacks: {
footer:footer,
label: toolLabel,
title:toolTitle
},
displayColors:false
}
}
}
});
this.bars.canvas.addEventListener('touchmove',(eve) => {
this.touchmove(eve,this.bars)
});
this.bars.canvas.addEventListener('touchstart',(eve) => {
this.touchstart(eve)
});
}
touchstart(e)
{
this.startX = e.touches[0].clientX;
this.startY = e.touches[0].clientY;
}
touchmove(e,chart)
{
var deltaX = e.touches[0].clientX - this.startX,
deltaY = e.touches[0].clientY - this.startY;
const dataLength = chart.data.labels.length;
let min = chart.options.scales.x.min;
if(deltaX < 0){
if( chart.options.scales.x.max >= dataLength ){
chart.options.scales.x.min = dataLength - 5;
chart.options.scales.x.max = dataLength;
}else{
chart.options.scales.x.min += 1;
chart.options.scales.x.max += 1;
}
// console.log( chart.options.scales.x.min);
// chart1line.options.scales.y.max = graphSize
}else if(deltaX > 0){
if( chart.options.scales.x.min <= 0 ){
chart.options.scales.x.min = 0;
chart.options.scales.x.max = 4;
}else{
chart.options.scales.x.min -= 1;
chart.options.scales.x.max -= 1;
}
}else{
}
chart.update();
}
HTML code:
<div class="chartWrapper">
<div class="chartAreaWrapper">
<canvas #barchart6 height="190" max-height="190" width="0"></canvas>
</div>
</div>
My expected output
horizontal scroll work smoothly.
after scrolling label should not overlap on y-axis.

Donut chart with custom tooltip using Chartist in React

I am creating a donut chart using Chartist but unable to add a custom tooltip with resize slice on the mouse hover event. Chartist "Draw" method is rerender on changing the position of mouse on hover of the chart
I pasted my code as below:
/* eslint-disable no-unused-vars */
import React, {useState} from 'react'
import PropTypes from 'prop-types'
import ChartistGraph from 'react-chartist'
import Chartist from 'chartist'
import styles from './MyChart.scss'
import {formatNumber} from '../../../../utils/formatter'
import MyChartInfo from './MyChartInfo'
function MyChart ({ dataSeries, getDetailsTable }) {
const [changePercent, setChangePercent] = useState(0)
const [showToolTip, setShowToolTip] = useState(false)
const [myChartInfoDetails, setMyChartInfoDetails] = useState({sectorName: '', changePercent: ''})
const [toolTipPosition, setToolTipPosition] = useState({})
function setToolTip () {
const PopOverData = [myChartInfoDetails.sectorName || '', `% Return: ${myChartInfoDetails.changePercent || ''}`, '(Click to view top & worst performing equities)']
return (<MyChartInfo dataTable={PopOverData} style={toolTipPosition} />)
}
let pieOptions = {
width: '520px',
height: '520px',
donut: true,
donutWidth: 150,
donutSolid: false,
startAngle: 270,
showLabel: true,
chartPadding: 60,
labelOffset: 105,
labelDirection: 'explode',
labelInterpolationFnc: function (value) {
return value
}
}
let pieData = {
series: dataSeries && dataSeries.length > 0 ? dataSeries.map(item => Math.abs(item.value)) : [100],
labels: dataSeries && dataSeries.length > 0 ? dataSeries.map(item => item.name.concat('<br>', formatNumber(item.value, {precision: 2, negSign: true, posSign: true}))) : ['']
}
// eslint-disable-next-line no-unused-vars
const listner = {
created: (chart) => {
const chartPie = document.querySelectorAll('.ct-chart-donut .ct-slice-donut')
chartPie && chartPie.forEach((pie) => {
pie.addEventListener('mouseenter', (e) => {
e.stopPropagation()
pie.setAttribute('style', 'stroke-width: 170px;')
console.log('hover aa ', changePercent, Math.abs(pie.getAttribute('ct:value')))
setChangePercent(Math.abs(pie.getAttribute('ct:value')))
let topPosition = e.layerY + 30
let leftPosition = e.layerX + 30
setToolTipPosition({top: topPosition, left: leftPosition})
const myChartData = dataSeries.find(sector => Math.abs(sector.value) === Math.abs(pie.getAttribute('ct:value')))
setSectorDetails({sectorName: myChartData.name, changePercent: myChartData.value})
setShowToolTip(true)
})
pie.addEventListener('mouseleave', (e) => {
pie.setAttribute('style', 'stroke-width: 150px;')
setShowToolTip(false)
})
pie.addEventListener('click', (e) => {
const Id = dataSeries.find(sector => Math.abs(sector.value) === Math.abs(pie.getAttribute('ct:value'))).id.toString()
console.log('click ', Id, pie.getAttribute('ct:value'))
getDetailsTable(Id)
})
})
},
draw: (data) => {
// console.log('data', data)
if (data.type === 'slice') {
// Get the total path length in order to use for dash array animation
var pathLength = data.element._node.getTotalLength()
// Set a dasharray that matches the path length as prerequisite to animate dashoffset
data.element.attr({
'stroke-dasharray': pathLength + 'px ' + pathLength + 'px'
})
let noOfNonZeros = dataSeries.length > 0 && dataSeries.filter(e => e.value > 0).length
// Create animation definition while also assigning an ID to the animation for later sync usage
var animationDefinition = {
'stroke-dashoffset': {
id: 'anim' + data.index,
dur: 1,
from: -pathLength + 'px',
to: noOfNonZeros > 1 ? '2px' : '0px',
easing: Chartist.Svg.Easing.easeOutQuint,
// We need to use `fill: 'freeze'` otherwise our animation will fall back to initial (not visible)
fill: 'freeze'
}
}
// If this was not the first slice, we need to time the animation so that it uses the end sync event of the previous animation
if (data.index !== 0) {
animationDefinition['stroke-dashoffset'].begin = 'anim' + (data.index - 1) + '.end'
}
// We need to set an initial value before the animation starts as we are not in guided mode which would do that for us
data.element.attr({
'stroke-dashoffset': -pathLength + 'px'
})
// We can't use guided mode as the animations need to rely on setting begin manually
// See http://gionkunz.github.io/chartist-js/api-documentation.html#chartistsvg-function-animate
data.element.animate(animationDefinition, false)
if (dataSeries.length === 0) {
data.element._node.setAttribute('data-value', 'no-composition')
}
}
if (data.type === 'label') {
let textHtml = ['<p>', data.text, '</p>'].join('')
let multilineText = Chartist.Svg('svg').foreignObject(
textHtml,
{
style: 'overflow: visible;',
x: data.x - 30,
y: data.y - 30,
width: 90,
height: 30
},
'ct-label'
)
data.element.replace(multilineText)
}
}
}
return (
<div data-testid='gPieChart' id='performance_chart'>
{<ChartistGraph
data={pieData}
options={pieOptions}
type='Pie'
style={{ width: '100%', height: '100%' }}
className={styles.MyChart}
listener={listner}
/>
}
{showToolTip && setToolTip()}
</div>
)
}
MyChart.propTypes = {
dataSeries: PropTypes.array,
getDetailsTable: PropTypes.func
}
export default MyChart
Output
I want to add a custom tooltip with resize slice functionality on mouse hover event

QML Drag & drop in a grid

I have an item (a rectangle) which can be moved through the different positions of the grid (see code below).
The reference to decide whether the rectangle is in one cell or another is the top-left (x=0;y=0) of the rectangle. I want the reference to be the center of the rectangle. Any ideas?
Find the code below:
import QtQuick 2.5
import QtPositioning 5.5
import QtLocation 5.6
import QtGraphicalEffects 1.0
import QtQml 2.2
import QtQuick.Controls 1.0
Rectangle{
id: mainContainer
width:800; height:width
color: "red"
Grid {
id:gridA
anchors.centerIn: parent
width: parent.width; height:width;
columns: 2; rows: 2; spacing: 2
verticalItemAlignment: Grid.AlignHCenter
horizontalItemAlignment: Grid.AlignVCenter
Repeater {
id:cells
model: gridA.columns*gridA.rows
property int draggedItemIndex: -1
Rectangle {
width: gridA.width/gridA.columns; height: gridA.height/gridA.rows
color: "white"
DropArea {
id: dragTarget
property alias dropProxy: dragTarget
anchors.fill: parent
keys: [ "KEY_A" ]
Rectangle {
id: dropRectangle
anchors.fill: parent
states: [
State {
when: dragTarget.containsDrag
PropertyChanges { target: dropRectangle; color: "grey"}
PropertyChanges { target: cells; draggedItemIndex: index}
}
]
}
}
}
}
}
Rectangle{
id: icon
property var draggedIndex: 0
width: parent.width/3; height:width
color:"blue"
Drag.active: dragArea.drag.active
Drag.keys: [ "KEY_A" ]
MouseArea {
id: dragArea
anchors.fill: parent
drag.target: icon
cursorShape: drag.active ? Qt.ClosedHandCursor : Qt.OpenHandCursor
onReleased: {
console.log("Drag: " + cells.draggedItemIndex)
if (cells.draggedItemIndex != icon.draggedIndex) {
icon.draggedIndex = cells.draggedItemIndex
cells.draggedItemIndex = -1
}
icon.x = cells.itemAt(icon.draggedIndex).x
icon.y = cells.itemAt(icon.draggedIndex).y
}
}
}
}
You have the Drag-attached property, which has the hotSpot-property of type PointF.
Set this property to Qt.point(width / 2, height / 2)

ChartJS display tooltip at maximum/minimum value

I would like to display a tooltip at the top datapoint of a dataset in my chart. That way the user sees a value for the maximum and the minimum datapoints. That helps understanding the graph immediately.
It should look somewhat like this:
I have created a JSFiddle with the code over here: https://jsfiddle.net/h2zscw6s/1/
For reference please also find my chart config below. It's in coffeescript but I don't mind if you post answers in plain javascript.
dataWeather = [
{
label: 'January'
value: 22
}
{
label: 'February'
value: 23
}
{
label: 'March'
value: 24
}
{
label: 'May'
value: 26
}
{
label: 'June'
value: 30
}
{
label: 'July'
value: 34
}
{
label: 'August'
value: 38
}
{
label: 'September'
value: 36
}
{
label: 'October'
value: 30
}
{
label: 'November'
value: 28
}
{
label: 'December'
value: 26
}
]
dataPrices = [
{
label: 'January'
value: 5000
}
{
label: 'February'
value: 4500
}
{
label: 'March'
value: 4450
}
{
label: 'May'
value: 3700
}
{
label: 'June'
value: 3700
}
{
label: 'July'
value: 3000
}
{
label: 'August'
value: 2900
}
{
label: 'September'
value: 3100
}
{
label: 'October'
value: 3200
}
{
label: 'November'
value: 3900
}
{
label: 'December'
value: 5500
}
]
class WeatherPriceChart
setWeatherData: (weatherData)->
#weatherData = weatherData
setPriceData: (priceData)->
#priceData = priceData
minPrice: ->
_.sortBy(#priceData, (w)-> w.value)[0]?.value || 0
maxPrice: ->
_.sortBy(#priceData, (w)-> -w.value)[0]?.value || 0
minTemperature: ->
_.sortBy(#weatherData, (w)-> w.value)[0]?.value || 0
maxTemperature: ->
_.sortBy(#weatherData, (w)-> -w.value)[0]?.value || 0
isMaxTemperature: (value)->
#maxTemperature() == value
isMinTemperature: (value)->
#minTemperature() == value
isMaxPrice: (value)->
#maxPrice() == value
isMinPrice: (value)->
#minPrice() == value
getLabels: ->
_.map(#weatherData, (w)-> w.label)
getWeatherDataPoints: ->
_.map(#weatherData, (w)-> w.value)
getPriceDataPoints: ->
_.map(#priceData, (w)-> w.value)
getNormalizedWeatherDataPoints: ->
data = #weatherData
min = -10
max = 60
_.map data, (d)->
norm = d.value + (min * -1)
maxed = max + (min * -1)
norm / maxed * 100
getNormalizedPriceDataPoints: ->
data = #priceData
max = #maxPrice() * 2.5
_.map data, (d)->
d.value / max * 100
chart = new WeatherPriceChart
chart.setWeatherData(dataWeather)
chart.setPriceData(dataPrices)
ctx = document.getElementById('myChart')
myChart = new Chart(ctx,
type: 'line',
data:
xLabels: chart.getLabels(),
yLabels: [""],
datasets: [
{
label: 'Temperature'
data: chart.getWeatherDataPoints()
backgroundColor: 'rgba(239,88,42,0.2)'
borderColor: 'rgba(239,88,42,0.5)'
borderWidth: 1
},
{
label: 'Prices'
data: chart.getNormalizedPriceDataPoints()
backgroundColor: 'rgba(22,195,245,0.2)'
borderColor:'rgba(22,195,245,0.4)'
borderWidth: 1
}
]
options:
scales:
yAxes: [
{
ticks: {
beginAtZero: false,
display: false
},
display: false
},
]
legend:
display: false
)
I found an answer to this option and posted it in the linked jsfiddle. I had to add a plugin that would display all labels. That one then dynamically decided whether to show this tooltip or not.
Chart.pluginService.register
beforeRender: (chart) ->
if chart.config.options.showAllTooltips
# create an array of tooltips
# we can't use the chart tooltip because there is only one tooltip per chart
chart.pluginTooltips = []
chart.config.data.datasets.forEach (dataset, i) ->
min = _.min(dataset.data, (d)-> d)
max = _.max(dataset.data, (d)-> d)
minShown = false
maxShown = false
chart.getDatasetMeta(i).data.forEach (sector, j) ->
isMax = dataset.data[j] == max
isMin = dataset.data[j] == min
# only show the min and the max once. we can have duplicates min/maxes
if isMax && !maxShown || isMin && !minShown
minShown = true if isMin
maxShown = true if isMax
tooltip = new (Chart.Tooltip)({
_chart: chart.chart
_chartInstance: chart
_data: chart.data
_options: chart.options.tooltips
_active: [ sector ]
}, chart)
chart.pluginTooltips.push(tooltip)
return
return
# turn off normal tooltips
chart.options.tooltips.enabled = false
return
afterDraw: (chart, easing) ->
if chart.config.options.showAllTooltips
# we don't want the permanent tooltips to animate, so don't do anything till the animation runs atleast once
if !chart.allTooltipsOnce
if easing != 1
return
chart.allTooltipsOnce = true
# turn on tooltips
chart.options.tooltips.enabled = true
Chart.helpers.each chart.pluginTooltips, (tooltip) ->
tooltip.initialize()
tooltip.update()
# we don't actually need this since we are not animating tooltips
tooltip.pivot()
tooltip.transition(easing).draw()
return
chart.options.tooltips.enabled = false
return

How to reorder children of QML Row (using drag and drop)?

Let's say, I have a QML Row which has some children objects (not necessarily of the same type). I would like to be able to reorder them at will. Bonus is, to be able to implement drag and drop behavior for the user. Here is a code sample, a Row with different components which I would like to reorder:
import QtQuick 2.5
import QtQuick.Controls 1.4
ApplicationWindow {
visible: true
width: 300
height: 300
Component {
id: rect
Rectangle {
color: "blue"
}
}
Component {
id: rectWithText
Rectangle {
property alias text: txt.text
color: "red"
Text {
id: txt
}
}
}
Row {
id: r
Component.onCompleted: {
rect.createObject(r,{"width": 60,"height":40})
rectWithText.createObject(r,{"width": 100,"height":40,text:"foo"})
rect.createObject(r,{"width": 40,"height":40})
}
}
}
In QtWidgets, there are some functions like layout->removeWidget(widget), layout->insertWidget(index,widget) and so on, but they seem to be missing in QML.
I found some examples that used ListView/GridView and model/delegate approach, but they usually can't handle different components and thus they are not really viable as a replacement for a Row.
Is there a way to do that in QML or am I out of luck?
This is a bit of a hack but works regardless. It does not require the use of a model.
import QtQuick 2.3
import QtQuick.Window 2.2
import QtQuick.Controls 1.1
Window {
visible: true
function shuffle(array) {
for (var i = array.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = array[i];
array[i] = array[j];
array[j] = temp;
}
return array;
}
Column {
id: column
anchors.fill: parent
anchors.centerIn: parent
Button {
text: "shuffle"
onClicked: {
var array = [button1, button2, button3]
for (var a in array) { array[a].parent = null }
array = shuffle(array)
for (a in array) { array[a].parent = column }
}
}
Button {
id: button1
text: "I am button 1"
}
Button {
id: button2
text: "I am button 2 "
}
Button {
id: button3
text: "I am button 3"
}
}
}
import QtQuick 2.3
import QtQuick.Window 2.2
import QtQuick.Controls 1.1
Window {
visible: true
Component {
id: buttonComponent
Button { text: "I'm a button" }
}
Component {
id: labelComponent
Label { text: "I'm a label" }
}
property var buttonModel: [buttonComponent, labelComponent, buttonComponent, buttonComponent,labelComponent]
function shuffle(array) {
for (var i = array.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = array[i];
array[i] = array[j];
array[j] = temp;
}
return array;
}
Column {
anchors.fill: parent
anchors.centerIn: parent
Repeater {
model: buttonModel
Loader {
sourceComponent: modelData
}
}
Button {
text: "shuffle"
onClicked: buttonModel = shuffle(buttonModel)
}
}
}
You can also use a ListView in the same way. That gives you even more flexibility when animating Items.