Bounds of Flickable.contentY in QML - qml

What is the correct way of asking the bounds of Flickable.contentY? I need that for a scrollbar.
Experimentally I have discovered that
offsetY <= contentY <= offsetY + contentHeight - height
where offsetY can be calculated as
var offsetY = contentY-Math.round(visibleArea.yPosition*contentHeight)
offsetY is zero at the start of the application and seems to be constant unless Flickable is resized.
This formula works in general, but there probably should be a dedicated function for it.

I did a scrollbar easily without offset :
// ScrollBar.qml
import QtQuick 2.0
Rectangle {
id: scrollbar;
color: "#3C3C3C";
visible: (flicker.visibleArea.heightRatio < 1.0);
property Flickable flicker : null;
width: 20;
anchors {
top: flicker.top;
right: flicker.right;
bottom: flicker.bottom;
}
Rectangle {
id: handle;
height: (scrollbar.height * flicker.visibleArea.heightRatio);
color: "#5E5E5E";
border {
width: 1;
color: "white";
}
anchors {
left: parent.left;
right: parent.right;
}
Binding { // Calculate handle's x/y position based on the content position of the Flickable
target: handle;
property: "y";
value: (flicker.visibleArea.yPosition * scrollbar.height);
when: (!dragger.drag.active);
}
Binding { // Calculate Flickable content position based on the handle x/y position
target: flicker;
property: "contentY";
value: (handle.y / scrollbar.height * flicker.contentHeight);
when: (dragger.drag.active);
}
MouseArea {
id: dragger;
anchors.fill: parent;
drag {
target: handle;
minimumX: handle.x;
maximumX: handle.x;
minimumY: 0;
maximumY: (scrollbar.height - handle.height);
axis: Drag.YAxis;
}
}
}
}
Just use it like this :
Flickable {
id: myFlick;
}
ScrollBar {
flicker: myFlick;
}
It can be moved with mouse, and moves automatically when Flickable is scrolled.

Related

How to rotate only the right or left part of an image

I want to simulate the closure of one page of a book.
Any suggestions on how to do that in qml?
Thanks in advance
Perhaps Flipable, in combination with the fillMode property of Image:
import QtQuick 2.0
import QtQuick.Window 2.0
Window {
id: window
width: 640
height: 480
visible: true
Image {
id: backgroundImage
source: "http://www.thebookdesigner.com/wp-content/uploads/2013/04/pages-vs-spreads.png"
anchors.centerIn: parent
Flipable {
id: flipable
anchors.fill: parent
anchors.leftMargin: parent.width / 2
property bool flipped: false
front: Image {
id: foldImage
source: backgroundImage.source
fillMode: Image.Pad
width: foldImage.implicitWidth / 2
horizontalAlignment: Image.AlignRight
}
back: Image {
source: backgroundImage.source
width: foldImage.implicitWidth / 2
fillMode: Image.Pad
horizontalAlignment: Image.AlignLeft
}
transform: Rotation {
id: rotation
origin.x: 0
origin.y: flipable.height / 2
axis.x: 0; axis.y: 1; axis.z: 0 // set axis.y to 1 to rotate around y-axis
angle: 0 // the default angle
}
states: State {
name: "back"
PropertyChanges {
target: rotation;
angle: -180
}
when: flipable.flipped
}
transitions: Transition {
NumberAnimation {
target: rotation
property: "angle"
duration: 1000
easing.type: Easing.InCubic
}
}
MouseArea {
anchors.fill: parent
onClicked: flipable.flipped = !flipable.flipped
}
}
}
}
Flipable does what its name suggests, and the fillMode property in combination with a width that is too small for the entire image allows you to "reposition" the contents of the image within the bounds of the item.
So, the front item contains the right side of the image, and the back item contains the left side. With an actual book that has many pages, you'd have to use the relevant pages instead of the same one.

(How) can i access relative position of qml element to main window

