Behold the button tutorial

Ok, there must be a million tutorials on how to make a pushbutton in QML, but I guess there’s room for one more and I’ll try to give some tips that are applicable elsewhere too.

Generally speaking there’s two roads you can take here. Either you draw the button base without any bitmapped graphics i.e. using a Rectangle element or use bitmaps with an Image or a BorderImage element. A Rectangle is faster to draw (might be close if you use rounded corners and the button isn’t too big), but using bitmapped graphics of course gives you the best control on the appearance of the button. If your graphic design is such that the button base contains a horizontally (and/or vertically) repeating part, going for the BorderImage is a nobrainer, because it allows you to determine the size of the button in QML. Hopefully the picture below will illustrate the issue:

The design on the left is suitable to be used with a BorderImage (all the vertical pixel columns inside the yellow rectangle are the same) and the right one isn’t because of the fancier gloss effect. We’ll stick here with the left one for now.

There must be a million way to make nice looking buttons with Photoshop (or some other program which has layers and good support for pixel level manipulation). I took the recipe from here. The Internet is full of similar tutorials for achieving different looks. Go explore.

You know how in those cooking shows the TV-chef always whips up something preprepared and everything looks nice and easy? (They never clean up after themselves either, which is somewhat related to testing, debugging and all that crap.) Well I’m going to do it too. Here are the ready border images to be used in our button (use freely if you want). One for the idle state and one for the pressed state. The pressed version is nudged down a pixel and has darker tones. If you want to reuse those, but the color tones don’t match your purse or manbag, you can tweak them easily with the hue/saturation control in Photoshop/GIMP/whatever (ctrl-u in PS).

On to the QML!

import Qt 4.7
BorderImage {
id: buttonbase
signal clicked
property bool pressed: hitbox.pressed && hitbox.containsMouse
source: pressed ? "button_down.png" : "button_up.png"
border.left: 50;
border.right: 50;
MouseArea {
id: hitbox
anchors.fill: parent
onClicked: parent.clicked()
}
}
view raw Button1.qml hosted with ❤ by GitHub
Not much to disect here, but you can press the button (ooh) and it emits a clicked signal which the component using the button can then handle. Now theoretically it might be better to have two BorderImages on top of each other and toggle their opacities depending on the pressed property instead of changing the source property of the single BorderImage element. Better? Well the first time the button is pressed down the button_down.png gets loaded. I tried it out with an N8 while having the images on the (notoriously slow) eMMC mass memory and didn’t notice any load lag. On any subsequent presses (of this particular or other instances of the button) it shouldn’t matter, because the QML engine caches the loaded bitmaps. So let’s just leave it this way. At least there’s less QML to parse. The next step is to add the text label to the button and means to change it from outside:

Nothing too difficult here either. The text is nudged one pixel down when the button is pressed, because the base image is nudged too. Now here’s a usefull tip though: if you use the raised or sunken styles of the Text element, define the styleColor semi-transparent black (or white) instead of trying to find a good value to match the base layer graphics. The benefit is that if you decide to change the graphics (or have something really wild there) the shadow color matches automatically. Works pretty well IMO.

Next a concept that is pretty important when making mobile UIs meant to be used with fingers (A stylus? Oh please…). Majority of the time the hitbox (the are it reacts to touch) of the element should be bigger than the element is visually. Everything is easy if you’re doing it with a mouse on a desktop (unless you had really really good time last night), but try doing it with your fingers on a small touchscreen (especially capacitive). Even if you manage to hit it, your finger slides a bit as you lift it causing your finger to exit the hitbox easily and your gesture not to be interpreted as a tap/click. Things get even worse in one-handed use where your thumb has limited mobility and wants to return to its natural position.

For a real life example on your Symbian touch phone try changing the month on the calendar or activating the clock/wlan/battery/whatever panel one handed without going rage guy in 15 seconds. You’ll be lucky to get what you want 50% of the time. I’m pretty sure most of the “the touchscreen is sometimes a bit unresponsive” stuff you can read in the reviews is caused by this phenomenon.

The solution isn’t to make everything bigger. Going all Duplo with your UI will look horrible. Good use of empty space and balanced composition are important aspects of making a good looking UI. Just make it so that it looks good and reacts to a large enough area.

</rant>Now where were we? The button, the button! Focus on the button… So extending the hitbox is pretty easy in QML. We defined our hitbox area to cover the are occupied by the BorderImage element with anchors.fill: parent (this WordPress theme needs a monotype style…) We can extend it easily by defining negative values for the margins. Like this:

This could be improved by providing a way to define different extension values for all four margins, but I’ll leave it to you. Just remember that the bottom margin is the most important, because that’s where your finger tends to slide when you tap.

As a finishing touch we’ll add haptics. Hapwhat? The mechanical feedback which most UI components give you when you manage to hit them. QtMobility has a good API for this so you’re safe in case you’re prone to developing a Symbian rash. There’s no QML binding though so we’ll have to do it ourselves, but that’s no biggie (and you’ll get familiar in integrating QML with C++ to which you’ll eventually bump anyway). As you might have read from here, you can invoke native code by simply defining the methods in your (QObject derived) class as slots or add Q_INVOKABLE macro in front of them. Then you just make your object visible in the QML context and that’s it.

And our complete Button.qml with haptics:

You can try out the button with a simple root QML document like this:

All righty, that should do it. If someone wants (drop a comment below) , I can make a sis file of the demo, so you can run it on your Symbian phone. Until next time, ta-ta you scallywags.

This entry was posted in Programming and tagged , , , , , . Bookmark the permalink.

2 Responses to Behold the button tutorial

  1. Mattias says:

    Great blog!

    Smallest tip of the day: You can save three lines (!!) of code by setting all anchor margins at the same time…
    anchor.margins: -hitboxExtension

    Cheers

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>