Recently Nokia released the much awaited pr 1.2 firmware for the N900, and among the many bug fixes and tweaks, came the ability to easily rotate applications to portrait by hitting ctrl+shift+r, then closing the keyboard and rotating to portrait. This feature had the knock on effect that I started to look seriously at portrait support in Witter, where upon I quickly found a pre-canned python module that I could just include to get persistent portrait support available from the talk.maemo.org forums No sooner had I got witter in portrait mode, than the obvious next step was suggested.... what about some way to actually tweet in portrait. For those not familiar with Maemo and the N900, although it does now support some appliactions such as the browser going portrait, there is no system wide on screen keyboard for portrait use. There is one for landscape (despite having a physical keyboard for landscape use) but not in portrait. this has left application developers to provide their own. This is deeply dissatisfying as an approach, however keen to provide a meaningful portrait use for Witter, I decided to go ahead and implement my own.
So how to go about it? Well it actually not very hard at all, just very boring. Basically a 'keyboard' is just a bunch of buttons, all wired to emit a signal with a single letter, number or symbol associated. So that's 26 button definitions, 3 lines per definition (create object, set title, connect signal to emit character) just to do all the lower case letters. So far that's a little under 600 lines of code, to provide upper and lower case letters, plus a keyboard with numbers and some of the more obvious symbols, plus some mode shifting buttons. Most of this is of course copy'n'paste work, with single character adjustments but in a desire to not have generic and confusing object names I also had to name all of them appropriately. In all it was a couple of hours work.
The implementation is a fairly simple architecture, The main witter layout has a single gtk.VBox which is the main contained for the keyboard. It defaults to empty and invisible. Each keyboard layout comes with two methods, one to define all the objects, and a wrapper which calls it then requests that the gtk.VBox and all it's children become visible. This is separated into two because it is helpful to be able to initialise the keyboard objects separately from choosing whether they should currently be visible or not. The main definition method starts by destroying everything already inside the top level gtk.VBox. This means each layout can have a different number of rows as wells as different content in them.
Each button is created like this: q = hildon.Button(gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL) q.set_title('q') q.connect("clicked", self.TypeLetter, "q")
The interesting code actually occurs in TypeLetter, because there are a few subtle behaviours that people expect from a text entry field that aren't that obvious. For instance you want to be able to select any point in the existing string, and enter or delete text from that point, not just add characters onto the end.
def TypeLetter(self, widget, letter):
#get the position of the current cursor
pos = self.tweetText.get_position()
#get the current text string
text = self.tweetText.get_text()
#if passed special delete eye catcher we are actually deleteing characters
if (letter =="*delete*"): self.tweetText.set_text(text[0:pos-1]+text[pos:len(text)]) self.setCursorAt(pos-1); else:
#normal entry of text at the selected pos
So you have to basically split the existing string around the position of the cursor and either insert or delete characters at that point, and leave the cursor set in the appropriate position.
All of this goes to make a keyboard that looks something like this:
The remaining question is how to launch the keyboard when you want it. At the moment in Witter I have re-purposed a PLUS icon in the top row of buttons, so that in portrait mode it displays or hides the portrait keyboard. (whenever the orientation changes I store a current orientation value so that this code can tell whether it should be displaying the portrait keyboard or not) However ideally it would be hooked off of the text entry field gaining or losing focus, I just haven't figured that bit out yet.