Commit 561fb809 authored by Claudio Cambra's avatar Claudio Cambra Committed by Carl Schwan
Browse files

Fixes to Getting Started with KDE Frameworks section

This branch contains a number of fixes for the 'Getting Started'
section of the doc. It focuses on clarifying certain concepts,
correcting grammar/spelling issues, ensuring all relevant code
snippets are rendered properly, linking to additional resources
for explanations of certain components, and more.
parent cb7aaa34
......@@ -7,7 +7,7 @@ weight: 6
## Introduction
Now that we have a text editor that can open and save files. We will now make the editor act more like a desktop application by enabling it to open files from command line arguments or even using Open with from within Dolphin.
We now have a working text editor that can open and save files. We might, however, want to extend its utility by enabling users to more quickly and efficiently use it to edit files. In this tutorial we will make the editor act more like a desktop application by enabling it to open files from command line arguments or even using Open with from within Dolphin.
![](result.png)
......@@ -15,15 +15,15 @@ Now that we have a text editor that can open and save files. We will now make th
### mainwindow.h
Here we have done nothing but add a new `openFile` function which takes a `QUrl`. Again, we use a `QUrl` instead of a `QString` so that we can also work with remote files as if they were local.
Here we have done nothing but add a new `openFileFromUrl` function which takes a `QUrl`. Again, we use a `QUrl` instead of a `QString` so that we can also work with remote files as if they were local.
{{< readfile file="/content/docs/getting-started/commandline/mainwindow.h" highlight="cpp" >}}
### mainwindow.cpp
There's no new code here, only rearranging. Everything from void `openFile()` has been moved into void `openFile(const QUrl &inputFileName)` except the call to `QFileDialog::getOpenFileUrl()`.
There's no new code here, only rearranging. Everything from `void openFile()` has been moved into `void openFile(const QUrl &inputFileName)` except the call to `QFileDialog::getOpenFileUrl()`.
This way, we can call `openFile()` if we want to display a dialog, or we can call `openFile(const QUrl &)` if we know the name of the file already. Which will be the case when we feed the file name through the command line.
This way, we can call `openFile()` if we want to display a dialog, or we can call `openFileFromUrl(const QUrl &)` if we know the name of the file already. Which will be the case when we feed the file name through the command line.
{{< readfile file="/content/docs/getting-started/commandline/mainwindow.cpp" highlight="cpp" >}}
......@@ -37,7 +37,7 @@ First, we tell `QCommandLineParser` that we want to add a new positional argumen
parser.addPositionalArgument(QStringLiteral("file"), i18n("Document to open"));
```
Later on, we start processing positional arguments, but only if there is one. Otherwise, we proceed as usual. In our case we can only open one file at a time, so only the first file is of interest to us. We call the openFile() function and feed it the URL of the file we want to open, whether it is a local file like $HOME/foo or a remote one like ftp.mydomain.com/bar. We use the overloaded form of QUrl::fromUserInput() in order to set the current path. This is needed in order to work with relative paths like "../baz".
Later on, we start processing positional arguments, but only if there is one. Otherwise, we proceed as usual. In our case we can only open one file at a time, so only the first file is of interest to us. We call the `openFileFromUrl()` function and feed it the URL of the file we want to open, whether it is a local file like "$HOME/foo" or a remote one like "ftp.mydomain.com/bar". We use the overloaded form of `QUrl::fromUserInput()` in order to set the current path. This is needed in order to work with relative paths like "../baz".
```c++
if (parser.positionalArguments().count() > 0) {
......
......@@ -78,7 +78,7 @@ void MainWindow::saveFile()
void MainWindow::openFile()
{
openFile(QFileDialog::getOpenFileUrl(this, i18n("Open File")));
openFileFromUrl(QFileDialog::getOpenFileUrl(this, i18n("Open File")));
}
void MainWindow::openFileFromUrl(const QUrl &inputFileName)
......
......@@ -8,12 +8,12 @@ description: >
## Abstract
Your first program shall greet the world with a friendly "Hello World!", what else? For that, we will use a [KMessageBox](docs:kwidgetsaddons;KMessageBox) and customise one of the buttons.
Your first program shall greet the world with a friendly "Hello World!". For that, we will use a [KMessageBox](docs:kwidgetsaddons;KMessageBox) and customise one of its buttons.
![](result.png)
{{< alert title="Note" color="info" >}}
To get more information about any class you come across, you can use the ‘kde’ search engine. For example, to look for information about KMessageBox, just type "kde:kmessagebox" into Konqueror, or KRunner, and you’ll be taken to the documentation.
To get more information about any class you come across, you can use [KDE's API Reference site](https://api.kde.org/index.html). It can be quickly accessed via KRunner with the 'kde:' search keyword (e.g. 'kde: KMessageBox'). You may also find it useful to consult Qt's documentation with `qt:`, since much of KDE's Frameworks builds upon it.
{{< /alert >}}
......
......@@ -9,7 +9,7 @@ description: >
This tutorial carries on from [First Program Tutorial](../hello_world) and will introduce the [KXmlGuiWindow](docs:kxmlgui;KXmlGuiWindow) class.
In the previous tutorial, the program caused a dialog box to pop up but we're going to take steps towards a functioning application.
In the previous tutorial, the program caused a dialog box to pop up. Now we're going to take steps towards creating a functioning application with a more advanced window structure.
![](result.png)
......@@ -24,9 +24,9 @@ In order to have a useful KXmlGuiWindow, we must subclass it. So we create two f
{{< readfile file="/content/docs/getting-started/main_window/mainwindow.h" highlight="cpp" >}}
First we subclass KXmlGuiWindow with `class MainWindow : public KXmlGuiWindow` then we declare the constructor with `MainWindow(QWidget *parent = nullptr);`.
First we [subclass](https://en.wikipedia.org/wiki/Inheritance_(object-oriented_programming#Subclasses_and_superclasses) KXmlGuiWindow with `class MainWindow : public KXmlGuiWindow` then we declare the [constructor](https://en.wikipedia.org/wiki/Constructor_(object-oriented_programming)) with `MainWindow(QWidget *parent = nullptr);`.
And finally, we declare a pointer to the object that will make up the bulk of our program. :ktextwidgetsapi:`KTextEdit` is a generic rich text editing widget with some niceties like cursor auto-hiding.
And finally, we declare a pointer to the object that will make up the bulk of our program. [`KTextEdit`](docs:ktextwidgets;KTextEdit) is a generic rich text editing widget with some niceties like cursor auto-hiding.
### mainwindow.cpp
......@@ -34,7 +34,7 @@ And finally, we declare a pointer to the object that will make up the bulk of ou
First, of course, we have to include the header file containing the class declaration.
We initialise our text editor with an object and use KXmlGuiWindow's built-in `setCentralWidget()` function on it which tells the KXmlGuiWindow what should appear in the central section of the window.
We initialise our text editor with a [QWidget](https://doc.qt.io/qt-5/qwidget.html) object and use KXmlGuiWindow's built-in `setCentralWidget()` function on it which tells the KXmlGuiWindow what should appear in the central section of the window.
Finally, [KXmlGuiWindow::setupGUI](docs:kxmlgui;KXmlGuiWindow::setupGUI) is called which does a lot of behind-the-scenes stuff and creates the default menu bars (Settings, Help).
......@@ -45,7 +45,7 @@ In order to actually run this window, we need to add a few lines in main.cpp:
{{< readfile file="/content/docs/getting-started/main_window/main.cpp" highlight="cpp" >}}
Again, we include our new header file in order to create our MainWindow object which we then display.
Again, we include our new header file ´mainwindow.h´. This lets us create our new MainWindow object which we then display near the end of the main function (by default, new window objects are hidden).
## CMake
......
......@@ -17,7 +17,7 @@ The KDE Frameworks provides a number of classes for working with files that make
### main.cpp
Nothing changed here.
We don't need to change anything in here.
### mainwindow.h
......@@ -35,17 +35,21 @@ We'll get into the details of mainwindow.cpp in a while.
### texteditorui.rc
This is identical to texteditorui.rc from the [previous tutorial](../using_actions). We do not need to add any information about any of the [KStandardAction](docs:kconfigwidgets;KStandardAction) since the placement of those actions is handled automatically by XMLGUI system.
This is identical to texteditorui.rc from the [previous tutorial](../using_actions). We do not need to add any information about any of the [KStandardAction](docs:kconfigwidgets;KStandardAction) since the placement of those actions is handled automatically by the XMLGUI system.
## Explanation
Okay, now to implement the code that will do the loading and saving. This will all be happening in `mainwindow.cpp`.
```cpp
MainWindow::MainWindow(QWidget *parent) : KXmlGuiWindow(parent), fileName(QString())
```
The first thing we do is add `fileName(QString())` to the MainWindow constructor list to make sure that fileName is empty right from the beginning.
### Adding the actions
The first thing we are going to do is provide the outward interface for the user so they can tell the application to load and save. Like with the quit action in [previous tutorial](../using_actions), we will use [KStandardAction](docs:kconfigwidgets;KStandardAction). We add the actions in the same way as for the quit action and, for each one, we connect it to the appropriate slot that we declared in the header file.
The first thing we are going to do is provide the outward interface for the user so they can tell the application to load and save. Like with the quit action in the [previous tutorial](../using_actions), we will use [KStandardAction](docs:kconfigwidgets;KStandardAction). We add the actions in the same way we did for the quit action and, for each one, we connect it to the appropriate slot that we declared in the header file.
### Creating a new document
......@@ -59,10 +63,10 @@ void MainWindow::newFile()
}
```
`fileName.clear()` sets the `fileName` QString to be empty to reflect the fact that this document does not yet have a presence on storage. `textArea->clear()` then clears the central text area using the same function that we connected the clear QAction to in [previous tutorial](../using_actions).
`fileName.clear()` sets the `fileName` QString to be empty to reflect the fact that this document does not yet have a presence on storage. `textArea->clear()` then clears the central text area using the same function that we connected the 'clear' QAction to in the [previous tutorial](../using_actions).
{{< alert title="Warning" color="warning" >}}
This simple example simply clears the text area without checking if the file has been saved first. It's only meant as a demonstration of file I/O and not as an example of best programming practices.
This example simply clears the text area without checking if the file has been saved first. It's only meant as a demonstration of file I/O and is *not* an example of best programming practices.
{{< /alert >}}
### Saving a file
......@@ -73,15 +77,15 @@ To make this tutorial simple, this example program can only save to local storag
### saveFileToDisk(const QString &)
Now we get onto our first file handling code. We're going to implement a function which will save the contents of the text area to the file name given as a parameter. Qt provides a class for safely saving a file called [QSaveFile ](https://doc.qt.io/qt-5/qsavefile.html).
Now we get onto our first file handling code. We're going to implement a function which will save the contents of the text area to the file name given as a parameter. Qt provides a class for safely saving a file called [QSaveFile](https://doc.qt.io/qt-5/qsavefile.html).
The function's prototype is
The function's prototype is:
```cpp
void MainWindow::saveFileAs(const QString &outputFileName)
```
We then create our QSaveFile object and open it with
We then create our QSaveFile object and open it with:
```cpp
QSaveFile file(outputFileName);
......@@ -140,7 +144,7 @@ There's nothing exciting or new in this function, just the logic to decide wheth
Finally, we get round to being able to load a file, from local storage or from a remote location like an FTP server. The code for this is all contained in `MainWindow::openFile()`.
First we must ask the user for the name of the file they wish to open. We do this using another one of the QFileDialog functions, this time `getOpenFileName()`:
First we must ask the user for the name of the file they wish to open. We do this using another one of the QFileDialog functions, this time `getOpenFileUrl()`:
```c++
const QUrl fileNameFromDialog = QFileDialog::getOpenFileUrl(this, i18n("Open File"));
......
......@@ -16,7 +16,7 @@ For example, if we wanted to let the user of your [window](../main_window) clear
## QAction
A [QAction ](https://doc.qt.io/qt-5/qaction.html) is an object which contains all the information about the icon and shortcuts that is associated with a certain action. The action is then connected to a slot which carries out the work of your action.
A [QAction ](https://doc.qt.io/qt-5/qaction.html) is an object which contains all the information about the icon and shortcuts that are associated with a certain action. The action is then connected to a [slot](https://doc.qt.io/qt-5/signalsandslots.html) which carries out the work of your action.
## The Code
......@@ -36,7 +36,7 @@ Only a function void `setupActions()` has been added which will do all the work
## Explanation
This builds upon the [KXmlGuiWindow](docs:kxmlgui;KXmlGuiWindow) code from [the main window](main_window). Most of the changes are to mainwindow.cpp, an important structural change being that the constructor for MainWindow now calls `setupActions()` instead of `setupGUI()`. `setupActions()` is where the new QAction code goes before finally calling `setupGUI()` itself.
This builds upon the [KXmlGuiWindow](docs:kxmlgui;KXmlGuiWindow) code from our previous [main window](main_window). Most of the changes are to mainwindow.cpp, an important structural change being that the constructor for MainWindow now calls `setupActions()` instead of `setupGUI()`. `setupActions()` is where the new QAction code goes before finally calling `setupGUI()` itself.
### Creating the QAction object
......@@ -58,9 +58,13 @@ Now that we have our QAction object, we can start setting its properties. The fo
clearAction->setText(i18n("&Clear"));
```
Note that the text is passed through the i18n() function; this is necessary for the UI to be translatable (more information on this can be found in the :doc:`i18n tutorial<../i18n/index>`).
Note that the text is passed through the i18n() function; this is necessary for the UI to be translatable (more information on this can be found in the [docs](docs:ki18n).
The text of the action should contain a & because that makes it easier to translate in non-latin1 languages. In Japanese, the translation might be ソース(&S) and without the & in the english text the translators cannot know if they have to add a & or not.
The ampersand (&) in the action text denotes which letter will be used as an accelerator for said action. If the user opens a menu and presses the 'Alt' key, this will highlight the first letter of 'Clear', denoting the key they can press to perform said action. In this case, they would press 'Alt+C' to clear the textbox when the 'file' menu is open.
![](accelerator-highlight.png)
The ampersand is also useful for internationalisation: in non-latin languages such as Japanese (where 'copy' is コピー), using the first letter of that language to accelerate the action could be cumbersome. The ampersand lets translators know whether they should include the latin character in parentheses, allowing non-English users to use the same accelerator key even if the translated string is completely different.
### Icon
......@@ -104,7 +108,7 @@ connect(clearAction, &QAction::triggered),
### KStandardAction
For actions which would likely appear in almost every KDE application such as 'quit', 'save', and 'load' there are pre-created convenience QActions, accessed through :kconfigwidgetsapi:`KStandardAction`.
For actions which would likely appear in almost every KDE application such as 'quit', 'save', and 'load' there are pre-created convenience QActions, accessed through [`KStandardAction`](docs:kconfigwidgets;KStandardAction).
They are very simple to use. Once the library has been included (`#include <KStandardAction>`), simply supply it with what you want the function to do and which QActionCollection to add it to. For example:
......@@ -112,13 +116,13 @@ They are very simple to use. Once the library has been included (`#include <KSta
KStandardAction::quit(qApp, &QCoreApplication::quit, actionCollection());
```
Here we call the QApplicaton's [quit ](https://doc.qt.io/qt-5/qapplication.html#quit) method whenever the :kconfigwidgetsapi:`KStandardAction` quit is triggered. We are able to access that QApplication method via the [qApp](https://doc.qt.io/qt-5/qapplication.html#qApp) macro.
Here we call the QApplicaton's [quit ](https://doc.qt.io/qt-5/qapplication.html#quit) method whenever [`KStandardAction::quit`](docs:kconfigwidgets;KStandardAction::quit) is triggered. We are able to access that QApplication method via the [qApp](https://doc.qt.io/qt-5/qapplication.html#qApp) macro, which returns a pointer to the specific QApplication object in question.
In the end, this creates a QAction with the correct icon, text and shortcut and even adds it to the File menu.
### Adding the action to menus and toolbars
At the moment, the new "Clear" action has been created but it hasn't been associated with any menus or toolbars. This is done with a KDE technology called XMLGUI, which does nice things like movable toolbars for you.
At the moment, the new "Clear" action has been created but it hasn't been associated with any menus or toolbars. This is done with a KDE technology called XMLGUI, which does nice things like create movable toolbars for you.
### Defining your own help menu
......@@ -128,11 +132,11 @@ The Help menu has been standardized to ease the lives of both developers and use
The `setupGUI()` function in [KXmlGuiWindow](docs:kxmlgui;KXmlGuiWindow) depends on the XMLGUI system to construct the GUI, which XMLGUI does by parsing an XML file description of the interface.
The rule for naming this XML file is `appnameui.rc`, where appname is the name you set in :kcorewidgetsapi:`KAboutData` (in this case, tutorial3). So in our example, the file is called `texteditorui.rc`, and is located in the build directory. Where the file will ultimately be placed is handled by CMake.
The rule for naming this XML file is `appnameui.rc`, where appname is the name you set in [`KAboutData`](docs:kcorewidgetsapi;KAboutData) (in this case, TextEditor). So in our example, the file is called `texteditorui.rc`, and is placed in the same folder as our other files. Where the file will ultimately be placed is handled by CMake.
#### appnameui.rc file
Since the description of the UI is defined with XML, the layout must follow strict rules. This tutorial will not go into great depth on this topic, but for more information, see the detailed :doc:`XMLGUI` page.
Since the description of the UI is defined with XML, the layout must follow strict rules. This tutorial will not go into great depth on this topic, but for more information, see the detailed [`XMLGUI`](docs:kxmlgui) page.
#### texteditorui.rc
......@@ -148,19 +152,17 @@ Change the 'version' attribute of the `<gui>` tag if you changed .rc file since
The version attribute must always be an integer number.
{{< /alert >}}
Some notes on the interaction between code and the .rc file: Menus appear automatically and should have a `<text/>` child tag unless they refer to standard menus. Actions need to be created manually and inserted into the `actionCollection()` using the name in the .rc file. Actions can be hidden or disabled, whereas menus can't.
Some notes on the interaction between code and the .rc file: menus appear automatically and should have a `<text/>` child tag unless they refer to standard menus. Actions need to be created manually and inserted into the `actionCollection()` using the same name as in the .rc file. Actions can be hidden or disabled, whereas menus can't.
## CMake
Finally, the `texteditorui.rc` needs to go somewhere where the system can find it (can't just leave it in the source directory!). **This means the project needs to be installed somewhere**, unlike in the previous tutorials.
Finally, the `texteditorui.rc` needs to go somewhere where the system can find it (you can't just leave it in the source directory!). **This means the project needs to be installed somewhere**, unlike in the previous tutorials.
### CMakeLists.txt
.. literalinclude:: using_actions/CMakeLists.txt
:language: cmake
:linenos:
{{< readfile file="/content/docs/getting-started/using_actions/CMakeLists.txt" highlight="cmake" >}}
This file is almost identical to the one for [previous tutorial](../main_window), but with two extra lines at the end that describe where the files are to be installed. Firstly, the `texteditor` target is installed to the `KDE_INSTALL_TARGETS_DEFAULT_ARGS` then the `texteditorui.rc` file that describes the layout of the user interface is installed to the application's data directory under `KDE_INSTALL_KXMLGUI5DIR`.
This file is almost identical to the one for [previous tutorial](../main_window), but with two extra lines at the end that describe where the files are to be installed. Firstly, the `texteditor` target is installed to the `KDE_INSTALL_TARGETS_DEFAULT_ARGS` then the `texteditorui.rc` file that describes the layout of the user interface is installed to the application's data directory under `KDE_INSTALL_KXMLGUI5DIR`.
## Make, Install And Run
......@@ -184,4 +186,4 @@ source prefix.sh # located in the build directory
texteditor
```
This temporarily adds (prepends) the newly created "share" location to `XDG_DATA_DIRS`, the standard path for application data files and change the `PATH` and other Qt environement variables.
This temporarily adds (prepends) the newly created "share" location to [`XDG_DATA_DIRS`](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html), the standard path for application data files, and changes the `PATH` and other Qt environment variables.
......@@ -180,7 +180,7 @@ Kirigami.CardsListView {
## CardsGridView
Use a [CardsGridView](docs:kirigami;org::kde::kirigami::CardsGridView] for displaying cards in a grid.
Use a [CardsGridView](docs:kirigami;org::kde::kirigami::CardsGridView) for displaying cards in a grid.
The behavior is the same as a CardsLayout, and it allows cards to be put in one or two columns depending on the available width.
......
......@@ -41,6 +41,10 @@ TAG_FILES = [
'tags': 'https://api.kde.org/frameworks/kwidgetsaddons/html/KWidgetsAddons.tags',
'base_url': 'https://api.kde.org/frameworks/kwidgetsaddons/html/'
},
{
'tags': 'https://api.kde.org/frameworks/ktextwidgets/html/KTextWidgets.tags',
'base_url': 'https://api.kde.org/frameworks/ktextwidgets/html/'
},
{
'tags': 'https://api.kde.org/frameworks/ktexteditor/html/KTextEditor.tags',
'base_url': 'https://api.kde.org/frameworks/ktexteditor/html/'
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment