HybridOS Specification 01-C
Topic: HybridOS Foundation Class Library
Author: Vincent Wei
Category: App Framework
Date: November 2018
Status: Proposal
Copyright Notice
Copyright (C) 2018, 2019 FMSoft Technologies
All Rights Reserved.
Introduction
HFCL (HybridOS Foundation Class Library) is a C++ class library. It is derived from mGNGUX, a C++ library for MiniGUI. mGNGUX had been used as the foundation of two MMI (man-machine interface) solutions for feature phones.
mGNGUX introduced a new MMI framework for feature phone, and borrowed some concepts from other operating system (especially Android). mGNGUX also provided a new set of controls/widgets in C++ classes. These classes construct the view hierarchy of mGNGUX app framework.
The most important feature of mGNGUX is it introduced a new way to define the UI elements, including resource, views, and menus. And we can easily define the maps of views to C++ class member variables. Indeed, the UI definition files are C++ source files, but we wrote them in pre-defined C++ macros.
However, mGNGUX still uses the traditional widget-based programming mechanism, which does not provide a good solution for decoupling relationship among data, interaction, and rendering.
Therefore, as described in HybridOS Architecture, we use HybridOS View Markup Language and hiWebKit to support HVML and render the activities.
HFCL implements the view types for HVML and help the developer to create and manage resource, activities, and services for HybrdiOS apps. The key features of HFCL are follow:
-
It acts as the framework for the C++ edition of your HybridOS app.
-
It manages the resource (images, fonts, l10n text, CSS definitions, and others assets) for your app. On device, these assets are loaded from disk or embedded directly into the program image. HFCL provides a universal way to manage the assets.
-
It manages the activites and the services of your app. You can launch an activity and pass an intent to it.
-
It provides a complete set of views which is conformed HVML specification.
-
It provides the standard DOM interfaces for your app to manipulate the DOM tree, including the nodes, the content, the style, and the attributes of them.
-
It is the foundation of Hybrid App Engine.
Resource Assets
We treat HVML files, CSS files, JavaScript files, images, and l10n text are all resource assets of a HybridOS apps.
Unlike web browser, for an app running in a device or client, the assets generally stored locally, either on the filesystem or embedded in the app program image.
HybridOS provides an easy and magic way for developer to define the assets and bind the assets with your C++ code.
Specifically, HFCL uses pre-defined C++ macros to define the resource assets. For our first sample, the main asset file will look like:
begin_app(firstSample)
begin_assets()
image(defAvatar, "file:///assets/image/def-avatar.png")
css(default, "file:///assets/css/default.css"
activity(userList, "file:///assets/hvml/userlist.hvml")
activity(userInfo, "file:///assets/hvml/userInfo.hvml")
l10n(en, "file:///assets/message/en.mo")
l10n(zh_CN, "file:///assets/message/zh_CN.mo")
l10n(zh_TW, "file:///assets/message/zh_TW.mo")
end_assets
end_app(en, userList)
L10N of Text
For the L10N (localization) text, HFCL provides two ways:
- Using .mo files of GNU gettext.
- Using identifiers instead of text as GNU
gettext
does.
The interpreter hfclify
can generate the C++ L10N translation
source files for the app according to the L10N translation file specified
in the assets section of the app.
The identifiers referred as STRID_TITLE
or STRID_COPYING
will be
defined as C++ macros, and the l10n text table can be zipped by
using zlib
.
Assets Management
Not like HVML apps, the link of an asset (e.g., an image file) defined in
a HFCL app is handled as a reference key first. We can integrate the assets
data into the executable image of your app. We call them as in-core resource.
If HFCL could not load a specific asset from the in-core resource, it will
try to load if from the URL specified in the begin_assets
section.
In this way, HFCL will provide a maximal flexibility for the app developers.
Activity and Intent
Customized View
Service
Components of HFCL
The components of HFCL are classified into the folllowing categories:
-
Activity management
- Resource management
- Define customized HVML view types
- Activity and intent
- Animation and transition
-
Service management
- WebSocket responder
- HTTP responder
- MQTT service provider
- ...
-
System classes
- hiBus event and task management
- SQLite queries
- JSON data
- Async task management
The below words are deprecated...
For our user list activity, the asset code will look like:
begin_activity(userList, ActivityUserList)
def_name(thePanel)
def_name(theHeader)
def_name(theList)
def_name(theFooter)
begin_view_template(UserItemView, ItemView, my_style_sheet(normalItem))
begin_view(HiddenView, NULL))
set(Name, "id")
end_view
begin_view(ImageView, my_style_sheet(userAvatar)))
set(Name, "avatar")
end_view
begin_view(TextView, my_style_sheet(userName)))
set(Name, "name")
end_view
end_view_template
begin_view(PanelView, my_style_sheet(panel))
map(my(thePanel))
begin_view(TextView, my_style_sheet(panelHeader))
map(my(theHeader))
set(Content, STRID_TITLE)
end_view
begin_view(ListView, my_style_sheet(userList))
map(my(theList))
set(ItemTemplate, my_template(UserItemView))
set(Name, "userItem")
end_view
begin_view(TextView, my_style_sheet(panelFooter))
map(my(theFooter))
set(Content, STRID_COPYING)
end_view
end_view
end_activity
begin_activity(userInfo)
begin_view(PanelView, ...)
...
end_view
end_activity
This file (assume named `firstsample.res.inc") will be pre-compiled by your C++ compiler in a magic way:
-
In a C++ header file, include the resource file in the following way to generate the identifiers and names of the resource:
#include <hfcl/resource/resdefines.head.h> #include "firstsample.res.inc" #include <hfcl/resource/resundefines.h> #include <hfcl/resource/resdefines.name.h> #include "firstsample.res.inc" #include <hfcl/resource/resundefines.h>
-
In a C++ source file, include the resource file in the following way to generate the source code and the initialization code:
#include <hfcl/resource/resdefines.source.h> #include "firstsample.res.inc" #include <hfcl/resource/resundefines.h> #include <hfcl/resource/resdefines.init.h> #include "firstsample.res.inc" #include <hfcl/resource/resundefines.h>
By using the method described above, we do not need a utility tool to interpret the resource file and generate the souce code, the C++ compiler will do this for us.
The another advantage is that we seperate the resource data with the app logical code in a good way.
The last advantage is that the resource file can be generated from the HVML tags. Thus, if you have to use C++ to write your device app because the poor hardware performance, you can easily keep up your C++ code with the JavaScript code.
If you have some expirience with HTML and CSS, you will easily get the the define the style sheet of views with the same manner of CSS. For example, the style sheet of the user name corresponds to the following CSS definition:
.userAvatar {
display: inline-block;
position: relative;
margin: 0px 0px 0px 0px;
padding: 0px 0px 0px 0px;
height: 20px;
width: 20px;
border-radius: 5px 5px 5px 5px;
}
For performance reason, we do not use the direct literal CSS definition in HFCL. We use the macros to define every style element in C++ way instead:
begin_css(userAvatar, ".avatar")
style(Display, PV_INLINE_BLOCK)
style(Position, PV_RELATIVE)
style(Margin, PV_LENGTH_PX, 0)
style(Padding, PV_LENGTH_PX, 0)
style(Height, PV_LENGTH_PX, 20.0f)
style(Width, PV_PERCENTAGE, 100.0f)
style(BorderRadius, PV_LENGTH_PX, 5.0f)
end_css
In the definition of a view which uses a style sheet, we passed the class of the way:
begin_view_ex(hvimage, "avatar", ...))
...
end_view
Translate HTML/HVML and CSS into C++ Code
It will be a trouble if we write the style sheets and views by hand in the way above. So we introduce a interpreting tool which can translate the HVML tags and CSS into the HFCL resource source files.
This tool is called hfclify
, and it will be written in Python.
If you want to change the style sheet of one view on the fly, you can call one of the following APIs of HFCL:
view->addStyleSheet (HFCL_STYLE_SET_NAME (firstSample, activeItem));
view->useStyleSheet (HFCL_STYLE_SET_NAME (firstSample, focusItem));
view->setStyleProperty (HCFL_STYLE_COLOR, Color::WHITE);
Note that the resource source file can not reflect all details in your
HVML tags. You need to write the interaction code in your C++ source
file. For example, in HVML tags, you can define the iteration of
an item from a template view by using the property hbd-iterate-by
,
but in HFCL, we can not do this for you. You need to initialize the
list view in your implementation classes manually.
When you re-generate the resource source file from HVML tags, because the real implemenations are seperated from the resource source files, the translator will not override your own code.
In HFCL, a view template is defined as a derived class of one standard view, as shown in the sample:
begin_view_template(UserItemView, ItemView, my_style_sheet(normalItem))
...
end_view_template
The UserItemView
will be defined by the macro as a new view class
in the resource header file, just a simply subclass of ItemView:
class UserItemView : public ItemView {
public:
UserItemView (View* parent) : ItemView (parent) { }
UserItemView (View* parent, StyleSheet* style_sheet) : ItemView (parent, style_sheet) { }
virtual ~UserItemView () {};
}
The UserItemView
will be defined by the macro as an interface class
in the resource header file. You should define the implemetation class
by yourself (in separated files):
class UserItemViewIf : public ItemView {
public:
UserItemViewIf (View* parent) : ItemView (parent) { }
UserItemViewIf (View* parent, StyleSheet* ss) : ItemView (parent, ss) { }
virtual View* getChildByName (const char* name) = 0;
virtual ~UserItemViewIf () {};
}
class UserItemViewIm : public UserItemViewIf {
public:
UserItemViewIm (View* parent);
UserItemViewIm (View* parent, StyleSheet* ss);
virtual View* getChildByName (const char* name);
...
private:
...
}
...The above words are deprecated.