I am having some problems with objects layout.
What I need is to create 2 blocks of text items. Second block should follow first.
Here is my code:
import QtQuick 2.5
import QtQuick.Window 2.2
import QtQuick.Layouts 1.1
Window
{
visible: true
id: page
Rectangle
{
id: contentRec
anchors.fill: parent
ColumnLayout
{
spacing: 16
anchors.fill: contentRec
anchors.margins: 16
Rectangle
{
id: hlpRec
color: "#fff"
ColumnLayout
{
anchors.fill: hlpRec
spacing: 8
Text
{
text: "Some text 1"
color: "#434D56"
}
Text
{
text: "Some text 1"
}
Text
{
text: "Some text 2"
}
Text
{
text: "Some text 3"
}
Text
{
text: "Some text 4"
}
}
}
Rectangle
{
Layout.preferredHeight: 16
}
Rectangle
{
id: infoRec
color: "#fff"
ColumnLayout
{
anchors.fill: infoRec
spacing: 8
Text
{
text: "Status text 1"
}
Text
{
text: "Status text 2"
}
Text
{
text: "Status text 3"
}
}
}
}
}
}
The problem is that second block overlaps first. What is wrong with my code?
Both of your inner ColumnLayout components are set to fit their parents, which have neither their width/height nor their anchor properties defined, hence their size is zero. Because those Rectangles don't clip their contents, you see the items overlapping.
When working with Column or ColumnLayout, the content height will be calculated from what you put in those containers. If you do it correctly, you can build pretty flexible and clever layouts without having to keep track of individual heights. However, you have to specify the other dimension by either setting the width property or the appropriate anchors. In case of Column and ColumnLayout you want to "snap" the component's width to fit the parent. At the same time you could leave the height unconstrained, allowing the item to grow vertically. Same goes for Row and RowLayout where the width will be calculated and you have to specify a certain height.
The solution in your case could be either based on ColumnLayout or Column. Note that the hlpRec and infoRec were removed and the anchors of Column/ColumnLayout were set.
Via ColumnLayout:
import QtQuick 2.6
import QtQuick.Layouts 1.1
Rectangle {
anchors.fill: parent
ColumnLayout {
spacing: 16
anchors.fill: parent
anchors.margins: 16
ColumnLayout {
anchors.left: parent.left
anchors.right: parent.right
spacing: 8
Repeater {
model: 5
Text {
text: "top " + index
}
}
}
Rectangle {
Layout.preferredHeight: 16
anchors.left: parent.left
anchors.right: parent.right
color: "#ff00ff"
}
ColumnLayout {
anchors.left: parent.left
anchors.right: parent.right
spacing: 8
Repeater {
model: 5
Text {
text: "bottom " + index
}
}
}
}
}
Via Column:
import QtQuick 2.6
Rectangle {
anchors.fill: parent
Column {
spacing: 16
anchors.fill: parent
anchors.margins: 16
Column {
anchors.left: parent.left
anchors.right: parent.right
spacing: 8
Repeater {
model: 5
Text {
text: "top " + index
}
}
}
Rectangle {
height: 16;
anchors.left: parent.left
anchors.right: parent.right
color: "#ff00ff"
}
Column {
anchors.left: parent.left
anchors.right: parent.right
spacing: 8
Repeater {
model: 5
Text {
text: "bottom " + index
}
}
}
}
}
The ColumnLayout will center content items individually and uses the total height available from the window whereas the Column will align all elements from top to bottom using only the content height. You'll notice the different behavior when changing your window's height.
In case those Rectangles are used to define individual backgrounds, you can do something like this (demonstrated with the Column based approach):
import QtQuick 2.6
Rectangle {
anchors.fill: parent
Column {
spacing: 16
anchors.fill: parent
anchors.margins: 16
Rectangle {
anchors.left: parent.left
anchors.right: parent.right
height: topColumn.height
color: "#ff0000"
Column {
id: topColumn
anchors.left: parent.left
anchors.right: parent.right
spacing: 8
Repeater {
model: 5
Text {
text: "top " + index
}
}
}
}
Rectangle {
height: 16;
anchors.left: parent.left
anchors.right: parent.right
color: "#ff00ff"
}
Rectangle {
anchors.left: parent.left
anchors.right: parent.right
height: bottomColumn.height
color: "#0000ff"
Column {
id: bottomColumn
anchors.left: parent.left
anchors.right: parent.right
spacing: 8
Repeater {
model: 5
Text {
text: "bottom " + index
}
}
}
}
}
}
Hope this helps!
Related
I am trying to use a ListView with a simple header that stays on top of the list. It is working fine for most situations. When I scroll all the way to the top manually the top item is located below the header:
When I set the current index from outside the list to highlight an item the list scrolls to the highlighted item. This is expected and desired behavior. However the list scrolls in a way, that the highlighted item will be on the same height (y) as the header. Therefore the header partially covers the item. In the picture the header is transparent, the highlight is light green:
How can I set up the list so that the list content always starts below the header? Or as a workaround, how can I set the height of the current item after the automatic scroll on selection?
For completeness here is the current code of my list.
ListView {
id: particleList
anchors.fill: parent
model: particleModel
clip: true
highlight: Rectangle { color: Material.color(Material.Green); opacity: 0.2}
highlightMoveDuration: Style.animationDurationMedium
headerPositioning: ListView.OverlayHeader
header: Item {
height: 40
anchors.left: parent.left
anchors.right: parent.right
Row {
anchors.fill: parent
MediumText {
horizontalAlignment: Text.AlignHCenter
width: parent.width / 3
text: qsTr("Width")
}
MediumText {
horizontalAlignment: Text.AlignHCenter
width: parent.width / 3
text: qsTr("Height")
}
MediumText {
horizontalAlignment: Text.AlignHCenter
width: parent.width / 3
text: qsTr("Area")
}
}
}
footer: SmallText {
anchors.left: parent.left
anchors.right: parent.right
text: particleModel.count
}
populate: Transition {
NumberAnimation { properties: "x,y"; duration: 1000 }
}
delegate: Item {
height: 40
anchors.left: parent.left
anchors.right: parent.right
Row {
anchors.fill: parent
MediumText {
anchors.verticalCenter: parent.verticalCenter
width: parent.width / 3
text: model.width
horizontalAlignment: Text.AlignRight
rightPadding: 20
}
MediumText {
anchors.verticalCenter: parent.verticalCenter
width: parent.width / 3
text: model.height
horizontalAlignment: Text.AlignRight
rightPadding: 20
}
MediumText {
anchors.verticalCenter: parent.verticalCenter
width: parent.width / 3
text: model.area
horizontalAlignment: Text.AlignRight
rightPadding: 20
}
}
Rectangle {
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
height: 1
visible: model.index !== 0
color: Material.color(Material.Grey)
}
}
}
The Listview rows always scroll behind the header.
So make it opaque (e.g. Rectangle with background instead of item) and increase the z value to have it on top.
For the scrolling use highlightRangeMode, preferredHighlightBegin and preferredHighlightEnd.
I have a tab bar with a stacklayout like the following:
Rectangle {
id: rect
height: 190
anchors.right: parent.right
anchors.left: parent.left
color: "transparent"
anchors.top: uniqueHandleText.bottom
anchors.topMargin: 100
TabBar {
id: frame
anchors.right: parent.right
anchors.left: parent.left
background: Rectangle {
color: "#737373"
}
x: -hbar.position * width
Repeater {
model: wizard.categories
TabButton {
id: tabData
property bool selected: false
text: modelData.name
width: 200
font.pixelSize: 18
contentItem: Text {
text: tabData.text
font: tabData.font
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
wrapMode: Text.WordWrap
color: "#FFFFFF"
}
background: Rectangle {
implicitWidth: frame.width
implicitHeight: 180
opacity: enabled ? 1 : 0.3
color: tabData.checked ? "#BD9CBE": "#737373"
}
}
}
}
ScrollBar {
id: hbar
hoverEnabled: true
active: hovered || pressed
orientation: Qt.Horizontal
size: rect.width / frame.width
anchors.left: parent.left
anchors.right: parent.right
anchors.top: frame.bottom
}
Text {
font.pixelSize: 18
text: "Next"
anchors.right: parent.right
visible: frame.x != frame.width ? true: false
}
StackLayout {
id: stack1
anchors.left: parent.left
anchors.right: parent.right
anchors.top: frame.bottom
currentIndex: frame.currentIndex
Repeater {
model: wizard.categories
Item {
id: homeTab
TabBar {
id: homeTabTab
anchors.right: parent.right
anchors.left: parent.left
anchors.top: parent.top
height: 180
background: Rectangle {
color: "#958096"
}
Repeater {
model: modelData.sub_categories
TabButton {
property bool selected: false
id: currentTab
text: modelData.name
width: 200
font.pixelSize: 18
background: Rectangle {
implicitWidth: frame.width
implicitHeight: 180
opacity: enabled ? 1 : 0.3
color: currentTab.checked ? "#958096": "#8D758E"
}
contentItem: Text {
text: currentTab.text
font: currentTab.font
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
wrapMode: Text.WordWrap
color: "#FFFFFF"
MouseArea {
anchors.fill: parent
onClicked: {
if(currentTab.checked){
currentTab.checked = false
} else {
currentTab.checked = true
}
}
onDoubleClicked: {
currentTab.selected = true
var found = false;
var someText = frame.itemAt(stack1.currentIndex).text;
print(someText)
for(var i = 0; i<wizard.selectedSkills.count; i++){
if(wizard.selectedSkills.get(i).name === someText){
wizard.selectedSkills.get(i).sub_categories.append({"name":currentTab.text});
wizard.skills.push({"name": someText})
found = true;
}
}
if(!found){
print(currentTab.text)
wizard.selectedSkills.append({"name":someText, "sub_categories":[{"name":currentTab.text}]})
}
print(window.selectedSkills)
}
}
}
}
}
}
}
}
}
}
I've tried many different things to add a scrollbar or to figure out how to use the flickable functionality that TabBar has. However, the documentation doesn't specify how it works, it just does. Therefore, they are not accessible (or even rewritteable, to use those properties). I want to add a small indicator like an arrow to specify that there is more elements for ease of navigation on desktop on top of the TabBar functionality.
It doesn't seem like the necessary properties are exposed in order to make this happen the easy way.
However, since this is QML, it means the whole object tree is gaping wide open to introspection, allowing us to establish that the item that does the flicking is the contentItem of a ListView inside the Container the ToolBar inherits. The view happens to be the second visible child, although this is technically "private implementation" that one should not rely on. So it is better to take some extra care to establish whether or not you have the correct object.
ApplicationWindow {
id: main
width: 640
height: 480
visible: true
TabBar {
id: toolbar
width: parent.width
height: 50
Repeater {
model: 10
TabButton {
text: "test " + index
width: 100
}
}
}
Rectangle {
height: 5
width: main.width * (view ? view.visibleArea.widthRatio : toolbar.width / toolbar.contentWidth)
color: "red"
anchors.top: toolbar.bottom
x: view ? (main.width - width) * (view.contentX / (view.contentWidth - view.width)) : 0
}
property ListView view: {
var l = toolbar.visibleChildren.length
while (--l) if ("cacheBuffer" in toolbar.visibleChildren[l]) return toolbar.visibleChildren[l]
return null
}
}
And there you have it. We iterate the tabview children until we find one that has a property cacheBuffer which is fairly unique to ListView, and once we have that, we can access the needed properties. As you see, for the indicator width we can do even without the list view, as the toolbar exposes a contentWidth property, but for the indicator position there is no workaround.
And it works:
I have a simple component which is designed to show an image with some text under it as follows:
Rectangle{
id: functionView
RowLayout {
spacing: 350
anchors.centerIn: parent
Item {
Image {
id: upload_pic
source: "http://icons.iconarchive.com/icons/custom-icon-design/mono-general-4/128/upload-icon.png"
Text {
text: "Upload Images"
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottomMargin: -20
}
}
Layout.fillWidth: true
}
Item {
Image {
id: workflow_pic
source: "http://icons.iconarchive.com/icons/icons8/windows-8/128/Data-Workflow-icon.png"
Text {
text: "Upload Workflow"
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottomMargin: -20
}
}
Layout.fillWidth: true
}
}
}
However, this is not aligned (either vertically or horizontally) on the component window. This can be verified using qmlscene. The images are directly linked in the component source.
You are trying to center your RowLayout in its parent, which is a Rectangle that doesn't have its width property set.
Set the rectangle's width to some value or to its parent width for example by width: parent.width or by setting its anchors like for example:
anchors.left: parent.left
anchors.right: parent.right
I have a Flickable that is holding two Repeaters with a column layout. I am hoping to scroll to the last item in the first repeater. Is this possible?
I suppose one way could be to count how many items are in the first repeater and then multiply that by the height of the delegate I am using. (The delegate is a fixed height.) Or take the height of the repeater and subtract the height of the last delegate. etc... Though I am hoping on a better way than this.
import QtQuick 2.7
import QtQuick.Controls 2.0
Item {
id:passwordsView
Flickable {
id: flickable1
anchors.fill: parent
contentHeight: passwordsView_column.height
ScrollBar.vertical: ScrollBar { }
Column {
id:passwordsView_column
spacing: 15
anchors.left: parent.left
anchors.right: parent.right
Repeater {
id: passwordsView_breadcrumb
anchors.left: parent.left
anchors.right: parent.right
model: BreadcrumbModel {}
delegate: PasswordFolderDelegate {
y: 8;
anchors.left: parent.left;
anchors.right: parent.right;
}
}
Repeater {
id: passwordsView_contents
model: PasswordModel {}
PasswordFolderDelegate {
y: 8
anchors.left: parent.left
anchors.right: parent.right
}
anchors.left: parent.left
anchors.right: parent.right
}
}
}
}
Or take the height of the repeater and subtract the height of the last delegate.
Repeater doesn't have a height, as it merely positions the items, so that might be a little difficult.
The easiest approach I can think of is to use mapToItem():
import QtQuick 2.7
import QtQuick.Controls 2.0
ApplicationWindow {
width: 400
height: 400
visible: true
Flickable {
id: flickable1
anchors.fill: parent
contentHeight: passwordsView_column.height
ScrollBar.vertical: ScrollBar { }
Column {
id:passwordsView_column
spacing: 15
anchors.left: parent.left
anchors.right: parent.right
Repeater {
id: passwordsView_breadcrumb
anchors.left: parent.left
anchors.right: parent.right
model: 10
delegate: Rectangle {
width: 50
height: 50
color: "transparent"
border.color: "salmon"
Text {
text: index
anchors.centerIn: parent
}
}
}
Repeater {
id: passwordsView_contents
model: 10
delegate: Rectangle {
width: 50
height: 50
color: "transparent"
border.color: "#444"
Text {
text: index
anchors.centerIn: parent
}
}
}
}
}
Button {
text: "Position at end"
anchors.top: parent.top
anchors.right: parent.right
onClicked: {
var lastItem = passwordsView_breadcrumb.itemAt(passwordsView_breadcrumb.count - 1);
flickable1.contentY = lastItem.mapToItem(flickable1.contentItem, 0, 0).y
}
}
}
Note that this makes the view move instantly. If you want smooth scrolling, you'll probably have to calculate the required velocity somehow and pass it to flick().
I'm using Qt 5.2.1 for windows (Qt creator 3.0.1)
I have a custom QML component, it works fine when I'm loading in into rectangle:
import QtQuick 2.0
import QtQuick.Controls 1.1
Rectangle {
id: mainRectangle
anchors.fill: parent
Loader {
anchors.top: parent.top;
anchors.left: parent.left;
anchors.right: parent.right;
id: ld01;
onLoaded: {
ld01.visible = true;
anchors.top = parent.top;
}
}
Loader {
anchors.top: ld01.bottom;
anchors.left: parent.left;
anchors.right: parent.right;
id: ld02;
onLoaded: {
anchors.top = ld01.bottom;
ld02.visible = true;
}
}
Component.onCompleted: {
ld01.setSource("View_item2.qml");
ld02.setSource("View_item2.qml");
}
}
But when I'm trying to put it all inside a ScrollView, elements of my component are moved somewhere. What kind of trick I should implement for correct use of ScrollView?
ScrollView {
id: mainTabLayout
anchors.fill: parent
anchors.margins: 4
//here I put a code from above (except imports, of course)
}
Component code is below:
import QtQuick 2.1
import QtQuick.Controls 1.1
import QtQuick.Layouts 1.0
Rectangle {
id: slv_layout
objectName: "itemColumnLayout"
anchors.left: parent.left
anchors.right: parent.right
anchors.margins: 1
property int minimal_height: 200
height: 400
color: "green"
MouseArea {
property bool is_pressed: false
property int initial_y: 0
property int proposed_y: 0
id: resizeStick
enabled: true
anchors.bottom: parent.bottom
height: 10
width: parent.width
hoverEnabled: true
onEntered: {
cursorShape = Qt.SizeVerCursor;
}
onPressed: {
is_pressed = true;
initial_y = mouseY;
}
onReleased: {
is_pressed = false;
}
onMouseYChanged: {
if (is_pressed) {
proposed_y = slv_layout.height + mouseY - initial_y;
if (proposed_y >= slv_layout.minimal_height) {
slv_layout.height += (mouseY - initial_y);
initial_y = mouseY;
}
}
}
}
Text {
id: slvTitle
text: "device name"
anchors.top: parent.top
anchors.left: parent.left
anchors.margins: 2
}
Rectangle {
anchors.top: slvTitle.bottom
anchors.left: parent.left
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.topMargin: 2
color: "blue"
Button {
id: slv_butt_run;
objectName: "slv_butt_run"
width: 60
height: width
text: "Run"
anchors.top: parent.top
anchors.left: parent.left
anchors.margins: 2
}
Button {
id: slv_butt_settings;
objectName: "slv_butt_settings"
width: 60
height: width
text: "Settings"
anchors.top: parent.top
anchors.left: slv_butt_run.right
anchors.margins: 2
}
Button {
id: slv_butt_stop;
objectName: "slv_butt_stop"
width: 60
height: width
text: "Stop"
anchors.top: slv_butt_run.bottom
anchors.left: parent.left
anchors.margins: 2
}
Button {
id: slv_butt_expand;
objectName: "slv_butt_expand"
width: 60
height: width
text: "Expand"
anchors.top: slv_butt_settings.bottom
anchors.left: slv_butt_stop.right
anchors.margins: 2
}
TextArea {
id: slv_log_area
anchors.left: slv_butt_expand.right
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.margins: 3
}
}
}
How it looks when all is ok:
How it looks when not ok:
Actually, I still don't know, why code works as described above. But I have found acceptable method to solve task other way.
Looks like "put a needle into egg, egg into duck, duck into rabbit":
ScrollView must contain a ListView component which has a corresponding ListModel and a custom component should act as delegate. Only with ListModel I've got correct automatic scrolling and relative emplacement support.
ScrollView {
id: id_scrollView
anchors.fill: parent
objectName: "ScrollView"
frameVisible: true
highlightOnFocus: true
style: ScrollViewStyle {
transientScrollBars: true
handle: Item {
implicitWidth: 14
implicitHeight: 26
Rectangle {
color: "#424246"
anchors.fill: parent
anchors.topMargin: 6
anchors.leftMargin: 4
anchors.rightMargin: 4
anchors.bottomMargin: 6
}
}
scrollBarBackground: Item {
implicitWidth: 14
implicitHeight: 26
}
}
ListView {
id: id_listView
objectName: "ListView"
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.rightMargin: 11
flickableDirection: Flickable.VerticalFlick
boundsBehavior: Flickable.StopAtBounds
delegate: view_component
model: id_listModel
ListModel {
id :id_listModel
objectName: "ListModel"
}
//delegate: View_item2.Item
Component {
id: view_component
View_item2 {
objectName: name
}
}
}
According to the ScrollView documentation,
A ScrollView can be used either to replace a Flickable or decorate an existing Flickable. ... The width and height of the child item will be used to define the size of the content area.
A ScrollView needs to know two width-height pairs: the first one is the width and height used to display the region, and the second one is the width and height of the content. If the area of the content is larger than the display area, the display area will add a scroll bar on it.
In your example:
ScrollView {
id: mainTabLayout
anchors.fill: parent
//other properties
Rectangle {
id: mainRectangle
anchors.fill: parent
//...
}
}
The width and height of the content is bound to the display area, making the two areas be in the same size. The width and height of display area is the one in mainTabLayout, which is bound to it's parent; and the width and height of the content is the one in mainRectangle, which is bound to it's parent, mainTabLayout. Therefore the ScrollView cannot work correctly since ScrollView expects the two values are different, not bound together.
To solve your problem, you can explicitly assign width and height to mainRectangle. Do not bind the width and height of mainRectangle to it's parent using anchors.fill:parent.
ScrollView {
id: mainTabLayout
anchors.fill: parent
//other properties
Rectangle {
id: mainRectangle
width: 800; height: 800 //not binding to parent.width & height
//...
}
}
And this can work correctly.