This is a text-only version of the following page on https://raymii.org:
---
Title       :   Responsive QML Layout (with scrollbars)
Author      :   Remy van Elst
Date        :   05-10-2021
URL         :   https://raymii.org/s/articles/Responsive_Qml_layout_with_scrollbars.html
Format      :   Markdown/HTML
---



![responsive screenshot][1]

> Screen recording of a responsive `GridLayout` in a `ScrollView`


In this article I'll show you how to make a responsive layout in Qt / QML that automatically adjusts the amount of columns and rows based on the window dimensions, including scrollbars for when the content does not fit inside the window. This also works if you have a portrait and landscape orientation of your application, since the screen or window dimensions will be different across those two builds. I also explain how the dynamic resizing works with an explanation of property bindings in QML and as a bonus this works on mobile (Android/iOS) as well.

<p class="ad"> <b>Recently I removed all Google Ads from this site due to their invasive tracking, as well as Google Analytics. Please, if you found this content useful, consider a small donation using any of the options below:</b><br><br> <a href="https://leafnode.nl">I'm developing an open source monitoring app called  Leaf Node Monitoring, for windows, linux & android. Go check it out!</a><br><br> <a href="https://github.com/sponsors/RaymiiOrg/">Consider sponsoring me on Github. It means the world to me if you show your appreciation and you'll help pay the server costs.</a><br><br> <a href="https://www.digitalocean.com/?refcode=7435ae6b8212">You can also sponsor me by getting a Digital Ocean VPS. With this referral link you'll get $100 credit for 60 days. </a><br><br> </p>


QML is a markup language (part of the QT framework) like HTML/CSS, with inline
JavaScript that can interact with the C++ code of your(QT) application. QML
has the [concept of][3] `Layouts` to arrange items in a user interface. You
can have a `RowLayout` for, unsurprisingly, a row of items, or a
`ColumnLayout` for a column of items. `GridLayout` is the most flexible, that
allows for a grid of items. There is also the `StackLayout`, where only one
item is visible at a time. You must specify te amount of rows and columns, but that
does not change when a user resizes the window. This means that the layout
is not responsive.

A responsive layout means that when the window dimension (or device rotation aspect)
changes, the contents of said window automatically reposition themselves in a way
that fits best. Like how modern websites look great on your desktop and phone, using
a different layout for each device. In Qt / Qml this is possible, but not by default.

Here are two pictures that show off a `RowLayout`  and a `ColumnLayout` to
help you visualize the concept:

![rowlayout][7]

> RowLayout with 2 rectangles

![columnLayout][6]

> ColumnLayout with 3 rectangles

We'll be re-using my [Traffic Light QML][8], that I used in my earlier article
describing the different ways of exposing C++ classes to Qml. The Traffic
Light control is in the `GridLayout`, within a `Repeater`, 16 instances.
(The example works just as well with 500 instances). Each traffic light has a
border around it to help visualize the flow and positioning and there is a
row and column counter at the top. As a fun bonus I added a `Timer {}` with a
random interval between 2 and 15 seconds per traffic light to cycle the
different lamps. Here is how it looks, but you've already seen that in a
recording at the top of this page.

![screenshot][9]

All the source code for this example project [can be found on my github here][16].

I'm using Qt 5.15 so you can match that up if you tag along with the guide.

I've also compiled this demo to WebAssembly [here][17].

### Responsive GridLayout

Automatically resizing the `GridLayout` based on the window size is done by
specifying a bit of JavaScript code in the `columns:` and `rows:` properties
of your `GridLayout`:

   readonly property int elementWidth: 150

       columns: Math.max(Math.floor(parent.width / elementWidth), 1)
       rows: Math.max(Math.ceil(children.length / columns), 1)

