Thursday, March 28, 2013

User Interaction with Ubuntu Components



I am so very able to amuse myself with all these funny pictures, but there are other amusing subreddits other than funny. So today I added the ability to choose a different subreddit. This involved diving into the world of Ubuntu Components.

Ubuntu Components were surprisingly functional, but as you will see, they are still a work in progress.

So, the first thing I needed to do was to make a way for the user to say that they want to change subreddits. Ubuntu Touch provides the bottom edge for your application to add a list of commands. This is a nice way to do it because it means that the screen isn't cluttered with commands, but users know exactly where to go when they want them.

The first thing to do is to ensure that the top level of your app is a MainView, and then you are presenting the content in a Page. So, roughly your apps structure looks like ...

//imports
MainView
{
    //set MainView properties
    Page
    {
        //app UI content
    }
    //components outside pages
}

I wanted to add a "subreddit" button. To do that, I set the tools property of the page to a list of actions. So far there is only one action. Essentially, I am defining a button. I tell it the text I want, the icon to use (I downloaded an icon that I wanted) and the action to take. So this goes inside the Page tag:

        tools: ToolbarActions
        {
        Action
        {
            text: "SubReddit"
            iconSource: Qt.resolvedUrl("reddit.png")
            onTriggered: PopupUtils.open(subRedditSheet)
        }
    }
Now you can see that if swipe from the button, I get my button.
But what is that action? What I did was create a ComposerSheet that allows the user to input the reddit that they want. You do this by defining a Component that wraps a ComposerSheet. A ComponentSheet is kind of like a dialog box. It handles putting Ok and Cancel buttons on for you. Note that you have to name the ComposerSheet "sheet", or you get errors. All I added was a TextField that I called "subRedditText, but you can fill the ComposerSheet with whatever you want.

I just have to tell it what to do when the user clicks Ok. I did a little refactoring from yesterday to create a "changeSubReddit function that gets called on start up, and can get called from here. I just pass it the subreddit. (Don't forget to import Ubuntu.Components.Popups 0.1 in order to use the ComposerSheet).

Then you call PopupUtils.open to pop it open when you want it (which I specify as the action for my reddit button in the ActionList).

    Component
    {
        id: subRedditSheet

        ComposerSheet {
            id: sheet;
            title: "Choose SubReddit"
            TextField
            {
                id: subRedditText
            }

            onCancelClicked: PopupUtils.close(sheet)
            onConfirmClicked:
            {
                changeSubReddit(subRedditText.text)
                PopupUtils.close(sheet)
            }
        }
    }


As you can see, the ComposerSheet doesn't have size logic yet (or as likely I did something wrong), but none the less, it works!

I got a surprising amount of free functionality from using Page, ToolbarActions, and ComposerSheet. On top of being easy and fun, it means that my app will inherit the look and feel, interaction patterns, translations and Ubuntu Touch style guidelines!



Time Waster Turbo Charge


After my post yesterday about browsing 9gag.com, I decided I should make an even more efficient time waster. Way back in the day, I made an app called "lolz". But a lot has changed since then. Including the emergence of Imgur. Imgur is a service that hosts images for Reddit. And Reddit is the world's most efficient time waster.

Also, Imgur has a nice API, so access to the data seemed pretty easy. Thus, I bring you, the greatest time wasting app of ever.

Here's how I got started.

QML often uses a Model-View-Controller architecture built in. I found that I can take advantage of this by for my app because. Specifically, the imgur app optionally serves up XML. If you have the option to get XML, use it! It makes things go much faster.

So, the heart of the app as it exists today is my XmlListModel. An XmlListModel essentially turns XML into a list of objects or key/value pairs that other QML components can use. So, I made a model like this:

    XmlListModel
    {
        id: imagesXML;
        query: "/data/item";
        XmlRole
        {
            name: "imgURL";
            query: "link/string()";
        }
    }

There are 2 parts, the first is the "query". This tells the model what types in the XML to look for. The picture from Imgur are "items" so I tell the model to look inside data tags for item tags.

The second part is a set of XmlRoles. XmlRoles convert the info in the Xml tags into key value pairs for other components to consume. So, I tell it to make a key called "imgURL" that is paired with value of whatever string is stored in the "link" tag. In the imgur API, "link" is a url that goes directly to the image.

But where does the XML come from? Typically, you would use the source property to point the XmlListModel to a url with xml. But this won't work with the imgur API because you need to set a header in the http request that includes your client idea for the API. Well, at least I couldn't figure out how to tell the XmlListModel to set a value in the header.