I have a qml element and want to show a (own) tooltip element as a new window right above this element. for this i need the absolute screen position to place the new window (AFAIK).
i got as far that the regular approach is to use "mapToItem" to get the relative position, but i cannot get to the "main window" - because the element in question is located within a "Loader" (which in this case is again located in another Loader).
So my question is: Is it possible to access the mainWindow from inside the dynamically loaded component, or is there maybe another easier way to anchor a new (tooltip) window right above an element ?
EDIT
mapToGlobal would probably work too, but i have to use qt 5.6.
i finally got it to work by setting the main window as a context property in c++:
this->qmlEngine->rootContext()->setContextProperty("mainWindow", this->root);
and in qml i can then access the main window position (on screen) and add the relative position the item has to the shown window like that:
tooltipWindow.setX(mainWindow.x +item1.mapToItem(item2,0,0).x )
The Window item has contentItem especially for that
[read-only] contentItem : Item
The invisible root item of the scene.
So you can refer to Window.contentItem as if it was Window:
import QtQuick 2.7
import QtQuick.Window 2.2
Window {
id: mainWindow
visible: true
width: 600
height: 300
Component {
id: testElement
Rectangle {
id: rect
width: 100
height: 100
color: "orange"
border { width: 1; color: "#999" }
MouseArea {
anchors.fill: parent
hoverEnabled: true
onEntered: tooltip.show(true);
onExited: tooltip.show(false);
onPositionChanged: tooltip.setPosition(mapToItem(mainWindow.contentItem,mouse.x, mouse.y));
}
}
}
Item {
x: 40
y: 50
Item {
x: 80
y: 60
Loader {
sourceComponent: testElement
}
}
}
Rectangle {
id: tooltip
visible: false
width: 100
height: 20
color: "lightgreen"
border { width: 1; color: "#999" }
Text {
anchors.centerIn: parent
text: "I'm here"
}
function show(isShow) {
tooltip.visible = isShow;
}
function setPosition(point) {
tooltip.x = point.x - tooltip.width / 2;
tooltip.y = point.y - tooltip.height;
}
}
}
As for me I would reparent tooltip Item to hovered item itself at MouseArea.onEntered and so you can avoid position recalculation etc.:
onEntered: tooltip.show(true, rect);
onExited: tooltip.show(false);
onPositionChanged: tooltip.setPosition(mouse.x, mouse.y);
...
function show(isShow, obj) {
obj = (typeof obj !== 'undefined' ? obj : null);
if(obj !== null) {
tooltip.parent = obj;
}
tooltip.visible = isShow;
}
function setPosition(x, y) {
tooltip.x = x - tooltip.width / 2;
tooltip.y = y - tooltip.height;
}

QML How reverse play animation

I want an object, need it to fellow a complex path and move as an animation.
The path is included line and curve. just like a train.
Two solution: 1. PathAnimation or 2. states with multi-animation
Problem of solution 1:
The train maybe stop at a random time-point(pause the animation), and go reverse back to the start position(play animation reversely).
So i want know any way to play PathAnimation reversely?
I think QML doesn't have that functionality.
You could set a new path whenever you need a new one. I.e, when you animation ends.
Here you have an example:
import QtQuick 2.3
import QtQuick.Controls 1.1
ApplicationWindow {
visible: true
width: 350
height: 300
Rectangle {
id: window
width: 350
height: 300
Canvas {
id: canvas
anchors.fill: parent
antialiasing: true
onPaint: {
var context = canvas.getContext("2d")
context.clearRect(0, 0, width, height)
context.strokeStyle = "black"
context.path = pathAnim.path
context.stroke()
}
}
SequentialAnimation {
id: mySeqAnim
running: true
PauseAnimation { duration: 1000 }
PathAnimation {
id: pathAnim
duration: 2000
easing.type: Easing.InQuad
target: box
orientation: PathAnimation.RightFirst
anchorPoint: Qt.point(box.width/2, box.height/2)
path: myPath1
}
onStopped: {
console.log("test")
pathAnim.path = myPath2;
mySeqAnim.start();
}
}
Path {
id: myPath1
startX: 50; startY: 100
PathLine { x: 300; y: 100 }
onChanged: canvas.requestPaint()
}
Path {
id: myPath2
startX: 300; startY: 100
PathLine { x: 50; y: 100 }
onChanged: canvas.requestPaint()
}
Rectangle {
id: box
x: 25; y: 75
width: 50; height: 50
border.width: 1
antialiasing: true
Text {
anchors.centerIn: parent
}
}
}
}

Using a slider under another mouse area QML / QT

