Monday, April 15, 2013

FingerPaint, my 20 minute app

Here's a quick demo of using InkCanvas.

First I create a rectangle to fill the window to make everything white. Then I made an InkCanvas and set the InkWidth to something like a kid would finger paint:
        InkCanvas
        {
            id: inkCanvas
            width:parent.width
            height:parent.height - paintPotSize
            inkWidth: 30
        }


So, now that I have an InkCanvas component, the inking is the easy part. I spent most of the 20 minutes working on the paint color selector.

Those blocks along the bottom are MouseAreas filled with UbuntuShapes. So I just respond to the clicked signal and set the InkCanvas's inkColor property ...

Using a Repeater, I can set them up like this:
        Row
        {
            width: parent.width
            height: paintPotSize
            Repeater
            {
                model: [Qt.rgba(1,0,0,.5), Qt.rgba(0,1,0,.5), Qt.rgba(0,0,1,.5),
                        Qt.rgba(1,1,0,.5), Qt.rgba(1,0,1,.5), Qt.rgba(0,1,1,.5)]
                MouseArea
                 {
                    height: paintPotSize
                    width: paintPotSize
                    onClicked: {inkCanvas.inkColor = modelData}
                    UbuntuShape
                    {
                        anchors.fill: parent
                        color: modelData
                    }
                }
            }
        }

Tada ... a finger paint program suitable for kids to get their grubby mits all over your device in 20 minutes :)

Code is here

Introducing InkCanvas

So, I drew a beard on Tard today!  Furthermore, I did it with just a few lines of QML. Here's the whole program for drawing on Tard ...

MainView {
    // objectName for functional testing purposes (autopilot-qt5)
    objectName: "mainView"
    applicationName: "InkArea"
    
    width: units.gu(100)
    height: units.gu(75)


    Image
    {
        source: "grumpycat.jpg"
        anchors.fill: parent;
        fillMode: Image.PreserveAspectFit
    }

    InkCanvas
    {
        anchors.fill: parent
        inkColor: Qt.rgba(0, 0, 0)
        inkWidth: 15
    }
}

I just added an InkCanvas to my MainView and covered the Image with it. Simple, right? Well, you may have guessed there is slightly more to it than that. Where did InkCanvas come from? InkCanvas is a custom component that I wrote in pure QML to allow users to draw an a surface.

You may be aware of my long interest in free from editing applications. Remember Photobomb?
So, I decided to try my hand at collecting Ink in QML. There was some surprising complexity in getting it to work and work quickly, and it is still very much a work in progress. None the less, I want to invite people to:

  • Download and use InkCanvas in their apps if they want. I hope it unlocks some fun things for people to do.
  • Contribute to making InkCanvas better. Extend it, fix it, break it, etc...

Note that it currently doesn't work perfectly on my Nexus7. Something seems to break the canvas when Ink starts getting drawn :(
I logged a bug about this, but for all I know, it has something to do with the way I am abusing the Canvas and Stroke Components. Though it does work fine on my desktop.

Easy Task Navigation With PageStack (Fixed)


Thanks to an update to the documentation for Ubuntu Components, I discovered that I was using PageStack all wrong. I've gone ahead and deleted my old blog post about PageStack, and now here is a corrected one.

My Feedzilla app has a very simply page by page structure. Each bit of UI that the user interacts with is a wholly separate task so can take over the whole screen to present to the user.

Turns out, that Ubuntu Components have a component to support this in a standard, and very convenient, manner. It's called PageStack.

Here's how it works.

