PDA

View Full Version : Slide windows



jaykappy
13th August 2019, 22:59
I have a page that has a header, body and footer. Its an app that allows the user to click a map in the body and return results. Right now I am popping those results in a rectangle in the main body.
But what I want to do is put those results in the footer but expand the footer to say 50% vertical of the screen. When the user clicks a close button in the footer or in the body the footer would go back to its original size...

Does that make sense? Wondering what my best options would be to research...

d_stranz
14th August 2019, 01:04
I have implemented exactly this. I call it a "collapsible widget". I am sorry I can't share the code because it is proprietary to my company, but it is easy to implement:

The CollapsibleWidget is derived from QWidget. It contains a QVBoxLayout as its main layout. Inside of this vbox is a QHBoxLayout.
The hbox contains a horizontal spacer, a QToolButton with an up / down arrow icon, and another horizontal spacer to keep the button centered. Depending on whether the collapsible widget is at the top or bottom of the window, the hbox is placed either at the bottom or top of the vbox, respectively.

The widget which is controlled by the collapsible widget is inserted at the other position in the vbox. This is usually a QWidget-based composite widget containing other child widgets. In my implementation, the collapsible widget needs to know the fully-expanded height of this controlled widget for use during the slide in / out.

In use, I usually put the collapsible widget in a pane of a QSplitter, but in the example shown in the screen shots, it is in the top-most pane of another vbox layout, and the data plot is in the pane below that. The controlled widget initially starts out collapsed. When the user clicks the expand button, a property animation slowly changes the "maximumHeight" property of the controlled widget from 0 to whatever has been set as its maximum height. Thus the controlled widget gradually slides out. This procedure is reversed when the widget slides back in.

See the screen shots for the static picture of the collapsed and expanded states.

13223 13224

My implementation is much more detailed than the description above - the collapsible widget can have horizontal or vertical orientation, it can be located on any of the four sides of the window it is used in, it can be animated or not, the button does not have to be centered (or even visible), the button can have a tool tip, the animation rate can be varied, etc. There's a public slot to set the collapsed state, and a signal that is emitted when the state is toggled. All told, it is only about 450 lines of code and a large chunk of that is logic to change what happens based on whether the widget has a horizontal or vertical orientation. It would probably drop to 300 lines or so if it was restricted to just horizontal orientation,

jaykappy
14th August 2019, 16:37
Thank you d_strnaz
What you said make sort of sense....very very new to this.....Anyone out there have an extremely basic example of this.....
a window that when clicked animates a window that slides from the bottom to fill 50% of the main window....

I am going to continue to look this up but any help would be very appreciated.

I currently have a few rectangles (Header, body and footer) That sit inside a rectangle for the whole area....
I need to embed these rectangles in the final solution....Not really sure where to go from here....


The CollapsibleWidget is derived from QWidget. It contains a QVBoxLayout as its main layout. Inside of this vbox is a QHBoxLayout.

Basically this is what I have....snipped everything out but what I think is relevant

Would be cool to get this working where the footer would be not shown...when a user clicks the map section the foot expands to 50%. then when clicked in map area again the footer animates to closed and non-visible



Rectangle {
id: loadService

ColumnLayout {
anchors.fill: parent
spacing: 0

Rectangle {
id: createPage_headerBar
Layout.alignment: Qt.AlignTop
color: app.headerBackgroundColor
Layout.preferredWidth: parent.width
Layout.preferredHeight: 50 * app.scaleFactor
visible: !isFullMap

MouseArea {
anchors.fill: parent
onClicked: {
mouse.accepted = false
}
}
}

// create MapView
MapView {
id:mapView

Map {
initialViewpoint: viewPoint
} // END OF MAP
}

Rectangle {
id: footer
//Layout.preferredHeight: page3_button1.height
Layout.preferredWidth: parent.width * 0.95
Layout.alignment: Qt.AlignHCenter
Layout.maximumWidth: app.units(600)
Layout.margins: 8 * scaleFactor
color: "transparent"
visible: !isFullMap
Layout.bottomMargin: app.isIPhoneX ? 28 * app.scaleFactor : 8 * scaleFactor
}

} // END OF COLUMN LAYOUT


} // END OF OPENING RECTANGLE

d_stranz
14th August 2019, 21:45
Can't help you with the QML, but in essence all my collapsible widget does is manage the maximumHeight property of the widget it controls. You don't actually need the CollapsibleWidget class itself to do this; I implemented it that way to have a reusable library component. You don't need the buttons to expand / collapse the slide widget if you will be doing it in response to other clicks.

