End to End GUI Development with Qt5
上QQ阅读APP看书,第一时间看更新

Fixing conflicts

The navigation bar problem is a simple one. As explained previously, QML is hierarchical in structure. This bears out in the way the elements are rendered—child elements that appear first are rendered first. In our case, we draw the navigation bar and then we draw the content frame. When the StackView component loads new content, by default it applies funky transitions to make it look nice. Those transitions can result in content moving out of bounds of the control and drawing over any content below it. There are a couple of ways to address this.

Firstly, we can rearrange the order that the components are rendered in and put the navigation bar after the content frame. This will draw the navigation bar over the top of the StackView, regardless of what was going on with it. The second option and the one we will implement is to simply set the clip property of the StackView:

clip: true

This clips any content that overlaps the boundary of the control and doesn’t render it.

The next problem is a little more esoteric. As we’ve discussed, the number one cause of confused head scratching I’ve encountered over the past few years of QML development is the sizing of components. Some components we’ve used, such as Rectangle, are intrinsically visual elements. If their size is not defined, either directly with the width/height properties or indirectly with anchors, then they will not render. Other elements such as Connections are not visual at all and size properties are redundant. Layout elements such as Column may have a fixed size in one axis, but be dynamic in the other by nature.

One thing that most components have in common is that they inherit from Item, which in turn inherits directly from QtObjectwhich is just a plain QObject. In much the same way that the Qt Framework on the C++ side implements a lot of default behavior for plain old QObject*, QML components often implement default behavior for Item components that we can leverage here.

In our child views, we have used Rectangle as our root object. This makes sense as we want to display a rectangle of a fixed size and color. However, this causes problems for the StackView as it doesn’t know what size it should be. To provide this information, we try and anchor it to its parent (the StackView), but then that causes problems of its own by conflicting with the transitions the StackView is trying to perform when we switch views.

Our way out of this dilemma is to instead have the root of our child views be a plain old Item. StackView components have internal logic to handle Item components and will just size it for us. Our Rectangle component then becomes the child of an Item component that has already been sized automatically, and we can anchor to that instead:

Item {
    Rectangle {
        ...
} }

This is all a bit confusing and feels like Voodoo, but the takeaway here is that having Item as the root element in your custom QML is often a good thing. Go ahead and add a root Item component in this way to all the child views (but not MasterView).

Run the application again, and you should now have nice smooth transitions and no warning messages in the console.