Hi everyone! I promised Allstar that I'll write this tutorial. In past few days, I was resting a bit, and now, let's get to work, shall we?
So, in this tutorial, I'm going to show you, how to implement homescreen widget in Qt and make necessary QML bindings. I hope you find it useful.
Beginning: What's the problem?So, why this stuff isn't exactly simple? The problem is, there are no up to date APIs for homescreen publishing in Belle. We're going to use N97 Homescreen Publishing API (Symbian s60v5). But don't worry, it's fairy simple
PREPARE YOURSELF!Before we get started, let's get required files. There is QHSWidget example somewhere, but it required copying files to SDK folder and compiling with s60v5 SDK. Fortunately, someone provided precompiled dll files, which we can simply use without doing all this stuff. You can get them
here. There are two DLL files, urel and udeb. You need to choose one, which suits your compilation method. If you're building your app with debug option, choose "udeb". If you're building your app with release option, choose "urel".
Then, you need qhswidget.h from the example. You can get it from linked website, or you can also find it in my GitHub repository for Lightbulb,
here. After adding it to your project and copying .dll into projects folder, let's do something fun, finally
Preparing projectFirst, let's rename dll file. It has to be done to avoid name clashing. Good idea would be to put UID there. Then, open qhswidget.h and change line containing DLL name, like this:
const QString DLLName("[your DLL name]");
Also, you need to update your .pro file, so the DLL would be a part of SIS file. This is, how I did it in Lightbulb.
addFiles.pkg_postrules += "\"C:\\Projekty\\Lightbulb\\HSWidgetPlugin0xE22AC278.dll\" - \"!:\\sys\\bin\\HSWidgetPlugin0xE22AC278.dll\""
DEPLOYMENT += addFiles
Replace "C:\Projekty\Lightbulb\HSWidgetPlugin0xE22AC278.dll" with path to your .dll file. Don't forget about double slashes
This way, our dll file will be put in !:\sys\bin, where ! is destination drive. You could also want to do last part of the original instruction
here if you want to prepare your app for publishing. As I haven't done it yet, I'm not explaining this.
You will also need some libraries, so put this stuff in .pro file:
#that library should be copied to SDK folder
#LIBS += -lhswidgetpublisher
#Libs for appswitcher
LIBS += -lapgrfx -lcone -lws32
#Libs for CFbsBimap
LIBS += -lbitgdi -lfbscli -laknskins -laknskinsrv -leikcore
Not sure, if all of these is even needed, but nevermind.
I hope you added qhswidget.h to project tree in this file. Just add it's name in "headers" section of the project file if you haven't done it yet.
Widget classNow, let's add new C++ class, the same way it was done in previous tutorial. If you don't remember, how it was done, or haven't read it, a simple reminder:
This time, use different name, for example "hswidget". Make sure class is derived from QObject, it's somehow important. If you're ready, let's go to cpp file. Add required includes first:
#include "hswidget.h"
#include <QDebug>
#include <apgtask.h>
#include <eikenv.h>
QDebug itself isn't required, but if you want to add debug stuff here, you would need it. Then, you have to choose proper type of widget, for example:
const QString sw_type = "threerows";
There are five layouts available.
First one, "wideimage". In this widget, image fills the space. You can use it as a template for your own layout, put text there etc. I guess that it can be transparent with svg or png file of your choice, though I've never explored this option.
Second one is "onerow". It contains one row of text and an icon. Really basic layout.
Need more space for your text? What about "tworows"? What is important, you need to set text in every row by yourself. So, they can contain different informations.
"threerows". Holds three rows of text and an icon.
You don't like icons anyway, do you? So, for people like you, there is also "threetextrows" layout, which contains only images.
If you have chosen right layout for your project, we can go further. You need a line that defines widgets name.
const QString sw_name = "Amazing and awesome widget";
Also, you should put your UID in the next line. Though, it can be any unique string. Even "ILoveSlicedBread", or whatever. But UID is the best solution against name clashing.
const QString sw_id = "0xE22AC278"; //can be any unique string
Now, let's specify some rows.
const QString sw_image ("image1");
const QString row1 ("text1");
I did it this way. Name of QString doesn't really matter though. This other stuff does. For more rows, just use "text2" and "text3".
We need to add some functions now.
HSWidget::HSWidget(QObject *parent) :
QObject(parent)
{
widget = QHSWidget::create(sw_type, sw_name, sw_id, this);
connect(widget, SIGNAL(handleEvent(QHSWidget*, QHSEvent)), this, SLOT(handleEvent(QHSWidget*, QHSEvent) ));
connect(widget, SIGNAL(handleItemEvent(QHSWidget*, QString, QHSItemEvent)), this, SLOT(handleItemEvent(QHSWidget*, QString, QHSItemEvent)));
}
This way, we're creating QHSWidget and doing necessary connections, so we could handle all the events.
void HSWidget::registerWidget()
{
widget->RegisterWidget();
}
This function will allow us to register widget directly from QML file. This makes widget available for the user and should be done in first run.
void HSWidget::publishWidget()
{
try {
widget->PublishWidget();
}
catch (...) {
}
}
This function updates widget. Also, I added this "try" stuff, so my app wouldn't crash when widget is not on the screen. I guess I could do it in much better way, but nevermind.
void HSWidget::removeWidget()
{
widget->RemoveWidget();
}
Unregisters the widget, which makes it unavailable, like uninstalling or something.
void HSWidget::handleEvent( QHSWidget* /*aSender*/, QHSEvent aEvent )
{
switch(aEvent)
{
case EActivate:
case EResume:
{
publishWidget();
}
break;
default:
break;
}
}
Let's handle some events, ok? In case widget was activated (EActivate) or resumed from suspension (EResume), it would be automatically updated. How it works? When widget is not on currently active homescreen, simply gets suspended, so we can't update it in this time. Also, activation event means that someone added our widget to homescreen. There is also "ESuspend" which let's you know that widget was suspended. For more details, just refer to "Home Screen Publishing API" documentation.
void HSWidget::handleItemEvent( QHSWidget* /*aSender*/, QString aTemplateItemName,
QHSItemEvent aEvent)
{
if(aTemplateItemName.compare(sw_image)==0)
{
publishWidget();
}
else
{
this->bringToFront();
}
}
This code is required to bring our app to front if someone tapped it's icon in widget. Every element can have different actions. You can also use signals etc. I haven't tried it yet though, so I won't explain it unfortunately.
void HSWidget::bringToFront()
{
TApaTask task( CEikonEnv::Static()->WsSession() );
task.SetWgId(CEikonEnv::Static()->RootWin().Identifier());
task.BringToForeground();
}
And this is a simple function that brings our app to front and automatically founds out, which is our UID. Nice stuff.
Finally, we would like to make a function which would allow us to change displayed data right from QML. This is my, extremely basic approach:
void HSWidget::updateWidget( QString icon, QString nRow1 )
{
widget->SetItem(sw_image, icon);
widget->SetItem(row1, nRow1);
publishWidget();
}
I guess this is quite simple, isn't it?
So... let's get to header file! Just copy it, I guess it doesn't require any explanations.
#ifndef HSWIDGET_H
#define HSWIDGET_H
#include <QObject>
#include "qhswidget.h"
class HSWidget : public QObject
{
Q_OBJECT
public:
explicit HSWidget(QObject *parent = 0);
Q_INVOKABLE void registerWidget();
Q_INVOKABLE void publishWidget();
Q_INVOKABLE void removeWidget();
Q_INVOKABLE void updateWidget( QString icon, nRow1 );
void bringToFront();
public slots:
void handleEvent(QHSWidget*, QHSEvent aEvent );
void handleItemEvent(QHSWidget*, QString aTemplateItemName,
QHSItemEvent aEvent);
private:
QHSWidget* widget;
};
#endif // HSWIDGET_H
Super simple stuff
QML bindings!Now, let's go back to main.cpp. Remember, what we did previously? We're going to do the same again! First, include header file of our new class.
#include "hswidget.h"
Then, register it as QML object:
qmlRegisterType<hswidget>("someVeryAwesomeStuff", 1, 0, "HSWidget");
And some explanations, if you haven't read previous tutorial:
Feel free to replace "someVeryAwesomeStuff" with anything you want. You will use this name later in QML file. Those numbers is version. "GlobalNote" is the name of our new QML object.
Now, just create an object in QML of "HSWidget" type, for example in main.qml, and you can use it, just like that:
hswidget.registerWidget()
hswidget.publishWidget()
hswidget.removeWidget()
hswidget.updateWidget(iconPath, "I really love sliced bread")
Thanks for reading, I hope you found it useful.
Sources: