Have you ever tried to create both Debian and RPM packages using OBS? Well, a little bit different from the package generation locally, but not that much ;)
Before continuing, you should prepare yourself with basic Debian and RPM packaging knowledge.
First, make a tar-ball for your source code, without any packaging information inside. Name it obs-sample.tar.gz.
Then for Debian packaging, you need the following files.
obs-sample_1.0.dsc: indicating you want to create a Debian package; the Files section is not needed, but the Build-Depends section is necessary.
debian.rules: the rules file for Debianization.
debian.control: the control file for Debianization.
debian.tar.gz: all the other files inside the debian folder.
For RPM packaging, you only need the .spec file as expected.
Finally, here goes the simple sample I created. Happy hacking!
7.12.10
The Beauty of Qt 3: Implicit Sharing
To maximize resource usage and minimize copying, Qt uses implicit data sharing in many classes, so that the data is copied only when a function writes to it. This trick is also referred to as flyweight pattern sometimes.
Now let's take QByteArray as an example to see how it's implemented. It uses a private struct of Data to track the shared data:
Here, we use both the pointer of data and array just because the data might actually be stored in another object. When an object is copied, e.g. through the assignment operator, it only copies the pointer to the shared data:
On the other hand, if it's to be changed by e.g. the resize() function, it copies the data if any other object also shares it:
With this in mind, let's see how to use QSharedData and QSharedDataPointer to implement our own implicit shared data objects.
Quite simple, right ;) Then just proceed to implement explicit shared data objects using QExplicitlySharedDataPointer yourself.
Now let's take QByteArray as an example to see how it's implemented. It uses a private struct of Data to track the shared data:
struct Data {
QBasicAtomicInt ref; // reference count, the operation on it is atomic
int alloc; // allocated space for the data
int size; // actual size of the data, not counting the ending '\0' added by QByteArray
char *data; // point to the data
char array[1]; // where the data is stored, and the data always end with '\0'
};
Here, we use both the pointer of data and array just because the data might actually be stored in another object. When an object is copied, e.g. through the assignment operator, it only copies the pointer to the shared data:
QByteArray &QByteArray::operator=(const QByteArray & other)
{
// increase the reference count of the shared data it's supposed to be used
other.d->ref.ref();
// decrease the reference count of the share data currently used, and free it if no one else is using
if (!d->ref.deref())
qFree(d);
// point to the shared data
d = other.d;
return *this;
}
On the other hand, if it's to be changed by e.g. the resize() function, it copies the data if any other object also shares it:
void QByteArray::resize(int size)
{
if (size <= 0) {
// if the target is no bigger than 0, points to an empty data block
Data *x = &shared_empty;
x->ref.ref();
if (!d->ref.deref())
qFree(d);
d = x;
} else if (d == &shared_null) {
// if currently a null array, just create a new data block
Data *x = static_cast<data *>(qMalloc(sizeof(Data)+size));
Q_CHECK_PTR(x);
x->ref = 1;
x->alloc = x->size = size;
x->data = x->array;
x->array[size] = '\0';
(void) d->ref.deref();
d = x;
} else {
// if any other object uses this data block, or the current memory is too small or too big
// reallocate space, and copy the data for it
// note that this operation might consume some time if the data is huge
if (d->ref != 1 || size > d->alloc || (size < d->size && size < d->alloc >> 1))
realloc(qAllocMore(size, sizeof(Data)));
if (d->alloc >= size) {
d->size = size;
if (d->data == d->array) {
d->array[size] = '\0';
}
}
}
}
With this in mind, let's see how to use QSharedData and QSharedDataPointer to implement our own implicit shared data objects.
// first implement your data object inheriting from QSharedData, which provides the reference count
class SharedData: public QSharedData
{
public:
SharedData()
: QSharedData()
, var(0)
{}
SharedData(const SharedData &other)
: QSharedData(other)
, var(other.var)
{}
int var;
};
// then the data owner
class DataOwner
{
public:
DataOwner()
: d(new SharedData)
{}
DataOwner(int var)
: d(new SharedData)
{
// for write access, the -> operator will automatically copy the shared data if needed
d->var = var;
}
private:
// this template class hides all the details for implicit sharing
// therefore, no need to provide copy constructor or assignment operator
QSharedDataPointer<SharedData> d;
};
Quite simple, right ;) Then just proceed to implement explicit shared data objects using QExplicitlySharedDataPointer yourself.
Labels:
qt
1.12.10
The Beauty of Qt 2: Meta Object
Besides the D-pointers, another interesting thing in Qt is the Q_OBJECT macro. It provides the access to meta objects, which enables you to enjoy more features of a QObject, e.g. signals and slots. The meta object provides information about class name, properties and methods of a QObject, which is also known as reflection.
Using QMetaObject, you can print the information of the class name, etc.:
As none of these information is supported in pure C++, Qt uses the Meta Object Compiler (moc) to do all the tricks. It reads each header file, and generates a C++ source file (moc_*.cpp) containing the code for meta-object, if it finds a Q_OBJECT macro used in any class declaration. With the code generation approach, Qt not only gains the flexibility as in e.g. Java, but also keeps the performance and scalability as in C++.
Suppose we have the following simple class:
The following code / information will be generated by moc, and can be retrieved through pointers defined in the anonymous struct of QMetaObject::d:
The above information, as well as the information for its base class, is stored as the static meta object for this class:
In this way, if you want to do type cast for QObject, instead of using the somewhat expensive operator of dynamic_cast, we can use qobject_cast. It exploits the benefits of the meta object system, thus avoiding the runtime type cast:
Here, the trick is that the QMetaObject of the target type checks if the object inherits from it:
Also, moc will generate some code for each signal. Whenever a signal is emitted, this function will be called internally:
In the end, the slots are invoked by the generated qt_metacall function:
Using QMetaObject, you can print the information of the class name, etc.:
QObject obj;
const QMetaObject *metaObj = obj.metaObject();
qDebug() << "class name: " << metaObj->className();
qDebug() << "class info count: " << metaObj->classInfoCount();
qDebug() << "methods: ";
// starting from QMetaObject::methodOffset() so it won't display the methods inherited
for (int i = metaObj->methodOffset(); i < metaObj->methodCount(); ++i)
qDebug() << metaObj->method(i).methodType() << " " << metaObj->method(i).signature();
As none of these information is supported in pure C++, Qt uses the Meta Object Compiler (moc) to do all the tricks. It reads each header file, and generates a C++ source file (moc_*.cpp) containing the code for meta-object, if it finds a Q_OBJECT macro used in any class declaration. With the code generation approach, Qt not only gains the flexibility as in e.g. Java, but also keeps the performance and scalability as in C++.
Suppose we have the following simple class:
class MyObject : public QObject
{
Q_OBJECT
public:
explicit MyObject(QObject *parent = 0);
void myFunc();
public slots:
void mySlot(int myParam);
signals:
void mySignal(int myParam);
};
The following code / information will be generated by moc, and can be retrieved through pointers defined in the anonymous struct of QMetaObject::d:
// pointed by QMetaObject::d.data, the beginning stored "as" a QMetaObjectPrivate struct
static const uint qt_meta_data_MyObject[] = {
5, // revision, the internals have been changed several times
0, // classname, offset of qt_meta_stringdata_MyObject
// the following are defined as (number, index) pair
0, 0, // classinfo
2, 14, // we have two methods, starting at index 14 (i.e. the signal)
0, 0, // properties
0, 0, // enums/sets
0, 0, // constructors
0, // flags
1, // signal counts
// for signals slots, and properties, the signatures and parameters are offset of qt_meta_stringdata_MyObject
// signals: signature, parameters, type, tag, flags
18, 10, 9, 9, 0x05,
// slots: signature, parameters, type, tag, flags
32, 10, 9, 9, 0x0a,
0 // eod
};
// pointed by QMetaObject::d.stringdata
static const char qt_meta_stringdata_MyObject[] = {
"MyObject\0\0myParam\0mySignal(int)\0"
"mySlot(int)\0"
};
The above information, as well as the information for its base class, is stored as the static meta object for this class:
const QMetaObject MyObject::staticMetaObject = {
{ &QObject::staticMetaObject, // pointer to its base class, stored at QMetaObject::d.superdata
qt_meta_stringdata_MyObject, qt_meta_data_MyObject, 0 }
};
In this way, if you want to do type cast for QObject, instead of using the somewhat expensive operator of dynamic_cast, we can use qobject_cast. It exploits the benefits of the meta object system, thus avoiding the runtime type cast:
template <class T> inline T qobject_cast(QObject *object)
{
#if !defined(QT_NO_QOBJECT_CHECK)
reinterpret_cast(0)->qt_check_for_QOBJECT_macro(*reinterpret_cast(object));
#endif
return static_cast(reinterpret_cast(0)->staticMetaObject.cast(object));
}
Here, the trick is that the QMetaObject of the target type checks if the object inherits from it:
const QObject *QMetaObject::cast(const QObject *obj) const
{
if (obj) {
const QMetaObject *m = obj->metaObject();
do {
if (m == this)
return obj;
} while ((m = m->d.superdata));
}
return 0;
}
Also, moc will generate some code for each signal. Whenever a signal is emitted, this function will be called internally:
void MyObject::mySignal(int _t1)
{
void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
// it checks all the connected slots, and call each of them based on the connection type
QMetaObject::activate(this, &staticMetaObject, 0, _a);
}
In the end, the slots are invoked by the generated qt_metacall function:
int MyObject::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
{
// if this function is called by the super class, just return
_id = QObject::qt_metacall(_c, _id, _a);
if (_id < 0)
return _id;
// call the function based on its ID
if (_c == QMetaObject::InvokeMetaMethod) {
switch (_id) {
case 0: mySignal((*reinterpret_cast< int(*)>(_a[1]))); break;
case 1: mySlot((*reinterpret_cast< int(*)>(_a[1]))); break;
default: ;
}
// remove the IDs "consumed" by this class so that in its subclass the ID always starts with 0, and the return value of -1 means already consumed
_id -= 2;
}
return _id;
}
Labels:
qt
25.11.10
The Beauty of Qt 1: D-Pointer / Private Implementation
I believe many of you who just start reading the source code of Qt will have the question: why Qt uses those private classes? What's the benefit of such a design pattern? Well, the most significant reason is to keep the binary compatibility.
Then what is binary compatibility? The article from KDE TechBase defines it like this:
Without the usage of D-pointers, we may have a class defined as below:
Obviously, the private member of myVar here is a big enemy of binary compatibility. There fore, we refactor the code into the following:
Here, we use a pointer d_ptr to refer to the private implementation, and the macro of Q_DECLARE_PRIVATE is employed to define some helper functions and friend class:
Then we define the private class as below:
Here, the q_ptr pointer points the public interface, and the Q_DECLARE_PUBLIC macro is used to define some helper functions:
And we can further use the following two macros to ease the access:
That's all about the usage of D-pointers and private implementations in Qt. Let me use a simple sample to end this post ;)
The Chinese translation is available at: http://blog.csdn.net/zhu_xz/archive/2010/11/25/6035861.aspx
Then what is binary compatibility? The article from KDE TechBase defines it like this:
A library is binary compatible, if a program linked dynamically to a former version of the library continues running with newer versions of the library without the need to recompile.With this definition in mind, the benefit is easy to see: without it, whenever a library is updated, all the applications rely on it should be recompiled to work. Definitely, it's totally unacceptable for any widely used libraries like Qt. More information on binary compatibility itself can be found on that article from KDE TechBase, and here I only want to share with you how it's used.
Without the usage of D-pointers, we may have a class defined as below:
class MyClass
{
public:
MyClass();
~MyClass();
private:
int myVar;
};
Obviously, the private member of myVar here is a big enemy of binary compatibility. There fore, we refactor the code into the following:
class MyClassPrivate;
class MyClass
{
public:
MyClass();
~MyClass();
private:
MyClassPrivate * const d_ptr;
Q_DECLARE_PRIVATE(MyClass);
};
Here, we use a pointer d_ptr to refer to the private implementation, and the macro of Q_DECLARE_PRIVATE is employed to define some helper functions and friend class:
#define Q_DECLARE_PRIVATE(Class) \
inline Class##Private* d_func() { return reinterpret_cast<Class##Private *>(qGetPtrHelper(d_ptr)); } \
inline const Class##Private* d_func() const { return reinterpret_cast<const Class##Private *>(qGetPtrHelper(d_ptr)); } \
friend class Class##Private;
Then we define the private class as below:
class MyClassPrivate
{
public:
MyClassPrivate(MyClass *parent);
private:
MyClass * const q_ptr;
Q_DECLARE_PUBLIC(MyClass);
int myVar;
};
Here, the q_ptr pointer points the public interface, and the Q_DECLARE_PUBLIC macro is used to define some helper functions:
#define Q_DECLARE_PUBLIC(Class) \
inline Class* q_func() { return static_cast<Class *>(q_ptr); } \
inline const Class* q_func() const { return static_cast<const Class *>(q_ptr); } \
friend class Class;
And we can further use the following two macros to ease the access:
#define Q_D(Class) Class##Private * const d = d_func()
#define Q_Q(Class) Class * const q = q_func()
That's all about the usage of D-pointers and private implementations in Qt. Let me use a simple sample to end this post ;)
// myclass.h
#ifndef MYCLASS_H
#define MYCLASS_H
#include <QtCore/QObject>
class MyClassPrivate;
class MyClass: public QObject
{
Q_OBJECT
public:
MyClass(QObject *parent = 0);
virtual ~MyClass();
void dummyFunc();
signal:
void dummySignal();
private:
MyClassPrivate * const d_ptr;
Q_DECLARE_PRIVATE(MyClass);
Q_DISABLE_COPY(MyClass);
};
#endif // MYCLASS_H
// myclass.cpp
#include "myclass.h"
class MyClassPrivate
{
public:
MyClassPrivate(MyClass *parent)
: q_ptr(parent)
{
}
void foobar()
{
Q_Q(MyClass);
emit q->dummySignal();
}
private:
MyClass * const q_ptr;
Q_DECLARE_PUBLIC(MyClass);
};
MyClass::MyClass(QObject *parent)
: QObject(parent)
, d_ptr(new MyClassPrivate(this))
{
}
MyClass::~MyClass()
{
Q_D(MyClass);
delete d;
}
void MyClass::dummyFunc()
{
Q_D(MyClass);
d->foobar();
}
The Chinese translation is available at: http://blog.csdn.net/zhu_xz/archive/2010/11/25/6035861.aspx
Labels:
qt
1.11.10
New release of Web Runtime for N900
Finally, I'm happy to push the latest Web Runtime (v1.1.0) for N900 to the extras-devel repository. Please note that this release is only tested against PR 1.3. There's no new features implemented compared to the previous release back in July, but only bug fixes. Personally, this is one of the last duties for me in the WRT project, and let's see what I would play with in the future ;)
If you have the previous release installed, you should first uninstall:
sudo gainroot
apt-get purge libwrt-experimental1
Then install the new release:
sudo gainroot
apt-get install qtwrt
Have fun and feel free to go to the public forum for more questions ;)
What is Web Runtime?To install WRT on N900, you should first enable the extras-devel repository.
It's a development framework with which you can write applications in standard Web technologies, like HTML, CSS, and JavaScript. Also, a bunch of Device APIs are provided to grant you the access to native resources, e.g. file system, sensors, etc., using JavaScript.
If you have the previous release installed, you should first uninstall:
sudo gainroot
apt-get purge libwrt-experimental1
Then install the new release:
sudo gainroot
apt-get install qtwrt
Have fun and feel free to go to the public forum for more questions ;)
28.10.10
Hybrid application using QML and Qt C++
Moved to http://www.zionsoft.net/2010/10/hybrid-application-qml-qt/
Though QML provides a nice way to design user interfaces, and JavaScript is employed there to implement the application logic and works pretty nice in many cases, we might still need Qt C++ in some situations (well, at least JavaScript has limited access outside its sandbox).
1) integrate QML into Qt C++
Suppose we have a QML file, named "myqml.qml", like this:
One easy way to integrate is to use the QDeclarativeView class, which provides a widget to display QML files. You just need the following three lines:
However, QDeclarativeView consumes more resources than normal widgets. Fortunately, we can integrate QML into a graphics scene. The following lines shows the basic usage:
Then with the help of the QDeclarativeItem class, you can easily access the properties of the QML element, e.g.:
2) exposing Qt C++ objects to QML
You can also expose native Qt C++ objects to QML through QDeclarativeContext:
Then in QML, you can have e.g. the following line to access them:
You can also use QDeclarativePropertyMap to manage the exposed properties:
To expose a self-defined object, we can use the following code:
Moreover, we can create new QML types:
In QML, you can use it like this:
Now let's jump to invoke a Qt C++ function from QML. Basically, QML can invoke slots and functions declared with Q_INVOKABLE. Suppose we have the following function in MyObject:
Then you can invoke it in QML:
3) write plugins as QML extension
The benefits for using plugins as QML extensions are similar to using shared libraries, and it can be easily achieved with the help of QDeclarativeExtensionPlugin. Let's reuse the MyType class defined in the previous section. First, we need to create a plugin:
Then create a file named "qmldir" to define which plugin to load from where (suppose the plugin is called "myplugin):
Now we can use qmlviewer to launch the QML file:
4) summary
Use QDeclarativeView or QDeclarativeComponent to integrate a QML file into native Qt C++. Qt C++ can access the properties of QML elements through QDeclarativeItem. Expose native objects to QML through QDeclarativeContext. New QML types can be exported through qmlRegisterType. The properties of native objects are exported as properties, and the slots or functions declared with Q_INVOKABLE can be invoked in QML. Create plugins for extension using QDeclarativeExtensionPlugin.
Though QML provides a nice way to design user interfaces, and JavaScript is employed there to implement the application logic and works pretty nice in many cases, we might still need Qt C++ in some situations (well, at least JavaScript has limited access outside its sandbox).
1) integrate QML into Qt C++
Suppose we have a QML file, named "myqml.qml", like this:
// this line should be "import QtQuick 1.0" since Qt 4.7.1
import Qt 4.7
Rectangle {
id: myRectangle
width: 800
height: 480
color: "lightgray"
Text {
id: myText
text: "I love hybrid application!"
anchors.centerIn: parent
font.pointSize: 28
font.bold: true
}
}
One easy way to integrate is to use the QDeclarativeView class, which provides a widget to display QML files. You just need the following three lines:
QDeclarativeView view;
view.setSource(QUrl::fromLocalFile("myqml.qml"));
view.show();
However, QDeclarativeView consumes more resources than normal widgets. Fortunately, we can integrate QML into a graphics scene. The following lines shows the basic usage:
// provides an environment for instantiating QML components
QDeclarativeEngine engine;
// encapsulates a QML component definition
QDeclarativeComponent component(&engine, QUrl::fromLocalFile("myqml.qml"));
// creates the graphics item for QML at the engine's root context
QDeclarativeItem *item = qobject_cast<QDeclarativeItem *>(component.create());
scene.addItem(item);
Then with the help of the QDeclarativeItem class, you can easily access the properties of the QML element, e.g.:
qDebug() << item->property("color").typeName();
item->setProperty("color", QColor(255, 255, 255));
2) exposing Qt C++ objects to QML
You can also expose native Qt C++ objects to QML through QDeclarativeContext:
QDeclarativeContext *context = engine->rootContext();
context->setContextProperty("textFromQt", QString("I love hybrid application!"));
Then in QML, you can have e.g. the following line to access them:
text: textFromQt
You can also use QDeclarativePropertyMap to manage the exposed properties:
QDeclarativePropertyMap map;
map.insert("key1", "value1");
map.insert("key2", "value2");
context->setContextProperty("map", &map);
In a QML engine, there could be a couple of contexts, forming a tree structure. The child contexts inherit properties in the parent context. By default, there is only one root context, but you can always add more to give finer control of the exposed data, i.e. different QDeclarativeComponent inside the same context have the same exposed data set.
To expose a self-defined object, we can use the following code:
// define a class with properties
class MyObject: public QObject
{
Q_OBJECT
// the NOTIFY signal is needed to inform about changes
// all properties will be exposed to QML
Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged)
public:
MyObject(QObject *parent = 0) : QObject(parent), m_text("I love hybrid application!") {}
QString text() const { return m_text; }
void setText(QString &text)
{
m_text = text;
emit textChanged();
}
signals:
void textChanged();
private:
QString m_text;
};
// then just expose it so QML can access it through name "myObject"
engine->rootContext()->setContextProperty("myObject", new MyObject());
Moreover, we can create new QML types:
// define the new type
class MyType : public QDeclarativeItem
{
Q_OBJECT
public:
MyType(QDeclarativeItem *parent = 0) : QDeclarativeItem(parent)
{
setFlag(QGraphicsItem::ItemHasNoContents, false);
}
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0)
{
QPen pen(QColor(100, 100, 100), 2);
painter->setPen(pen);
painter->drawLine(0, 100, 100, 100);
}
};
// then register to expose it
qmlRegisterType<mychart>("com.blogspot.xizhizhu.mytype", 1, 0, "MyType");
In QML, you can use it like this:
import com.blogspot.xizhizhu.mytype 1.0
MyChart {
id: myChart
width: 100
height: 200
}
Now let's jump to invoke a Qt C++ function from QML. Basically, QML can invoke slots and functions declared with Q_INVOKABLE. Suppose we have the following function in MyObject:
Q_INVOKABLE void showMessage()
{
QMessageBox::information(NULL, "My Test", "Invoking a native function ;)");
}
Then you can invoke it in QML:
myObject.showMessage();
3) write plugins as QML extension
The benefits for using plugins as QML extensions are similar to using shared libraries, and it can be easily achieved with the help of QDeclarativeExtensionPlugin. Let's reuse the MyType class defined in the previous section. First, we need to create a plugin:
class MyPlugin : public QDeclarativeExtensionPlugin
{
Q_OBJECT
public:
void registerTypes(const char *uri)
{
qmlRegisterType<MyType>(uri, 1, 0, "MyType");
}
};
Q_EXPORT_PLUGIN2(myPlugin, MyPlugin);
Then create a file named "qmldir" to define which plugin to load from where (suppose the plugin is called "myplugin):
plugin myplugin /path/to/plugin
Now we can use qmlviewer to launch the QML file:
// no need to import now
MyChart {
id: myChart
width: 100
height: 200
}
4) summary
10.9.10
Unofficial Qt Web Runtime tutorial 2: Menu API
If there is no API, Qt WRT is merely nothing but a naive web browser. Then what APIs are supported other than standard HTML and JavaScript now? You can first get a list of features already supported, or to be supported in the near future here.
As a summary, we have already supported the APIs of menus, widget object, URI schemes, web inspector, console log, some Device APIs, as well as some HTML 5 features and standard JavaScript from Qt WebKit. Let's start with the menu API.
1 view menu
You can create the menu items to the chrome's menu bar (we call it view menu) with the following JavaScript code:
Here, you have created a menu item named "Menu item" and added it to the menu bar. When it's triggered, the function changeTitle() will be called to set the menu's title to "New title".
Note that since on the device the menu's title is also the same as the window's title, it actually changes the window's title too.
2 context menu
The context menu can be created with the following piece of code:
Here, a context menu is created and an item named "Change Title" is added to the menu. When you long tap on the HTML element with id "contextMenuArea", this context menu will pop up. Then if you select the "Change Title" item, the changeTitle() function will be called.
Note that there's a known bug (already fixed for the next release) that if you long tap on the area where no context menu is defined, an empty context menu is popped-up.
3 sample
Only the HTML file this time ;)
As you can see, one menu item can be added to multiple menus, exactly the same as native Qt apps. Here goes some screenshots:
The first one shows the simple sample when launched. The second and third one show the view menu and the context menu, respectively, while the last one shows that the title has been updated after you tap on the menu. More screenshots can be found here, while the sample code is available here.
As a summary, we have already supported the APIs of menus, widget object, URI schemes, web inspector, console log, some Device APIs, as well as some HTML 5 features and standard JavaScript from Qt WebKit. Let's start with the menu API.
1 view menu
You can create the menu items to the chrome's menu bar (we call it view menu) with the following JavaScript code:
var menuItem = new nokia.device.MenuItem("Menu item", changeTitle);
nokia.device.menu.addMenuItem(menuItem);
function changeTitle()
{
nokia.device.menu.title = "New title";
}
Here, you have created a menu item named "Menu item" and added it to the menu bar. When it's triggered, the function changeTitle() will be called to set the menu's title to "New title".
Note that since on the device the menu's title is also the same as the window's title, it actually changes the window's title too.
2 context menu
The context menu can be created with the following piece of code:
var contextMenu = new nokia.device.ContextMenu("contextMenuArea");
var changeTitleItem = new nokia.device.MenuItem("Change Title");
changeTitleItem.onselect = changeTitle;
contextMenu.addMenuItem(changeTitleItem);
Here, a context menu is created and an item named "Change Title" is added to the menu. When you long tap on the HTML element with id "contextMenuArea", this context menu will pop up. Then if you select the "Change Title" item, the changeTitle() function will be called.
Of course, only the widgets in the windowed or fullscreen mode could have context menus; and only the widgets in the windowed mode can have view menus.
Note that there's a known bug (already fixed for the next release) that if you long tap on the area where no context menu is defined, an empty context menu is popped-up.
3 sample
Only the HTML file this time ;)
<html>
<header>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Menu sample</title>
<script type="text/javascript">
function changeTitle()
{
if (nokia.device.menu.title == "Title 1")
nokia.device.menu.title = "Title 2";
else
nokia.device.menu.title = "Title 1";
}
function init()
{
var menuItem = new nokia.device.MenuItem("item", changeTitle);
nokia.device.menu.addMenuItem(menuItem);
var contextMenu = new nokia.device.ContextMenu("contextMenuArea");
contextMenu.addMenuItem(menuItem);
}
</script>
</header>
<body onload="init()">
<div id="contextMenuArea">There is a context menu.</div>
</body>
</html>
As you can see, one menu item can be added to multiple menus, exactly the same as native Qt apps. Here goes some screenshots:
The first one shows the simple sample when launched. The second and third one show the view menu and the context menu, respectively, while the last one shows that the title has been updated after you tap on the menu. More screenshots can be found here, while the sample code is available here.
Subscribe to:
Posts (Atom)