Skip to content

Commit

Permalink
Merge pull request qmlbook#90 from qmlbook/ch06-common-patterns
Browse files Browse the repository at this point in the history
common-patterns.md: fix language issues
  • Loading branch information
e8johan authored Sep 21, 2021
2 parents 02ad9d4 + c251f49 commit 8b4e5d7
Showing 1 changed file with 26 additions and 26 deletions.
52 changes: 26 additions & 26 deletions docs/ch06-controls/common-patterns.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ For this example we will create a tree of pages that can be reached from the pre

![](./assets/nested-screens.png)

The key component in this type of user interface is the `StackView`. It allows us to place pages on a stack which then can be poped when the user wants to go back. In the example here, we will show how this can be implemented.
The key component in this type of user interface is the `StackView`. It allows us to place pages on a stack which then can be popped when the user wants to go back. In the example here, we will show how this can be implemented.

The initial home screen of the application is shown in the figure below.

![](./assets/interface-stack-home.png)

The application start in `main.qml` where we have an `ApplicationWindow` containing a `ToolBar`, a `Drawer`, a `StackView` and a home page element, `Home`. We will look into each of the components below.
The application starts in `main.qml`, where we have an `ApplicationWindow` containing a `ToolBar`, a `Drawer`, a `StackView` and a home page element, `Home`. We will look into each of the components below.

```qml
import QtQuick
Expand Down Expand Up @@ -44,7 +44,7 @@ ApplicationWindow {
}
```

The home page, `Home.qml` consists of a `Page`, which is an control element that support headers and footers. In this example we simply center a `Label` with the text *Home Screen* on the page. This works because the contents of a `StackView` automatically fills the stack view, so the page has the right size for this to work.
The home page, `Home.qml` consists of a `Page`, which is n control element that support headers and footers. In this example we simply center a `Label` with the text *Home Screen* on the page. This works because the contents of a `StackView` automatically fill the stack view, so the page will have the right size for this to work.

```qml
import QtQuick
Expand All @@ -60,9 +60,9 @@ Page {
}
```

Returning back to the `main.qml`, we now look at the drawer part. This is where the navigation to the pages begin. The active parts of the user interface is the `ÌtemDelegate` items. In the `onClicked` handler, the next page is pushed onto the `stackView`.
Returning back to `main.qml`, we now look at the drawer part. This is where the navigation to the pages begin. The active parts of the user interface are the `ÌtemDelegate` items. In the `onClicked` handler, the next page is pushed onto the `stackView`.

As shown in the code below, it possible to push either a `Component` or a reference to a specific QML file. Either way results in that a new instance is created and is pushed onto the stack.
As shown in the code below, it possible to push either a `Component` or a reference to a specific QML file. Either way results in a new instance being created and pushed onto the stack.

```qml
ApplicationWindow {
Expand Down Expand Up @@ -109,11 +109,11 @@ ApplicationWindow {
}
```

The other half of the puzzle is the toolbar. The idea is that a back button is shown when the `stackView` contains more than one page, otherwise a menu button is shown. The logic for this can be seen on the `text` property where the `"\\u..."` strings represents the unicode symbols that we need.
The other half of the puzzle is the toolbar. The idea is that a back button is shown when the `stackView` contains more than one page, otherwise a menu button is shown. The logic for this can be seen in the `text` property where the `"\\u..."` strings represents the unicode symbols that we need.

In the `onClicked` handler, we can see that when there is more than one page on the stack, the stack is poped, i.e. the top page is removed. If the stack contains only one item, i.e. the home screen, the drawer is opened.
In the `onClicked` handler, we can see that when there is more than one page on the stack, the stack is popped, i.e. the top page is removed. If the stack contains only one item, i.e. the home screen, the drawer is opened.

Below the `ToolBar`, you find a `Label`. This element shows the title of each page in the center of the header.
Below the `ToolBar`, there is a `Label`. This element shows the title of each page in the center of the header.

```qml
ApplicationWindow {
Expand Down Expand Up @@ -180,7 +180,7 @@ For this example we create a user interface consisting of three pages that the u

![](./assets/side-by-side-screen.png)

The illustration below shows how the *Current* page looks in the application. The main part of the screen is managed by a `SwipeView`, which is what enables the side by side screens interaction pattern. The title and text shown in the figure come from the page inside the `SwipeView`, while the `PageIndicator` (the three dots at the bottom) come from `main.qml` and sit under the `SwipeView`. The page indicator shows the user which page that is currently active, helping when navigating.
The illustration below shows how the *Current* page looks in the application. The main part of the screen is managed by a `SwipeView`, which is what enables the side by side screen interaction pattern. The title and text shown in the figure come from the page inside the `SwipeView`, while the `PageIndicator` (the three dots at the bottom) comes from `main.qml` and sits under the `SwipeView`. The page indicator shows the user which page is currently active, which helps when navigating.

![](./assets/interface-side-by-side-current.png)

Expand Down Expand Up @@ -235,7 +235,7 @@ ApplicationWindow {
}
```