Here is how it looks inside an entire `GridLayout` contol:

       GridLayout{
           id: exampleLayout
           readonly property int elementWidth: 150

           columns: Math.max(Math.floor(parent.width / elementWidth), 1)
           rows: Math.max(Math.ceil(children.length / columns), 1)

           anchors.fill: parent
           rowSpacing: 5
           columnSpacing: rowSpacing

           Repeater{
               id: model
               model: 16
               Rectangle {
                   width: exampleLayout.elementWidth
                   height: 250
                   border.color: "pink"
                   Layout.alignment : Qt.AlignLeft | Qt.AlignTop
               }
           }
       }

I've defined a property `elementWidth` to make sure the formula is correct. It calculates
how many columns there should be based on the width of the parent (which is the width of the
`GridLayout` due to `anchors.fill: parent`) and the width of each element.

The amount of rows is calculated based on the amount of columns and how many children there
are. I'm using the implicit property `children.length` for that, so even if you dynamically
place new items in the layout, it will still resize properly.

The `Math.max` safeguard is required so we have at least one row and one column at all times.
I had crashes when I omitted it:

   terminate called after throwing an instance of 'std::bad_alloc'
     what():  std::bad_alloc

Due to property bindings and implicit change signals the values in `rows` and `columns` are
automatically updated on each window resize. In the next paragraph I'll go in to more detail
how that all works.

You don't explicitly need to set the amount of `rows:` but because I want to show that
number in a `Text{}` I did set explicitly. Otherwise it would be `-1`.

#### Implicit change signals for every QML property

How does this work? How does the `GridLayout` knows when the window is
resized? QML has built-in property change signals (**for each property**) that are emitted whenever a
property value changes. Since width and height are properties of a control,
when they change, a signal is emitted, `widthChanged`, which you can hook up
to an `onWidthChanged:` signal handler. The ins and outs are [documented here][10]
and you can see it in action for yourself by adding a signal handler to your root `Window` control
and to your `GridLayout` or `ScrollView` control:

   onWidthChanged: { console.log("Window Width changed: " + width) }
   onHeightChanged: { console.log("Window Height changed: " + height)}

Here's how that looks in the example application when the window is resized:

![screenshot logging][11]

The `GridLayout` or `ScrollView` width and height are coupled to their parents (thus the `Window`)
in our example. When those parent properties change, their own properties change as well, including
each other property that uses such a value. The mechanics of  property binding are [documented here][12],
I'm quoting the relevant part below:

   When a property's dependencies change in value, the property is
   automatically updated according to the specified relationship.

   Behind the scenes, the QML engine monitors the property's dependencies
   (that is, the variables in the binding expression). When a change is
   detected, the QML engine re-evaluates the binding expression and applies
   the new result to the property.


Property binding and re-evaluation is extremely useful but if you have a property that
is used all over the place, stuff can get messy quickly.

### Scrolling, scrollbars and a ScrollView

In the introduction I also promised to show you how to add scrollbars. If we
have too much content to fit in the window, even when the `GridLayout`
automatically resizes, scrollbars are required for the user to navigate. A
Qml `Window` does not automatically have scrollbars, you have to add them by
specifying an explicit `ScrollView` and adding your items inside of that.

You can have a scrollbar for your entire `Window` but you can also add a
`ScrollView` for certain elements only. Like a text field or an image viewer,
if something doesn't fit inside the dimensions of the element, the user can
scroll to still see everything.

This is an example of a `ScrollView`, in my example code that houses the `GridLayout`:

   ScrollView {
       id: scroller
       anchors.top: parent.top
       anchors.left: parent.left
       anchors.leftMargin: 5
       anchors.topMargin: 5
       width: parent.width
       height: parent.height * 0.8
       clip : true

       GridLayout{
           ...
       }
   }


Here is a screenshot of the example application with a GridLayout without the
`rows:` or `columns:` property set. It results in 1 row, unlimited columns:

![rowlayout][4]

In the screenshot you see a horizontal scrollbar at the bottom. If that would
not be there, only the controls on screen would be visible and usable by the
user. If they have a small screen, they might not be able to use all items
inside the layout.

