Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
scalabilityintro.qdoc
Go to the documentation of this file.
1
// Copyright (C) 2016 The Qt Company Ltd.
2
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
3
4
/*!
5
\title Scalability
6
\page scalability.html
7
\brief How to develop applications that scale well on devices with different
8
screen configurations and UI conventions.
9
\ingroup explanations-accessibility
10
11
When you develop applications for several different mobile device platforms,
12
you face the following challenges:
13
14
\list
15
\li Mobile device platforms support devices with varying screen
16
configurations: size, aspect ratio, orientation, and density.
17
\li Different platforms have different UI conventions and you need to
18
meet the users' expectations on each platform.
19
\endlist
20
21
Qt Quick enables you to develop applications that can run on different types
22
of devices, such as tablets and handsets. In particular, they can cope
23
with different screen configurations. However, there is always a certain
24
amount of fixing and polishing needed to create an optimal user experience
25
for each target platform.
26
27
You need to consider scalability when:
28
29
\list
30
\li You want to deploy your application to more than one device
31
platform, such as Android and iOS, or more than one
32
device screen configuration.
33
\li Your want to be prepared for new devices that might appear on the
34
market after your initial deployment.
35
\endlist
36
37
To implement scalable applications using \l{Qt Quick}:
38
39
\list
40
\li Design UIs using \e {Qt Quick Controls} that provide sets of UI controls.
41
\li Define layouts using \e {Qt Quick Layouts}, which can resize their
42
items.
43
\li Use \e {property binding} to implement use cases
44
not covered by the layouts. For example, to display alternative
45
versions of images on screens with low and high pixel density or
46
automatically adapt view contents according to the current screen
47
orientation.
48
\li Select a reference device and calculate a \e {scaling ratio} for
49
adjusting image and font sizes and margins to the actual screen
50
size.
51
\li Load platform-specific assets using \e {file selectors}.
52
\li Load components on demand by using a \e {Loader}.
53
\endlist
54
55
Consider the following patterns when designing your application:
56
57
\list
58
\li The contents of a view might be quite similar on all
59
screen sizes, but with an expanded content area. If you use the
60
ApplicationWindow QML type from Qt Quick Controls, it will
61
automatically calculate the window size based on the sizes of its
62
content items. If you use Qt Quick Layouts to position the content
63
items, they will automatically resize the items pushed to them.
64
\li The contents of an entire page in a smaller
65
device could form a component element of a layout in a
66
larger device. Therefore, consider making that a separate
67
component (that is, defined in a separate QML file), and in the
68
smaller device, the view will simply contain an instance of
69
that component. On the larger device, there may be enough
70
space to use loaders to show additional items. For example, in an
71
email viewer, if the screen is large enough, it may be possible to
72
show the email list view, and the email reader view side by
73
side.
74
\li For games, you would typically want to create a game board that does not
75
scale, so as not to provide an unfair advantage to players on larger
76
screens. One solution is to define a \e {safe zone} that fits the screen
77
with the smallest supported aspect ratio (usually, 3:2), and add
78
decorative-only content in the space that will be hidden on a 4:3 or
79
16:9 screen.
80
\endlist
81
82
\section1 Resizing Application Windows Dynamically
83
84
\l{Qt Quick Controls} provide a set of UI controls to create user interfaces
85
in Qt Quick. Typically, you declare an ApplicationWindow control as the root
86
item of your application. The ApplicationWindow adds convenience for
87
positioning other controls, such as MenuBar, ToolBar, and StatusBar in a
88
platform independent manner. The ApplicationWindow uses the size constraints
89
of the content items as input when calculating the effective size
90
constraints of the actual window.
91
92
In addition to controls that define standard parts of application windows,
93
controls are provided for creating views and menus, as well as presenting or
94
receiving input from users. You can use \l {Using Styles in Qt Quick Controls}{Qt Quick Controls Styles} to
95
apply custom styling to the predefined controls.
96
97
Qt Quick Controls, such as the ToolBar, do not provide a layout
98
of their own, but require you to position their contents. For this, you can
99
use Qt Quick Layouts.
100
101
\section1 Laying Out Screen Controls Dynamically
102
103
\l{Qt Quick Layouts} provide ways of laying out screen controls in a row,
104
column, or grid, using the RowLayout, ColumnLayout, and GridLayout QML
105
types. The properties for these QML types hold their layout direction and
106
spacing between the cells.
107
108
You can use the \l{Qt Quick Layouts} QML types to attach additional properties to the
109
items pushed to the layouts. For example, you can specify minimum, maximum,
110
and preferred values for item height, width, and size.
111
112
The layouts ensure that your UIs are scaled properly when windows and
113
screens are resized and always use the maximum amount of space available.
114
115
A specific use case for the GridLayout type is to use it as a row or a
116
column depending on the screen orientation.
117
118
\image scalability-gridlayout.png
119
{Portrait and landscape layouts showing Top or left and Bottom or right}
120
121
The following code snippet uses
122
the \c flow property to set the flow of the grid from left to right (as a
123
row) when the screen width is greater than the screen height and from top to
124
bottom (as a column) otherwise:
125
126
\code
127
ApplicationWindow {
128
id: root
129
visible: true
130
width: 480
131
height: 620
132
133
GridLayout {
134
anchors.fill: parent
135
anchors.margins: 20
136
rowSpacing: 20
137
columnSpacing: 20
138
flow: width > height ? GridLayout.LeftToRight : GridLayout.TopToBottom
139
Rectangle {
140
Layout.fillWidth: true
141
Layout.fillHeight: true
142
color: "#5d5b59"
143
Label {
144
anchors.centerIn: parent
145
text: "Top or left"
146
color: "white"
147
}
148
}
149
Rectangle {
150
Layout.fillWidth: true
151
Layout.fillHeight: true
152
color: "#1e1b18"
153
Label {
154
anchors.centerIn: parent
155
text: "Bottom or right"
156
color: "white"
157
}
158
}
159
}
160
}
161
\endcode
162
163
Constantly resizing and recalculating screens comes with a performance cost.
164
Mobile and embedded devices might not have the power required to recalculate
165
the size and position of animated objects for every frame, for example. If
166
you run into performance problems when using layouts, consider using some
167
other methods, such as bindings, instead.
168
169
Here are some things not to do with layouts:
170
171
\list
172
173
\li Do not have bindings to the x, y, width, or height properties of items
174
in a Layout, since this would conflict with the goal of the Layout, and
175
also cause binding loops.
176
\li Do not define complex JavaScript functions that are regularly
177
evaluated. This will cause poor performance, particularly
178
during animated transitions.
179
\li Do not make assumptions about the container size, or about
180
the size of child items. Try to make flexible layout
181
definitions that can absorb changes in the available space.
182
\li Do not use layouts if you want the design to be pixel perfect. Content
183
items will be automatically resized and positioned depending on the
184
space available.
185
\endlist
186
187
\section1 Using Bindings
188
189
If Qt Quick Layouts do not fit your needs, you can fall back to using
190
\l{Property Binding}{property binding}. Binding enables objects to
191
automatically update their properties in response to changing attributes in
192
other objects or the occurrence of some external event.
193
194
When an object's property is assigned a value, it can either be assigned a
195
static value, or bound to a JavaScript expression. In the former case, the
196
property's value will not change unless a new value is assigned to the
197
property. In the latter case, a property binding is created and the
198
property's value is automatically updated by the QML engine whenever the
199
value of the evaluated expression changes.
200
201
This type of positioning is the most highly dynamic. However, constantly
202
evaluating JavaScript expressions comes with a performance cost.
203
204
You can use bindings to handle low and high pixel density on platforms that
205
do not have automatic support for it (like Android, \macos and iOS do).
206
The following code snippet uses the \l{Screen}{Screen.pixelDensity}
207
attached property to specify different images to display on screens with
208
low, high, or normal pixel density:
209
210
\code
211
Image {
212
source: {
213
if (Screen.pixelDensity < 40)
214
"image_low_dpi.png"
215
else if (Screen.pixelDensity > 300)
216
"image_high_dpi.png"
217
else
218
"image.png"
219
}
220
}
221
\endcode
222
223
On Android, \macos and iOS, you can provide alternative resources with higher
224
resolutions by using the corresponding identifier (e.g. \e @2x, \e @3x,
225
or \e @4x) for icons and images, and place them in the resource file. The
226
version that matches the pixel density of the screen is automatically selected
227
for use.
228
229
For example, the following code snippet will try to load \e artwork@2x.png
230
on Retina displays:
231
232
\code
233
Image {
234
source: "artwork.png"
235
}
236
\endcode
237
238
\section1 Handling Pixel Density
239
240
Some QML types, such as \l Image, BorderImage, and \l Text, are
241
automatically scaled according to the properties specified for them.
242
If the width and height of an Image are not specified, it automatically uses
243
the size of the source image, specified using the \c source property. By
244
default, specifying the width and height causes the image to be scaled to
245
that size. This behavior can be changed by setting the \c fillMode property,
246
allowing the image to be stretched and tiled instead. However, the original
247
image size might appear too small on high DPI displays.
248
249
A BorderImage is used to create borders out of images by scaling or tiling
250
parts of each image. It breaks a source image into 9 regions that are scaled
251
or tiled according to property values. However, the corners are not scaled
252
at all, which can make the results less than optimal on high DPI displays.
253
254
A \l Text QML type attempts to determine how much room is needed and set the
255
\c width and \c height properties accordingly, unless they are explicitly
256
set. The \c fontPointSize property sets the point size in a
257
device-independent manner. However, specifying fonts in points and other
258
sizes in pixels causes problems, because points are independent of the
259
display density. A frame around a string that looks correct on low DPI
260
displays is likely to become too small on high DPI displays, causing the
261
text to be clipped.
262
263
The level of high DPI support and the techniques used by the supported
264
platforms varies from platform to platform. The following sections describe
265
different approaches to scaling screen contents on high DPI displays.
266
267
For more information about high DPI support in Qt and the supported
268
platforms, see \l{High DPI}.
269
270
\section2 High DPI Scaling
271
272
If a target device supports high DPI scaling, the operating system provides
273
Qt with a scaling ratio that is used to scale graphics output.
274
275
The advantage of this approach is that vector graphics and fonts scale
276
automatically and existing applications tend to work unmodified. For raster
277
content, high-resolution alternative resources are needed, however.
278
279
Scaling is implemented for the Qt Quick and Qt Widgets stacks, as well as
280
general support in Qt Gui.
281
282
Low level graphics APIs operate in device pixels. This includes code which
283
uses the OpenGL API, and code which uses the QRhi API. For example, this
284
means that a QWindow with a size() of 1280x720 and a
285
QWindow::devicePixelRatio() of 2 has a render target (swapchain) with a
286
device pixel size of 2560x1440.
287
288
The OS scales window, event, and desktop geometry. The Cocoa platform plugin
289
sets the scaling ratio as QWindow::devicePixelRatio() or
290
QScreen::devicePixelRatio(), as well as on the backing store.
291
292
For Qt Widgets, QPainter picks up \c devicePixelRatio() from the backing
293
store and interprets it as a scaling ratio.
294
295
However, in OpenGL, pixels are always device pixels. For example, geometry
296
passed to glViewport() needs to be scaled by devicePixelRatio().
297
298
The specified font sizes (in points or pixels) do not change and strings
299
retain their relative size compared to the rest of the UI. Fonts are
300
scaled as a part of painting, so that a size 12 font effectively becomes a
301
size 24 font with 2x scaling, regardless of whether it is specified in
302
points or in pixels. The \e px unit is interpreted as device independent
303
pixels to ensure that fonts do not appear smaller on a high DPI display.
304
305
\section2 Calculating Scaling Ratio
306
307
You can select one high DPI device as a reference device and calculate
308
a scaling ratio for adjusting image and font sizes and margins to the actual
309
screen size.
310
311
The following code snippet uses reference values for DPI, height, and
312
width from the Nexus 5 Android device, the actual screen size returned by
313
the QRect class, and the logical DPI value of the screen returned by the
314
\c qApp global pointer to calculate a scaling ratio for image sizes and
315
margins (\c m_ratio) and another for font sizes (\c m_ratioFont):
316
317
\code
318
qreal refDpi = 216.;
319
qreal refHeight = 1776.;
320
qreal refWidth = 1080.;
321
QRect rect = QGuiApplication::primaryScreen()->geometry();
322
qreal height = qMax(rect.width(), rect.height());
323
qreal width = qMin(rect.width(), rect.height());
324
qreal dpi = QGuiApplication::primaryScreen()->logicalDotsPerInch();
325
m_ratio = qMin(height/refHeight, width/refWidth);
326
m_ratioFont = qMin(height*refDpi/(dpi*refHeight), width*refDpi/(dpi*refWidth));
327
\endcode
328
329
For a reasonable scaling ratio, the height and width values must be set
330
according to the default orientation of the reference device, which in this
331
case is the portrait orientation.
332
333
The following code snippet sets the font scaling ratio to \c 1 if it would
334
be less than one and thus cause the font sizes to become too small:
335
336
\code
337
int tempTimeColumnWidth = 600;
338
int tempTrackHeaderWidth = 270;
339
if (m_ratioFont < 1.) {
340
m_ratioFont = 1;
341
\endcode
342
343
You should experiment with the target devices to find edge cases that
344
require additional calculations. Some screens might just be too short or
345
narrow to fit all the planned content and thus require their own layout. For
346
example, you might need to hide or replace some content on screens with
347
atypical aspect ratios, such as 1:1.
348
349
The scaling ratio can be applied to all sizes in a QQmlPropertyMap to
350
scale images, fonts, and margins:
351
352
\code
353
m_sizes = new QQmlPropertyMap(this);
354
m_sizes->insert(QLatin1String("trackHeaderHeight"), QVariant(applyRatio(270)));
355
m_sizes->insert(QLatin1String("trackHeaderWidth"), QVariant(applyRatio(tempTrackHeaderWidth)));
356
m_sizes->insert(QLatin1String("timeColumnWidth"), QVariant(applyRatio(tempTimeColumnWidth)));
357
m_sizes->insert(QLatin1String("conferenceHeaderHeight"), QVariant(applyRatio(158)));
358
m_sizes->insert(QLatin1String("dayWidth"), QVariant(applyRatio(150)));
359
m_sizes->insert(QLatin1String("favoriteImageHeight"), QVariant(applyRatio(76)));
360
m_sizes->insert(QLatin1String("favoriteImageWidth"), QVariant(applyRatio(80)));
361
m_sizes->insert(QLatin1String("titleHeight"), QVariant(applyRatio(60)));
362
m_sizes->insert(QLatin1String("backHeight"), QVariant(applyRatio(74)));
363
m_sizes->insert(QLatin1String("backWidth"), QVariant(applyRatio(42)));
364
m_sizes->insert(QLatin1String("logoHeight"), QVariant(applyRatio(100)));
365
m_sizes->insert(QLatin1String("logoWidth"), QVariant(applyRatio(286)));
366
367
m_fonts = new QQmlPropertyMap(this);
368
m_fonts->insert(QLatin1String("six_pt"), QVariant(applyFontRatio(9)));
369
m_fonts->insert(QLatin1String("seven_pt"), QVariant(applyFontRatio(10)));
370
m_fonts->insert(QLatin1String("eight_pt"), QVariant(applyFontRatio(12)));
371
m_fonts->insert(QLatin1String("ten_pt"), QVariant(applyFontRatio(14)));
372
m_fonts->insert(QLatin1String("twelve_pt"), QVariant(applyFontRatio(16)));
373
374
m_margins = new QQmlPropertyMap(this);
375
m_margins->insert(QLatin1String("five"), QVariant(applyRatio(5)));
376
m_margins->insert(QLatin1String("seven"), QVariant(applyRatio(7)));
377
m_margins->insert(QLatin1String("ten"), QVariant(applyRatio(10)));
378
m_margins->insert(QLatin1String("fifteen"), QVariant(applyRatio(15)));
379
m_margins->insert(QLatin1String("twenty"), QVariant(applyRatio(20)));
380
m_margins->insert(QLatin1String("thirty"), QVariant(applyRatio(30)));
381
\endcode
382
383
The functions in the following code snippet apply the scaling ratio to
384
fonts, images, and margins:
385
386
\code
387
int Theme::applyFontRatio(const int value)
388
{
389
return int(value * m_ratioFont);
390
}
391
392
int Theme::applyRatio(const int value)
393
{
394
return qMax(2, int(value * m_ratio));
395
}
396
\endcode
397
398
This technique gives you reasonable results when the screen sizes of the
399
target devices do not differ too much. If the differences are huge, consider
400
creating several different layouts with different reference values.
401
402
\section1 Loading Files Depending on Platform
403
404
You can use the QQmlFileSelector to apply a QFileSelector to QML file
405
loading. This enables you to load alternative resources depending on the
406
platform on which the application is run. For example, you can use the
407
\c +android file selector to load different image files
408
when run on Android devices.
409
410
You can use file selectors together with singleton objects to access a
411
single instance of an object on a particular platform.
412
413
File selectors are static and enforce a file structure where
414
platform-specific files are stored in subfolders named after the platform.
415
If you need a more dynamic solution for loading parts of your UI on demand,
416
you can use a Loader.
417
418
The target platforms might automate the loading of alternative resources for
419
different display densities in various ways. On Android and iOS, the \e @2x
420
filename suffix is used to indicate high DPI versions of images. The \l Image
421
QML type and the QIcon class automatically load @2x versions of images and
422
icons if they are provided. The QImage and QPixmap classes automatically set
423
the \c devicePixelRatio of @2x versions of images to \c 2, but you need to
424
add code to actually use the @2x versions:
425
426
\code
427
if ( QGuiApplication::primaryScreen()->devicePixelRatio() >= 2 ) {
428
imageVariant = "@2x";
429
} else {
430
imageVariant = "";
431
}
432
\endcode
433
434
Android defines generalized screen sizes (small, normal, large, xlarge) and
435
densities (ldpi, mdpi, hdpi, xhdpi, xxhdpi, and xxxhdpi) for
436
which you can create alternative resources. Android detects the current
437
device configuration at runtime and loads the appropriate resources for your
438
application. However, beginning with Android 3.2 (API level 13), these size
439
groups are deprecated in favor of a new technique for managing screen sizes
440
based on the available screen width.
441
442
\section1 Loading Components on Demand
443
444
A \l{Loader} can load a QML file (using the \c source property) or a Component
445
object (using the \c sourceComponent property). It is useful for delaying the
446
creation of a component until it is required. For example, when a component
447
should be created on demand, or when a component should not be created
448
unnecessarily for performance reasons.
449
450
You can also use loaders to react to situations where parts of your UI are
451
not needed on a particular platform, because the platform does not support
452
some functionality. Instead of displaying a view that is not needed
453
on the device the application is running on, you can determine that the
454
view is hidden and use loaders to display something else in its place.
455
456
\section1 Switching Orientation
457
458
The \l{Screen}{Screen.orientation} attached property contains the current
459
orientation of the screen, from the accelerometer (if available). On a
460
desktop computer, this value typically does not change.
461
462
If \c primaryOrientation follows \c orientation, it means that the screen
463
automatically rotates all content that is displayed, depending on how you
464
hold the device. If orientation changes even though \c primaryOrientation
465
does not change, the device might not rotate its own display. In that case,
466
you may need to use \l{QtQuick::Item::rotation}{Item.rotation} or
467
\l{QtQuick::Item::transform}{Item.transform} to rotate your content.
468
469
Application top-level page definitions and reusable component
470
definitions should use one QML layout definition for the layout
471
structure. This single definition should include the layout design
472
for separate device orientations and aspect ratios. The reason for
473
this is that performance during an orientation switch is critical,
474
and it is therefore a good idea to ensure that all of the
475
components needed by both orientations are loaded when the
476
orientation changes.
477
478
On the contrary, you should perform thorough tests if you choose
479
to use a \l{Loader} to load additional QML that is needed in separate
480
orientations, as this will affect the performance of the
481
orientation change.
482
483
In order to enable layout animations between the orientations, the
484
anchor definitions must reside within the same containing
485
component. Therefore the structure of a page or a component
486
should consist of a common set of child components, a common set
487
of anchor definitions, and a collection of states (defined in a
488
StateGroup) representing the different aspect ratios supported by
489
the component.
490
491
If a component contained within a page needs to be
492
hosted in numerous different form factor definitions, then the
493
layout states of the view should depend on the aspect ratio of the
494
page (its immediate container). Similarly, different instances of
495
a component might be situated within numerous different containers
496
in a UI, and so its layout states should be determined by the
497
aspect ratio of its parent. The conclusion is that layout states
498
should always follow the aspect ratio of the direct container (not
499
the "orientation" of the current device screen).
500
501
Within each layout \l{State}, you should define the relationships
502
between items using native QML layout definitions. See below for
503
more information. During transitions between the states (triggered
504
by the top level orientation change), in the case of anchor
505
layouts, AnchorAnimation elements can be used to control the
506
transitions. In some cases, you can also use a NumberAnimation on
507
e.g. the width of an item. Remember to avoid complex JavaScript
508
calculations during each frame of animation. Using simple anchor
509
definitions and anchor animations can help with this in the
510
majority of cases.
511
512
There are a few additional cases to consider:
513
514
\list
515
\li What if you have a single page that looks completely
516
different between landscape and portrait, that is, all of the
517
child items are different? For each page, have two child
518
components, with separate layout definitions, and make one
519
or other of the items have zero opacity in each state. You
520
can use a cross-fade animation by simply applying a
521
NumberAnimation transition to the opacity.
522
\li What if you have a single page that shares 30% or more of
523
the same layout contents between portrait and landscape? In
524
that case, consider having one component with landscape and
525
portrait states, and a collection of separate child items
526
whose opacity (or position) depends on the orientation
527
state. This will enable you to use layout animations for the
528
items that are shared between the orientations, whilst the
529
other items are either faded in/out, or animated on/off
530
screen.
531
\li What if you have two pages on a handheld device that need to
532
be on screen at the same time, for example on a larger form
533
factor device? In this case, notice that your view component
534
will no longer be occupying the full screen. Therefore it's
535
important to remember in all components (in particular, list
536
delegate items) should depend on the size of the containing
537
component width, not on the screen width. It may be
538
necessary to set the width in a Component.onCompleted()
539
handler in this case, to ensure that the list item delegate
540
has been constructed before the value is set.
541
\li What if the two orientations take up too much memory to have
542
them both in memory at once? Use a \l{Loader} if necessary, if
543
you cannot keep both versions of the view in memory at once,
544
but beware performance on the cross-fade animation during
545
layout switch. One solution could be to have two "splash
546
screen" items that are children of the Page, then you cross
547
fade between those during rotation. Then you can use a
548
\l{Loader} to load another child component that loads the actual
549
model data to another child Item, and cross-fade to that
550
when the \l{Loader} has completed.
551
\endlist
552
553
\sa {Qt Quick Responsive Layouts}
554
555
*/
qtdeclarative
src
quick
doc
src
guidelines
scalabilityintro.qdoc
Generated on
for Qt by
1.16.1