However, I didn't fret. Instead I used good old javascript XMLHttpRequest to get the XML, and then to set the xml property for the list model. So I made an init() function that runs when the app is ready.

    function init()
    {
        var req = new XMLHttpRequest();
        var location = "https://api.imgur.com/3/gallery/r/funny/time/1.xml";
        req.open("GET", location, true);
        req.setRequestHeader('Authorization', 'Client-ID xxxxxxxx');
        req.send(null);
        req.onreadystatechange = function()
        {
            if (req.readyState == 4)
            {
                imagesXML.xml = req.responseText;
                activityIndicator.running = false;
                activityIndicator.visible = false;
                imagesView.visible = true;
            }
        };

This function makes a request, and when it gets a response it sets the XmlListModel's xml property to the responseText. Again, this is very classic AJAXy programming. Then I do a few lines of setting the UI. You may notice that this includes making the imagesView visible. imagesView is the list view that I created to be the view for the model.

        ListView
        {
            visible: false;
            id: imagesView;
            model: imagesXML;

            orientation: Qt.Horizontal;
            anchors.fill: parent;
            delegate: MouseArea
            {
                height: parent.height;
                width: root.width;

                Image
                {
                    width: root.width;
                    height: parent.height;
                    fillMode: Image.PreserveAspectFit;
                    source: imgURL;
                }

                onClicked:
                {
                    if(children[0].fillMode == Image.Pad)
                    {
                        children[0].fillMode = Image.PreserveAspectFit;
                    }
                    else
                    {
                     children[0].fillMode = Image.Pad;
                    }
                }
            }
        }

A ListView has 2 key parts. The first part specifies the model for the view using the model property. Easy enough.

The second part is the "delegate". A delegate is the component that you create for each entry in the model. As you can see, I chose to make a MouseArea for each imgurURL. The MouseArea, in turn, contains an Image for displaying the image. The MouseArea is set up so I can do some interactions with the Image as desired. Currently, a tap toggles the Image between resizing to fit the size of the Image and displaying the Image at it's normal size.

The great thing about the ListView is that it is a Flickable. So I get the nice flicking behavior of scrolling the list left and right for free! The ListView does other key things, especially, loading the images on demand. It only loads the images when they are scrolled into view. This saves a lot of network and memory resources.

So, for the next features, I think I shall allow the user to choose a subreddit to browse.

Wednesday, March 27, 2013

Time Killer Extrordinaire

Every morning I update to the new Ubuntu Touch Image [change log] on my Nexus 7. Then at lunch, I  "use" it to check Facebook and read funny posts. Lunch time time killing use case nailed.

Tuesday, March 19, 2013

Sweet Ubuntu Device QtCreator Integration

I spent a bit of today using the Ubuntu Device integration features in Qt. It's fresh software, but it's really easy and fun. Here is the development version of the game I am running running on my desktop. Noticed that I set the size of the window and therefore the play area very intentionally. But, I had to think to myself "will the touch interactions work ok on my tablet?" "What about the sizes?". Fortunately, getting it onto my tablet is pretty easy.

There is a device button in the left hand channel of QtCreator. I connected my Nexus 7 to my desktop via USB. Clicked Detect Devices, and there it is! Look at the many buttons that will make managing my device easier. For example, I am looking forward to trying the Upgrade to Daily Image button tomorrow. 

So, how do I run it on my Nexus 7? I just use the command to do so! Notice there are other cool commands there too to try later. 
After picking "Run on Device" my app showed up on my tablet. As you can see from the screenshot, it had some issues! However, the touch screen worked the way I was hoping. Obviously, I need to think more about sizing and containment to make it all work correctly. Fortunately, it will be very easy to test it all.

Of course, I wanted a screenshot for this post. But how would I get that? With the Tools -> Ubuntu -> Device menu, of course! This menu has some other useful functions for managing the device. For example, the apt-get menu will allow me to install dependencies for my app.
All in all, I'm really pleased with the Ubuntu Device integration. It seems like will help make app development for my tablet and phone easy and fun.

Extract Class Refactor Built in QtCreator

Following up from my post about how I think about inheritance yesterday, I thought I'd do a quick post about a refactoring feature built into the QtCreator editor.

In this example, I decided I wanted to add a box that lets the user enter a name for a high score if they achieved it, and then to display all the high scores. I got started by creating an UbuntuShape with a column and sub components in main.qml, but quickly realized that I will have a lot of behavior and presentation to manage. This would be much easier to develop in it's own component (or "class" as I think of it).

So, I just right clicked in the editor and used the refactoring menu to "Move Component into Separate File." 
I got a dialog that asked for a name, I chose "HighScoreBox" and it created a new file for me, and replaced all of my QML code in main.qml with just the little bit of code needed to declare the object.

Now I am ready to properly develop the behavior for the component. Like any good refactoring tool, the code kept working. 




Monday, March 18, 2013

How I Learned to Love QML and Inheritance Therein

Gotta love the "developer art" ... those placeholder images should be replaced by sweet Zombie artwork as the game nears completion.
For a long time I resisted the QML wave. I had good reasons for doing so at the time. Essentially, compared to Python, there was not much desktop functionality that you could access without writing C++ code to wrap existing libraries and expose them to QML. I liked the idea of writing apps in javascript, but I really did not relish going back to writing C++ code. It seemed like a significant regression. C++ brings a weird set of bugs around memory management and rogue pointers. While manageable  this type of coding is just not fun and easy.

However, things change, and so did QML. Now, I am convinced and am diving into QML.
  • The base QML libraries have pretty much everything I need to write the kinds of apps that I want to write.
  • The QtCreator IDE is "just right". It has an editor with syntax highlighting and an integrated debugger (90% of what people are looking for when they ask for an IDE) and it has an integrated build/run system.
  • There are some nice re-factoring features thrown in, that make it easier to be pragmatic about good design as you are coding. I also like the automatic formatting features.
  • The QML Documentation is not quite complete, but it is systematic. I am looking forward to more samples, though, that's for sure.

In my first few experiences with QML, I was a tiny bit thrown by the "declarative" nature of QML. However, after a while, I found that my normal Object Oriented thought processes transferred quite well. The rest of this post is about how I think about coding up classes and objects in QML.

In Python, C++, and most other languages that support OO, classes inherit from other classes. JavaScript is a bit different, objects inherit from objects. While QML is really more like javascript in this way, it's easy for me to think about creating classes instead.

I will use some code from a game that I am writing as an easy example. I have written games in Python using pygame, and it turned out that a lot of the structure of those programs worked well in QML. For example, having a base class to manage essential sprite behavior, then a sub class for the "guy" that the player controls, and various subclasses for enemies and powerups.

For me, what I call a QML "baseclass" (which is just a component, like everything else in QML) has the following parts:
  1. A section of Imports - This is a typical list of libraries that you want to use in yor code. 
  2. A definition of it's "isa"/superclass/containing component - Every class is really a component, and a compnent is defined by declaring it, and nesting all of it's data and behaviors in curly brackets.
  3. Paramaterizable properties - QML does not have contructors. If you want to paraterize an object (that is configure it at run time) you do this by setting properties.
  4. Internal compotents - These are essentially private properties used within the component.
  5. Methods - These are methods that are used within the component, but are also callable from outside the component. Javascript does, actually, have syntax for supporting private methods, but I'll gloss over that for now.
In my CharacterSprite baseclass his looks like:

Imports

 import QtQuick 2.0  
 import QtQuick.Particles 2.0  
 import QtMultimedia 5.0  

Rectangle is a primative type in QML. It manages presentation on the QML surface. All the code except the imports exists within the curly braces for Rectangle.

Paramaterizable Properties

   property int currentSprite: 0;  
   property int moveDistance: 10  
   property string spritePrefix: "";  
   property string dieSoundSource: "";  
   property string explodeParticleSource: "";  
   property bool dead: false;  
   property var killCallBack: null;

Internal Components

For readability, I removed the specifics.
   Repeater  
   {  
   }  
   Audio  
   {  
   }  
   ParticleSystem  
   {  
     ImageParticle  
     {  
     }  
     Emitter  
     {  
     }  
   }  

Methods

With implementation removed for readability.
   function init()   
   {   
    //do some default behavior at start up   
   }   
   function takeTurn(target)   
   {   
    //move toward the target   
   }   
   function kill()   
   {   
    //hide self   
    //do explosion effect   
    //run a callback if it has been set   
   }   

Now I can make a zombie component by creating a new file called ZombeSprite.qml and simply set some properties (and add some behavior as desired). Note that I declare this component to be a CharacterSprite instead of a Rectangle as in the CharacterSprite base class. For me, that is the essence of defining inheritance in QML.

 CharacterSprite  
 {  
   spritePrefix: "";  
   dieSoundSource: "zombiedie.wav"  
   explodeParticleSource: "droplet.png"  
   Behavior on x { SmoothedAnimation{ velocity:20}}  
   Behavior on y { SmoothedAnimation{ velocity:20}}  
   height: 20  
   width: 20  
 }  

I can similarly make a GuySprite for the sprite that the player controls. Note that
I added a  function to Guy.qml becaues the guy can teleport, but other sprites can't.
I can call the kill() function in the collideWithZombie() function because it was inherited from the CharacterSprite baseclass. I could choose to override kill() instead by simply redefining it here.
 CharacterSprite   
  {   
   id: guy   
   Behavior on x { id: xbehavoir; SmoothedAnimation{ velocity:30}}   
   Behavior on y { id: ybehavoir; SmoothedAnimation{ velocity:30}}   
   spritePrefix: "guy";   
   dieSoundSource: "zombiedie.wav"   
   explodeParticleSource: "droplet.png"   
   moveDistance: 15   
   height: 20;   
   width: 20;   
   function teleportTo(x,y)   
   {   
    xbehavoir.enabled = false;   
    ybehavoir.enabled = false;   
    guy.visible = false;   
    guy.x = x;   
    guy.y = y;   
    xbehavoir.enabled = true;   
    ybehavoir.enabled = true;   
    guy.visible = true;   
   }   
   function collideWithZombie()   
   {   
    kill();   
   }   
  }  

So now I can set up the guy easily in the main qml scene just by setting connecting up some top level properties:
   Guy {  
     id: guy;  
     killCallBack: gameOver;  
     x: root.width/2;  
     y: root.height/2;  
   }