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