First, I created a PageStack component, and named it "rootStack". The categories view is always the starting point of the app, so I made it a Page inside the PageStack, and named it "rootPage". Then I added SubCategoriesPage (which is a ListView that shows the list of Technology sub-categories from Feedzilla:

It looks like this.
    PageStack
    {
        id: rootStack
        Page
        {
            title: "Categories"
            id: rootPage
            SubCategoriesComponent
            {
                id: subCategoriesComponent
                anchors.fill: parent
            }
        }
    }

Now, even though rootPage is visible, it's not actually on the PageStack yet. PageStack is a "stack" in the programming sense. That means you can push items on top of the stack, and pop items off. So I need to write some code to push rootPage onto the PageStack. I do this by adding an onCompleted handler to the PageStack and pushing the rootPage onto the PageStack there.

    PageStack
    {
        id: rootStack
        Page
        {
            title: "Categories"
            id: rootPage
            SubCategoriesComponent
            {
                id: subCategoriesComponent
                anchors.fill: parent
            }
        }
      Component.onCompleted:
      {
            push(rootPage)
      }
   }

So, how does the user navigate? I added a signal to SubCategoriesComponent for when the user has selected a category.

I made two other Components. One for displaying a list of articles and one for displaying articles themselves. I host these components inside of pages. I also set the pages to be invisible.


    Page
    {
       visible: false
       id: articlesListPage;
       ArticlesListComponent
        {
            id: articlesList
            anchors.fill: parent;
            onArticleSelected:
            {
                articleComponent.url = url
                rootStack.push(articlePage)
            }
            onTitleChanged:
            {
                articlesListPage.title = articlesList.title
            }
        }
      }
      Page
      {
        visible: false
        id: articlePage
        ArticleComponent {
            id: articleComponent
            anchors.fill: parent;
        }
      }

When the user selects a category, they need to see a list of articles. I made ArticlesListComponent todo that job. When they select a specific article, the user should see the article, I created ArticleComponent for that. Note:

So, getting back to the story, how do we push these onto the PageStack?

        Page
        {
            title: "Categories"
            id: rootPage
            SubCategoriesComponent
            {
                id: subCategoriesComponent
                anchors.fill: parent
                onSubCategorySelected:
                {
                    articlesList.subCategoryId = subCategoryId;
                    rootStack.push(articlesListPage)
                }
            }
        }

You may recall that I added a onSubCategorySelected signal to my SubCategoriesComponent component. All I have to do is respond to that signal. First I configure my ArticlesListPage to use the sunCategoryId passed into the signal handler. Then I tell the PageStack to push articlesListPage (the instance of ArticlesListPage declared in the QML above). Pushing 'pushes' pushes the component on top of the categories list.

I use similar code to push the ArticlesComponent when the user selects an article.
            onArticleSelected:
            {
                articleComponent.url = url
                rootStack.push(articlePage)
            }

The ArticleComponent can only be on top, so it doesn't push anything.

But what about going back? That's where popping comes in. I added an action to rootPage which simply tells rootStack to pop. Pop is opposite of push. Instead of adding something to the top of the stack, it takes whatever is on top off. It pops off the top. rootPage can't be popped off, though, because it is at the root. This makes the code easy. If I wanted to, I could handle popping myself with code like this:
            tools: ToolbarActions
            {
                Action
                {
                    id: backAction
                    text: "Back"
                    iconSource: "back.png"
                    onTriggered:
                    {
                        rootStack.pop();
                    }
                }
            }
     

However, this is not necessary because PageStack automatically creates and maintains a back button for me!

I pushed a branch with my corrected code.

Tuesday, April 2, 2013

ListView with JSON model (and the world's ugliest application)


First, I want to extend my deepest apologies for how ugly this app is at the moment. It's embarrassing. Sorry. I will put lipstick on it later, I hope.

This app is for browsing technical news from Feedzilla. It has 3 pages, A page for categories (well, subcategories because Tech is itself a category in Feedzill), a list of articles, and WebView to display an article.
Main Category View

Article List View

Reading an Article in Embedded Webkit

However, I did want to blog about part of how I built it, because I think it is generally useful for folks to see how I made my ListViews from JSON.

I blogged before about how I used an XmlListModel to make a ListView, and how easy that was. However, for this App, I wanted to use a JSON feed from feedzilla. However, there is no JsonListView. So what did I do? Here's how I made the main category view.

To get started, I created a ListView, as usual:
    ListView
    {
        id: categoriesList
        anchors.fill: parent;
    }

It doesn't do anything yet, because there is no model set. Remember, a ListView's only purpose in life is to display data in a list format. The data that I want is available in JSON format, so, next I need to get the JSON and set the model of the ListView.

So, in onComplete method, I wrote some good old fashioned Ajaxy javascript.
    Component.onCompleted:
    {
        //create a request and tell it where the json that I want is
        var req = new XMLHttpRequest();
        var location = "http://api.feedzilla.com/v1/categories/30/subcategories.json"

        //tell the request to go ahead and get the json
        req.open("GET", location, true);
        req.send(null);

        //wait until the readyState is 4, which means the json is ready
        req.onreadystatechange = function()
        {
            if (req.readyState == 4)
            {
                //turn the text in a javascript object while setting the ListView's model to it
                categoriesList.model = JSON.parse(req.responseText)
            }
        };
    }

The key line is the one that sets the ListView's model to a javascript object:
categoriesList.model = JSON.parse(req.responseText)

This works because all of the javascript that is returned is a list. So, I fetched a list in Javascript Object Notation, parsed it, and used it as a model for a ListView. Easy, right?

So now that the ListView has a model, I can create a delegate. Remember, a delegate is a bit of code, like an anonymous method, that gets called for each element in the ListViews model, and creates a component for it. Whenever the model for the ListView changes, the delegate is called for each item in the model. Every time the delegate is called, it gets passed "index" which is the index of the current item being created. So the delegate uses that to get the correct element from the list of javascript objects.

    ListView
    {
        id: categoriesList
        anchors.fill: parent;
        delegate: Button
        {
            width: parent.width
            text: categoriesList.model[index]["display_subcategory_name"];
            onClicked:
            {
                subCategorySelected(categoriesList.model[index]["subcategory_id"])
            }
        }
    }

My ListView is just a row of buttons, and the text is defined as "display_subcategory_name" in the JSON that I got from the server. So I can index in and set the text like so:
            text: categoriesList.model[index]["display_subcategory_name"];

Bonus snippet! The code that I wrote is a component that gets used in a PageStack. I don't want the PageStack to have to know about how the category list is implemented, of course, so I want my component to emit a signal when when of the categories is selected by the user (which is by clicking a button in this implementation).

Adding signals to a component is really easy in QML. First, you declare the signal where you declare properties and such for your component:
    signal subCategorySelected(int subCategoryId);

In this case, the signal has a single parameter. A signal can as many parameters as you want.

Then, the onClicked function for each button "fires" the signal by calling it like a function:
                subCategorySelected(categoriesList.model[index]["subcategory_id"])

That means my PageStack can just listen for that signal:

                onSubCategorySelected:
                {
                    //do stuff with subCategoryId
                }
That's all you have to know to simplify your code with custom signals!