Showing posts with label qml. Show all posts
Showing posts with label qml. Show all posts

Saturday, February 2, 2013

Still Riding The Qt Train?

(Or: Welcome, BB10!)

On popular request, I have finally added some documentation to my OAuth library "O2". Check out the new README here: https://github.com/pipacs/o2/blob/master/README.md

Tuesday, May 8, 2012

Ionic: Number Two In Nokia Store

Ionic, my e-book reader is number 2 in the Nokia Store right now, beaten only by Firefox! Thanks for all your support.

Sunday, March 4, 2012

Keep Accelerometer Running While The Screen Is Locked

On N9, the accelerometer is disabled while the screen is locked. In order to enable it all the time, set the "alwaysOn" property to true:


    accelerometer = new QAccelerometer(this);
    accelerometer->setProperty("alwaysOn", true);

This is a Meego-specific property, and it is only documented here.

Friday, February 10, 2012

Help! Virtual keyboard is hiding the input on Symbian

Modern GUIs often place labels, input fields and other controls on a scrollable pane - think of a Settings page for example. Implementing this in QML is easy: just lay out the controls in a Column, and place the Column in a Flickable.

For example:

Page {
  Flickable {
    anchors.fill: parent
    Column {
      Label {text: "Input 1:"}
      TextField {id: input1}
      Label {text: "Input 2:"}
      TextField {id: input2}
      // More controls
    }
  }
}


This works fine on Meego, but on Symbian it has a flaw: the virtual keyboard (VKB) can hide the input fields towards the bottom of the page. This is a serious shortcoming without a trivial solution, so I had to come up with a non-trivial one.

Basic idea: Whenever the virtual keyboard is displayed, the input field with the focus should be moved up within the Flickable, if it was originally covered by the VKB.

Here is the implementation:

Page {
  Flickable {
    id: flickable
    anchors.fill: parent
    Column {
      Label {text: "Input 1:"}
      TextField {id: input1}
      // More controls
    }

    // (1)
    Timer {
      id: adjuster
      interval: 200
      onTriggered: flickable.adjust()
    }

    // (2)
    Component.onCompleted: {
      inputContext.visibleChanged.connect(adjuster.restart)
    }

    // (3)
    function adjust() {
      if (!inputContext.visible) {
        return
      }

      var focusChild = null
      function findFocusChild(p) {
        if (p["activeFocus"] === true) {
          focusChild = p
        } else {
          for (var i = 0; i < p["children"].length; i++) {
            findFocusChild(p["children"][i])
            if (focusChild !== null) {
              break
            }
          }
        }
      }
      findFocusChild(flickable)

      if (focusChild === null) {
        return
      }
      var focusChildY = focusChild["y"]
      var focusChildHeight = focusChild["height"]
      if ((flickable.contentY + flickable.height) < (focusChildY + focusChildHeight)) {
        flickable.contentY = focusChildY + focusChildHeight - flickable.height
      }
    }
  }
}

Some explanations:

(1) We need a timer to adjust the focus item's position. We can't adjust it immediately after the virtual keyboard appears, because the the Flickable component will get resized with a nice animation, which takes time.

(2) So when the VKB gets visible, we just start a timer, that will adjust the focus item later

(3) Shifting the focus item (and all the others of course in the Flickable) happens in the adjust() function. The function tries to find a child item with active focus. If there is such an item, and it is currently hidden, the whole Flickable content will be elevated by changing it's contentY property.

Oh one more important remark: The PageStack component that owns the Page should have its platformSoftwareInputPanel property set to true. Otherwise the Page (and with it the Flickable) won't get resized when the VKB is shown.

Finally: QML is changing fast, so I'm sure one day this bug will be fixed by Nokia. The problem was found on, and the solution was written for the following environment:

  • Qt 4.7.4 on Symbian Anna or Belle
  • QtQuick 1.1
  • com.nokia.symbian 1.1

Wednesday, February 1, 2012

Count Your Steps

Steps, the open source step counter is available from Nokia Store. Besides counting steps, it can also log to Google Fusion Tables.

The Symbian version is downloadable now, Meego version is coming soon.

Sunday, January 29, 2012

Translating QML Applications: The Full Story

1. In the QML code, text should be inside qsTr(), e.g. instead of

    Label {text: "Hello"}

