Coding Conventions
Contents
- General
- Coding Conventions for Qt-based apps and middleware
- Qt Coding Conventions
- QML coding conventions
- Additional Sailfish Qt coding conventions
- Additional Sailfish QML coding conventions
- Omit “;” behind JavaScript function lines
- Put spaces between conditional statements, code, and curly braces
- Write short one line functions in one line
- Omit redundant property assignments
- Don’t define id for an element if id is not used
- Use braces in one-line conditionals
- Group properties together when grouped property form is available: font {}, anchors {}, border {}, etc.
- Avoid unnecessary negatives
- Keep similar properties close to each other
- Mark private API private
- Testing Conventions
- Sailfish Project and deployment conventions
General
As a rule of thumb follow the coding conventions of the open source project you want to contribute to. Most big projects like GStreamer, oFono and Telepathy have their own coding conventions that should be respected when contributing those frameworks.
Coding Conventions for Qt-based apps and middleware
When developing features for Sailfish applications, follow Qt C++ (which follows standard C++) and QML coding conventions. Sailfish has traditionally been relatively flexible on coding style issues, leaving the decision on the enforcement of conventions to the individual teams and developers. Coding convention rules stir up a lot of opinions, but at the end are not so important as long as developers write clear, concise, readable, testable and in general high quality code they can be proud of.
Qt Coding Conventions
QML coding conventions
Additional Sailfish Qt coding conventions
Sources: Sailfish OS Middleware conventions
Prefer Qt5 Signal/Slot Connection Syntax
Write
QObject::connect(&sender, &Sender::signalName, &receiver, &Receiver::slotName);
instead of
QObject::connect(&sender, SIGNAL(signalName()), &receiver, SLOT(slotName()));
Rationale: ensures that the compiler can detect parameter mismatch issues, and provides a minor performance benefit at runtime.
Prefer C++11 Ranged-For
Write
for (const QString &str : someStringList) { ... }
or
for (const auto &str : someStringList) { ... }
instead of
for (int i = 0; i < someStringList.size(); ++i) {
const QString &str(someStringList[i]);
...
}
Rationale: in general we should prefer C++11 features where supported by the platform compiler.
Use CamelCase Namespace Names
As for class names, prefer camel-cased namespace names.
Write
namespace AppNamespace { /* ... */ }
instead of
namespace appnamespace { /* ... */ }
Rationale: provides consistency with class naming as specified by the Qt Coding Style.
Additional Sailfish QML coding conventions
Sources: Qt Quick examples, Qt Components conventions
Omit “;” behind JavaScript function lines
Write
x = a + b
instead of
x = a + b;
Rationale: less code, basically either style way would be fine, but one was chosen.
Put spaces between conditional statements, code, and curly braces
Write
if (a) {
instead of
if(a){
or
if ( a ) {
Rationale: criteria is mostly esthetic and thus subject to argumentation. Overall adding spaces improves visual grouping (but only if not overused) and helps legibility.
Write short one line functions in one line
It is ok to write
onClicked: if (a) doSomething()
instead of
onClicked: {
if (a) {
doSomething()
}
}
Rationale: less lines needed, both forms equally readable.
Omit redundant property assignments
Write
property bool propertyName
property int propertyName
instead of
property bool propertyName: false
property int propertyName: 0
Rationale: less code, QML developer should already know the default values.
Don’t define id for an element if id is not used
Write
Image {}
instead of
Image {
id: background
}
if you don’t need the id.
Rationale: potentially less code. Like with properties only define something if you really need that something.
Use braces in one-line conditionals
Write
if (a) {
doSomething()
}
instead of
if (a)
doSomething()
Rationale: Agreed with the team. Goes against Qt Coding convention, but is generally considered safer.
Group properties together when grouped property form is available: font {}, anchors {}, border {}, etc.
Write
anchors {
horizontalCenter: parent.horizontalCenter
horizontalCenterOffset: Theme.paddingSmall
}
instead of
anchors.horizontalCenter: parent.horizontalCenter
anchors.horizontalCenterOffset: Theme.paddingSmall
Rationale: less code, grouping communicates relation.
Avoid unnecessary negatives
It is more readable to state
if (a) .. else ..
than
if (not a) .. else ..
and
#ifdef USE_SQL
..
#endif
instead of
#ifdef NO_SQL
..
#endif
Rationale: negatives require more mental load to decipher. Avoids double negatives like !NO_SOMETHING
.
Keep similar properties close to each other
There is no one clear rule with this, grouping is often contextual: base element properties versus specialized properties, visual versus non-visual properties.
Rationale: proximity implies relation, code is easier to read the less you need to jump between lines when interested in particular behavior/feature.
Some rough general rules to consider :
id declaration
property declarations
signal declarations
property bindings, functions and signal handlers
- Same type items grouped together, javascript functions and signals handlers close to each other.
- Attached properties after bindings.
- The most important details that define the component first.
For example if an interactive item sets some text label property, the bindings define
the item more than a javascript expression blob.
child items, visual and non-visual grouped together
states
transitions
Mark private API private
You can’t stop developers from reading your code, and if they can’t tell public symbols from private symbols their code will end up depending on something that changes in later versions. QML doesn’t support for private members, however there are a couple of conventions useful for keeping symbols out of the way. The simplest and usually fastest is to prepend an underscore:
Item {
property int iAmPublic
property int _iAmPrivate
Text {
text: "Hello"
}
}
Testing Conventions
Sailfish app development follows Qt conventions, and autotesting is no different.
There are couple of common anti-patterns highlighted below that you should avoid.
Prefer compare() over verify() when possible
Write
compare(value, 10)
instead of
verify(value == 10)
Rationale: Compare produces more descriptive error messages and does more type checking.
Use SignalSpy instead of wait()
Write
SignalSpy { id: valueSpy; target: object; signalName: "onValueChanged" }
function test_case() {
signalSpy.clear()
signalSpy.wait()
compare(object.value, newValue)
}
instead of
function test_case() {
wait(100)
compare(object.value, newValue)
}
Rationale: Too short arbitrary wait times may fail, at worst cases irregularly. Too long wait times unnecessarily postpone the completion of the autotest.
Sailfish Project and deployment conventions
Application project hierarchy
Project hierarchy of the Sailfish OS system applications.
Path | Explanation |
---|---|
sailfish-appname | Use dashes “-“ where the space would normally go |
sailfish-appname/sailfish-appname.pro | Match the pro file name with the folder name |
sailfish-appname/appname.cpp | C++ application stub that loads the top-level QML document, not named as main.cpp to avoid having dozen main.cpp files in the search index |
sailfish-appname/appname.qml | Top-level folder contains only one lower-cased qml file |
sailfish-appname/pages | Folder containing only page components, a convention for easier autotesting |
sailfish-appname/pages/folder/*.qml | Subcomponents used by the pages |
sailfish-appname/cover | Folder containing application’s active cover UI |
sailfish-appname/tests/auto/tst_*.qml | Autotests for the application |
sailfish-appname/tests/benchmarks | Benchmarks |
Application deployment folders
This applies to Sailfish OS system applications.
Path | Explanation |
---|---|
/usr/bin/sailfish-appname | Binary that gets executed when the application is opened |
/usr/share/sailfish-appname | QML documents and other resources, don’t bake QML components into binary to allow updating individual components without re-compilation of the binary |
/usr/share/applications/sailfish-appname.desktop | Desktop file |
Module project hierarchy
Path | Explanation |
---|---|
sailfish-modulename | Use dashes “-“ where the space would normally go |
sailfish-modulename/sailfish-modulename.pro | Match the pro file name with the folder name |
sailfish-modulename/sailfish-modulename.qmlproject | Optional |
sailfish-modulename/modulename | QML components and C++ elements exposed by the module |
sailfish-modulename/modulename/modulename.pro | Compilation and deployment rules for the module |
sailfish-modulename/modulename/qmldir | QML module definition file |
sailfish-modulename/modulename/*.qml | Public QML components mentioned in qmldir |
sailfish-modulename/modulename/private | Internal, non-public components |
sailfish-modulename/modulename/src | C++ code goes here |
sailfish-modulename/modulename/src/src.pri | List C++ header and source files here |
sailfish-modulename/modulename/src/*.h/cpp | C++ classes exposed to QML |
sailfish-modulename/modulename/src/modulename.cpp | Don’t use plugin.cpp to avoid having dozen plugin.cpp files in the search index |
sailfish-modulename/applications | Example applications showcasing components provided by the module |
sailfish-modulename/tests/auto/tst_*.qml | Autotests for the module |
sailfish-modulename/tests/benchmarks | Benchmarks for the module |
sailfish-modulename/doc | Documentation |
Module deployment folders
Path | Explanation |
---|---|
/usr/lib/qt5/qml/Sailfish/Modulename | Use Java-style name spacing |
/usr/lib/qt5/qml/Sailfish/Modulename/qmldir | QML module definition file |
/usr/lib/qt5/qml/Sailfish/Modulename/*.qml | Public QML components |
/usr/lib/qt5/qml/Sailfish/Modulename/private | Internal, non-public components |
/usr/lib/qt5/qml/Sailfish/Modulename/libmodulenameplugin.so | Binary plugin exposing C++ elements to QML |