Finally the `count` and `currentIndex` properties of the `SwipeView` are bound to the `PageIndicator` element. This completes the structure around the pages.
Finally, the `count` and `currentIndex` properties of the `SwipeView` are bound to the `PageIndicator` element. This completes the structure around the pages.

```qml
ApplicationWindow {
Expand All @@ -259,7 +259,7 @@ ApplicationWindow {
}
```

Each page consists of a `Page` with a `header` consisting of a `Label` and some contents. For the *Current* and *User Stats* pages the contents is a simple `Label`, but for the *Community Stats* page, a back button is included.
Each page consists of a `Page` with a `header` consisting of a `Label` and some contents. For the *Current* and *User Stats* pages the contents consist of a simple `Label`, but for the *Community Stats* page, a back button is included.

```qml
import QtQuick
Expand All @@ -282,7 +282,7 @@ Page {
The back button explicitly calls the `setCurrentIndex` of the `SwipeView` to set the index to zero, returning the user directly to the *Current* page. During each transition between pages the `SwipeView` provides a transition, so even when explicitly changing the index the user is given a sense of direction.

::: tip
When navigating in a `SwipeView` programatically it is important not to set the `currentIndex` by assignment in Javascript. This is because doing so will break any QML bindings it overrides. Instead use the methods `setCurrentIndex`, `incrementCurrentIndex`, and `decrementCurrentIndex`. This preserves the QML bindings.
When navigating in a `SwipeView` programatically it is important not to set the `currentIndex` by assignment in JavaScript. This is because doing so will break any QML bindings it overrides. Instead use the methods `setCurrentIndex`, `incrementCurrentIndex`, and `decrementCurrentIndex`. This preserves the QML bindings.
:::

```qml
Expand All @@ -308,14 +308,14 @@ Page {

## Document Windows

This example shows how to implement a desktop oriented, document centric user interface. The idea is to have one window per document. When opening a new document, a new window is opened. To the user, each window is a self contained world with a single document.
This example shows how to implement a desktop-oriented, document-centric user interface. The idea is to have one window per document. When opening a new document, a new window is opened. To the user, each window is a self contained world with a single document.

![Two document windows and the close warning dialog.](assets/interface-document-window.png)


The code starts from an `ApplicationWindow` with a *File* menu with the standard operations: *New*, *Open*, *Save* and *Save As*. We put this in the file `DocumentWindow.qml`.
The code starts from an `ApplicationWindow` with a *File* menu with the standard operations: *New*, *Open*, *Save* and *Save As*. We put this in the `DocumentWindow.qml`.

We import the `Qt.labs.platform` for native dialogs and have made the subsequent changes to the project file and `main.cpp` as described in the section on native dialogs above.
We import `Qt.labs.platform` for native dialogs, and have made the subsequent changes to the project file and `main.cpp` as described in the section on native dialogs above.

```qml
import QtQuick
Expand Down Expand Up @@ -359,7 +359,7 @@ ApplicationWindow {
}
```

To bootstrap the program, we create the first `DocumentWindow` instance from the `main.qml`, which is the entry point of the application.
To bootstrap the program, we create the first `DocumentWindow` instance from `main.qml`, which is the entry point of the application.

```qml
import QtQuick
Expand All @@ -369,7 +369,7 @@ DocumentWindow {
}
```

In the example at the beginning of this chapter, each `MenuItem` results in a call to a corresponding function when triggered. Let’s start with the *New* item, which ends up in the `newDocument` function.
In the example at the beginning of this chapter, each `MenuItem` calls a corresponding function when triggered. Let’s start with the *New* item, which calls the `newDocument` function.

The function, in turn, relies on the `_createNewDocument` function, which dynamically creates a new element instance from the `DocumentWindow.qml` file, i.e. a new `DocumentWindow` instance. The reason for breaking out this part of the new function is that we use it when opening documents as well.

Expand Down Expand Up @@ -398,9 +398,9 @@ ApplicationWindow {
}
```

Looking at the *Open* item results in a call to `openDocument` function. The function simply opens the `openDialog` which let’s the user pick a file to open. As we don’t have a document format, file extension or anything like that, the dialog has most properties set to their default value. In a real world application, this would be better configured.
Looking at the *Open* item, we see that it calls the `openDocument` function. The function simply opens the `openDialog`, which lets the user pick a file to open. As we don’t have a document format, file extension or anything like that, the dialog has most properties set to their default value. In a real world application, this would be better configured.

In the `onAccepted` handler a new document window is instantiated using the `_createNewDocument` method, but then a file name is set before the window is shown. In this case, no real loading takes place.
In the `onAccepted` handler a new document window is instantiated using the `_createNewDocument` method, and a file name is set before the window is shown. In this case, no real loading takes place.

::: tip
We imported the `Qt.labs.platform` module as `NativeDialogs`. This is because it provides a `MenuItem` that clashes with the `MenuItem` provided by the `QtQuick.Controls` module.
Expand Down Expand Up @@ -432,7 +432,7 @@ ApplicationWindow {
}
```

The file name belogs to a pair of properties describing the document: `_fileName` and `_isDirty`. The `_fileName` holds the file name of the document name and `_isDirty` is set when the document has unsaved changes. This is used by the save and save as logic, which is shown below.
The file name belongs to a pair of properties describing the document: `_fileName` and `_isDirty`. The `_fileName` holds the file name of the document name and `_isDirty` is set when the document has unsaved changes. This is used by the save and save as logic, which is shown below.

When trying to save a document without a name, the `saveAsDocument` is invoked. This results in a round-trip over the `saveAsDialog`, which sets a file name and then tries to save again in the `onAccepted` handler.

Expand Down Expand Up @@ -493,11 +493,11 @@ ApplicationWindow {

This leads us to the closing of windows. When a window is being closed, the `onClosing` handler is invoked. Here, the code can choose not to accept the request to close. If the document has unsaved changes, we open the `closeWarningDialog` and reject the request to close.

The `closeWarningDialog` asks the user if the changes should be changed, but the user also has the option to cancel the close operation. The cancelling, handeled in `onRejected`, is the easiest case, as we rejected the closing when the dialog was opened.
The `closeWarningDialog` asks the user if the changes should be saved, but the user also has the option to cancel the close operation. The cancelling, handled in `onRejected`, is the easiest case, as we rejected the closing when the dialog was opened.

When the user does not want to save the changes, i.e. in `onNoClicked`, the `_isDirty` flag is set to `false` and the window is closed again. This time around, the `onClosing` will accept the closing as the `_isDirty` is false.
When the user does not want to save the changes, i.e. in `onNoClicked`, the `_isDirty` flag is set to `false` and the window is closed again. This time around, the `onClosing` will accept the closure, as `_isDirty` is false.

Finally, when the user wants to save the changes, we set the `_tryingToClose` flag to true before calling save. This leads us to the save - save as logic.
Finally, when the user wants to save the changes, we set the `_tryingToClose` flag to true before calling save. This leads us to the save/save as logic.

```qml
ApplicationWindow {
Expand Down Expand Up @@ -533,13 +533,13 @@ ApplicationWindow {
}
```

The entire flow for the close and save - save as logic is shown below. The system is entered at the *close* state, while *closed* and *not closed* states are outcomes.
The entire flow for the close and save/save as logic is shown below. The system is entered at the *close* state, while the *closed* and *not closed* states are outcomes.

This looks complicated compared to implementing this using `QtWidgets` and C++. This is because the dialogs are not blocking to QML. This means that we cannot wait for the outcome of a dialog in a `switch` statement. Instead we need to remember the state and continue the operation in the respective `onYesClicked`, `onNoClicked`, `onAccepted`, and `onRejected` handlers.
This looks complicated compared to implementing this using `Qt Widgets` and C++. This is because the dialogs are not blocking to QML. This means that we cannot wait for the outcome of a dialog in a `switch` statement. Instead we need to remember the state and continue the operation in the respective `onYesClicked`, `onNoClicked`, `onAccepted`, and `onRejected` handlers.

![](./assets/dialog-state-machine.png)

The final piece of the puzzle is the window title. It is composed form the `_fileName` and `_isDirty`.
The final piece of the puzzle is the window title. It is composed from the `_fileName` and `_isDirty` properties.

```qml
ApplicationWindow {
Expand Down

0 comments on commit 8b4e5d7

Please sign in to comment.