How do I properly change the x, y of an object so that it changes its position when the parent is resized? There is, I will introduce that if I drag the rectangle to the middle, then when the window is resized, it should remain in the middle. (middle for example only, rectangle can be moved freely)
import QtQuick 2.9
import QtQuick.Window 2.2
import QtQuick.Controls 2.3
Window {
visible: true
width: 640
height: 480
onWidthChanged: {
block.x -= block.previousWidth - width
block.previousWidth = width
}
onHeightChanged: {
block.y -= block.previousHeight - height
block.previousHeight = height
}
Rectangle {
id: block
color: "red"
width: 50
height:50
x: 100
y: 50
property int previousWidth: 0
property int previousHeight:0
Component.onCompleted: {
previousWidth = parent.width
previousHeight = parent.height
}
MouseArea {
anchors.fill: parent
drag.target: block
}
}
}
I must admit, at first I was not impressed by the question. However, when I thought about it, it represents a very interesting and valid use case. So I would be happy to provide a solution.
Solution
I would approach the problem like this:
Make the frame a child of the background image.
Instead of manually calculating the coordinates, use Item.scale to scale the image, effectively preserving the relative position of the frame with regard to the image.
Example
Here is an example I have prepared for you to demonstrate how the proposed solution could be implemented:
import QtQuick 2.15
import QtQuick.Window 2.15
Window {
visible: true
width: 640
height: 480
Image {
anchors.centerIn: parent
source: "alphabet.png"
scale: parent.width/sourceSize.width
Rectangle {
id: frame
width: parent.width/7
height: parent.height/4
border.color: "black"
color: "transparent"
antialiasing: true
MouseArea {
anchors.fill: parent
drag.target: parent
}
}
}
}
Result
The example produces the following result:
Original window
Resized window
The frame is moved
The window is resized again
As I said in my comment, the best solution is anchoring, for example:
Window {
id: root
width: 600
height: 400
title: qsTr("Parent window")
visible: true
flags: Qt.WindowStaysOnTopHint
Grid {
anchors.fill: parent
Repeater {
model: 16
Rectangle {
width: root.width / 4
height: root.height / 4
color: Qt.rgba(Math.random(),Math.random(),Math.random(),1)
}
}
}
Rectangle {
border {
width: 5
color: "black"
}
color: "transparent"
width: root.width / 4
height: root.height / 4
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.leftMargin: root.width / 4
anchors.bottomMargin: root.height / 4
}
}
Related
I have a custom scrollbar QML type that I am working on. The problem I'm having is that if the scroll bar is all the way at the bottom of the page and the height of the main application window is increased, the translated contents stay in place and the size of the scroll bar is not updated. After this window resize occurs, clicking on the scroll bar causes the content to snap to its proper place and the scroll bar to snap to its proper size. What changes might could be made to the code below so the position of the contents (red blocks) and scroll bar size update while the window height is changing? Not afterwards when the scroll bar has been clicked again. To see the issue just open the code below, scroll the blue scroll bar all the way to the bottom, increase the height of the main window (observing the scroll bar size and the content position), and then click on the scroll bar after the resize. Here is my code:
main.qml
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Layouts 1.12
import QtQuick.Shapes 1.15
Window {
id: main_window
width: 640
height: 480
visible: true
title: qsTr("Hello World")
color: 'light blue'
// container
ColumnLayout {
id: my_column
anchors.centerIn: parent
width: main_window.width / 3
height: main_window.height / 3
spacing: 0
// contents
ColumnLayout {
id: repeater_element
Layout.fillWidth: true
Layout.fillHeight: false
spacing: 4
Repeater {
model: 7
Rectangle {
Layout.fillWidth: true
Layout.fillHeight: false
Layout.preferredHeight: 75
Layout.alignment: Qt.AlignTop
color: 'red'
}
}
transform: Translate {
id: rect_translate
y: 0
}
}
}
// scroll bar type
Scroll_Bar {
x: 0
y: 0
height: parent.height
width: 20
container_element: my_column
content_element: repeater_element
translate_element: rect_translate
orientation: Qt.Vertical
}
// just a border for the container element
Shape {
ShapePath {
strokeWidth: 4
strokeColor: "black"
fillColor: Qt.rgba(.09, .05, .86, 0)
joinStyle: ShapePath.MiterJoin
startX: my_column.x
startY: my_column.y
PathLine {
relativeX: my_column.width
relativeY: 0
}
PathLine {
relativeX: 0
relativeY: my_column.height
}
PathLine {
relativeX: -my_column.width
relativeY: 0
}
PathLine {
relativeX: 0
relativeY: -my_column.height
}
}
}
}
Scroll_Bar.qml
import QtQuick 2.0
import QtQuick.Controls 2.5
ScrollBar {
property var container_element
property var content_element
property var translate_element
QtObject {
id: internal
property real vertical_size: container_element.height / content_element.height
property real horizontal_size: container_element.width / content_element.width
property real off_the_bottom: (content_element.height - container_element.height) + translate_element.y
}
id: scroll_bar_element
hoverEnabled: true
active: size
orientation: orientation
size: orientation === Qt.Vertical ? internal.vertical_size : internal.horizontal_size
padding: 0
contentItem: Rectangle {
id: ci
radius: 0
color: 'blue'
}
onSizeChanged: {
if(size > 1){
visible = false
}
else{
visible = true
}
}
onPositionChanged: {
if (orientation === Qt.Horizontal) {
translate_element.x = -scroll_bar_element.position * content_element.width
} else {
translate_element.y = -scroll_bar_element.position * content_element.height
}
}
Component.onCompleted: {
scroll_bar_element.onPositionChanged()
}
}
You can hardly write better scrollbar than the existing one, so I made the following code which does the same thing I saw in your example. ScrollBar can be the sibling of a flickable, so it won't take ownership and you can position it where you want. You can even make it rotated.
Is it something that solves your problem?
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Layouts 1.12
import QtQuick.Controls 2.12
Window {
id: main_window
width: 640
height: 480
visible: true
title: qsTr("Hello World")
color: 'light blue'
Flickable {
id: flickable
anchors.centerIn: parent
width: main_window.width / 3
height: main_window.height / 3
contentWidth: repeater_element.width
contentHeight: repeater_element.height
ScrollBar.vertical: scrollBar
// container
ColumnLayout {
id: my_column
width: main_window.width / 3
height: main_window.height / 3
spacing: 0
// contents
ColumnLayout {
id: repeater_element
Layout.fillWidth: true
Layout.fillHeight: false
spacing: 4
Repeater {
model: 7
Rectangle {
Layout.fillWidth: true
Layout.fillHeight: false
Layout.preferredHeight: 75
Layout.alignment: Qt.AlignTop
color: 'red'
}
}
transform: Translate {
id: rect_translate
y: 0
}
}
}
}
ScrollBar {
id: scrollBar
anchors.left: parent.left
anchors.top: parent.top
anchors.bottom: parent.bottom
//try this for fun
//rotation: 5
contentItem: Rectangle {
implicitWidth: 20
implicitHeight: 20
color: "blue"
}
}
Rectangle {
color: "transparent"
border.width: 4
anchors.fill: flickable
}
}
I'm new at QML and I have a very confusing situation.
So, this is my main.qml file:
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.0
Window {
id:rootWin
visible: true
width: 350
height: 330
ConnectBox
{
id:boxConnect
MouseArea
{
id: connectMouse
hoverEnabled: true
anchors.fill: boxConnect
}
}
Rectangle {
id: randomRec
width: parent.width/2
height: parent.height/6
// x: 50
anchors.top: boxConnect.bottom
// anchors.horizontalCenter: parent
anchors.topMargin: 10
border.color: "dimgray"
border.width: 5
radius: 14
}
}
ConnectBox.qml:
import QtQuick 2.0
Rectangle {
id: connectRec
width: parent.width/2
height: parent.height/6
anchors.centerIn: parent
border.color: "dimgray"
border.width: 5
radius: 14
}
I want it to be randomRec below boxConnect, so it does but it is all left and it only moves when I put for example x: 50 but its not convenient for every size of the window.
First off: I'd advise to put the anchors that are related to each other all on the same file.
But to get the randomRec to center below the connectBox you should use the following anchors:
Rectangle {
id: rect1
width: 100
height: 200
color: "red"
anchors.centerIn: parent
}
Rectangle {
id: rect2
width: 75
height: 50
color: "yellow"
anchors.top: rect1.bottom
anchors.horizontalCenter: rect1.horizontalCenter
}
So I guess you where almost there, with the horizontalCenter: parent line, which should have .horizontalCenter as well. However, I anchored it on rect1 since that is what you want (let's say you might want to move the boxConnect in the future... you would have to find all of the references)
lets just say theoreticaly, that I have
Rectangle {
id: testRect
width: 100
}
and once i start the timer with interval tick 50ms, it should just extend the width of Rect:
Timer {
id: testTimer
interval: 50
onTriggered: testRect.width += 50
}
which works fine, but even when its onlz 50ms, its still seems to be quite not smooth transition.
Any idea how to smoothen the width change?
Please note this is only for learning purposes, what I will learn here will use in different situations, therefore please dont ask what is the puspose of the code...
Thank you!
You should rely on the animation features available in QtQuick to animate property changes.
In your case, you can define different states, with transitions between states where you define how an item should behave when going from one state to another. (See relevant documentation about states)
import QtQuick 2.9
import QtQuick.Window 2.2
Window {
visible: true
width: 640
height: 480
Rectangle {
id: rect
anchors.left: parent.left
anchors.top: parent.top
anchors.margins: 100
height: 200
color: "red"
state: "default"
states: [
State {
name: "default"
PropertyChanges {
target: rect
width: 200
}
},
State {
name: "bigger"
PropertyChanges {
target: rect
width: 250
}
}
]
transitions: Transition {
NumberAnimation {
duration: 500 //ms
target: rect
properties: "width"
}
}
// Just there to trigger the state change by clicking on the Rectangle
MouseArea {
anchors.fill: parent
onClicked: {
if (rect.state === "default")
rect.state = "bigger"
else
rect.state = "default"
}
}
}
}
Or you can define a behavior, which is more simple to define when you only act on a single property:
import QtQuick 2.9
import QtQuick.Window 2.2
Window {
visible: true
width: 640
height: 480
Rectangle {
id: rect
anchors.left: parent.left
anchors.top: parent.top
anchors.margins: 100
height: 200
width: 200
color: "red"
Behavior on width {
NumberAnimation {
duration: 500 //ms
}
}
// Just there to trigger the width change by clicking on the Rectangle
MouseArea {
anchors.fill: parent
onClicked: {
if (rect.width === 200)
rect.width = 250
else
rect.width = 200
}
}
}
}
Finally, if you really want a smooth animation, you can use SmoothedAnimation instead of NumberAnimation (which is a linear animation by default)
I have a ColumnLayout with a RowLayout in it. The RowLayout is positioned as expected. This is also true if the windows is being resized. Even if the windows is smaller than the entire ColumnLayout (see second image)
But if I replace the RowLayout by a (horizontal) ListView, this ListView is not positioned as I would expect. I would expect this behaves like the example with the RowLayout but the ListView is positioned higher:
And if the window gets 'to small' the blue rectangle 'moves into' the ListView unlike the first example:
How can I achieve the behaviour of the first example with a ListView?
Source
import QtQuick 2.0
import QtQuick.Layouts 1.1
Rectangle {
width: 360
height: 360
ColumnLayout {
anchors.fill: parent
anchors.margins: 20
Item {
Layout.fillHeight: true
}
/*
ListView {
Layout.fillHeight: true
Layout.fillWidth: true
orientation: ListView.Horizontal
spacing: 5
model: 3
delegate: Rectangle {
width: 50
height: 50
color: "red"
}
}
*/
RowLayout {
Layout.fillHeight: true
Layout.fillWidth: true
Rectangle {
width: 50
height: 50
color: "red"
}
Rectangle {
width: 50
height: 50
color: "red"
}
Rectangle {
width: 50
height: 50
color: "red"
}
}
Item {
Layout.fillHeight: true
}
Rectangle {
width: 50
height: 50
color: "blue"
}
}
}
You just need to define width and height for your ListView. In that way your column layout will consider its size and keep it as a fixed size.
Here your code updated:
import QtQuick 2.0
import QtQuick.Layouts 1.1
Rectangle {
width: 360
height: 360
ColumnLayout {
anchors.fill: parent
anchors.margins: 20
Item {
Layout.fillHeight: true
}
ListView {
//take as much width as possible with a binding
width: parent.width
//keep enought height to display each delegate instance
height: 50
orientation: ListView.Horizontal
spacing: 5
model: 3
delegate: Rectangle {
width: 50
height: 50
color: "red"
}
}
Item {
Layout.fillHeight: true
}
Rectangle {
width: 50
height: 50
color: "blue"
}
}
}
I have a nested ScrollView, similar to the following QML:
import QtQuick 2.0
import QtQuick.Controls 1.1
Rectangle {
width: 200
height: 600
ScrollView {
id: sView
anchors.fill: parent
ListView {
id: list
boundsBehavior: Flickable.StopAtBounds
clip: true
focus: true
interactive: true
model: 5
delegate: Component {
MouseArea {
id: hoverArea
width: 100
height: 200
onClicked: list.currentIndex = index;
Rectangle {
id: fauxParent
anchors.fill: parent
border.width: 1
border.color: "black"
Rectangle {
anchors.top: parent.top
anchors.left: parent.left
height: parent.height
width: parent.width / 2
border.width: 1
border.color: "purple"
color: "green"
Text {
anchors.centerIn: parent
text: "stuff"
}
}
ScrollView {
//parent: sView
anchors.top: fauxParent.top
anchors.right: fauxParent.right
height: fauxParent.height
width: fauxParent.width / 2
ListView {
model: 3
delegate: Component {
Rectangle {
radius: 10
height: 100
width: 100
color: "blue"
}
}
}
}
}
}
}
}
}
}
It seems to run correctly, except that the inner ScrollView won't respond to the mousewheel: the outer ScrollView intercepts that event. The only fix I've found in research for this, is to set the inner scrollview's parent directly to the outer scrollview (uncomment the parent: sView line). Unfortunately, this re-positions all five scrollview delegates onto the top right corner of the outer scrollview. It seems that ScrollView positions itself based on its parent?
For the record, my actual application is wrapping a large section of the page in a scrollview so as to allow the user to access sections of it that may be out of bounds for the current window size. The content of this section, though, has a variety of different controls for a variety of different purposes, including some scrollviews. So I'd also accept an alternate way of moving around a set of generic content that's too large for the window.
This is a Windows desktop app, so I don't need to consider mobile-specific issues.
You nested four elements that handle scroll Events.
Why do you put a ScrollView arround a ListView?
If you remove the ScrollViews the Mousewheel work fine.
Rectangle {
width: 200
height: 600
ListView {
anchors.fill: parent
id: list
boundsBehavior: Flickable.StopAtBounds
clip: true
focus: true
interactive: true
model: 5
delegate: Component {
MouseArea {
id: hoverArea
width: 100
height: 200
onClicked: list.currentIndex = index;
Rectangle {
id: fauxParent
anchors.fill: parent
border.width: 1
border.color: "black"
Rectangle {
anchors.top: parent.top
anchors.left: parent.left
height: parent.height
width: parent.width / 2
border.width: 1
border.color: "purple"
color: "green"
Text {
anchors.centerIn: parent
text: "stuff"
}
}
ListView {
anchors.top: fauxParent.top
anchors.right: fauxParent.right
height: fauxParent.height
width: fauxParent.width / 2
model: 3
delegate: Component {
Rectangle {
radius: 10
height: 100
width: 100
color: "blue"
}
}
}
}
}
}
}
}
If you miss the Scrollbar look at this:
How to create scrollbar in QtQuick 2.0?