[Development] [QtQuick] Views Version 2 from Qt CS; animated layout
Alan Alpert
416365416c at gmail.com
Thu Jul 25 23:51:30 CEST 2013
On Thu, Jul 25, 2013 at 12:49 PM, Shawn Rutledge
<shawn.t.rutledge at gmail.com> wrote:
> On 25 July 2013 18:31, Alan Alpert <416365416c at gmail.com> wrote:
>> On Thu, Jul 25, 2013 at 12:37 AM, Rutledge Shawn
>> <Shawn.Rutledge at digia.com> wrote:
>>>
>>> On 24 Jul 2013, at 5:45 PM, Alan Alpert wrote:
>>>
> ...
>> Have you considered using the Positioner layouts for inspiration? They
>> have a similar set of add/move/remove transitions to the view classes,
>> and you don't need a special "positioner" property on Item to make use
>> of them. Just reparenting items to a different positioner and using an
>> add transition is enough for an animated change.
>
> But reparenting is cumbersome: it typically needs to be done via a for
> loop in JS, and takes time to run. So it seems easier to assign a new
> layout somewhere. The loop to "take over" the layout management of
> all the items will be in C++ at least.
>
> When you are building a reusable container component of some sort, the
> layout is sometimes just an implementation detail that you would
> prefer to hide. But you can't, if every item that you add to the
> container is actually added to the layout inside the container
> instead; then the items' parents are not the container, even though
> the application author declared them that way. Next thing you know,
> you have to tell application authors that they can't trust the parent
> property, and should refer to the container by ID. Or else you have
> to give up on layout classes and write the layout logic in JS just to
> flatten the containment hierarchy. For me this points to the
> conclusion that layout should be a plug-in behavior rather than trying
> to be the container.
If you make the layout logic implicit, then you're just telling
applications authors that they can't use anchors in case it gets laid
out. We have the semi-hidden item pattern for flickable already, and
it's better than making the x/y/anchors unusable. It's the same with
putting the layout logic in JS, you're taking total control over the
positioning of the items and ignoring anything that the developer has
set - which is just one reason why I'd argue you should never write
the layout logic in JS.
Remember that this is a scripting language for pixel-perfect UIs. It's
more important to have more insight and control over the pixel
positioning of items than it is to have proper component encapsulation
(the two are usually at odds).
>> Repeaters have no children
>
> OK you're right; I've forgotten some details since the last time the
> problem I'm thinking of came up. Maybe the problem was that there was
> a container component written with the expectation that items would be
> directly added to it, but the user put a Repeater inside instead. The
> container had a loop to iterate its children, and then it had to have
> an extra test to ignore the repeater (the abnormal child).
If you're iterating over children then you're doing something wrong
already. But I think there's a way to iterate over the generated items
instead of the children of the Item it gets reparented into, which
will always have to ignore other possible children if you're only
interested in the generated items.
> The automatic reparenting is still arguably unintuitive.
It's the Qt Magic Language, lots of crazy stuff happens behind the
scenes ;) . This leads to lots of cases where there's this arguably
more/less intuitive way of doing things, but once you learn it then
it's (usually) really efficient.
>> Also QtQuick can't depend
>> on QtQuick.Layouts, so there's a technical and conceptual problem from
>> that too.
>
> That's not a real obstacle though; I can think of more than one way to solve it.
I can't. Can you elaborate on at least one way to add a Layout type
property to QtQuick without any knowledge of QtQuick.Layouts? Without
moving Layouts into QtQuick of course.
>
>>> 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.
>>
>> Sounds like all you need is a convenient reparentAll() mechanism.
>
> It would still be a procedural hackaround. The more we stick to "what
> you declare is what you get", the easier it is for designer tools and
> such.
Depends on whether the reparent all mechanism is procedural. The below
list binding approach should be fine.
>> Does a binding on the children: property work?
>
> A binding to a list, so you can swap them?
Yes. I don't know if that currently works, but it should.
>> Delegates remembering their own selection state means that you lose
>> selection state the moment the item goes offscreen. So that's
>> unacceptable in a lot of cases. Selection is also about more than just
>> click-to-select, some usecases require selection to be managed or
>> controlled externally, which is where having a separate selection
>> model that can also be interacted with in C++ (like the existing
>> selection models) are ideal.
>
> I agree.
>
>>> 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
>>
>> This example does not actually include QQuickItem children at all
>> (except that View is a child of the Viewport), making it hard to see
>> either point. Any Item level parent relationship is being done custom
>> by the View type, or not.
>
> The delegates are not children of the View? If that was a ListView,
> they would be.
It's not explicit in the QML. If it was a ListView, the delegates
would be children of the contentItem, not of the ListView directly.
You *could* do the same thing with all the other items, my point is
that it's entirely implicit behavior in the implementation of View{}
and thus any "children organization" sort of problems are already
entirely in our control.
>>> • There can be multiple controllers, to handle as many behaviors as we like
>>
>> How do they interact? Like if I specify two FlickAreas with different sizes?
>
> If you mean that there are ways to shoot yourself in the foot, yeah
> probably. If you mean to divide up an item into regions using
> multiple FlickAreas, then the events should be delivered to the right
> instance based on location.
>
>>> • 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.
>>
>> What about interaction items, like MouseArea and MultiPointTouchArea,
>> which need to be in the visual hierarchy? It seems like a pointless
>> complication to me that "every Item" can take on certain interaction
>> behaviors by specifying controllers on it. It's much simpler to have
>> those behaviors as separate items, which conceptually simplifies Item
>> while also allowing the user greater manual control over the
>> interaction of multiple 'controllers'.
>
> Nesting Areas is a trial-and-error affair though. The photosurface
> example happens to work: you can drag items via the MouseArea and also
> do pinch zooming and rotation via the PinchArea; but if you nest the
> PinchArea and MouseArea the other way, it doesn't work anymore; or if
> you stack them in either order it doesn't work. I wouldn't be
> surprised if the present arrangement stopped working or became
> cumbersome to keep working at some point. The example exists so that
> we can remember which way is the one that happens to work, but it's
> not a good example of how to make declarations in such a way that you
> will intuitively understand why it works.
Interactions between complex elements is always going to be a bit of
trial-and-error, which is why I like having the elements exposed in a
more direct fashion. This allows the app developer to do extensive
trial-and-error until it works for them, as opposed to having it
buried in a list where you feel stuck after you tried swapping the
order.
>
>>> • 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.
>>
>> How do they communicate? They don't even have a reference to each other,
>
> Events get delivered recursively anyway, so if one area rejects an
> event based on velocity or some other property that is out of range,
> the other area will get a shot at handling it. But the list could be
> treated as an order of precedence. (And I'm not sure that's intuitive
> enough, either.) The list of controllers is just a brainstorm, but I
> think it will be limiting to say that there is only one controller per
> View.
Depends how you define "controller" and "view", both things we're
trying to define. If is isn't going to be arguably a controller in the
classic Model-View-Controller paradigm, I'd prefer a different term to
avoid confusion. In the sketchy diagram I had, it was actually
supposed to be that kind of controller, if only because MVC is further
along in design than our current Views 2 ideas.
>> The problem with splitting them up like this has always been about how
>> the communication is managed between components without unacceptable
>> overhead. The biggest example being how the Instantiator, Layout and
>> ViewPort need to work together to determine which items are on screen
>> and need delegates. Might even need to also communicate with the
>> controller to preload incoming items while scrolling. If everything
>> goes through the View, like it seems to in your example, then it's
>> still a big monolithic class that knows everything and we've just
>> added more customizability to it (which might be enough, I don't
>> know).
>
> Well what architecture did you have in mind? I wasn't able to be at
> QtCS, so sorry if it's asking for too much repetition. I agree that
> decoupling is good.
My point, and this was the same point made at QtCS, is that I don't
know what architecture is needed for this. I was so excited to see
your mockup, until I realized it's does not appear to be solving this
problem. Further research is required.
--
Alan Alpert
More information about the Development
mailing list