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 */