If an amount of `columns:` is specified, there will be no more than that
amount of columns, but unlimited rows. Here's how a property of `columns: 2`
looks:

![columnlayout][5]

With a `ScrollView` you don't have to specify if you want a horizontal and/or
vertical scrollbar, based on the contents and dimensions of the `ScrollView`
the QML engine decides which one (or both) is required.

Depending on the window manager theme and preferences the user has
set, the scrollbars will be hidden by default until they mouse over them.
Doesn't help usability wise, but there are two properties you can set in the
`ScrollView` to control the visibility:


       ScrollBar.horizontal.policy: ScrollBar.AlwaysOn
       ScrollBar.vertical.policy: ScrollBar.AlwaysOn

More information on those two properties and how they work when used
with touch gestures instead of a mouse can be found [here][12].


### Does this work on Mobile?

I've compiled this project for Android and tested it, when rotating the phone the
amount of rows and columns changes and the scrolling works as you would expect.

Nothing special had to be done except for installing the correct Android SDK and tools,
which all can be done from Qt Creator. Plugged in one of my older Android phones and
like magic, the application popped up. Below are screenshots and a screen recording.

![landscape][14]

> This is a screenshot in Landscape mode

![portrait][13]

> This is a screenshot in Portrait mode

Notice how the amount of columns and rows changes per aspect?

Here is [a video screen recording][15] showing how the application
runs on the phone.


### WebAssembly demo

For fun I compiled the example application to webassembly. Run it [here][17]
or, if it loads, an `iframe` below:

<iframe style="border:1px solid black;" width="640" height="480" src="https://raymii.org/s/inc/software/qt-wasm-traffic/index.html">


[1]: /s/inc/img/qt-responsive-0.gif
[2]: https://web.archive.org/web/20210907091818/https://doc.qt.io/qt-5/qtquick-positioning-anchors.html
[3]: https://web.archive.org/web/20211001114720/https://doc.qt.io/qt-5/qml-qtquick-layouts-layout.html
[4]: /s/inc/img/qt-responsive-1.png
[5]: /s/inc/img/qt-responsive-2.png
[6]: /s/inc/img/qt-responsive-3.png
[7]: /s/inc/img/qt-responsive-4.png
[8]: /s/articles/Qt_QML_Integrate_Cpp_with_QML_and_why_ContextProperties_are_bad.html
[9]: /s/inc/img/qt-responsive-5.png
[10]: https://web.archive.org/web/20210507013538/https://doc.qt.io/qt-5/qtqml-syntax-objectattributes.html#property-change-signal-handlers
[11]: /s/inc/img/qt-responsive-6.png
[12]: https://web.archive.org/web/20211001124453/https://doc.qt.io/qt-5/qml-qtquick-controls2-scrollview.html
[13]: /s/inc/img/qt-responsive-7.jpg
[14]: /s/inc/img/qt-responsive-8.jpg
[15]: /s/inc/img/qt-responsive-9.mp4
[16]: https://github.com/RaymiiOrg/qml-responsive-layout-example
[17]: http://raymii.org/s/inc/software/qt-wasm-traffic/

---

License:
All the text on this website is free as in freedom unless stated otherwise.
This means you can use it in any way you want, you can copy it, change it
the way you like and republish it, as long as you release the (modified)
content under the same license to give others the same freedoms you've got
and place my name and a link to this site with the article as source.

This site uses Google Analytics for statistics and Google Adwords for
advertisements. You are tracked and Google knows everything about you.
Use an adblocker like ublock-origin if you don't want it.

All the code on this website is licensed under the GNU GPL v3 license
unless already licensed under a license which does not allows this form
of licensing or if another license is stated on that page / in that software:

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.

Just to be clear, the information on this website is for meant for educational
purposes and you use it at your own risk. I do not take responsibility if you
screw something up. Use common sense, do not 'rm -rf /' as root for example.
If you have any questions then do not hesitate to contact me.

See https://raymii.org/s/static/About.html for details.