EWOL: Hello world

Objectif:

  • Understand basis of ewol
  • Create a simple windows with a label "Hello Word"

debug tools:

I will use for all test a basic template elog for debug logger that redirect logs in Android and IOs

File appl/debug.hpp:

#pragma once
#include <elog/log.hpp>
namespace appl {
int32_t getLogId();
};
#define APPL_BASE(info,data) ELOG_BASE(appl::getLogId(),info,data)
#define APPL_CRITICAL(data) APPL_BASE(1, data)
#define APPL_ERROR(data) APPL_BASE(2, data)
#define APPL_WARNING(data) APPL_BASE(3, data)
#ifdef DEBUG
#define APPL_INFO(data) APPL_BASE(4, data)
#define APPL_DEBUG(data) APPL_BASE(5, data)
#define APPL_VERBOSE(data) APPL_BASE(6, data)
#define APPL_TODO(data) APPL_BASE(4, "TODO : " << data)
#else
#define APPL_INFO(data) do { } while(false)
#define APPL_DEBUG(data) do { } while(false)
#define APPL_VERBOSE(data) do { } while(false)
#define APPL_TODO(data) do { } while(false)
#endif
#define APPL_ASSERT(cond,data) \
do { \
if (!(cond)) { \
APPL_CRITICAL(data); \
assert(!#cond); \
} \
} while (0)

File appl/debug.cpp:

#include <appl/debug.hpp>
int32_t appl::getLogId() {
static int32_t g_val = elog::registerInstance("example");
return g_val;
}

Application Sources:

Application Main:

A generic Ewol application is manage by creating an ewol::context::Application that is the basis of your application.

Due to the fact the ewol library is a multi-platform framework (base on GALE), you will have many contraint like:

  • One application at the same time (note an exception for android wallpaper)
  • One Windows displayable at the time (main point of view of apple developpers)
  • Not a big CPU ...

Then we will create the application:

First things: Some includes:

#include <etk/types.hpp>
#include <ewol/ewol.hpp>
#include <gale/context/commandLine.hpp>

Declare the application:

namespace appl {
class MainApplication : public ewol::context::Application {
public:
void onCreate(ewol::Context& _context) override {
APPL_INFO("==> CREATE ... " PROJECT_NAME " (BEGIN)");
localCreate(_context);
APPL_INFO("==> CREATE ... " PROJECT_NAME " (END)");
}
void onStart(ewol::Context& _context) override {
APPL_INFO("==> START ... " PROJECT_NAME " (BEGIN)");
// nothing to do ...
APPL_INFO("==> START ... " PROJECT_NAME " (END)");
}
void onResume(ewol::Context& _context) override {
APPL_INFO("==> RESUME ... " PROJECT_NAME " (BEGIN)");
// nothing to do ...
APPL_INFO("==> RESUME ... " PROJECT_NAME " (END)");
}
void onPause(ewol::Context& _context) override {
APPL_INFO("==> PAUSE ... " PROJECT_NAME " (BEGIN)");
// nothing to do ...
APPL_INFO("==> PAUSE ... " PROJECT_NAME " (END)");
}
void onStop(ewol::Context& _context) override {
APPL_INFO("==> STOP ... " PROJECT_NAME " (START)");
// nothing to do ...
APPL_INFO("==> STOP ... " PROJECT_NAME " (END)");
}
void onDestroy(ewol::Context& _context) override {
APPL_INFO("==> DESTROY ... " PROJECT_NAME " (START)");
// nothing to do ...
APPL_INFO("==> DESTROY ... " PROJECT_NAME " (END)");
}

The input ewol::Context is the main system context (for ewol).

Note:

It is important to know that the system can create your application multiple times, the basic example of this is the Wallpaper on Android.
What is done:
- When we select the wallpaper it create a new application (to show an example)
- When applying your choice, it create the real one an remove the previous one.

In all program we need to have a main()

To be portable on Android, the "main" in the java might call your main through the Android wrapper.

To simplify compabilities between platform it is recommanded to not add other things in the application main:

int main(int _argc, const char *_argv[]) {
// second possibility
return ewol::run(new appl::MainApplication(), _argc, _argv);
}

Some configuration are needed

In your application you can use many configuration, it is really better to set all your configuration dynamic. With this basic condition will simplify the interface of the library if you would have many different application (never forget the compilator garbage collector is really very efficient).

All of this will be done one time: Then we will do it in:

void onCreate(ewol::Context& _context) override {
APPL_INFO("==> CREATE ... " PROJECT_NAME " (BEGIN)");
localCreate(_context);
APPL_INFO("==> CREATE ... " PROJECT_NAME " (END)");
}

Parse arguments:

All the argument is store in the ewol main application context: just get it...

// parse all the argument of the application
for (int32_t iii=0 ; iii<_context.getCmd().size(); iii++) {
std::string tmpppp = _context.getCmd().get(iii);
if ( tmpppp == "-h"
|| tmpppp == "--help") {
APPL_INFO(" -h/--help display this help" );
exit(0);
}
}

Set basic windosw size (for desktop):

On descktop you can specify a start windows size:

// TODO : Remove this: Move if in the windows properties
_context.setSize(vec2(800, 600));

Select fonts:

This can be a problem when you design an application for some other operating system (OS), They do not have the same default fonts, than you can embended some of them or try to use the system fonts.

We select an order to search the font names and the system basic size.

// eneble the search of the font in the system font path
_context.getFontDefault().setUseExternal(true);
// select font preference of der with a basic application size
_context.getFontDefault().set("FreeSerif;DejaVuSansMono", 19);

Main Windows:

Create the main Windows:

For this point we will create a class that herited form the basic ewol::widget::Windows class:

#pragma once
namespace appl {
class Windows;
using WindowsShared = ememory::SharedPtr<appl::Windows>;
using WindowsWeak = ememory::WeakPtr<appl::Windows>;
class Windows : public ewol::widget::Windows {
protected:
Windows();
void init();
public:
DECLARE_FACTORY(Windows);
};
}

The C macro "DECLARE_FACTORY" create a simple factory function "create" that return the ewol::Object well create.

For some internal reason, we create the object and we call the "init" function after creating the object. When well done we return the shared object created.

See EWOL: Object model to understand why this structure is so complex.

#include <ewol/ewol.hpp>
#include <appl/debug.hpp>
#include <appl/Windows.hpp>
appl::Windows::Windows() {
addObjectType("appl::Windows");
propertyTitle.setDirectCheck(std::string("sample ") + PROJECT_NAME);
}
void appl::Windows::init() {
ewol::widget::Windows::init();
ewol::widget::LabelShared tmpWidget = ewol::widget::Label::create();
if (tmpWidget == nullptr) {
APPL_ERROR("Can not allocate widget ==> display might be in error");
} else {
tmpWidget->propertyValue.set("Hello <font color='blue'>World</font>");
tmpWidget->propertyExpand.set(bvec2(true,true));
// confidure the label as a windows sub-widget
setSubWidget(tmpWidget);
}
}

The init function is virtual and you must call your parent object (or at least the ewol::Object::init)

ewol::widget::Windows::init();

The title is associated on the current windows then it is a simple property of ewol::widget::Windows.

We can change with calling the "setDirectCheck" function instead of "set" function when you are in the constructor (the callback can be unstable when we construct the object)

propertyTitle.setDirectCheck(std::string("sample ") + PROJECT_NAME);

The object ewol::widget::Windows is a simple container. But the reference between Object is ememory::SharedPtr, and this is not accessible in the constructor. This is the reason we use init function.

After we simple create a ewol::widget::Label in the main windows init. We set label and basic properties:

ewol::widget::LabelShared tmpWidget = ewol::widget::Label::create();
if (tmpWidget == nullptr) {
APPL_ERROR("Can not allocate widget ==> display might be in error");
} else {
tmpWidget->propertyValue.set("Hello <font color='blue'>World</font>");
tmpWidget->propertyExpand.set(bvec2(true,true));
// confidure the label as a windows sub-widget
setSubWidget(tmpWidget);
}

When we call the function ewol::Windows::setSubWidget, it use the SharedFromThis() function that create an exception if we are in constructor (when setting the sub-widget parrent)

We can see in this example that the label have some other property like the font color.

The label can have decorated text based on the html generic writing but it is composed with really simple set of balise. I will take a really long time to create a real html parser.

The availlable property is:

  • <br/> : New line
  • <font color="#FF0000\"> ... </font> : change the font color.
  • <center> ... </center> : center the text.
  • <left> ... </left> : Set the text on the left.
  • <right> ... </right> : Set the text on the right.
  • <justify> ... </justify> : Set the text mode in justify.

Note:

The xml parser is a little strict on the case and end node (!! </br> !!),
but it support to:
- Not have a main node.
- replace '"' with ''' to simplify xml writing in C code.

Configure Ewol to have display the windows

At this point we have created the basic windows. But the system does not know it. Then we create windows and set it in the main context main appl::MainApplication::onCreate:

// Create the windows
ewol::widget::WindowsShared basicWindows = appl::Windows::create();
// configure the ewol context to use the new windows
_context.setWindows(basicWindows);

Here we call the create function that is created by the DECLARE_FACTORY macro

Note:

You can use many windows and select the one you want to display, but I do not think it is the best design.

Build declaration:

Ewol commonly use the lutin build system.

Then we need to add a "lutin_YourApplicationName.py", then for this example: lutin_ewol-sample-HelloWord.py

#!/usr/bin/python
import lutin.debug as debug
import lutin.tools as tools
def get_type():
return "BINARY"
def get_sub_type():
return "SAMPLE"
def get_desc():
return "Tutorial 001 : Hello Word"
def get_licence():
return "APACHE-2"
def get_compagny_type():
return "com"
def get_compagny_name():
return "atria-soft"
def get_maintainer():
return ["Mr DUPIN Edouard <yui.heero@gmail.com>"]
def get_version():
return [0,1]
def configure(target, my_module):
my_module.add_src_file([
'appl/Main.cpp',
'appl/debug.cpp',
'appl/Windows.cpp',
])
my_module.add_depend([
'ewol'
])
my_module.add_flag('c++', [
"-DPROJECT_NAME=\"\\\""+my_module.get_name()+"\\\"\"",
"-DAPPL_VERSION=\"\\\"" + tools.version_to_string(get_version()) + "\\\"\""
])
my_module.add_path(".")
return True

Show lutin doc for more information...

Build your application

Go to your workspace folder and launch:

lutin -C -mdebug ewol-sample-HelloWord
# or
lutin -C -mdebug ewol-sample-HelloWord?build

You can now execute your application:

lutin -C -mdebug ewol-sample-HelloWord?run