Building custom widgets with the Zinzala SDK - page 9

Its algorithm is simple. We loop over all the buttons and check if aPoint is contained in the button frame. If none of the buttons contain the position being tested, the method will return kButtonsCount. Although simple, there is one little issue ... What if the buttons are circular?

Because we are using the rectangular frame of the button, if we were to tap in a corner of the frame, which is dearly outside of the button, it will still act as if we clicked in the center of the button. Can we live with it? If our UI were to be used on a touch-screen in a moving vehicle, it wouldn't matter much since the user's input will not be too precise. However, if a pen triggers the input, then we should do something to improve it.

One solution, on top of what we've already done, is to use the button's bitmap to find out if the position where the user has clicked, is transparent or not. If it is transparent, then we are outside of the button.

Here's the improved method:

tUint8 cDConsoleView::FindButton(uPoint aPoint) const { tUint8 lButton; for(lButton=0;lButton<kButtonsCount;lButton++) { if(iButtons[lButton].iBFrame.Contains(aPoint)) { tUint8 lIndex; tUint8 lTrans; uPoint lPoint; // calculate position within the button frame lPoint.iX = aPoint.iX - iButtons[lButton].iBFrame.iLeft; lPoint.iY = aPoint.iY - iButtons[lButton].iBFrame.iTop; // get transparent color index iBStates[kStateNormal]->GetTransparent(lTrans); // get pixel color index iBStates[kStateNormal]->GetPixelColor(lPoint.iX,lPoint.iY,&lIndex); // if the color is transparent, then we are outside of the button if(lIndex != lTrans) break; } } return lButton; }

Now, there are two main events that we are going to handle: the user presses down on a button, and the user releases the button. The first one will be handled in the method MouseDown(), the second in MouseUp():

void cDConsoleView::MouseDown(uPoint aPoint) { Pressed(FindButton(aPoint)); } void cDConsoleView::MouseUp(uPoint aPoint) { if(FindButton(aPoint) == iPressedButton) Invoked(); else { if(iButtons[iPressedButton].iState == kStatePressed) { // revert the state of the button iButtons[iPressedButton].iState = iPrevState; // redraw the button Render(iPressedButton); } iPressedButton = kButtonsCount; } }

When the user clicks, taps or presses somewhere on the console, the method MouseDown() will be called. This method will execute Pressed() with the identifier of the button pressed. If a button was indeed pressed, we will mark the button as pressed. It is only when the user releases the button, that we will invoke it from MouseUp(). If the button is released outside of its boundary, we will not invoke it.

Here is the method Pressed():

void cDConsoleView::Pressed(tUint8 aButton) { // if the mouse event was on a button if(aButton < kButtonsCount) { iPressedButton = aButton; // if the button is not dimmed if(iButtons[aButton].iState == kStateNormal || iButtons[aButton].iState >= kStateActive) { // set the button as pressed iPrevState = iButtons[aButton].iState; iButtons[aButton].iState = kStatePressed; // redraw the button Render(aButton); } } }

Whether the button is dimmed or not, we mark it as pressed. However, we only really set it as pressed and redraw it, if it is not dimmed.

The purpose of the method Invoked() is to accept and acknowledge the user action on a given button. In order to improve the illusion of a physical button, we are going to redraw the button with a bit of a delay. That way it will look pressed a little bit longer. Here it goes:

void cDConsoleView::Invoked() { tBool lGood = false; if(iPressedButton < kButtonsCount) { if(iButtons[iPressedButton].iState == kStatePressed) { // delay a bit to have a better effect sSnooze::SomeMS(kDelayPressed); // restore the previous state of the button iButtons[iPressedButton].iState = iPrevState; // redraw the button Render(iPressedButton); lGood = true; } // call the hook method linked to the pressed button switch(iPressedButton) { case kButtonPlay: { if(iButtons[kButtonPlay].iLabel == kLabelStop) StopPressed(lGood); else PlayPressed(lGood); break; } case kButtonPause: { PausePressed(lGood); break; } case kButtonBwd: { BwdPressed(lGood); break; } case kButtonFwd: { FwdPressed(lGood); break; } } iPressedButton = kButtonsCount; } }

Print version

All content © 2004-2007, hexaZen - Vancouver BC, Canada