write

    Label {text: qsTr("Hello")

2. Make a directory containing the translations, let's call it translations. Add let's assume the QML sources are in the directory qml

3. Collect the texts to be translated into translations/<language>.ts, using the lupdate command. The following command

    lupdate qml -ts translations/hu.ts translations/es.ts

will recursively collect texts from source code in qml, into hu.ts and es.ts.

Name the translation files after the name of the target language, like es.ts (Spanish) or hu.ts (Hungarian).  Use two-letter language codes as in ISO 639-1. See http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes.

To make a country-specific translation variant, append the two-digit ISO country code after the language code, for example hu_HO.ts (the Hungarian dialect spoken in Honduras).

If lupdate is not found on the PATH, find it in the Qt SDK. I have a version in ~/QtSDK/Desktop/Qt/473/gcc/bin for example

4. Open the .ts files with the Qt translation tool Linguist. Assuming Qt SDK on Mac, it is in ~/QtSDK/Linguist.app/Contents/MacOS/Linguist

Using Linguist, set the .ts file's language and country, and translate. Make sure all texts are translated, and translations are marked "Done" (so there is a check mark in front of them)

5. When done with translating, release the translations (File → Release). "Releasing" in this context means converting the translation file to a compact binary, which has the same name as the original, but with the extension ".qm". For example, hu.ts will be released as hu.qm

6. Create a resource file translations.qrc and add it to the project

7. Add the released translations to this resource file, so it contains

    /
      translations/hu.qm
      translations/es.qm

8. Finally in main(), activate the translation corresponding to the current language:

#include <QTranslator>
#include <QLocale>

int main(int argc, char *argv[]) {
    QApplication app;
    QString locale = QLocale::system().name();
    QTranslator translator;
    if (translator.load(locale, ":/translations")) {
        app.installTranslator(&translator);
    } else {
        // Could not load translation
    }
    ...
}


Sunday, November 13, 2011

Cross-Platform UI with Qt Components

Cross-platform UI development doesn't make much sense, if the platform UI paradigms are wildly different. It does however in my case: I develop Nokia Store applications targeting two very similar Nokia platforms: Meego and Symbian.

Both platforms are mobile phones with a touchscreen, and for both, Nokia provides UI toolkits (QML Components), that are similar, but full of maddening small differences. I'm sure these differences will disappear over time. In the meanwhile, I work around them using the Qt resource system.

So the idea is to wrap the platform-specific components into components providing uniform interfaces, and then put the wrappers into platform-specific resource files, with the content aliased to the same location. In my project, I have three resource files: meego.qrc, symbian.qrc, and for the common UI code, common.qrc.

This is how meego.qrc looks like:

<rcc>
 <qresource prefix="/qml">
  <file alias="MainPage.qml">qml/meego/MainWindow.qml</file>
 </qresource>
</rcc>

And this is how symbian.qrc looks like:

<rcc>
 <qresource prefix="/qml">
  <file alias="MainPage.qml">qml/symbian/MainWindow.qml</file>
 </qresource>
</rcc>


Lastly, this is in common.qml:

<rcc>
 <qresource prefix="/">
  <file>qml/main.qml</file>
 </qresource>
</rcc>

MainWindow,qml is implementing the wrapper for the platform-specific application main windows, that can be used from the platform-independent main.qml. To achieve this, both meego/MainWindow.qml and symbian/MainWindow.qml are aliased to the same resource file location (/qml/MainWindow.qml), while they physically reside in different source files (qml/meego/MainPage.qml and qml/symbian/MainPage.qml).

The project file of course only includes meego.qrc when building for Meego, and symbian.qrc when building for Symbian.

Final touches

When editing main.qml in Qt Creator, it won't find MainWindow.qml on the import path (because it doesn't exist in the current directory). To work around this, we should add the sub-directories "meego" and/or "symbian" to the import path. The final main.qml looks like this:

import symbian
import meego
import MainWindow.qml


MainWindow {
    ... (common UI code goes here)
}

Unfortunately, this is still not enough. Qt Creator is now happy, but when running main.qml from the resource file on the target platform, it will complain the directories "meego" and "symbian" do not exist.

This can be resolved by adding some dummy QML files (meego/dummy.qml and symbian/dummy.qml), and including them to the common resource file, which now looks like this:

<rcc>
 <qresource prefix="/">
  <file>qml/main.qml</file>
  <file>qml/meego/Dummy.qml</file>
  <file>qml/symbian/dummy.qml</file>
 </qresource>
</rcc>

Summary

What we have achieved with this somewhat complicated setup is simple: We can now wrap platform-specific QML components with a cross-platform API, and use them from a platform-agnostic UI code.



Tuesday, October 18, 2011

Ionic E-Book Reader

Ionic, my e-book reader for the Nokia N9 and N950 is out! Get it from the Nokia Store: http://store.ovi.com/content/212271.

Sunday, October 16, 2011

My E-book Reader: Coming Soon To The Nokia N9

The first release is done, waiting for Nokia Store approval. It's called Ionic, and it's the only EPUB reader for the Nokia N9 I know of. Stay tuned.