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

Clicking

Early on in this book, we looked at a component called MouseArea. This was soon superseded by our use of Button components, which provide the clicking functionality for us. However, now that we are rolling our own form of buttons, we need to implement the clicking functionality ourselves. Much like the Button components, our NavigationButton shouldn’t really do anything when they are clicked on, other than informing their parent that the event has occurred. Components should be as generic and ignorant about context as possible so that you can use them in multiple places. What we need to do is add a MouseArea component and simply pass on the onClicked event via a custom signal.

In NavigationButton, we first add the signal that we want to emit whenever the component has been clicked on. Add this just after the properties:

signal navigationButtonClicked()
Try and give the signals quite specific names, even if they are a little long. If you simply call everything clicked(), then things can get a little confusing and sometimes you may find yourself referencing a different signal to the one you intended.

Next, we’ll add another property to support some mouse hover magic we’ll implement. This will be a color type, and we’ll default it to be the regular background color:

property color hoverColour: Style.colourNavigationBarBackground

We’ll use this color in conjunction with the states property of Rectangle:

states: [
    State {
        name: "hover"
        PropertyChanges {
            target: background
            color: hoverColour
        }
    }
]

Think of each state in the array as a named configuration. The default configuration has no name ("") and consists of the properties we have already set within the Rectangle element. The “hover” state applies changes to the properties specified in the PropertyChanges element, that is, it will change the color property of the element with ID background to be whatever the value of hoverColour is.

Next, inside the Rectangle but below the Row, add our MouseArea:

MouseArea {
    anchors.fill: parent
    cursorShape: Qt.PointingHandCursor
    hoverEnabled: true
    onEntered: background.state = "hover"
    onExited: background.state = ""
    onClicked: navigationButtonClicked()
}

We use the anchors property to fill the whole button background area, including icon and description. Next, we’ll jazz things up a bit by changing the mouse cursor to a pointing hand when it enters the button area and enabling hovering with the hoverEnabled flag. When enabled, the entered and exited signals are emitted when the cursor enters and exits the area, and we can use the corresponding slots to change the appearance of our background Rectangle by switching between the hover state we’ve just implemented and the default (""). Finally, we respond to the clicked() signal of MouseArea with the onClicked() slot and simply emit our own signal.

We can now react to the navigationButtonClicked() signal in our NavigationBar component and add some hover colors while we’re at it. Implement the toggle button first:

NavigationButton {
    iconCharacter: "uf0c9"
    description: ""
    hoverColour: "#993333"
    onNavigationButtonClicked: isCollapsed = !isCollapsed
}

We implement the <MyCapitalisedSignalName> convention to create a slot for our signal and when it fires, we simply toggle the value of isCollapsed between true and false.

You can now run the application. Click on the Toggle button to expand and collapse the navigation bar:

Note how because of our use of anchorsthe child views dynamically resize themselves to accommodate the navigation bar. You will also see the pointing hand cursor and a flash of color when you hover over the button, which helps the user understand that it is an interactive element and visualizes the boundaries.

For the remaining navigation buttons, what we want to do in reaction to the clicked event is to emit the goDashboardView(), goCreateClientView(), and goFindClientView() signals on the NavigationCoordinator.

Add the onNavigationButtonClicked slots to the other buttons and drill down through the masterController object to get to the signals we want to call. Add some fancy colors of your choice too:

NavigationButton {
 iconCharacter: "uf015"
 description: "Dashboard"
 hoverColour: "#dc8a00"
 onNavigationButtonClicked: masterController.ui_navigationController.goDashboardView();
}
NavigationButton {
 iconCharacter: "uf234"
 description: "New Client"
 hoverColour: "#dccd00"
 onNavigationButtonClicked: masterController.ui_navigationController.goCreateClientView();
}
NavigationButton {
 iconCharacter: "uf002"
 description: "Find Client"
 hoverColour: "#8aef63"
 onNavigationButtonClicked: masterController.ui_navigationController.goFindClientView();
}

You can now click on the buttons to navigate to the different child views.

A few last little tweaks to finish the navigation bar are to align the content of our buttons a little better and resize a few things.

The description text should align vertically with the center of the icon rather than the top, and our icons should be centered rather than pinned up against the edge of the window. The first issue is easy to solve, because we’ve already been consistent and explicit with our sizings. Simply add the following property to both the Text components in NavigationButton:

verticalAlignment: Text.AlignVCenter

Both the Text elements were sized to take up the full height of the button, so we simply need to align the text vertically within that space.

Fixing the alignment of the icons is just the same, but this time in the horizontal axis. Add the following to the Text component of the icon:

horizontalAlignment: Text.AlignHCenter

As for the sizings, our description text is a little small and there is a lot of empty space after the text. Add a new property to our Style object:

readonly property int pixelSizeNavigationBarText: 22

Use the new property in the description Text element:

font.pixelSize: Style.pixelSizeNavigationBarText

Next, reduce the widthNavigationButtonDescription property in Style to 160.

Run the app and we’re nearly there. The sizing and alignment is much better now:

However, one thing you may not note is that when the bar is collapsed and only the icon is displayed, the MouseArea is still the full width of the button including the description. Try moving the mouse where the description would be, and you can see the pointing hand cursor appear. You can even click on the components and the transition happens. What we need to do to fix this is rather than the root Item element in NavigationButton being a fixed width (Style.widthNavigationButton), we need to make it dynamic and set it to parent.width instead. In order for that to work, we then need to walk up the QML hierarchy and ensure that its parent has a width too. Its parent is the Column element in NavigationBar. Set the width property of Column to be parent.width too.

With those changes in place, the navigation bar now behaves as expected.