Basically, all you need to do is: 1) keep track of the overall height of your large window, 2) put your header, body, footer, and map widgets into a vertical layout, 3) set the maximumHeight property of the map widget to 0, then 4) when you want the map to show, change its maximumHeight property from 0 to half the window height and 5) when you want to dismiss it, set the maximumHeight property back to 0. The vertical box layount management will take care of adjusting the sizes of the other widgets.

The animation is icing on top of this, changing the maximumHeight property as a function of time so the map slides up or down. So instead of a click on the main window instantly changing maximumHeight, it start the animation which changes it gradually. Likewise a click on the map starts the reverse animation.

jaykappy
15th August 2019, 20:07
I cant find this anywhere

changing the maximumHeight property as a function of time so the map slides up or down

Added after 1 23 minutes:

I can get this to work ...BUT

It waits for the time to change the color and then when done just changes the height of the rectangle in a second....
I want the transition of the height change to happen along with the change of the color

Any idea when the height transition is not changing with the color change?




Rectangle {
id: footer
anchors.bottom: parent.bottom
Layout.maximumHeight: 200;

height: 20
width: parent.width
color: "red"
visible: true


state: "RELEASED"

MouseArea {
anchors.fill: parent
onPressed: footer.state = "PRESSED"
onReleased: footer.state = "RELEASED"
}

states: [
State {
name: "PRESSED"
PropertyChanges { target: footer; color: "green"; height: parent.height * .25}
PropertyChanges { target: mapView; height: parent.height * .60}
},
State {
name: "RELEASED"
PropertyChanges { target: footer; color: "purple"; Layout.height:20;}
PropertyChanges { target: mapView; height: parent.height * .80}

}
]

transitions: [
Transition {
from: "PRESSED"
to: "RELEASED"
ColorAnimation { target: footer; duration: 2000}
},
Transition {
from: "RELEASED"
to: "PRESSED"
ColorAnimation { target: footer; duration: 2000}
}
]

jaykappy
15th August 2019, 22:11
In addition to not being able to animate the rectangle opening ....My main problem is that I want this to "Pressed" property changes to happen after the user clicks the mapView Rectangle and to close when I click a button on the footer Rectangle. Not sure how to modify this to make that happen

d_stranz
15th August 2019, 22:32
Any idea when the height transition is not changing with the color change?

I see a "ColorAnimation" but do not see a corresponding animation for the height. So basically the height is changed instantly during the state transitions.


Not sure how to modify this to make that happen

Add a handler for the pushbutton "clicked" signal. In that handler set the footer state to whatever starts the "slide in" transition..

I am not sure your logic is correct, however. A "click" is the combination of a press and a release, both occurring on the same mouse area. By handling the press separately from the release and doing different things in response to each, that isn't the same behavior as a click. And you could end up in a stuck state - if the user presses the mouse in the footer, then moves out of the footer to release it, the footer only gets a press event and the window is stuck in that state.

jaykappy
15th August 2019, 23:15
Thats where I am puzzled....what Animation do I use for the Height?

transitions: [
Transition {
from: "PRESSED"
to: "RELEASED"
ColorAnimation { target: footer; duration: 2000}
},
Transition {
from: "RELEASED"
to: "PRESSED"
ColorAnimation { target: footer; duration: 2000}
}
]

d_stranz
16th August 2019, 17:27
There is a QML "NumberAnimation" type, inherited from the "PropertyAnimation" type. Set the target to your footer, property to "height" or "maximumHeight", "from" and "to" to the start and end heights

jaykappy
19th August 2019, 18:44
I thank you for your time and patience....

Would I get rid of the states below? This is where I am changing the heights...


state: "RELEASED"

MouseArea {
anchors.fill: parent
//onClicked: footer.state = "PRESSED"
onPressed: footer.state = "PRESSED"
onReleased: footer.state = "RELEASED"
}

states: [
State {
name: "PRESSED"
PropertyChanges {
target: footer;
color: "green";
height: !isFullMap ? parent.height * .32 : parent.height * .32;
}
PropertyChanges {
target: mapView;
height: !isFullMap ? parent.height * .61 : parent.height * .68
}
},
State {
name: "RELEASED"
PropertyChanges { target: footer; color: "gray"; Layout.height:20;}
PropertyChanges { target: mapView; Layout.fillHeight: true;}
}
]




This gives me errors

transitions: [
PropertyAnimation {
from: "PRESSED"
to: "RELEASED"
NumberAnimation { target: footer; from: 50; to:200; duration: 2000}
},
PropertyAnimation {
from: "RELEASED"
to: "PRESSED"
NumberAnimation { target: footer; from: 200; to:50; duration: 2000}
}
]

d_stranz
19th August 2019, 23:35
I don't know QML, but shouldn't the keyword "PropertyAnimiation" in your "transitions" block be the keyword "Transition" instead?

jaykappy
20th August 2019, 13:34
If I do this...the color animation works fine....but it waits till the color animation is done and then the height adjustment is almost instant



state: "RELEASED"

MouseArea {
anchors.fill: parent
//onClicked: footer.state = "PRESSED"
onPressed: footer.state = "PRESSED"
onReleased: footer.state = "RELEASED"
}

states: [
State {
name: "PRESSED"
PropertyChanges {
target: footer;
color: "white";
height: !isFullMap ? parent.height * .32 : parent.height * .32;
}
PropertyChanges {
target: mapView;
height: !isFullMap ? parent.height * .65 : parent.height * .68
}
},
State {
name: "RELEASED"
PropertyChanges { target: footer; color: "gray"; Layout.height:50;}
PropertyChanges { target: mapView; Layout.fillHeight: true;}
}
]
transitions: [
Transition {
from: "PRESSED"
to: "RELEASED"
ColorAnimation { target: footer; duration: 2000}
PropertyAnimation {target: footer; properties: height; }
NumberAnimation { target: footer; from: 50; to:200; duration: 2000 }
},
Transition {
from: "RELEASED"
to: "PRESSED"
ColorAnimation { target: footer; duration: 2000}
PropertyAnimation { target: footer.height; properties: height; }
NumberAnimation { target: footer.height; from: 200; to:50; duration: 2000 }
}
]

d_stranz
20th August 2019, 18:44
I think you need to understand something about QML object types. When I said that "NumberAnimation" is derived from "PropertyAnimation", it means that PropertyAnimation is the base class for the NumberAnimation type. ColorAnimation is also derived from PropertyAnimation. It does not mean that you include both PropertyAnimation and NumberAnimation in the same Transition.

Try this:



transitions: [
Transition {
from: "PRESSED"
to: "RELEASED"
ColorAnimation { target: footer; duration: 2000}
NumberAnimation { target: footer; property: "height"; from: 50; to:200; duration: 2000 }
},
Transition {
from: "RELEASED"
to: "PRESSED"
ColorAnimation { target: footer; duration: 2000}
NumberAnimation { target: footer; property: "height"; from: 200; to:50; duration: 2000 }
}
]

jaykappy
20th August 2019, 23:25
Thank you....I SWEAR i tried that....I tried a million different ways...

Here was one that I forgot to put in the properties: height...

I was all over the green on this.....THANK YOU VERY much....



/*
transitions: [
PropertyAnimation {
from: "PRESSED"
to: "RELEASED"
NumberAnimation { target: footer; from: 50; to:200; duration: 2000}
},
PropertyAnimation {
from: "RELEASED"
to: "PRESSED"
NumberAnimation { target: footer; from: 200; to:50; duration: 2000}
}
]
*/

jaykappy
21st August 2019, 13:48
After all that this is all I needed

Behavior on height{
NumberAnimation {duration: 5000}
}



states: [
State {
name: "PRESSED"
PropertyChanges {
target: footer;
color: "white";
height: !isFullMap ? parent.height * .32 : parent.height * .32
}
PropertyChanges {
target: mapView;
height: !isFullMap ? parent.height * .65 : parent.height * .68
}
},
State {
name: "RELEASED"
PropertyChanges { target: footer; color: "gray"; height:parent.height * .04}
PropertyChanges { target: mapView; Layout.fillHeight: true}
}
]

Behavior on height{
NumberAnimation {duration: 5000}
}

d_stranz
22nd August 2019, 17:26
After all that this is all I needed

Glad you got it sorted. I am at the limit on my QML knowledge - I have never used it for a project, just looked at it when it was first introduced.

anda_skoa
24th August 2019, 09:13
Since you have the same animation timing into both directions you can also just work with property bindings and Behavior elements



Rectangle {
id: footer
anchors.bottom: parent.bottom
Layout.maximumHeight: 200;

height: pressArea.pressed ? parent.height * .32 : parent.height * .65
width: parent.width
color: pressArea.pressed ? "white" : "grey"
visible: true

MouseArea {
id: pressArea
}

Behavior on height {
NumberAnimation { duration: 2000 }
}

Behavior on color {
ColorAnimation { duration: 2000 }
}
}


Cheers,
_

Joselyn
4th October 2019, 13:53
Thanks for the code samples, guys!