I am designing an UI interface in Qt where one of the pages can swipe between views. I have defined a SwipeArea region in the whole screen (which is a MouseArea I defined in another QML file), to be able to swipe between pages, with the property propagateComposedEvents set to true so that I can swipe between pages, and still being able to click on all buttons.
The problem comes when using a Slider. Apparently it can't get these mouse events, and I can't interact with it because when I click on the screen it starts the swiping handler. Any idea of how can I solve this?
This is where I define the SwipeArea.qml code:
import QtQuick 2.0
MouseArea {
property point origin
property bool ready: false
signal move(int x, int y)
signal swipe(string direction)
propagateComposedEvents: true
onPressed: {
drag.axis = Drag.XAndYAxis
origin = Qt.point(mouse.x, mouse.y)
}
onPositionChanged: {
switch (drag.axis) {
case Drag.XAndYAxis:
if (Math.abs(mouse.x - origin.x) > 16) {
drag.axis = Drag.XAxis
}
else if (Math.abs(mouse.y - origin.y) > 16) {
drag.axis = Drag.YAxis
}
break
case Drag.XAxis:
move(mouse.x - origin.x, 0)
break
case Drag.YAxis:
move(0, mouse.y - origin.y)
break
}
}
onReleased: {
switch (drag.axis) {
case Drag.XAndYAxis:
canceled(mouse)
break
case Drag.XAxis:
swipe(mouse.x - origin.x < 0 ? "left" : "right")
break
case Drag.YAxis:
swipe(mouse.y - origin.y < 0 ? "up" : "down")
break
}
}
}
And here is the main.qml where I use it:
Item {
id: content
width: root.width * itemDataLength
height:parent.height
property double k: (content.width - root.width) / (img.width - root.width)
Repeater {
id:repeater
model: itemDataLength
width:parent.width
height:parent.height
Loader{
id:loader
width:parent.width / 2
x: root.width * index
height:parent.height
source:loaderItems[index]
}
}
}
SwipeArea {
id: mouse
anchors.fill: parent
onMove: {
content.x = (-root.width * currentIndex) + x
}
onSwipe: {
switch (direction) {
case "left":
if (currentIndex === itemDataLength - 1) {
currentIndexChanged()
}
else {
currentIndex++
}
break
case "right":
if (currentIndex === 0) {
currentIndexChanged()
}
else {
currentIndex--
}
break
}
}
onCanceled: {
currentIndexChanged()
}
}
Maybe important to add, my slider code:
Slider {
id: slider
anchors.centerIn: parent
updateValueWhileDragging:true
activeFocusOnPress:true
style: SliderStyle {
id:sliderStyle
//Slider handler
handle: Rectangle {
id:handler
width: 22
height: 44
radius:2
antialiasing: true
Image{
source:"icons/slidercenter.png"
anchors.centerIn: parent
}
}
//Slider Groove
groove: Item {
id:slidergroove
implicitHeight: 50
implicitWidth: temperaturePage.width -50
}
}
}
I appreciate any help or opinion, and thank you in advance!

Why does this QML code have poor performance?

If QML rendering is hardware accelerated, shouldn't this simple example outperform the equivalent implementation in Qt classic?
import QtQuick 2.3
import QtQuick.Controls 1.2
ApplicationWindow {
id: app
visible: true
width: 640
height: 480
Column {
id: cc
property real val: 0
Row {
spacing: 10
TextField {
id: numRows
text: "1"
property int value: parseInt(text);
validator: IntValidator { bottom: 1; top: 100; }
}
TextField {
id: numCols
text: "1"
property int value: parseInt(text);
validator: IntValidator { bottom: 1; top: 100; }
}
}
Repeater {
model: numRows.value
Row {
Repeater {
model: numCols.value
Slider {
width: app.width / numCols.value
height: 18.5
value: cc.val
onValueChanged: cc.val = value
}
}
}
}
}
}
I idea is to fill the screen with rows and columns of sliders, and have each slider connect to every other slider. For my screen I use 55 rows and 20 columns. As I move a slider, I expect to see a fluid motion of all the sliders on the screen, yet the frame rate is very low (I would guess 5 to 10 fps). I have a very beefy GPU, and I was expecting much better performance. What could be wrong?
Looking at this under a (CPU, not QML) profiler, the majority of the time spent is actually in rendering the Slider items using native styling. If performance is an important issue, and native desktop styling is not, I would suggest not using QtQuickControls 1, as the styling has a significant amount of overhead.
If I update your example to use QtQuickControls 2, it is significantly smoother:
import QtQuick 2.3
import QtQuick.Controls 2.0
ApplicationWindow {
id: app
visible: true
width: 640
height: 480
Column {
id: cc
property real val: 0
Row {
spacing: 10
TextField {
id: numRows
text: "1"
property int value: parseInt(text);
validator: IntValidator { bottom: 1; top: 100; }
}
TextField {
id: numCols
text: "1"
property int value: parseInt(text);
validator: IntValidator { bottom: 1; top: 100; }
}
}
Repeater {
model: numRows.value
Row {
Repeater {
model: numCols.value
Slider {
width: app.width / numCols.value
height: 18.5
value: cc.val
onPositionChanged: cc.val = position
}
}
}
}
}
}
If you get yourself 1100 sliders, you are asking to continuously update 1100 sliders. That is a lot of signals being sent. I will not ask you what you need 1100 synchronized sliders for ...
Although you can inch out a few cycles by only updating cc.val when the slider is pressed, this will not change very much.
All in all, you can only seriously reduce the work being done by choosing a stepSize or setting updateValueWhileDragging: false. You will still see a delay when you release a slider, but the frame rate will not disturb the experience as much.