Commit 49f13167 authored by Carl Schwan's avatar Carl Schwan 🚴
Browse files

Update documentation

parent d3d8c9d7
baseURL = "/"
title = "Goldydocs"
title = "Developer"
enableRobotsTXT = true
......@@ -53,8 +53,8 @@ anchor = "smart"
[languages]
[languages.en]
title = "Goldydocs"
description = "A Docsy example site"
title = "Developer"
description = "All the resources for developing using the KDE Platform"
languageName ="English"
# Weight used for sorting.
weight = 1
......@@ -67,7 +67,7 @@ weight = 1
# Everything below this are Site Params
[params]
copyright = "The Docsy Authors"
copyright = "The KDE Community"
privacy_policy = "https://policies.google.com/privacy"
# First one is picked as the Twitter card image if not set on page.
......
---
title: Introduction To KConfig
description: >
An overview of the KConfig classes and how to use them in your application code
---
## Abstract
This tutorial looks at the KDE configuration data system, starting with an overview of the design fundamentals from an application developer's point of view. It then looks at the classes relevant to application development one by one before moving on to kiosk (user and group profiles) integration.
## Design Essentials
KConfig is designed to abstract away the concept of actual storage and retrieval of configuration settings behind an API that allows easy fetching and setting of information. Where and in what format the data is stored is not relevant to an application using KConfig. This keeps all KDE applications consistent in their handling of configurations while alleviating each and every application author to build such a system on their own from scratch, which can be a highly error prone exercise.
A KConfig object represents a single configuration object. Each configuration object is referenced by its unique name and may be actually read from multiple local or remote files or services. Each application has a default configuration object associated with it and there is also a global configuration object.
These configuration objects are broken down into a two level hierarchy: groups and keys. A configuration object can have any number of groups and each group can have one or more keys with associated values.
Values stored may be of any number of data types. They are stored and retrieved as the objects themselves. For example, a QColor object is passed to a config object directly when storing a color value and when retrieved a QColor object is returned. Applications themselves therefore generally do not have to perform serialization and deserialization of objects themselves.
## The KConfig Class
The [KConfig](https://api.kde.org/frameworks/kconfig/html/classKConfig.html) object is used to access a given configuration object. There are a number of ways to create a config object:
{{< highlight cpp >}}
// a plain old read/write config object
KConfig config("myapprc");
// a specific file in the filesystem
// currently must be an INI style file
KConfig fullPath("/etc/kderc");
// not merged with global values
KConfig globalFree( "localsrc", KConfig::NoGlobals );
// not merged with globals or the $KDEDIRS hierarchy
KConfig simpleConfig( "simplerc", KConfig::SimpleConfig );
// outside the standard config resource
KConfig dataResource("data", KConfig::SimpleConfig, QStandardPaths::AppDataLocation);
{{< /highlight >}}
The KConfig object create on line 2 is a regular config object. We can read values from it, write new entries and ask for various properties of the object. This object will be loaded from the config resource as determined by [QStandardPaths](https://doc.qt.io/qt-5/qstandardpaths.html), meaning that every instance of the myapprc object in each of the directories in the config resource hierarchy will be merged to create the values seen in this object. This is how system wide and per-user/group profiles are generated and supported and it all happens transparently to the application itself.
{{< alert title="Tip" color="success" >}}
For more information on how the merging works, see the [KDE Filesystem Hierarchy](https://userbase.kde.org/KDE_System_Administration/KDE_Filesystem_Hierarchy) article.
{{< /alert >}}
On line 6 we open a specific local file, this case `/etc/kderc`. This performs no merging of values and expects an INI style file.
Line 9 sees the creation of a configuration object that is not merged with the global kdeglobals configuration object, while the configuration file on line 12 is additionally not merged with any files in the XDG directories hierarchy. This can noticeably improve performance in the case where one is simply reading values out of a simple configuration for which global values are not meaningful.
Finally on line 18 we see the creation of a configuration object that does not exist in the config resource but rather in the application data resource. You may use any StandardLocation that QStandardPaths contains.
## Special Configuration Objects
Each application has its own configuration object that uses the name provided to [KAboutData](https://api.kde.org/frameworks/kcoreaddons/html/classKAboutData.html) appended with "rc" as its name. So an app named "myapp" would have the default configuration object of "myapprc" (located in $HOME/.local/config/). This configuration file can be retrieved in this way:
```cpp
#include <KComponentData>
#include <KConfig>
#include <KGlobal>
MyClass::MyClass()
{
// note that this is actually a KSharedConfig
// more on that class in a bit!
KConfig *config = KGlobal::config();
}
```
The default configuration object for the application is accessed when no name is specified when creating a `KConfig` object. So we could also do this instead - but it would be slower because it would have to parse the whole file again:
```cpp
#include <KConfig>
MyClass::MyClass()
{
KConfig config;
}
```
Finally there is a global configuration object, `kdeglobals`, that is shared by every application. It holds such information as the default application shortcuts for various actions. It is "blended" into the configuration object if the `KConfig::IncludeGlobals` flag is passed to the KConfig constructor, which is the default.
## Commonly Useful Methods
To save the current state of the configuration object we call the `sync()` method. This method is also called when the object is destroyed. If no changes have been made or the resource reports itself as non-writable (such as in the case of the user not having write permissions to the file) then no disk activity occurs. `sync()` merges changes performed concurrently by other processes - local changes have priority, though.
cmake_minimum_required(VERSION 3.0)
project(texteditor)
set(QT_MIN_VERSION "5.11.0")
set(KF5_MIN_VERSION "5.68.0")
find_package(ECM 1.0.0 REQUIRED NO_MODULE)
set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
include(KDEInstallDirs)
include(KDECMakeSettings)
include(KDECompilerSettings NO_POLICY_SCOPE)
include(FeatureSummary)
find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS
Core # QCommandLineParser, QStringLiteral
Widgets # QApplication, QAction
)
find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS
CoreAddons # KAboutData
I18n # KLocalizedString
XmlGui # KXmlGuiWindow, KActionCollection
TextWidgets # KTextEdit
ConfigWidgets # KStandardActions
WidgetsAddons # KMessageBox
KIO # KIO
)
feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES)
set(texteditor_SRCS main.cpp mainwindow.cpp)
add_executable(texteditor ${texteditor_SRCS})
target_link_libraries(texteditor
Qt5::Widgets
KF5::CoreAddons
KF5::I18n
KF5::XmlGui
KF5::TextWidgets
KF5::ConfigWidgets
KF5::WidgetsAddons
KF5::KIOCore
)
install(TARGETS texteditor ${KDE_INSTALL_TARGETS_DEFAULT_ARGS})
install(FILES texteditorui.rc DESTINATION ${KDE_INSTALL_KXMLGUI5DIR}/texteditor)
---
title: Command Line Interface
description: >
Adds the ability to specify which file to open from the command line to our text editor.
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.
![](result.png)
## Code and Explanation
### 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.
{{< readfile file="/content/en/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()`.
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.
{{< readfile file="/content/en/docs/getting-started/commandline/mainwindow.cpp" highlight="cpp" >}}
### main.cpp
This is where all the [QCommandLineParser ](https://doc.qt.io/qt-5/qcommandlineparser.html) magic happens. In previous examples, we only used the class to feed QApplication the necessary data for using flags like `--version` or `--author`. Now we actually get to use it to process command line arguments.
First, we tell `QCommandLineParser` that we want to add a new positional arguments. In a nutshell, these are arguments that are not options. `-h` or `--version` are options, `file` is an argument.
```c++
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".
```c++
if (parser.positionalArguments().count() > 0) {
window->openFile(QUrl::fromUserInput(parser.positionalArguments().at(0), QDir::currentPath()));
}
```
These are the changes:
{{< readfile file="/content/en/docs/getting-started/commandline/main.cpp" highlight="cpp" start="40" lines="13" >}}
#include <cstdlib>
#include <QApplication>
#include <QCommandLineParser>
#include <QDir>
#include <QUrl>
#include <KAboutData>
#include <KLocalizedString>
#include "mainwindow.h"
int main (int argc, char *argv[])
{
QApplication app(argc, argv);
KLocalizedString::setApplicationDomain("texteditor");
KAboutData aboutData(
// The program name used internally. (componentName)
QStringLiteral("texteditor"),
// A displayable program name string. (displayName)
i18n("TextEditor"),
// The program version string. (version)
QStringLiteral("1.0"),
// Short description of what the app does. (shortDescription)
i18n("A simple text area using QAction etc."),
// The license this code is released under
KAboutLicense::GPL,
// Copyright Statement (copyrightStatement = QString())
i18n("(c) 2015"),
// Optional text shown in the About box.
// Can contain any information desired. (otherText)
i18n("Some text..."),
// The program homepage string. (homePageAddress = QString())
QStringLiteral("https://mytexteditor.kde.org/"),
// The bug report email address
// (bugsEmailAddress = QLatin1String("submit@bugs.kde.org")
QStringLiteral("submit@bugs.kde.org"));
aboutData.addAuthor(i18n("Name"), i18n("Task"), QStringLiteral("your@email.com"),
QStringLiteral("https://your.website.com"), QStringLiteral("OSC Username"));
KAboutData::setApplicationData(aboutData);
QCommandLineParser parser;
aboutData.setupCommandLine(&parser);
parser.addPositionalArgument(QStringLiteral("file"), i18n("Document to open"));
parser.process(app);
aboutData.processCommandLine(&parser);
MainWindow *window = new MainWindow();
window->show();
if (parser.positionalArguments().count() > 0) {
window->openFile(QUrl::fromUserInput(parser.positionalArguments().at(0), QDir::currentPath()));
}
return app.exec();
}
#include <QApplication>
#include <QAction>
#include <QSaveFile>
#include <QFileDialog>
#include <QTextStream>
#include <QByteArray>
#include <KTextEdit>
#include <KLocalizedString>
#include <KActionCollection>
#include <KStandardAction>
#include <KMessageBox>
#include <KIO/Job>
#include "mainwindow.h"
MainWindow::MainWindow(QWidget *parent) : KXmlGuiWindow(parent), fileName(QString())
{
textArea = new KTextEdit();
setCentralWidget(textArea);
setupActions();
}
void MainWindow::setupActions()
{
QAction *clearAction = new QAction(this);
clearAction->setText(i18n("&Clear"));
clearAction->setIcon(QIcon::fromTheme("document-new"));
actionCollection()->setDefaultShortcut(clearAction, Qt::CTRL + Qt::Key_W);
actionCollection()->addAction("clear", clearAction);
connect(clearAction, &QAction::triggered, textArea, &KTextEdit::clear);
KStandardAction::quit(qApp, &QCoreApplication::quit, actionCollection());
KStandardAction::open(this, &MainWindow::openFile, actionCollection());
KStandardAction::save(this, &MainWindow::saveFile, actionCollection());
KStandardAction::saveAs(this, &MainWindow::saveFileAs, actionCollection());
KStandardAction::openNew(this, &MainWindow::newFile, actionCollection());
setupGUI(Default, "savingloadingui.rc");
}
void MainWindow::newFile()
{
fileName.clear();
textArea->clear();
}
void MainWindow::saveFileToDisk(const QString &outputFileName)
{
if (!outputFileName.isNull()) {
QSaveFile file(outputFileName);
file.open(QIODevice::WriteOnly);
QByteArray outputByteArray;
outputByteArray.append(textArea->toPlainText().toUtf8());
file.write(outputByteArray);
file.commit();
fileName = outputFileName;
}
}
void MainWindow::saveFileAs()
{
saveFileToDisk(QFileDialog::getSaveFileName(this, i18n("Save File As")));
}
void MainWindow::saveFile()
{
if (!fileName.isEmpty()) {
saveFileToDisk(fileName);
} else {
saveFileAs();
}
}
void MainWindow::openFile()
{
openFile(QFileDialog::getOpenFileUrl(this, i18n("Open File")));
}
void MainWindow::openFileFromUrl(const QUrl &inputFileName)
{
if (!inputFileName.isEmpty()) {
KIO::Job *job = KIO::storedGet(inputFileName);
fileName = inputFileName.toLocalFile();
connect(job, &KIO::Job::result, this, &MainWindow::downloadFinished);
job->exec();
}
}
void MainWindow::downloadFinished(KJob *job)
{
if (job->error()) {
KMessageBox::error(this, job->errorString());
fileName.clear();
return;
}
const KIO::StoredTransferJob *storedJob = qobject_cast<KIO::StoredTransferJob *>(job);
if (storedJob) {
textArea->setPlainText(QTextStream(storedJob->data(), QIODevice::ReadOnly).readAll());
}
}
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <KXmlGuiWindow>
class KTextEdit;
class KJob;
class MainWindow : public KXmlGuiWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
void openFileFromUrl(const QUrl &inputFileName);
private:
void setupActions();
void saveFileToDisk(const QString &outputFileName);
private Q_SLOTS:
void newFile();
void openFile();
void saveFile();
void saveFileAs();
void downloadFinished(KJob *job);
private:
KTextEdit *textArea;
QString fileName;
};
#endif // MAINWINDOW_H
<?xml version="1.0" encoding="UTF-8"?>
<gui name="texteditor"
version="1"
xmlns="http://www.kde.org/standards/kxmlgui/1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.kde.org/standards/kxmlgui/1.0
http://www.kde.org/standards/kxmlgui/1.0/kxmlgui.xsd" >
<MenuBar>
<Menu name="file" >
<Action name="clear" />
</Menu>
<Menu >
<text>A&amp;nother Menu</text>
<Action name="clear" />
</Menu >
</MenuBar>
<ToolBar name="mainToolBar" >
<text>Main Toolbar</text>
<Action name="clear" />
</ToolBar>
</gui>
......@@ -2,6 +2,7 @@
title: Saving and loading
description: >
Introduces the KIO library while adding loading and saving support to our application.
weight: 4
---
## Introduction
......
---
title: "Integrate your application with Plasma"
linkTitle: "Plasma Integration"
weight: 1
description: >
Discover KDE Frameworks and start building your first application
---
In these series of tutorials, you will learn how to integrate better your application with Plasma.
---
title: Desktop File
weight: 3
description: >
Let your application show up in the Launcher and in application menus.
---
In order for your application to show up in menus and/or to be automatically associated with mime types in file browsers, you need to provide a `.desktop` file like follows:
{{< highlight ini >}}
[Desktop Entry]
Type=Application
Exec=your-app %u
MimeType=application/x-your-mime-type;
Icon=some-icon
X-DocPath=yourapp/index.html
Terminal=false
Name=Your App
GenericName=Some Generic Name
Comment=Short Description Of Your App
Categories=Qt;KDE;
{{< /highlight >}}
Take a look at the [.desktop Free Desktop Spec](http://standards.freedesktop.org/desktop-entry-spec/latest/) to find our more about the key/value pairs above. It's important to pick a good set of Categories, see the spec for a list of valid values.
## Translations
Like you can see on the example .desktop file, only a english version is available. To translate it in more languages, you can add a [lang] suffix to the keys, you want to translate. For example:
```ini
Name=Your Application
Name[fr]=Ton application
...
GenericName=Some Generic Name
GenericName[fr]=Un nom générique
```
If you are using KDE infrastructure, this is done automatically by the `scripy`.
## Install the .desktop file
In you CMakeLists.txt file, you can add the following lines to install your .desktop file:
```cmake
install(PROGRAMS org.example.my-app.desktop DESTINATION ${XDG_APPS_INSTALL_DIR})
```
---
title: Icons
description: >
Deliver your application with icons.
weight: 2
---
When you create your own KDE application you will probably want to have icons in it respecting your users icon theme.
This is possible with the help of [QIcon::fromTheme](https://doc.qt.io/qt-5/qicon.html#fromTheme) for QtWidgets application or [Kirigami](https://api.kde.org/frameworks/kirigami/html/classIcon.html) for QtQuick application. Both function take as argument, the name of the icon in the icon theme.
Plasma and all the other major Linux deskops follow the [freedesktop icon specification](http://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html) and [naming specification](http://standards.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html). A fallback mechanism is also present so that if the `document-open-recent` icon is not present, the `document-open` icon will be displayed instead and if the `document-open` icon is also not present the `document` icon will be displayed.
## Finding the right icons
KDE provide an application to search for icons. This application is called cuttlefish and can generally available in the `plasma-sdk` package in your packet manager (on Linux).
![Screenshot of cuttlefish](cuttlefish.png)
Cuttlefish allows you to search for icons, by name, by category. Look at the icons using different color schemes and view a selected icon in other themes.
## Example
In a traditional QtWidget application:
{{< highlight cpp >}}
QIcon undoicon = QIcon::fromTheme("edit-undo");
{{< /highlight >}}
In a QtQuick application:
{{< highlight json >}}
import org.kde.kirigami 2.14 as Kirigami
Kirigami.Icon {
source: "edit-undo"
}
{{< /highlight >}}
## Other platforms than Linux
On other plateform, the icon theme is not available.
### Android
On Android, you can use the [kirigami_package_breeze_icons](https://invent.kde.org/frameworks/kirigami/-/blob/master/KF5Kirigami2Macros.cmake#L5) CMake function to package the Breeze icons you are using.
{{< highlight cmake >}}
if(ANDROID)
kirigami_package_breeze_icons(ICONS
edit-undo
edit-redo
)
endif()
{{< /highlight >}}
---
title: Session Management
description: >
Make your application aware of X sessions
weight: 1
---
## About KDE and X11 session management
Since KDE 1.0, KDE supports the legacy X11R4 and ICCCM session management protocols. It will restart with the specified command all legacy applications that define the `WM_COMMAND` property or support the `WM_SAVE_YOURSELF` protocol. It will also restore the window geometries on a best effort basis.
Since KDE 2.0, KDE also supports the standard X11R6 session management protocol XSMP and uses it. You can download the official documentation of the standard from the X Consortium's FTP server [ftp.x.org](http://stuff.mit.edu/afs/sipb/contrib/doc/X11/hardcopy/SM/xsmp.PS.gz). Unlike the legacy protocols, the new X11R6 session management gives a chance to save application dependent settings when you log out. A text editor, for instance, would save the names of the loaded files and would reload them when you log in again. Another major advantage of the new protocol is the support for a clean and safe logout procedure even if the users decides not to restore the session next time. The protocol gives applications the possibility to interact with the user if they are in danger to lose some data, and to cancel the shutdown process if necessary.
## Further Reading
An introductory overview of session management functionality and the Qt API for it is available from [Qt documentation](https://doc.qt.io/qt-5/session.html).
In KDE, the classes [KConfigGui](https://api.kde.org/frameworks/kconfig/html/namespaceKConfigGui.html) and [KMainWindow](https://api.kde.org/frameworks/kxmlgui/html/classKMainWindow.html) hide all the ugly details from the programmer. Basically, a KConfigGui manages a KConfig configuration object (available trough [KConfigGui::sessionConfig()](https://api.kde.org/frameworks/kconfig/html/namespaceKConfigGui.html#a36d66204429764d84f4f7728dfb0805e)) for you, that your application can use to store session specific data.
Please read the class documentation, especially the one of [KMainWindow](https://api.kde.org/frameworks/kxmlgui/html/classKMainWindow.html), for a detailed interface description. With the advanced functionality in [KMainWindow](https://api.kde.org/frameworks/kxmlgui/html/classKMainWindow.html), it's really just a matter of a few lines to get even a multi-window application to retains its state between different user sessions.
## Implementing session management in your application
Here's just a brief overview how you can add session management to your application. Again, see the class documentation for details.
Implementing session management in Plasma is easy. If your main window inherits from KMainWindow, you have just 2 things to do:
+ Reimplement some virtual functions of [KMainWindow](https://api.kde.org/frameworks/kxmlgui/html/classKMainWindow.html).