- End to End GUI Development with Qt5
- Nicholas Sherriff Guillaume Lazar Robin Penea Marco Piccolino
- 1207字
- 2021-06-10 19:26:58
Anchors
One slight problem with our wonderful new SplashView is that it doesn’t actually fill the window. Sure, we can change the 400 x 200 dimensions to 1024 x 768 so that it matches MasterView, but then what happens if the user resizes the window? Modern UI is all about responsive design—dynamic content that can adapt to the display it’s being presented on, so hard-coding properties appropriate for only one platform aren’t ideal. Fortunately, anchors come to our rescue.
Let’s put our trusty old scratchpad project to use and take a look at anchors in action.
Right-click on qml.qrc and add a new AnchorsDemo.qml QML file alongside the existing main.qml file in the scratchpad folder. Don’t worry about subfolders or .qrc prefixes, aliases, or any of that jazz.
Dip into main.cpp and load our new file instead of main.qml:
engine.load(QUrl(QStringLiteral("qrc:/AnchorsDemo.qml")));
Next, paste the following code into AnchorsDemo:
import QtQuick 2.9 import QtQuick.Window 2.2
Window { visible: true width: 1024 height: 768 title: qsTr("Scratchpad") color: "#ffffff" Rectangle { id: paleYellowBackground anchors.fill: parent color: "#cece9e" } Rectangle { id: blackRectangleInTheCentre width: 120 height: 120 anchors.centerIn: parent color: "#000000" } Rectangle { id: greenRectangleInTheCentre width: 100 height: 100 anchors.centerIn: parent anchors.verticalCenterOffset: 20 color: "#008000" } Rectangle { id: redRectangleTopLeftCorner width: 100 height: 100 anchors { top: parent.top left: parent.left } color: "#800000" } Rectangle { id: blueRectangleTopLeftCorner width: 100 height: 100 anchors{ top: redRectangleTopLeftCorner.bottom left: parent.left } color: "#000080" } Rectangle { id: purpleRectangleTopLeftCorner width: 100 height: 100 anchors{ top: blueRectangleTopLeftCorner.bottom left: parent.left leftMargin: 20 } color: "#800080" } Rectangle { id: turquoiseRectangleBottomRightCorner width: 100 height: 100 anchors{ bottom: parent.bottom right: parent.right margins: 20 } color: "#008080" } }
Build and run the application, and you’ll be presented with this rather bewildering sight:
This may all look a bit confusing at first and I apologize if your color perception is suboptimal, but all we’ve done is draw a sequence of gaudily colored rectangles with differing anchors values. Let’s walk through each rectangle one by one and see what is going on:
Rectangle { id: paleYellowBackground anchors.fill: parent color: "#cece9e" }
Our first rectangle is the dull yellow brown background; anchors.fill: parent tells the rectangle to fill its parent, however big that may be. The parent of any given QML component is the QML component that contains it—the next level up in the hierarchy. In this case, it is the Window element. The Window element is 1024 x 768 pixels, so that’s how big the rectangle is. Note that we don’t need to specify width and height properties for the rectangle because they are inferred from the anchors.
This is exactly the behavior we want for our SplashView, but let’s look at some other capabilities of anchors before we return to our main project:
Rectangle { id: blackRectangleInTheCentre width: 120 height: 120 anchors.centerIn: parent color: "#000000" } Rectangle { id: greenRectangleInTheCentre width: 100 height: 100 anchors.centerIn: parent anchors.verticalCenterOffset: 20 color: "#008000" }
We’ll look at the next two rectangles together. First, we have a black rectangle that is 120 pixels square; anchors.centerIn: parent positions it at the center of its parent. We must specify the width and height because we are only positioning it, not sizing it.
Next, we have a slightly smaller green rectangle, also centered in its parent. We then use the anchors.verticalCenterOffset property to move it 20 pixels further down the screen. The x, y coordinate system used for positioning has its root (0, 0) at the top-left of the screen; verticalCenterOffset adds to the y coordinate. Positive numbers move the item down the screen, and negative numbers move the item up the screen. Its sister property—horizontalCenterOffset—is used for adjustments in the x axis.
One last thing to note here is that the rectangles overlap, and it is the green rectangle that wins out and is displayed in full. The black rectangle is pushed back and obscured. Similarly, all of our small rectangles sit in front of the large background rectangle. QML is rendered in a top-down fashion, so when the root element (Window) gets painted, its children are processed one by one from the top of the file to the bottom. So, items at the bottom of the file will be rendered in front of those rendered at the top of the file. The same is true if you paint a wall white and then paint it black, the wall will appear black because that’s what was painted (rendered) last:
Rectangle { id: redRectangleTopLeftCorner width: 100 height: 100 anchors { top: parent.top left: parent.left } color: "#800000" }
Next, we draw a red rectangle and rather than positioning or sizing the whole rectangle at once, we just anchor certain sides. We take the anchor on its top side and align it to the anchor on the top side of its parent (Window). We anchor its left side to its parent’s left side. Hence, it becomes “attached” to the top-left corner.
We have to type the following:
anchors.top: parent.top anchors.left: parent.left
Another helpful piece of syntactic sugar at work here is rather than doing that, we can remove the duplication and set the subproperties of the anchors group within curly braces:
anchors { top: parent.top left: parent.left }
Next, the blue rectangle:
Rectangle { id: blueRectangleTopLeftCorner width: 100 height: 100 anchors{ top: redRectangleTopLeftCorner.bottom left: parent.left } color: "#000080" }
This follows the same pattern, though this time rather than attaching only to its parent, we also anchor to a sibling (the red rectangle), which we can reference though the id property:
Rectangle { id: purpleRectangleTopLeftCorner width: 100 height: 100 anchors{ top: blueRectangleTopLeftCorner.bottom left: parent.left leftMargin: 20 } color: "#800080" }
The purple rectangle anchors to the bottom of the blue rectangle and to the left-hand side of the Window, but here we introduce our first margin. Each side has its own margin and in this case, we use leftMargin to give us an offset from the left anchor in exactly the same way as we saw with verticalCenterOffset earlier:
Rectangle { id: turquoiseRectangleBottomRightCorner width: 100 height: 100 anchors{ bottom: parent.bottom right: parent.right margins: 20 } color: "#008080" }
Finally, our turquoise rectangle uses some of that empty space over on the right-hand side of the screen and demonstrates how we can set the margin on all four sides simultaneously using the margins property.
Note that all of these bindings are dynamic. Try resizing the window, and all the rectangles will adapt automatically. Anchors are a great tool for responsive UI design.
Let’s head back to our SplashView in our cm-ui project and apply what we’ve just learned. Replace the fixed width and height attributes with the more dynamic anchors.fill property:
Rectangle { anchors.fill: parent color: "#f4c842" }
Now, the SplashView will fill whatever its parent element is. Build and run, and you’ll see that rather than our lovely colorful rectangle filling the screen as we expected, it has disappeared altogether. Let’s take a look at why that is.