The source code for the TextEditor application was presented in the previous post. In this post I will describe the different use cases and how they have been implemented in the QML/C++ code using signals, slots and QML elements and event handlers. I will use simple flow charts and signalling flow diagrams to illustrate the implementation.
Creating a new file
The TextEditor user can create a new file by opening the file menu and selecting “New”. If there are unsaved changes we need to ask whether we can discard the changes. These steps are shown in the flowchart below.
The figure below illustrates the signalling flow for the File>New action. The user has the QML editPage open and then selects New from the the File menu. This emits the signal fileNewRequested which has been connected to the slot with the same name in the C++ side. We will pass the editPage.content property (= the current content as a string) to the fileNewRequested slot and compare that to the current content. If they match we will call newConfirmed slot which will clear the current content and emit the signal editorCleared. This will invoke the QML event handler onEditorCleared that will clear the editor buffer and initialize the file name and path (and return control to the user). If there were unsaved changes we will emit newToBeConfirmed signal that will be catched by the onNewToBeConfirmed event handler on the QML side. We will open a dialog (newConfirmDialog) to ask whether we can discard the changes. If the user answers NO we will return control to the user, otherwise we will emit the signal newConfirmed that is connected to the slot newConfirmed on the C++ side. From there we will follow the same steps as described earlier.
Here is the color coding used in the figure above.
Opening an existing file
The TextEditor user can open an existing file by opening the file menu and selecting “Open”. If there are unsaved changes we need to ask whether we can discard the changes. The file browser can then be used to select a file. These steps are shown in the flowchart below.
The signalling flow is shown below. As before we will check if there are unsaved changes. If there are no unsaved changes or if the user decides to discard the changes we will emit the signal browseRequested which will trigger the onBrowseRequested event handler on the QML side. This will open the browsePage (will be pushed into the page stack). As input the browsePage needs to know the path to browse and whether we are browsing to open or save a file. This information is passed from the C++ side with the parameters currentFolder and saveRequested (set to 0 in this case).
When the browsePage is open the user can interact with the folder view presented by the FolderListModel. The FileDelegate component will display each file-system entry on the screen. Each entry has a MouseArea element that will trigger a signal when clicked. The current folder and file names are provided to the delegate via context properties (model roles) filePath and fileName.
Clicking a folder name will emit the signal folderChanged that will then be catched by the onFolderChanged event handler. This will update the folder name to the page header and to the folderModel which will update the view by opening the selected folder. Then the signal currentFolderChanged signal is emitted and processed by the corresponding C++ slot which will just store the new folder name.
Clicking a file name will emit the signal fileOpenRequested which will then be processed by the corresponding slot on the C++ side. The selected file name will be saved to the currentFile variable. If the file can be opened successfully the file content will be stored to the variable currentContent. The signal openCompleted will pass control back to the QML side and to the event handler onOpenCompleted which will update editPage properties: content, currentFolder and currentFile. Finally the browsePage will be popped from the page stack to get back to the editPage.
Saving to a new file
The user can save the editor contents to a new file by selecting “Save As” from the file menu. The file browser will be opened to select the destination folder and file. The user can also enter the file name directly using normal text entry method with the on-screen keyboard. If the file already exists we will ask whether we can overwrite the file. If the file is write-protected we will give an appropriate error message. These steps are shown in the flow chart below.
The signalling flow for the first steps is shown below. When FIle>Save As is clicked the signal menuSaveAsClicked is sent to the C++ slot. The slot method sends the current folder information back to the QML side. The browsePage element is pushed into the page stack to make it visible. The saveRequested parameter is used to select whether we want to open or save a file.
When the browsePage is open we can use it to select a file. The fileDelegate displays the file and folder names. When a file name is clicked the file name is placed into the saveasfile TextField element (the user can also enter the file name directly into the TextField). When a folder name is clicked the folderModel will be updated to change the current folder. The new folder name is also send to the C++ side with the signal currentFolderChanged.
When the user clicks the saveAsButton (or presses enter in the on-screen keyboard) the saveAsRequested signal passes the editor content and the file name to the C++ side (to the slot saveAsRequested). If the file already exists we need to get confirmation from the user to overwrite the file. This is done with the saveConfirmDialog. If we get the confirmation (or the file didn’t exist in the first place) we call the method saveCurrentContent which will try to save the file. If save fails (e.g. when trying to overwrite a read-only file) we will show an error message with the saveFailedDialog. Otherwise we will send the saveAsCompleted signal to the QML side passing the current folder and file name to the event handler onSaveAsCompleted. Here we will update the editPage and pop the browsePage from the page stack. To give some feedback to the user we will also set the editPage.showSave property to true which will trigger a simple state transition to briefly show the text: “Saving…” in the middle of the screen.
Saving the file
When the user clicks the “Quick Save” button in the editor toolbar the editor content will be saved to the current file. If there is no file name (file name is “Untitled”) the file browser will be opened to let the user select the file name (here the steps are the same as described above). If the file name is already defined we will try to save the file. If save failed we will show an appropriate error message. If save succeeded we will just return to the editor.
The signalling flow is shown below. Clicking the “Quick Save” button will send the signal menuSaveClicked to the C++ TextEditor object. If the editor is still untitled we will open the file browser by calling the menuSaveAsClicked method. Otherwise we will call the saveCurrentContent method to save the editor content and go back to the editPage.