[Development] [QtQuick] Views Version 2 from Qt CS; animated layout

Rutledge Shawn Shawn.Rutledge at digia.com
Thu Jul 25 09:37:31 CEST 2013


On 24 Jul 2013, at 5:45 PM, Alan Alpert wrote:

> There's a rough outline of how we want views to look in QtQuick,
> because the current ListView is unmaintainable and not extensible. See
> http://qt-project.org/groups/qt-contributors-summit-2013/wiki/Qt-Quick-views-version-2
> for details.

It was already obvious for a long time that instantiation, layout and flicking behavior ought to have been separated from the beginning.  (At least Flickable is inherited, so hopefully we don't have to do a lot extra to get the touch events working in the views.)

Yesterday we were talking more about animating transitions between layouts.  I think we might be able to get there by giving a layout manager a list of items to manage (not necessarily a public property), and adding a layout: property to Item.  So if you declare

Rectangle {
    Repeater {
        id: peter
        anchors.fill: parent
        layout: FlowLayout { anchors.fill: peter; spacing: … }
        model: …
        Rectangle { … }
    }
}

then the Repeater will give the FlowLayout the repeater's children as its list of items to manage.  Later you could assign a different layout, and when the property changes it will give the items to the second layout to manage.  The delegate Rectangle could have its own state transitions or Behavior on x and y or something like that to animate from its position as determined by the first layout to its position determined by the second.  That part needs more thought.

The layout must have correct geometry, but it doesn't have to get it from its parent.  The layout would not be required to be in the item containment hierarchy.  Yet the app author can still easily see which items have which layouts, because of the layout: declaration.

The current way is that the items must be children of the layout, so you can't switch layouts without reparenting all of the items.  That way could continue to work for the simple use case: if the layout has children then it will assign them to its own list of items-to-manage.

The QQuickLayoutAttached will have to continue to be universal, having properties that apply to any kind of layout.  But it would be useful to have a mechanism to change the properties depending on which layout is applied, so e.g. if you switch a device from portrait to landscape, the layout can change, and the items can also change their preferred width/height etc.

Yesterday I began to write a file manager to test the DnD behavior, and was thinking about how one fundamental usability feature which has been present in the Mac finder from the beginning (and missing from many other file managers) is that in the icon view you can rearrange the icons however you want, and the OS will remember those positions.  (Spatial memory might help you to organize sub-parts of a project or something like that.)  They are not forced to sit on a grid all the time, and yet there is a handy "cleanup" command which will arrange them to a grid on-demand.  (In OS X it's been extended to have Cleanup by Name etc, which basically sorts the items at the same time, and you can also toggle "Arrange by name" etc. to keep the sorted and grid-aligned all the time.)  So generalizing this idea provides some further motivation to have switchable layout managers (including switching to no layout at all), and animated position movements.  Since I cannot do that yet, in my file manager I cannot use layouts; I can set the position of each icon in component.onCompleted, and the user is free to drag the icons around afterwards.  The Cleanup command would have to be implemented in Javascript.  (Fortunately a flow layout is easy to implement.)

The file manager, along with many other types of "infinite canvas" drawing and diagramming applications, need 2-dimensional panning functionality, so the Flickable seems a natural fit.  And many of those could benefit from dynamically creating and destroying delegates too, to save memory when there are a lot of objects in the model, because the Flickable could manage a really large area.  So the instantiation needs to support creating and destroying delegates in both dimensions (whereas ListView, GridView and PathView only create at one end and destroy at the other, depending on the single direction of movement).

Under Controller you have viewport and reaction to input, but Flickable takes care of those.  Do you think there's anything wrong with that?  And then there is the selection model.  So far it has been enough in such cases for each item to have its own MouseArea and to remember its own selection state, but it would be nice to have a means to implement a lasso tool, and a means of maintaining a list of selections, although it's possible to iterate all the children and find the ones that are selected, and it's also possible to maintain a separate list in JS.

The Instantiator name is already taken, but was it designed to be used for views?

Come to think of it, the trouble with Repeater has always been that it's in the containment hierarchy, but it's an interloper.  It's similar to how the layout has a behavioral role, and therefore should not be required to be in the containment hierarchy.  If you want to make a component which is something like a ListView, but you want to customize it by rebuilding it from scratch, then you might use a Repeater to instantiate the rows in the list.  You will soon run into the trouble that each item's parent is the Repeater, not the list component which you were trying to create.  This must be fixed.  So hopefully the "new view" will make the Repeater obsolete, and we need to start upholding the design pattern that the parent of a delegate or inner component should always be the useful parent component (the one the user is trying to create), not the layout or the repeater.  So most of these interchangeable components should be properties.  You could do something like this:

Viewport {
    id: vp
    View {
        id: view
        model: ...
        delegate: Rectangle { 
            id: del
            MouseArea {
                anchors.fill: parent
                onClicked: view.selection.toggle(del)
                drag.target: del
            }
        }
        instantiator: ...
        layout: FlowLayout { anchors.fill: parent; spacing ? }
        controllers: [
            MultipleSelectionArea {
                anchors.fill: view
                target: view.selection
            }
            FlickArea {
                anchors.fill: vp
                target: vp
            }
        ]
    }
}

• Every child of the view can see that its parent is the view itself: no interlopers (such as layout or repeater)

• The view does NOT contain children other than the ones the user wants to see; so if you iterate the children you won't have to check the type of each

• There can be multiple controllers, to handle as many behaviors as we like

• A Flickable has two roles: it's a viewport, which is why it is normally the view's parent, and it also does the flicking.  This can be split up further as shown, but I'm not sure we gain much from writing the extra declarations.  The View could just have a Flickable as its parent.  But splitting it up like this makes the design principle more consistent: the view hierarchy resembles the scene graph, while the behaviors are implemented by independent controllers which are NOT in the view hierarchy.  The same logic could be extended to the MouseArea: if every Item had a controllers list (or if we had a habit of hiding controllers in the data property), we could say that a controller is never a visual child of an item.

• MultipleSelectionArea would handle mouse and touch events which fall on any of the items in its target list, so dragging one drags them all.  If you drag somewhere else on its parent, then it would lasso-select items from the target.

• The MultipleSelectionArea could handle slow drags, and the FlickArea could handle quick drags.  They could have minimum/maximumVelocity properties.  Or the MultipleSelectionArea's lasso behavior could be configured to handle mouse events only, not touch events.

I don't think the "controller" can be just one thing, because there can be a lot of separate behaviors, done with separate gestures: flicking/panning, lasso selection, drawing new items, dragging individual items, dragging multiple selections, etc.




More information about the Development mailing list