My own QML TreeView

It’s Valentine’s Day and here’s the point where I have to confess my love as a software engineer for QML. It’s a markup language for building simple modern UIs with Javascript controls, and can be bound to C++ and Python via Qt. Since it’s based on Qt it runs on pretty much any modern desktop or mobile platform you can think of.

But like any relationship, sometimes one is left wanting for more. Sure, QML is great but it has flaws that are hard to overlook. For example, there’s no “tree view” component (think: file system UIs, Windows RegEdit, etc.) which is a deal breaker for some use cases.

That deficiency ends today.

I’ve been busily working on my own tree view implementation, which you can find on GitHub. It supports drag and drop rearranging and folder creation with a mouse or touch interface. Like the iOS home screen, folders are limited to one level (i.e. no subfolders.)

Here’s the sample test harness in action:

The trick? It’s all a standard QML ListView with a special type of delegate, my own RearrangeableDelegate.

The items can be rearranged by pressing (or long-pressing, see update below) on them, then dragging to the desired space. If you position it between two items a line appears, and releasing the mouse positions the item at that location. Positioning on top of an item causes the two items to pop out into a new folder. Dragging the last item out of a folder deletes the folder. If you want to have special items at the top of the list that can’t be rearranged, that’s supported via the numStationary property.

Everything is designed to be styled to your liking. Want to change the drag border, the opener image, the indentation, etc? Easy! Just set some of RearrangeableDelegate’s existing properties and you’re good to go.

The UI state of each item is stored in the list model itself, which provides an easy (if somewhat hacky) way of maintaining the UI state with a database or settings file. Here’s what you need to provide, subject to change:

ListElement {
    // Unique id (integer)
    uid: 1;

    // Used for drag and drop UI. (Persistence not required.)
    dropTarget: "none";  

    // True if a folder, else false
    isFolder: false;     

    // -1 if not in a folder, else the uid of the parent
    parentFolder: -1;    

    // For folders, this indicates whether their children are
    // displayed. Otherwise, indicates if visible.
    folderOpen: true;
}

Best part: I’m giving away the entire thing for free under the MIT license, which ought to satisfy pretty much everyone (except for Richard Stallman.) Take my code and do what thou wilt. If you encounter a bug please file a new issue or fix it on your own and submit a Pull Request. Either way I — and perhaps other QML developers — will be eternally grateful for your ongoing efforts to make up for this missing QML component.

 
UPDATE: After convincing Hryx to do some user testing, we decided that long-pressing wasn’t discoverable enough for a desktop. So now there’s a flag called dragOnLongPress to control this behavior. By default it’s set to false so that a long press isn’t required to move an item around. You can set it to true in situations where a long press to move makes sense, such as on touch and mobile devices.