CuteHMI
|
Extensions can act as QML extensions, but they can be also used like standard libraries.
Directory structure of extensions follows standard QML extensions scheme. According to QML identified module rules path of an extension relative to extensions
directory defines module identifier, which is also extension name denoted as BaseName.MajorVersion. Typically BaseName consists of dot-separated VendorName.ExtensionName parts, but this is a convention rather than a requirement. Extension name must only contain base name and major version suffix. Base name parts have no special meaning.
As a reference we can take Templates.CppSkeleton.0
extension. Fully qualified name consists of base name (Templates.CppSkeleton
) and major version suffix (0
). Base name consists of two parts: vendor name (Templates
) and extension name (CppSkeleton
). It is allowed to omit extension name for a single extension from a specific vendor. It is also allowed to provide dot-separated extension name (e.g. Examples.CppSkeleton
); or do the same with vendor (e.g. org.nokia
).
Extension directories follow camel-case (PascalCase) rules in order to remain consistent with QML extension naming convention used by Qt. On the other hand following this convention within C++ would be inconvenient in many places, so compromises have been made. Sometimes semantic members of extension directory name are lowercased. To express such intention, a pattern VendorName.ExtensionName.MajorVersion will be transformed according to the intention (i.e. vendorname represents lowercased VendorName).
Typically each extension will have directory structure similar to this one.
include
- public header files (available to other modules).templates
.cppskeleton
.internal
- some headers need to be publicly available, but they are implementation detail. This directory is for such files.src
- private header files and implementation.templates
.cppskeleton
.internal
- corresponds with internal
subdirectory inside include
directory.tests
- Tests.doc
- Documentation related files.dev
- Development notes.To keep things in order and avoid naming conflicts, C++ namespaces should reflect above directory structure within include
and src
directories.
CuteHMI uses Qbs as build system, therefore each extension starts its life in dedicated Qbs file. Extensions tend to produce many by-products (like tests for example), so commonly they are defined within Project Qbs item.
Project item allows one to put inside a collection of products or reference subprojects. What products are placed inside project item depends on type of extension. CuteHMI provides customized Product items and a bunch of helper Qbs modules for fast and convenient extension setup.
Every CuteHMI product item requires one to provide some basic metadata, such as vendor name, description or domain. This information can be extracted by cutehmi.metadata
Qbs module and exported to JSON or header file.
Extensions can be divided into two major categories:
Pure QML extensions are easy to deal with. In their roots they only need qmldir
file to work. Such extensions should be defined with cutehmi.Extension
product item, which takes care about things like tagging or intalling files to correct destination directories.
If you would like to provide a QML extension, then you should refer to documentation on qmldir files. You can either create qmldir by hand or use cutehmi.qmldir
Qbs module dependency to generate it automatically.
You can also use cutehmi.qmltyperegistrar
Qbs module dependency to generate plugins.qmltypes
file (though it is rather needed by C++ QML plugins).
Extensions that use C++ code are more sophisticated than QML extensions. In order to create C++ extension one should use cutehmi.CppExtension
product item. It hides unpleasant C++ aspects, so extension creator just has to provide the metadata and add some C++ or QML files (cutehmi.CppExtension
extends cutehmi.Extension
). To boost the process of creating C++ extension even further one may use cutehmi.skeleton.cpp
module by adding it as product dependency.
This module will create skeletal header and source files commonly found in all C++ extensions. In fact it will create minimal operational C++ extension, which can be then provided with some custom code to do something useful.
C++ extensions can act as libraries (to be linked against), QML plugins or they can be a mix of both.
To create an extension that behaves like a library (i.e. other extensions can be linked against it) you should refer to Qt documentation on Creating Shared Libraries. Note that mentioned MYSHAREDLIB_EXPORT
macros are defined within platform.hpp
generated by cutehmi.skeleton.cpp
module, according to the pattern VENDORNAME_EXTENSIONNAME_API
.
To make an extension that acts as a QML extension, but uses C++ code, you may refer to Creating C++ Plugins for QML. You can ignore the creation of .pro
files and simply add files to Qbs project. Key concepts to focus on is a class that extends QQmlExtensionPlugin
, qmlRegisterType
function template that exposes QObject
derived classes and entries in qmldir
file. You may also check out the tutorial Writing QML Extensions with C++.
C++ extensions, which act as QML plugins must provide QML plugin class. By convention in CuteHMI this class is called QMLPlugin
and must be inside extension's internal
namespace. Sticking to this convention allows one to rely on defaults, when using other Qbs helper modules, such as cutehmi.qmldir
for example.
All CuteHMI naming conventions have been defined in cutehmi.conventions
Qbs module, which can be referenced by other Qbs modules. Among them there is mentioned QML plugin class name (qmlPluginClassName
property). All properties can be printed to the console by setting debugOutput
property to true
. From the command line this can be done as follows.
To print naming conventions for specific extension replace modules
with products.Vendor.Extension.MajorVersion.cutehmi.conventions.debugOutput:true
.
As stated - sticking to naming conventions is not hard requirement, but it helps avoid setting property values of various Qbs modules explicitly.
Following extensions can be used as a reference for creating custom extensions:
Often you may want to keep your extension in separate repository. A simple trick, which makes life easier is to create .gitignore
file in extensions
directory and make Git ignore the file itself along with your extension(s).
This way you can clone your extension to extensions
subdirectory and your reposiotry won't interfere with CuteHMI repository.