I'm trying to make a mouse selection rectangle.
But Rectangle {} object can't have negative width & height values.
So it only selects left-to-right & top-to-bottom:
Rectangle {
id: selectorRectangle
x: 0
y: 0
width: 0
height: 0
color: "#8000abcd"
border.color: "#00fbfb"
}
MouseArea {
anchors.fill: parent
propagateComposedEvents: true
onPressed: {
selectorRectangle.x = mouse.x
selectorRectangle.y = mouse.y
}
onPositionChanged: {
selectorRectangle.width = mouse.x - selectorRectangle.x
selectorRectangle.height = mouse.y - selectorRectangle.y
}
}
How can I achieve it?
Thanks!
Here is my implementation. You just need to keep the pressed point in a property and calculate the x,y,width,height accordingly to keep width and height positive.
Rectangle {
id: selectorRectangle
x: 0
y: 0
width: 0
height: 0
color: "#8000abcd"
border.color: "#00fbfb"
}
MouseArea {
anchors.fill: parent
propagateComposedEvents: true
property var lastPressPoint: Qt.point(0,0)
onPressed: {
lastPressPoint.x = mouse.x;
lastPressPoint.y = mouse.y;
}
onPositionChanged: {
selectorRectangle.x = Math.min(lastPressPoint.x , mouse.x)
selectorRectangle.y = Math.min(lastPressPoint.y , mouse.y)
selectorRectangle.width = Math.abs(lastPressPoint.x - mouse.x);
selectorRectangle.height = Math.abs(lastPressPoint.y - mouse.y);
}
onReleased: {
selectorRectangle.x = 0;
selectorRectangle.y = 0;
selectorRectangle.width = 0;
selectorRectangle.height = 0;
}
}
Related
I'm trying to make zoomable Item (Image or any other component) and make it for tablet. Currently, my code looks like this:
Rectangle {
id: root
anchors.fill: parent
color: mainWindow.toGradient
Flickable {
id: flick
anchors.fill: parent
contentWidth: width
contentHeight: height
boundsBehavior: Flickable.StopAtBounds
PinchArea {
id: pArea
width: Math.max(flick.contentWidth, flick.width)
height: Math.max(flick.contentHeight, flick.height)
property real initialWidth
property real initialHeight
onPinchStarted: {
initialWidth = flick.contentWidth
initialHeight = flick.contentHeight
}
onPinchUpdated: {
flick.contentX += pinch.previousCenter.x - pinch.center.x
flick.contentY += pinch.previousCenter.y - pinch.center.y
flick.resizeContent(initialWidth * pinch.scale, initialHeight * pinch.scale, pinch.center)
}
onPinchFinished: {
flick.returnToBounds()
}
Rectangle {
width: flick.contentWidth
height: flick.contentHeight
color: "black"
Image {
anchors.fill: parent
source: "qrc:/image.jpg"
}
MouseArea {
anchors.fill: parent
onDoubleClicked: {
flick.contentWidth = root.width
flick.contentHeight = root.height
}
}
}
}
}
}
It works good, but when it's over all screen, i can still zoom out (make it smaller, that parent).
How can I stop zooming out (and if possible zooming in too) at some point/scale?
Thank you for your help!
In the end, I entrily removed Flickable and scaled Item instead.
Rectangle {
id: root
anchors.fill: parent
color: "black"
Item {
id: photoFrame
width: root.width
height: root.height
Image {
id: image
anchors.fill: parent
source: "image.jpg"
}
PinchArea {
anchors.fill: parent
pinch.target: photoFrame
pinch.minimumRotation: 0 //Rotation (i guess we don't need to rotate it)
pinch.maximumRotation: 0
pinch.minimumScale: 1 //Minimum zoom for camera
pinch.maximumScale: 4 //Maximum zoom for camera
onPinchUpdated: { //Dont zoom behind border
if(photoFrame.x < dragArea.drag.minimumX)
photoFrame.x = dragArea.drag.minimumX
else if(photoFrame.x > dragArea.drag.maximumX)
photoFrame.x = dragArea.drag.maximumX
if(photoFrame.y < dragArea.drag.minimumY)
photoFrame.y = dragArea.drag.minimumY
else if(photoFrame.y > dragArea.drag.maximumY)
photoFrame.y = dragArea.drag.maximumY
}
MouseArea {
id: dragArea
hoverEnabled: true
anchors.fill: parent
drag.target: photoFrame
scrollGestureEnabled: false // 2-finger-flick gesture should pass through to the Flickable
drag.minimumX: (root.width - (photoFrame.width * photoFrame.scale))/2
drag.maximumX: -(root.width - (photoFrame.width * photoFrame.scale))/2
drag.minimumY: (root.height - (photoFrame.height * photoFrame.scale))/2
drag.maximumY: -(root.height - (photoFrame.height * photoFrame.scale))/2
onDoubleClicked: { //Reset size and location of camera
photoFrame.x = 0
photoFrame.y = 0
photoFrame.scale = 1
}
onWheel: {
var scaleBefore = photoFrame.scale
photoFrame.scale += photoFrame.scale * wheel.angleDelta.y / 120 / 10
if(photoFrame.scale < 1)
photoFrame.scale = 1
else if(photoFrame.scale > 4)
photoFrame.scale = 4
if(photoFrame.x < drag.minimumX)//Dont zoom behind border of image
photoFrame.x = drag.minimumX
else if(photoFrame.x > drag.maximumX)
photoFrame.x = drag.maximumX
if(photoFrame.y < drag.minimumY)
photoFrame.y = drag.minimumY
else if(photoFrame.y > drag.maximumY)
photoFrame.y = drag.maximumY
}
}
}
}
}
I am animating the grid item to upwards on swiping up and removes the corresponding item on the animation stopped function.
But the problem is when trying to remove the 0th index item. when removing 0th index item with animation, the whole screen moves up instead of the corresponding item animation.
Is there any solution for this ?
Item {
id: fake_drag
x:0
y:0
width: 288*scaleFactor
height: 215*scaleFactor
property bool isReleased: false
NumberAnimation {
id: animationY
target: fake_drag
property: "y"
from: mouseYValue;
to: -200
duration: 500
onStopped: {
console.log("removed index : ", indexValue)
favouriteapp.remove(indexValue)
}
}
MouseArea {
id: mouseArea
property int difference: 0
width: 288*scaleFactor
height: 215*scaleFactor
anchors.centerIn: parent
onPressed : {
itemInitialYPosition = mouseY
indexValue = index
}
drag.target: fake_drag_image
drag.axis: Drag.YAxis
onReleased: {
onceloaded = false;
}
onMouseYChanged: {
mouseYValue = mouseY
difference = itemInitialYPosition - mouseY
if(!onceloaded) {
if(itemInitialYPosition >= mouseY){
if(difference >= 25){
console.log("swipe..")
animationY.start()
onceloaded = true;
}
}
}
}
Image {
id: fake_drag_image
width: 64; height: 64
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
source: "images/Feature_Galary.png"
states: State {
when: mouseArea.drag.active
ParentChange { target: fake_drag_image; parent: itemid }
AnchorChanges { target: fake_drag_image; anchors.verticalCenter: undefined; anchors.horizontalCenter: undefined }
}
}
}
This is the code used in grid item delegate.
I have two images:
1. normal image
2. same image with the ability to zoom.
I have 4 draggable shapes (A,B,C,D) on second image (image with the ability to zoom).
I want to display position of mouse on normal image (with draw a shape (canvas3)) when the position of A/B/C/D shapes changed on the zoomed image.
I used the following code but it does not show correct position. In fact, I want user can see position of mouse.
property int xwheel: 0
property int ywheel: 0
property alias source :image.source
id:mywin
property int highestZ: 0
property real defaultSize: mywin.width
property real surfaceViewportRatio: 1.5
ScrollView {
anchors.fill: parent
flickableItem.interactive: true
frameVisible: true
highlightOnFocus: true
style: ScrollViewStyle {
transientScrollBars: true
.........
}
Flickable {
id: flick
anchors.fill: parent
contentWidth: parent.width
contentHeight: parent.height
Rectangle {
id: photoFrame
width: parent.width
height: parent.height
scale:defaultSize / parent.width
Behavior on scale { NumberAnimation { duration: 200 } }
Behavior on x { NumberAnimation { duration: 200 } }
Behavior on y { NumberAnimation { duration: 200 } }
smooth: true
antialiasing: true
Image {
id:image
anchors.fill: parent
fillMode: Image.PreserveAspectFit
smooth: true
}
PinchArea {
anchors.fill: parent
pinch.target: photoFrame
pinch.minimumRotation: -360
pinch.maximumRotation: 360
pinch.minimumScale: 0.1
pinch.maximumScale: 10
pinch.dragAxis: Pinch.XAndYAxis
property real zRestore: 0
onSmartZoom: {
.....
}
MouseArea {
id: dragArea
hoverEnabled: true
anchors.fill: parent
drag.target: photoFrame
scrollGestureEnabled: false
onPressed: {
photoFrame.z = ++mywin.highestZ;
}
onWheel: {
...
}
}
Point {
id: pointA
onDragged: {
xwheel=pointA.x;
ywheel=pointA.y;
canvas3.requestPaint()
}
}
//Point B
//Point D
//Pint C
}
}
}
Image {
id:mainimage
width: parent.width/4
height: parent.height/4
smooth: true
source: image.source
Canvas {
id: canvas3
width: parent.width
height: parent.height
onPaint: {
var context = getContext("2d");
// Make canvas all white
......
context.beginPath();
//Style
.......
context.moveTo(xwheel/4, ywheel/4);
context.arc(xwheel/4, ywheel/4, 5, 0, 2*Math.PI, true)
context.fill();
context.stroke();
}
}
}
I have one DropArea and two elements. I want DropArea reject the drop event if the DropArea already got one element be dropped, the another element not allow drop into, unless the first one move out.
DropArea {
property bool dropped: false
onDropped: {
drop.accepted = !dropped;
dropped = true;
}
onExited: dropped = false
}
But looks like drop.accepted not work,
BTW anyway to get the objects was dropped in DropArea
You should control if the item must be dropped or not in onReleased, checking the dropped property.
Full example:
import QtQuick 2.5
import QtQuick.Window 2.2
import QtQuick.Controls 1.4
Window {
id: win
visible: true
width: 800
height: 600
title: qsTr("Hello World")
Repeater {
model: 10
Rectangle {
id: rect
width: 50
height: 50
z: mouseArea.drag.active || mouseArea.pressed ? 2 : 1
color: Qt.rgba(Math.random(), Math.random(), Math.random(), 1)
x: Math.random() * (win.width / 2 - 100)
y: Math.random() * (win.height - 100)
property point beginDrag
property bool caught: false
border { width:2; color: "white" }
radius: 5
Drag.active: mouseArea.drag.active
Text {
anchors.centerIn: parent
text: index
color: "white"
}
MouseArea {
id: mouseArea
anchors.fill: parent
drag.target: parent
onPressed: {
rect.beginDrag = Qt.point(rect.x, rect.y);
}
onReleased: {
if(!rect.caught || dragTarget.dropped) {
backAnimX.from = rect.x;
backAnimX.to = beginDrag.x;
backAnimY.from = rect.y;
backAnimY.to = beginDrag.y;
backAnim.start()
}
parent.Drag.drop()
console.log("MouseArea - containsDrag " + dragTarget.dropped)
}
}
ParallelAnimation {
id: backAnim
SpringAnimation { id: backAnimX; target: rect; property: "x";
duration: 500; spring: 2; damping: 0.2 }
SpringAnimation { id: backAnimY; target: rect; property: "y";
duration: 500; spring: 2; damping: 0.2 }
}
}
}
Rectangle {
anchors {
top: parent.top
right: parent.right
bottom: parent.bottom
}
width: parent.width / 2
color: "gold"
DropArea {
id: dragTarget
anchors.fill: parent
property bool dropped: false
onEntered: {
console.log("onEntered " + containsDrag)
drag.source.caught = true;
}
onExited: {
console.log("onExited " + containsDrag)
dropped = false;
}
onDropped:
{
console.log("onDropped");
dropped = true;
}
}
}
}
Use drop.accept() instead. The above can be done as follows:
property bool containsItem: false
DropArea {
id: dropArea
anchors.fill: parent
onDropped: {
if(containsItem)
drop.accept(Qt.IgnoreAction)
else
drop.accept()
containsItem = true;
}
}
Also donot use dropped property as it is already an attached property inside onDropped event handler.
Edit:
If rect is the Item to be dragged and dropped then:
Rectangle {
id: rect
width: 40; height: 40
color: "red"
Drag.active: dragArea.drag.active
Drag.hotSpot.x: 20
Drag.hotSpot.y: 20
MouseArea {
id: dragArea
anchors.fill: parent
drag.target: parent
onReleased: if (rect.Drag.drop() !== Qt.IgnoreAction) {
console.log("Accepted!");
} else {
console.log("Rejected!");
}
}
}
How do you tell flickable to move to the bottom of the current page. QML - Move to top #TOP does not provide an answer for this.
You will need to calculate it and set it to contentY. For e.g:
Flickable {
width: 200; height: 200
contentWidth: image.width; contentHeight: image.height
Image { id: image; source: "file:///path/toBigImageFile" }
contentY : contentHeight-height
}
I kept adding text to a TextArea and wanted it to stay at the bottom unless someone changed the scroll position.
Flickable{
id: flickable
anchors.fill: parent
boundsBehavior: Flickable.DragAndOvershootBounds
flickableDirection: Flickable.VerticalFlick
ScrollBar.vertical: ScrollBar {
id: flickScroll
}
TextArea.flickable: TextArea{
id: monitor
width: parent.width
height: parent.height
readOnly: true
wrapMode: TextArea.Wrap
persistentSelection: true
leftPadding: 6
rightPadding: 6
topPadding: 0
bottomPadding: 0
background: null
}
function currPos(){
return flickable.contentY
}
function setPos(pos){
flickable.contentY = pos;
}
function getEndPos(){
var ratio = 1.0 - flickable.visibleArea.heightRatio;
var endPos = flickable.contentHeight * ratio;
return endPos;
}
function scrollToEnd(){
flickable.contentY = getEndPos();
}
function append(text){
var pos, endPos, value;
value = monitor.text + String(text);
// Limit value size here
endPos = getEndPos();
pos = currPos();
monitor.text = value;
if(pos == endPos){
scrollToEnd();
} else {
setPos(pos);
}
}
}