Merge lp:~zsombi/ubuntu-ui-toolkit/79-more-simplification into lp:ubuntu-ui-toolkit/staging

Proposed by Zsombor Egri
Status: Merged
Approved by: Tim Peeters
Approved revision: 1420
Merged at revision: 1406
Proposed branch: lp:~zsombi/ubuntu-ui-toolkit/79-more-simplification
Merge into: lp:ubuntu-ui-toolkit/staging
Prerequisite: lp:~zsombi/ubuntu-ui-toolkit/78-action-property
Diff against target: 1738 lines (+485/-462)
15 files modified
components.api (+2/-6)
modules/Ubuntu/Components/Themes/Ambiance/ListItemPanel.qml (+15/-10)
modules/Ubuntu/Components/Themes/Ambiance/ListItemStyle.qml (+2/-8)
modules/Ubuntu/Components/plugin/propertychange_p.cpp (+24/-20)
modules/Ubuntu/Components/plugin/propertychange_p.h (+5/-4)
modules/Ubuntu/Components/plugin/ucactionpanel_p.cpp (+4/-2)
modules/Ubuntu/Components/plugin/uclistitem.cpp (+256/-262)
modules/Ubuntu/Components/plugin/uclistitem.h (+29/-5)
modules/Ubuntu/Components/plugin/uclistitem_p.h (+39/-79)
modules/Ubuntu/Components/plugin/uclistitemattached.cpp (+1/-17)
modules/Ubuntu/Components/plugin/uclistitemstyle.cpp (+6/-2)
modules/Ubuntu/Components/plugin/uclistitemstyle.h (+5/-4)
modules/Ubuntu/Components/plugin/ucviewitemsattached.cpp (+2/-8)
tests/resources/listitems/ListItemTest.qml (+11/-5)
tests/unit_x11/tst_components/tst_listitem.qml (+84/-30)
To merge this branch: bzr merge lp:~zsombi/ubuntu-ui-toolkit/79-more-simplification
Reviewer Review Type Date Requested Status
Tim Peeters Approve
PS Jenkins bot continuous-integration Approve
Review via email: mp+247798@code.launchpad.net

Commit message

Second stage of simplification - divider group property is an item, simplifying the anchoring of the contentItem. PropertyChange does not backup the property binding, uses QQmlPropertyPrivate::write() with parameter to keep bindings. ListItemAnimator uses behavior to animate snapping. Styling got "styledItem" context property to obey general styling rules. clicked() and pressAndHold() emission fix when content is swiped.

To post a comment you must log in.
Revision history for this message
Zsombor Egri (zsombi) wrote :

More simplification in an upcoming MR.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Tim Peeters (tpeeters) wrote :

52 + // anchor to the top of the item but to the bottom of the contentItem, so we do not draw over the divider
53 + top: styledItem.top
54 + bottom: styledItem.contentItem.bottom
55 + bottomMargin: -styledItem.contentItem.anchors.bottomMargin

Why does the panel need to take the bottomMargin of the contentItem into account? If the contents of the listitem has a bottom margin, the panel should still cover the whole contentItem

review: Needs Information
Revision history for this message
Tim Peeters (tpeeters) wrote :

56 + // adjust margins
57 + leftMargin: leading ? 0 : styledItem.contentItem.anchors.rightMargin
58 + rightMargin: leading ? styledItem.contentItem.anchors.leftMargin : 0

what does this do? I don't understand why the left/right margins are needed here

Revision history for this message
Tim Peeters (tpeeters) wrote :

66 - leftMargin: (leading && panel.ListItem.item) ? -units.gu(4 * panel.ListItem.item.swipeOvershoot) : 0
67 - rightMargin: (!leading && panel.ListItem.item) ? -units.gu(4 * panel.ListItem.item.swipeOvershoot) : 0
68 + leftMargin: (leading && styledItem) ? -units.gu(4 * styledItem.swipeOvershoot) : 0
69 + rightMargin: (!leading && styledItem) ? -units.gu(4 * styledItem.swipeOvershoot) : 0

Is the swipeOvershoot defined in grid units?
The docs don't say that:

/*!
 * \qmlproperty real ListItem::swipeOvershoot
 * The property configures the overshoot value on swiping. Its default value is
 * configured by the \l {ListItemStyle}{style}. Any positive value overrides the
 * default value, and any negative or undefined value resets it back to the default.
 */

So I'd rather have it in pixels. You can still set it like swipeOvershoot: units.gu(2) then.

Also, why is it multiplied by 4 here?

review: Needs Information
Revision history for this message
Tim Peeters (tpeeters) wrote :

253 -UCListItemSnapAnimator::UCListItemSnapAnimator(UCListItem *item)
254 - : QObject(item)
255 - , item(item)
256 +ListItemAnimator::ListItemAnimator(QObject *parent)
257 + : QObject(parent)
258 + , activeAnimations(0)
259 + , item(0)

why did you rename this class?

Revision history for this message
Tim Peeters (tpeeters) wrote :

105 + snapAnimation: UbuntuNumberAnimation {
106 + duration: UbuntuAnimation.SnapDuration
107 }

I would prefer a velocity instead of a duration (or a duration depending on the number of pixels it needs to move). As it is now, when you release really close to the end-state (so not much to animate), the animation appears quite slow.

But, this is something to discuss with design, and, if we change it, to implement in a separate MR.

Revision history for this message
Tim Peeters (tpeeters) wrote :

506 +class UCListItemDividerPrivate : public QQuickItemPrivate

why inherit from QQuickItemPrivate and not QQuickItem?

review: Needs Information
Revision history for this message
Tim Peeters (tpeeters) wrote :

803 + // udpate divider

typo

and maybe this commend doesn't add information that is not clear from the code

804 + divider->setImplicitHeight(UCUnits::instance().dp(DIVIDER_THICKNESS_DP));

Revision history for this message
Tim Peeters (tpeeters) wrote :

1017 + * ListItem's bottom in case teh divider is not visible. The content is clipped

your favorite typo :)

Revision history for this message
Tim Peeters (tpeeters) wrote :

1020 + * An example where 2 grid units on left and right, as well as 0.5 grid units
1021 + * top and bottom margins are required:
1022 + * \qml
1023 + * import QtQuick 2.3
1024 + * import Ubuntu.Components 1.2
1025 + *
1026 + * ListItem {
1027 + * contentItem.anchors {
1028 + * leftMargin: units.gu(2)
1029 + * rightMargin: units.gu(2)
1030 + * topMargin: units.gu(0.5)
1031 + * bottomMargin: units.gu(0.5)
1032 + * }
1033 + * }
1034 + * \endqml

Just a little detail (maybe a matter of taste): I would replace the introduction sentence on l.1020,1021 by: "Example: ". The rest is clear enough from the code.

Do we have guidelines when to include the "import" statements? On a previous MR we decided not to include them for code snippets.

Revision history for this message
Tim Peeters (tpeeters) wrote :

1055 + * the starting and ending colors of the divider. Beside thes properties all Item
1056 + * specific properties can be accessed.

*thes

Revision history for this message
Tim Peeters (tpeeters) wrote :

1569 + function test_verify_no_click_when_swiped() {
1583 + function test_verify_no_pressAndHold_when_swiped() {

I prefer as function names: test_no_click_when_swiped() and test_no_pressAndHold_when_swiped(). All tests are there to verify something.

Revision history for this message
Tim Peeters (tpeeters) wrote :

1621 + TestExtras.touchRelease(0, listItem, Qt.point(listItem.width / 2, dy));
1622 + wait(400);

Instead of wait(400), can we wait for contentMoving to become false?

Revision history for this message
Tim Peeters (tpeeters) wrote :

1745 + wait(200)

Again, can we wait for contentMoving == false instead?

Also missing semicolon.

Revision history for this message
Zsombor Egri (zsombi) wrote :

> 52 + // anchor to the top of the item but to the bottom of the
> contentItem, so we do not draw over the divider
> 53 + top: styledItem.top
> 54 + bottom: styledItem.contentItem.bottom
> 55 + bottomMargin: -styledItem.contentItem.anchors.bottomMargin
>
>
> Why does the panel need to take the bottomMargin of the contentItem into
> account? If the contents of the listitem has a bottom margin, the panel should
> still cover the whole contentItem

Exactly, that's why is taken with negative value, so the panel covers the area till the divider. I cannot anchor to the divider because it is not a sibling, so I had to do it this way.

Revision history for this message
Zsombor Egri (zsombi) wrote :

> 56 + // adjust margins
> 57 + leftMargin: leading ? 0 :
> styledItem.contentItem.anchors.rightMargin
> 58 + rightMargin: leading ?
> styledItem.contentItem.anchors.leftMargin : 0
>
> what does this do? I don't understand why the left/right margins are needed
> here

This makes sure the panel takes into account the margins of the contentItem, so when you swipe, the panel won't jump in to the edge of the contentItem, but will be swiped in like it would be "glued" to the edge of the ListItem.

From this MR on, the ListItem.contentItem.anchors.margins alteration is allowed.

Revision history for this message
Zsombor Egri (zsombi) wrote :

> 66 - leftMargin: (leading && panel.ListItem.item) ?
> -units.gu(4 * panel.ListItem.item.swipeOvershoot) : 0
> 67 - rightMargin: (!leading && panel.ListItem.item) ?
> -units.gu(4 * panel.ListItem.item.swipeOvershoot) : 0
> 68 + leftMargin: (leading && styledItem) ? -units.gu(4 *
> styledItem.swipeOvershoot) : 0
> 69 + rightMargin: (!leading && styledItem) ? -units.gu(4 *
> styledItem.swipeOvershoot) : 0
>
>
> Is the swipeOvershoot defined in grid units?
> The docs don't say that:
>
> /*!
> * \qmlproperty real ListItem::swipeOvershoot
> * The property configures the overshoot value on swiping. Its default value
> is
> * configured by the \l {ListItemStyle}{style}. Any positive value overrides
> the
> * default value, and any negative or undefined value resets it back to the
> default.
> */
>
> So I'd rather have it in pixels. You can still set it like swipeOvershoot:
> units.gu(2) then.
>
> Also, why is it multiplied by 4 here?

Yeah, it supposed to be in pixels, but tbh, this whole overshooting did not work ever properly. It'll be removed completely, like the whole ListItemPanel.qml in the next MR. The multiplication was needed to be able to cover the area on the eventual spring animation bouncing, when the panel was too short and the background of the ListItem got visible :/

So, no more overshoot in the next MR, should we still bother fixing this?

Revision history for this message
Zsombor Egri (zsombi) wrote :

> 253 -UCListItemSnapAnimator::UCListItemSnapAnimator(UCListItem *item)
> 254 - : QObject(item)
> 255 - , item(item)
> 256 +ListItemAnimator::ListItemAnimator(QObject *parent)
> 257 + : QObject(parent)
> 258 + , activeAnimations(0)
> 259 + , item(0)
>
> why did you rename this class?

I was thinking that I should use this class for something other than just snapping animator, to control the animations for selection, drag panel and expand/collapse. But then I realised that there is too much functionality is hardcoded in the ListItem, so I stopped doing that. But then reverting it would have been a pain, so I stabilised it and saved it as is. It'll disappear in the next MR as well.

Revision history for this message
Zsombor Egri (zsombi) wrote :

> 105 + snapAnimation: UbuntuNumberAnimation {
> 106 + duration: UbuntuAnimation.SnapDuration
> 107 }
>
> I would prefer a velocity instead of a duration (or a duration depending on
> the number of pixels it needs to move). As it is now, when you release really
> close to the end-state (so not much to animate), the animation appears quite
> slow.
>
> But, this is something to discuss with design, and, if we change it, to
> implement in a separate MR.

Uhm, I don't get what you want here. Animations don't have velocity property, maybe you want to upstream that :)

But yes, if you release close to the end, that is what it happens. Easing curves are the ones which can speed up when reaching the target position, and we must tweak it to get a decent behaviour. Will also suffer changes in the 79b MR.

Revision history for this message
Zsombor Egri (zsombi) wrote :

> 506 +class UCListItemDividerPrivate : public QQuickItemPrivate
>
> why inherit from QQuickItemPrivate and not QQuickItem?

Well, if you see this is the private API of the UCListItemDivider. As UCListItemDivider is derived from QQuickItem, it is recommended that its private also derives from QQuickItemPrivate to avoid duplicity of the d-pointers. In this way the UCListItem will have only one d_func() or d-pointer.

Revision history for this message
Zsombor Egri (zsombi) wrote :

> 803 + // udpate divider
>
> typo
>
> and maybe this commend doesn't add information that is not clear from the code
>
> 804 +
> divider->setImplicitHeight(UCUnits::instance().dp(DIVIDER_THICKNESS_DP));

Fixed.

Revision history for this message
Zsombor Egri (zsombi) wrote :

> 1017 + * ListItem's bottom in case teh divider is not visible. The content
> is clipped
>
> your favorite typo :)

Haha, you love it ;) Fixed.

Revision history for this message
Zsombor Egri (zsombi) wrote :

> 1020 + * An example where 2 grid units on left and right, as well as 0.5
> grid units
> 1021 + * top and bottom margins are required:
> 1022 + * \qml
> 1023 + * import QtQuick 2.3
> 1024 + * import Ubuntu.Components 1.2
> 1025 + *
> 1026 + * ListItem {
> 1027 + * contentItem.anchors {
> 1028 + * leftMargin: units.gu(2)
> 1029 + * rightMargin: units.gu(2)
> 1030 + * topMargin: units.gu(0.5)
> 1031 + * bottomMargin: units.gu(0.5)
> 1032 + * }
> 1033 + * }
> 1034 + * \endqml
>
> Just a little detail (maybe a matter of taste): I would replace the
> introduction sentence on l.1020,1021 by: "Example: ". The rest is clear enough
> from the code.

Ok, removed.
>
> Do we have guidelines when to include the "import" statements? On a previous
> MR we decided not to include them for code snippets.

Wherever there's a need to provide full examples, we do. In case a detail is explained like different usages or flags to be explained, we can have snippets. These can be either replacements on the topmost documentation's example (if one example is provided), or simple insertable code in an existing application. That's how Qt docs are made as well.

Revision history for this message
Zsombor Egri (zsombi) wrote :

> 1055 + * the starting and ending colors of the divider. Beside thes
> properties all Item
> 1056 + * specific properties can be accessed.
>
> *thes

Fixed.

Revision history for this message
Zsombor Egri (zsombi) wrote :

> 1569 + function test_verify_no_click_when_swiped() {
> 1583 + function test_verify_no_pressAndHold_when_swiped() {
>
> I prefer as function names: test_no_click_when_swiped() and
> test_no_pressAndHold_when_swiped(). All tests are there to verify something.

Right. I think at some point the test was failing if it wasn't executed at the end of the tests, I guess because of some leftovers of the other tests...

Revision history for this message
Zsombor Egri (zsombi) wrote :

> 1621 + TestExtras.touchRelease(0, listItem,
> Qt.point(listItem.width / 2, dy));
> 1622 + wait(400);
>
> Instead of wait(400), can we wait for contentMoving to become false?

This wait is not for that. It is to avoid double-click event to be triggered in the next tests. There's even a comment about that.

Revision history for this message
Zsombor Egri (zsombi) wrote :

> 1745 + wait(200)
>
> Again, can we wait for contentMoving == false instead?
>
> Also missing semicolon.

That is a leftover from a debugging. It is not needed.

1419. By Zsombor Egri

review comments applied

1420. By Zsombor Egri

staging merge

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Tim Peeters (tpeeters) wrote :

> > 66 - leftMargin: (leading && panel.ListItem.item) ?
> > -units.gu(4 * panel.ListItem.item.swipeOvershoot) : 0
> > 67 - rightMargin: (!leading && panel.ListItem.item) ?
> > -units.gu(4 * panel.ListItem.item.swipeOvershoot) : 0
> > 68 + leftMargin: (leading && styledItem) ? -units.gu(4 *
> > styledItem.swipeOvershoot) : 0
> > 69 + rightMargin: (!leading && styledItem) ? -units.gu(4 *
> > styledItem.swipeOvershoot) : 0
> >
> >
> > Is the swipeOvershoot defined in grid units?
> > The docs don't say that:
> >
> > /*!
> > * \qmlproperty real ListItem::swipeOvershoot
> > * The property configures the overshoot value on swiping. Its default value
> > is
> > * configured by the \l {ListItemStyle}{style}. Any positive value overrides
> > the
> > * default value, and any negative or undefined value resets it back to the
> > default.
> > */
> >
> > So I'd rather have it in pixels. You can still set it like swipeOvershoot:
> > units.gu(2) then.
> >
> > Also, why is it multiplied by 4 here?
>
> Yeah, it supposed to be in pixels, but tbh, this whole overshooting did not
> work ever properly. It'll be removed completely, like the whole
> ListItemPanel.qml in the next MR. The multiplication was needed to be able to
> cover the area on the eventual spring animation bouncing, when the panel was
> too short and the background of the ListItem got visible :/
>
> So, no more overshoot in the next MR, should we still bother fixing this?

No need to spend time on fixing something that will be removed tomorrow :)

Revision history for this message
Tim Peeters (tpeeters) wrote :

> > 105 + snapAnimation: UbuntuNumberAnimation {
> > 106 + duration: UbuntuAnimation.SnapDuration
> > 107 }
> >
> > I would prefer a velocity instead of a duration (or a duration depending on
> > the number of pixels it needs to move). As it is now, when you release
> really
> > close to the end-state (so not much to animate), the animation appears quite
> > slow.
> >
> > But, this is something to discuss with design, and, if we change it, to
> > implement in a separate MR.
>
> Uhm, I don't get what you want here. Animations don't have velocity property,
> maybe you want to upstream that :)
>
> But yes, if you release close to the end, that is what it happens. Easing
> curves are the ones which can speed up when reaching the target position, and
> we must tweak it to get a decent behaviour. Will also suffer changes in the
> 79b MR.

Some animations have a velocity property, like http://doc-snapshot.qt-project.org/qt5-5.4/qml-qtquick-smoothedanimation.html

But yeah I get the problem. An option would be set the speed as a function of the distance when the mouse is released.

Revision history for this message
Tim Peeters (tpeeters) wrote :

> > 506 +class UCListItemDividerPrivate : public QQuickItemPrivate
> >
> > why inherit from QQuickItemPrivate and not QQuickItem?
>
> Well, if you see this is the private API of the UCListItemDivider. As
> UCListItemDivider is derived from QQuickItem, it is recommended that its
> private also derives from QQuickItemPrivate to avoid duplicity of the
> d-pointers. In this way the UCListItem will have only one d_func() or
> d-pointer.

Where did you find that recommendation? It is weird, especially because http://code.woboq.org/qt5/qtdeclarative/src/quick/items/qquickitem_p.h.html contains this:

37 //
38 // W A R N I N G
39 // -------------
40 //
41 // This file is not part of the Qt API. It exists purely as an
42 // implementation detail. This header file may change from version to
43 // version without notice, or even be removed.
44 //
45 // We mean it.
46 //

Revision history for this message
Zsombor Egri (zsombi) wrote :

> > > 506 +class UCListItemDividerPrivate : public QQuickItemPrivate
> > >
> > > why inherit from QQuickItemPrivate and not QQuickItem?
> >
> > Well, if you see this is the private API of the UCListItemDivider. As
> > UCListItemDivider is derived from QQuickItem, it is recommended that its
> > private also derives from QQuickItemPrivate to avoid duplicity of the
> > d-pointers. In this way the UCListItem will have only one d_func() or
> > d-pointer.
>
>
> Where did you find that recommendation? It is weird, especially because
> http://code.woboq.org/qt5/qtdeclarative/src/quick/items/qquickitem_p.h.html
> contains this:
>
> 37 //
> 38 // W A R N I N G
> 39 // -------------
> 40 //
> 41 // This file is not part of the Qt API. It exists purely as an
> 42 // implementation detail. This header file may change from version to
> 43 // version without notice, or even be removed.
> 44 //
> 45 // We mean it.
> 46 //

Yes, true, the same is written into every private header. And we have to be prepared for that as well. And as we need to use plenty of privates, this is something we have to live with.

Revision history for this message
Tim Peeters (tpeeters) wrote :

okay

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'components.api'
2--- components.api 2015-02-06 11:49:35 +0000
3+++ components.api 2015-02-10 13:46:20 +0000
4@@ -888,17 +888,13 @@
5 prototype: "QObject"
6 Property { name: "actions"; type: "UCListItemActions"; isReadonly: true; isPointer: true }
7 Property { name: "visibleActions"; type: "UCAction"; isList: true; isReadonly: true }
8- Property { name: "item"; type: "UCListItem"; isReadonly: true; isPointer: true }
9 Property { name: "index"; type: "int"; isReadonly: true }
10 Property { name: "panelStatus"; type: "UCListItem::PanelStatus"; isReadonly: true }
11 Method {
12 name: "snapToPosition"
13 Parameter { name: "position"; type: "double" }
14 name: "UCListItemDivider"
15- prototype: "QObject"
16- Property { name: "visible"; type: "bool" }
17- Property { name: "leftMargin"; type: "double" }
18- Property { name: "rightMargin"; type: "double" }
19+ prototype: "QQuickItem"
20 Property { name: "colorFrom"; type: "QColor" }
21 Property { name: "colorTo"; type: "QColor" }
22 name: "UCListItemStyle"
23@@ -907,7 +903,7 @@
24 Property { name: "actionsDelegate"; type: "QQmlComponent"; isPointer: true }
25 Property { name: "selectionDelegate"; type: "QQmlComponent"; isPointer: true }
26 Property { name: "dragHandlerDelegate"; type: "QQmlComponent"; isPointer: true }
27- Property { name: "snapAnimation"; type: "QQuickPropertyAnimation"; isPointer: true }
28+ Property { name: "snapAnimation"; type: "QQuickAbstractAnimation"; isPointer: true }
29 Property { name: "swipeOvershoot"; type: "double" }
30 name: "UCMouse"
31 prototype: "QObject"
32
33=== modified file 'modules/Ubuntu/Components/Themes/Ambiance/ListItemPanel.qml'
34--- modules/Ubuntu/Components/Themes/Ambiance/ListItemPanel.qml 2015-01-13 14:41:37 +0000
35+++ modules/Ubuntu/Components/Themes/Ambiance/ListItemPanel.qml 2015-02-10 13:46:20 +0000
36@@ -53,14 +53,19 @@
37 Specifies whether the panel is used to visualize leading or trailing actions.
38 */
39 readonly property bool leading: ListItem.panelStatus == ListItem.Leading
40- readonly property real swipedOffset: leading ? width + x : ListItem.item.width - x;
41- readonly property bool swiping: ListItem.item.highlighted && ListItem.item.contentMoving
42+ readonly property real swipedOffset: leading ? width + x : styledItem.width - x;
43+ readonly property bool swiping: styledItem.highlighted && styledItem.contentMoving
44
45 anchors {
46- left: leading ? undefined : ListItem.item.contentItem.right
47- right: leading ? ListItem.item.contentItem.left : undefined
48- top: ListItem.item.contentItem.top
49- bottom: ListItem.item.contentItem.bottom
50+ left: leading ? undefined : styledItem.contentItem.right
51+ right: leading ? styledItem.contentItem.left : undefined
52+ // anchor to the top of the item but to the bottom of the contentItem, so we do not draw over the divider
53+ top: styledItem.top
54+ bottom: styledItem.contentItem.bottom
55+ bottomMargin: -styledItem.contentItem.anchors.bottomMargin
56+ // adjust margins
57+ leftMargin: leading ? 0 : styledItem.contentItem.anchors.rightMargin
58+ rightMargin: leading ? styledItem.contentItem.anchors.leftMargin : 0
59 }
60
61 Rectangle {
62@@ -68,15 +73,15 @@
63 anchors {
64 fill: parent
65 // add 4 times the overshoot margins to cover the background when tugged
66- leftMargin: (leading && panel.ListItem.item) ? -units.gu(4 * panel.ListItem.item.swipeOvershoot) : 0
67- rightMargin: (!leading && panel.ListItem.item) ? -units.gu(4 * panel.ListItem.item.swipeOvershoot) : 0
68+ leftMargin: (leading && styledItem) ? -units.gu(4 * styledItem.swipeOvershoot) : 0
69+ rightMargin: (!leading && styledItem) ? -units.gu(4 * styledItem.swipeOvershoot) : 0
70 }
71 color: panel.backgroundColor
72 }
73
74 // handle action triggering
75 Connections {
76- target: panel.ListItem.item
77+ target: styledItem
78 onContentMovementEnded: {
79 if (actionsRow.selectedAction) {
80 actionsRow.selectedAction.trigger(actionsRow.listItemIndex);
81@@ -125,7 +130,7 @@
82 leftMargin: spacing
83 }
84
85- property real maxItemWidth: panel.ListItem.item.width / panel.ListItem.visibleActions.length
86+ property real maxItemWidth: styledItem.width / panel.ListItem.visibleActions.length
87
88 property Action selectedAction
89 property int listItemIndex: -1
90
91=== modified file 'modules/Ubuntu/Components/Themes/Ambiance/ListItemStyle.qml'
92--- modules/Ubuntu/Components/Themes/Ambiance/ListItemStyle.qml 2015-01-12 15:44:59 +0000
93+++ modules/Ubuntu/Components/Themes/Ambiance/ListItemStyle.qml 2015-02-10 13:46:20 +0000
94@@ -23,13 +23,7 @@
95 swipeOvershoot: units.gu(2)
96 actionsDelegate: ListItemPanel{}
97
98- snapAnimation: PropertyAnimation {
99- property: "x"
100- easing {
101- type: Easing.OutElastic
102- period: 0.5
103- }
104- duration: UbuntuAnimation.BriskDuration
105+ snapAnimation: UbuntuNumberAnimation {
106+ duration: UbuntuAnimation.SnapDuration
107 }
108-
109 }
110
111=== modified file 'modules/Ubuntu/Components/plugin/propertychange_p.cpp'
112--- modules/Ubuntu/Components/plugin/propertychange_p.cpp 2014-11-06 13:53:46 +0000
113+++ modules/Ubuntu/Components/plugin/propertychange_p.cpp 2015-02-10 13:46:20 +0000
114@@ -25,16 +25,27 @@
115 * The class is used to save properties and their bindings while the property is
116 * altered temporarily.
117 */
118-PropertyChange::PropertyChange(QObject *item, const char *property)
119- : m_backedUp(false)
120+PropertyChange::PropertyChange(QObject *item, const char *property, bool autoBackup)
121+ : backedUp(false)
122 , qmlProperty(item, property, qmlContext(item))
123 {
124+ if (autoBackup) {
125+ backup();
126+ }
127 }
128 PropertyChange::~PropertyChange()
129 {
130 restore(this);
131 }
132
133+void PropertyChange::backup()
134+{
135+ if (!backedUp) {
136+ backupValue = qmlProperty.read();
137+ backedUp = true;
138+ }
139+}
140+
141 /*
142 * Sets a value to the property. Will back up the original values if it wasn't yet.
143 * This function can be called many times, it will not destroy the backed up value/binding.
144@@ -44,12 +55,11 @@
145 if (!change) {
146 return;
147 }
148- if (!change->m_backedUp) {
149- change->backup.first = QQmlPropertyPrivate::setBinding(change->qmlProperty, 0);
150- change->backup.second = change->qmlProperty.read();
151- change->m_backedUp = true;
152- }
153- change->qmlProperty.write(value);
154+ change->backup();
155+ // write using QQmlPropertyPrivate so we can keep the bindings
156+ QQmlPropertyPrivate::write(change->qmlProperty,
157+ value,
158+ QQmlPropertyPrivate::BypassInterceptor | QQmlPropertyPrivate::DontRemoveBinding);
159 }
160
161 /*
162@@ -60,17 +70,11 @@
163 if (!change) {
164 return;
165 }
166- if (change->m_backedUp) {
167- // if there was a binding, restore it
168- if (change->backup.first) {
169- QQmlAbstractBinding *prevBinding = QQmlPropertyPrivate::setBinding(change->qmlProperty, change->backup.first);
170- if (prevBinding && prevBinding != change->backup.first) {
171- prevBinding->destroy();
172- }
173- } else {
174- // there was no binding, restore previous value
175- change->qmlProperty.write(change->backup.second);
176- }
177- change->m_backedUp = false;
178+ if (change->backedUp) {
179+ // write using QQmlPropertyPrivate to keep the bindings
180+ QQmlPropertyPrivate::write(change->qmlProperty,
181+ change->backupValue,
182+ QQmlPropertyPrivate::BypassInterceptor | QQmlPropertyPrivate::DontRemoveBinding);
183+ change->backedUp = false;
184 }
185 }
186
187=== modified file 'modules/Ubuntu/Components/plugin/propertychange_p.h'
188--- modules/Ubuntu/Components/plugin/propertychange_p.h 2014-11-06 13:53:46 +0000
189+++ modules/Ubuntu/Components/plugin/propertychange_p.h 2015-02-10 13:46:20 +0000
190@@ -21,19 +21,20 @@
191 #include <QtCore/QObject>
192 #include <QtQml/QQmlProperty>
193
194-class QQmlAbstractBinding;
195 class PropertyChange
196 {
197 public:
198- PropertyChange(QObject *item, const char *property);
199+ PropertyChange(QObject *item, const char *property, bool autoBackup = false);
200 ~PropertyChange();
201
202 static void setValue(PropertyChange* change, const QVariant &value);
203 static void restore(PropertyChange* change);
204 private:
205- bool m_backedUp;
206+ bool backedUp;
207 QQmlProperty qmlProperty;
208- QPair<QQmlAbstractBinding*, QVariant> backup;
209+ QVariant backupValue;
210+
211+ void backup();
212 };
213
214 #endif // PROPERTYCHANGE_P_H
215
216=== modified file 'modules/Ubuntu/Components/plugin/ucactionpanel_p.cpp'
217--- modules/Ubuntu/Components/plugin/ucactionpanel_p.cpp 2015-01-13 14:46:22 +0000
218+++ modules/Ubuntu/Components/plugin/ucactionpanel_p.cpp 2015-02-10 13:46:20 +0000
219@@ -55,12 +55,14 @@
220 return false;
221 }
222 if (!panelDelegate->isError()) {
223- QQmlContext *context = new QQmlContext(qmlContext(listItem));
224+ // use ListItem's style context to be able to access ther styledItem context property
225+ UCListItemPrivate *pListItem = UCListItemPrivate::get(listItem);
226+ QQmlContext *parentContext = qmlContext(pListItem->styleItem);
227+ QQmlContext *context = new QQmlContext(parentContext, parentContext);
228 panelItem = qobject_cast<QQuickItem*>(panelDelegate->beginCreate(context));
229 if (!panelItem) {
230 qmlInfo(listItem) << UbuntuI18n::tr("Error creating ListItem actions panel");
231 } else {
232- context->setParent(panelItem);
233 QQml_setParent_noEvent(panelItem, listItem);
234 panelItem->setParentItem(listItem);
235 // create attached property!
236
237=== modified file 'modules/Ubuntu/Components/plugin/uclistitem.cpp'
238--- modules/Ubuntu/Components/plugin/uclistitem.cpp 2015-02-05 14:11:27 +0000
239+++ modules/Ubuntu/Components/plugin/uclistitem.cpp 2015-02-10 13:46:20 +0000
240@@ -34,6 +34,8 @@
241 #include <QtQuick/private/qquickanimation_p.h>
242 #include <QtQuick/private/qquickmousearea_p.h>
243 #include "uclistitemstyle.h"
244+#include <QtQuick/private/qquickbehavior_p.h>
245+#include <QtQml/QQmlEngine>
246
247 QColor getPaletteColor(const char *profile, const char *color)
248 {
249@@ -58,13 +60,15 @@
250 * x coordinate.
251 * The animation is defined by the style.
252 */
253-UCListItemSnapAnimator::UCListItemSnapAnimator(UCListItem *item)
254- : QObject(item)
255- , item(item)
256+ListItemAnimator::ListItemAnimator(QObject *parent)
257+ : QObject(parent)
258+ , activeAnimations(0)
259+ , item(0)
260 {
261 }
262-UCListItemSnapAnimator::~UCListItemSnapAnimator()
263+ListItemAnimator::~ListItemAnimator()
264 {
265+ stop();
266 // make sure we cannot animate anymore, for safety
267 item = 0;
268 }
269@@ -74,61 +78,83 @@
270 * in "to" parameter. If the position is 0, a snap out will be executed - see
271 * snapOut(). In any other cases a snap in action will be performed - see snapIn().
272 */
273-bool UCListItemSnapAnimator::snap(qreal to)
274+bool ListItemAnimator::snap(qreal to)
275 {
276 if (!item) {
277 return false;
278 }
279 UCListItemPrivate *listItem = UCListItemPrivate::get(item);
280- QQuickPropertyAnimation *snap = getDefaultAnimation();
281+ bool doSnapOut = (to == 0.0);
282+ activeAnimations |= (doSnapOut ? SnapOutAnimation : SnapInAnimation);
283+ // fix snap position, take leftMargin into account!
284+ to += QQuickItemPrivate::get(listItem->contentItem)->anchors()->leftMargin();
285+ if (to == listItem->contentItem->x()) {
286+ // there was no move, so we only do some cleanup
287+ completeAnimation();
288+ return true;
289+ }
290+
291+ QQuickAbstractAnimation *snap = getSnapBehavior();
292+ if (snap) {
293+ snap->setAlwaysRunToEnd(false);
294+ connect(snap, &QQuickAbstractAnimation::runningChanged,
295+ this, &ListItemAnimator::completeAnimation,
296+ Qt::DirectConnection);
297+ }
298+ listItem->setContentMoving(true);
299+ if (snapBehavior) {
300+ snapBehavior->setEnabled(true);
301+ snapBehavior->write(to);
302+ }
303 if (!snap) {
304- // no animation, so we simply position the component
305- listItem->contentItem->setX(to);
306- // and complete snap logic
307- if (to == 0.0) {
308- snapOut();
309- } else {
310- snapIn();
311- }
312- return false;
313- }
314- snap->setTargetObject(listItem->contentItem);
315- if (to == 0.0) {
316- QObject::connect(snap, &QQuickAbstractAnimation::stopped,
317- this, &UCListItemSnapAnimator::snapOut);
318- } else {
319- QObject::connect(snap, &QQuickAbstractAnimation::stopped,
320- this, &UCListItemSnapAnimator::snapIn);
321- }
322- if (snap->properties().isEmpty() && snap->property().isEmpty()) {
323- snap->setProperty("x");
324- }
325- // make sure the animation is not running
326- snap->stop();
327- snap->setFrom(listItem->contentItem->property(snap->property().toLocal8Bit().constData()));
328- snap->setTo(to);
329- snap->setAlwaysRunToEnd(false);
330- listItem->setContentMoving(true);
331- snap->start();
332+ // complete, as we don't have animation
333+ completeAnimation();
334+ }
335 return true;
336 }
337
338-void UCListItemSnapAnimator::stop()
339-{
340- QQuickPropertyAnimation *snap = getDefaultAnimation();
341- if (snap && snap->isRunning()) {
342- snap->stop();
343- }
344-}
345-
346 /*
347 * The function completes a running snap animation.
348 */
349-void UCListItemSnapAnimator::complete()
350-{
351- QQuickPropertyAnimation *snap = getDefaultAnimation();
352- if (snap && snap->isRunning()) {
353- snap->complete();
354+void ListItemAnimator::stop()
355+{
356+ if (snapBehavior && snapBehavior->enabled()) {
357+ QQuickAbstractAnimation *animation = snapBehavior->animation();
358+ if (animation) {
359+ // set animation to be user controlled temporarily so we can invoke stop()
360+ animation->setEnableUserControl();
361+ animation->stop();
362+ animation->setDisableUserControl();
363+ }
364+ snapBehavior->setEnabled(false);
365+ }
366+}
367+
368+// handles animation completion
369+void ListItemAnimator::completeAnimation()
370+{
371+ QQuickAbstractAnimation *animation = static_cast<QQuickAbstractAnimation*>(sender());
372+ if (animation && animation->isRunning()) {
373+ return;
374+ }
375+
376+ // complete animations
377+ UCListItemPrivate *listItem = UCListItemPrivate::get(item);
378+ if (activeAnimations & SnapInAnimation) {
379+ listItem->snapIn();
380+ activeAnimations &= ~SnapInAnimation;
381+ } else if (activeAnimations & SnapOutAnimation) {
382+ listItem->snapOut();
383+ activeAnimations &= ~SnapOutAnimation;
384+ }
385+
386+ // clean animations
387+ if (animation && !activeAnimations) {
388+ disconnect(animation, &QQuickAbstractAnimation::runningChanged,
389+ this, &ListItemAnimator::completeAnimation);
390+ }
391+ if (snapBehavior && snapBehavior->enabled()) {
392+ snapBehavior->setEnabled(false);
393 }
394 }
395
396@@ -138,72 +164,95 @@
397 * be disconnected, ascending Flickables will get unlocked (interactive value restored
398 * to the state before they were locked) and ListItem.contentMoving will be reset.
399 */
400-void UCListItemSnapAnimator::snapOut()
401+void UCListItemPrivate::snapOut()
402 {
403- if (senderSignalIndex() >= 0) {
404- // disconnect animation, otherwise snapping will disconnect the panel
405- QQuickAbstractAnimation *snap = getDefaultAnimation();
406- QObject::disconnect(snap, 0, 0, 0);
407- }
408- UCListItemPrivate *listItem = UCListItemPrivate::get(item);
409- if (listItem->parentAttached) {
410+ setHighlighted(false);
411+ setSwiped(false);
412+ if (parentAttached) {
413+ Q_Q(UCListItem);
414 // restore flickable's interactive and cleanup
415- listItem->parentAttached->disableInteractive(item, false);
416+ parentAttached->disableInteractive(q, false);
417 // no need to listen flickables any longer
418- listItem->listenToRebind(false);
419+ listenToRebind(false);
420 }
421 // disconnect actions
422- UCActionPanel::ungrabPanel(listItem->leadingPanel);
423- UCActionPanel::ungrabPanel(listItem->trailingPanel);
424+ UCActionPanel::ungrabPanel(leadingPanel);
425+ UCActionPanel::ungrabPanel(trailingPanel);
426+ // lock contentItem left/right edges
427+ lockContentItem(true);
428 // set contentMoved to false
429- listItem->setContentMoving(false);
430- // lock contentItem left/right edges
431- listItem->lockContentItem(true);
432+ setContentMoving(false);
433 }
434
435 /*
436 * Snap in only resets the ListItem.contentMoving property, but will keep leading/trailing
437 * actions connected as well as all ascendant Flickables locked (interactive = false).
438 */
439-void UCListItemSnapAnimator::snapIn()
440+void UCListItemPrivate::snapIn()
441 {
442- if (senderSignalIndex() >= 0) {
443- // disconnect animation
444- QQuickAbstractAnimation *snap = getDefaultAnimation();
445- QObject::disconnect(snap, 0, 0, 0);
446- }
447 // turn content moving off
448- UCListItemPrivate *listItem = UCListItemPrivate::get(item);
449- listItem->setContentMoving(false);
450+ setContentMoving(false);
451 }
452
453 /*
454- * Returns the animation specified by the style.
455+ * Returns the animation specified by the style, and configures the snapBehavior
456+ * controlling the animation.
457 */
458-QQuickPropertyAnimation *UCListItemSnapAnimator::getDefaultAnimation()
459+QQuickAbstractAnimation *ListItemAnimator::getSnapBehavior()
460 {
461+ if (snapBehavior) {
462+ return snapBehavior->animation();
463+ }
464+
465 UCListItemPrivate *listItem = UCListItemPrivate::get(item);
466+ // in order to get Behavior working properly, it must be created to have
467+ // ListItem.contentItem as parent, and must be in the same context as its
468+ // parent item
469+ snapBehavior = new QQuickBehavior(listItem->contentItem);
470+ snapBehavior->setParent(listItem->contentItem);
471+ QQmlContext *context = qmlContext(listItem->contentItem);
472+ QQmlEngine::setContextForObject(snapBehavior.data(), context);
473+
474 listItem->initStyleItem();
475- return listItem->styleItem ? listItem->styleItem->m_snapAnimation : 0;
476+ QQuickAbstractAnimation *animation = listItem->styleItem ? listItem->styleItem->m_snapAnimation : 0;
477+ if (animation) {
478+ // patch behavior, use the same context as the animation
479+ snapBehavior->setAnimation(animation);
480+
481+ // transfer animation to the contentItem
482+ animation->setParent(listItem->contentItem);
483+ }
484+ QQmlProperty property(listItem->contentItem, "x", context);
485+ snapBehavior->setTarget(property);
486+ return animation;
487 }
488
489 /******************************************************************************
490 * Divider
491 */
492-UCListItemDivider::UCListItemDivider(QObject *parent)
493- : QObject(parent)
494- , m_visible(true)
495- , m_colorFromChanged(false)
496- , m_colorToChanged(false)
497- , m_thickness(0)
498- , m_leftMargin(0)
499- , m_rightMargin(0)
500- , m_listItem(0)
501-{
502- connect(&UCUnits::instance(), &UCUnits::gridUnitChanged, this, &UCListItemDivider::unitsChanged);
503- connect(&UCTheme::instance(), &UCTheme::paletteChanged, this, &UCListItemDivider::paletteChanged);
504- unitsChanged();
505- paletteChanged();
506+class UCListItemDividerPrivate : public QQuickItemPrivate
507+{
508+ Q_DECLARE_PUBLIC(UCListItemDivider)
509+public:
510+ UCListItemDividerPrivate()
511+ : QQuickItemPrivate()
512+ , colorFromChanged(false)
513+ , colorToChanged(false)
514+ , listItem(0)
515+ {}
516+
517+ bool colorFromChanged:1;
518+ bool colorToChanged:1;
519+ QColor colorFrom;
520+ QColor colorTo;
521+ QGradientStops gradient;
522+ UCListItemPrivate *listItem;
523+};
524+
525+UCListItemDivider::UCListItemDivider(UCListItem *parent)
526+ : QQuickItem(*(new UCListItemDividerPrivate), parent)
527+{
528+ setFlag(ItemHasContents);
529 }
530 UCListItemDivider::~UCListItemDivider()
531 {
532@@ -211,17 +260,17 @@
533
534 void UCListItemDivider::init(UCListItem *listItem)
535 {
536+ Q_D(UCListItemDivider);
537 QQml_setParent_noEvent(this, listItem);
538- m_listItem = UCListItemPrivate::get(listItem);
539-}
540-
541-void UCListItemDivider::unitsChanged()
542-{
543- m_thickness = UCUnits::instance().dp(DIVIDER_THICKNESS_DP);
544- if (m_listItem) {
545- m_listItem->adjustContentItemHeight();
546- m_listItem->update();
547- }
548+ d->listItem = UCListItemPrivate::get(listItem);
549+ setParentItem(listItem);
550+ // anchor to left/right/bottom of the ListItem
551+ QQuickAnchors *anchors = d->anchors();
552+ anchors->setLeft(d->listItem->left());
553+ anchors->setRight(d->listItem->right());
554+ anchors->setBottom(d->listItem->bottom());
555+ // connect visible change so we relayout contentItem
556+ connect(this, SIGNAL(visibleChanged()), listItem, SLOT(_q_relayout()));
557 }
558
559 void UCListItemDivider::paletteChanged()
560@@ -232,14 +281,15 @@
561 }
562 // FIXME: we need a palette value for divider colors, till then base on the background
563 // luminance
564- if (!m_colorFromChanged || !m_colorToChanged) {
565+ Q_D(UCListItemDivider);
566+ if (!d->colorFromChanged || !d->colorToChanged) {
567 qreal luminance = (background.red()*212 + background.green()*715 + background.blue()*73)/1000.0/255.0;
568 bool lightBackground = (luminance > 0.85);
569- if (!m_colorFromChanged) {
570- m_colorFrom = lightBackground ? QColor("#26000000") : QColor("#26FFFFFF");
571+ if (!d->colorFromChanged) {
572+ d->colorFrom = lightBackground ? QColor("#26000000") : QColor("#26FFFFFF");
573 }
574- if (!m_colorToChanged) {
575- m_colorTo = lightBackground ? QColor("#14FFFFFF") : QColor("#14000000");
576+ if (!d->colorToChanged) {
577+ d->colorTo = lightBackground ? QColor("#14FFFFFF") : QColor("#14000000");
578 }
579 updateGradient();
580 }
581@@ -247,27 +297,30 @@
582
583 void UCListItemDivider::updateGradient()
584 {
585- m_gradient.clear();
586- m_gradient.append(QGradientStop(0.0, m_colorFrom));
587- m_gradient.append(QGradientStop(0.49, m_colorFrom));
588- m_gradient.append(QGradientStop(0.5, m_colorTo));
589- m_gradient.append(QGradientStop(1.0, m_colorTo));
590- if (m_listItem) {
591- m_listItem->update();
592+ Q_D(UCListItemDivider);
593+ d->gradient.clear();
594+ d->gradient.append(QGradientStop(0.0, d->colorFrom));
595+ d->gradient.append(QGradientStop(0.49, d->colorFrom));
596+ d->gradient.append(QGradientStop(0.5, d->colorTo));
597+ d->gradient.append(QGradientStop(1.0, d->colorTo));
598+ if (d->listItem) {
599+ d->listItem->update();
600 }
601 }
602
603-QSGNode *UCListItemDivider::paint(QSGNode *node, const QRectF &rect)
604+QSGNode *UCListItemDivider::updatePaintNode(QSGNode *node, UpdatePaintNodeData *data)
605 {
606+ Q_UNUSED(data);
607+ Q_D(UCListItemDivider);
608 QSGRectangleNode *dividerNode = static_cast<QSGRectangleNode*>(node);
609- bool lastItem = m_listItem->countOwner ? (m_listItem->index() == (m_listItem->countOwner->property("count").toInt() - 1)): false;
610- if (m_visible && !lastItem && (m_gradient.size() > 0) && ((m_colorFrom.alphaF() >= (1.0f / 255.0f)) || (m_colorTo.alphaF() >= (1.0f / 255.0f)))) {
611- if (!dividerNode) {
612- dividerNode = m_listItem->sceneGraphContext()->createRectangleNode();
613- }
614- QRectF divider(m_leftMargin, rect.height() - m_thickness, rect.width() - m_leftMargin - m_rightMargin, m_thickness);
615- dividerNode->setRect(divider);
616- dividerNode->setGradientStops(m_gradient);
617+ if (!dividerNode) {
618+ dividerNode = d->sceneGraphContext()->createRectangleNode();
619+ }
620+
621+ bool lastItem = d->listItem->countOwner ? (d->listItem->index() == (d->listItem->countOwner->property("count").toInt() - 1)): false;
622+ if (!lastItem && (d->gradient.size() > 0) && ((d->colorFrom.alphaF() >= (1.0f / 255.0f)) || (d->colorTo.alphaF() >= (1.0f / 255.0f)))) {
623+ dividerNode->setRect(boundingRect());
624+ dividerNode->setGradientStops(d->gradient);
625 dividerNode->update();
626 return dividerNode;
627 } else if (node) {
628@@ -277,55 +330,36 @@
629 return 0;
630 }
631
632-void UCListItemDivider::setVisible(bool visible)
633-{
634- if (m_visible == visible) {
635- return;
636- }
637- m_visible = visible;
638- Q_EMIT visibleChanged();
639- // set/reset contentItem's bottomMargin
640- m_listItem->adjustContentItemHeight();
641-}
642-
643-void UCListItemDivider::setLeftMargin(qreal leftMargin)
644-{
645- if (m_leftMargin == leftMargin) {
646- return;
647- }
648- m_leftMargin = leftMargin;
649- m_listItem->update();
650- Q_EMIT leftMarginChanged();
651-}
652-
653-void UCListItemDivider::setRightMargin(qreal rightMargin)
654-{
655- if (m_rightMargin == rightMargin) {
656- return;
657- }
658- m_rightMargin = rightMargin;
659- m_listItem->update();
660- Q_EMIT rightMarginChanged();
661-}
662-
663+QColor UCListItemDivider::colorFrom() const
664+{
665+ Q_D(const UCListItemDivider);
666+ return d->colorFrom;
667+}
668 void UCListItemDivider::setColorFrom(const QColor &color)
669 {
670- if (m_colorFrom == color) {
671+ Q_D(UCListItemDivider);
672+ if (d->colorFrom == color) {
673 return;
674 }
675- m_colorFrom = color;
676- m_colorFromChanged = true;
677+ d->colorFrom = color;
678+ d->colorFromChanged = true;
679 updateGradient();
680 Q_EMIT colorFromChanged();
681 }
682
683+QColor UCListItemDivider::colorTo() const
684+{
685+ Q_D(const UCListItemDivider);
686+ return d->colorTo;
687+}
688 void UCListItemDivider::setColorTo(const QColor &color)
689 {
690- if (m_colorTo == color) {
691+ Q_D(UCListItemDivider);
692+ if (d->colorTo == color) {
693 return;
694 }
695- m_colorTo = color;
696- m_colorToChanged = true;
697+ d->colorTo = color;
698+ d->colorToChanged = true;
699 updateGradient();
700 Q_EMIT colorToChanged();
701 }
702@@ -354,7 +388,6 @@
703 , trailingActions(0)
704 , leadingPanel(0)
705 , trailingPanel(0)
706- , animator(0)
707 , mainAction(0)
708 , styleComponent(0)
709 , implicitStyleComponent(0)
710@@ -368,9 +401,11 @@
711 void UCListItemPrivate::init()
712 {
713 Q_Q(UCListItem);
714+ animator.init(q);
715 contentItem->setObjectName("ListItemHolder");
716 QQml_setParent_noEvent(contentItem, q);
717 contentItem->setParentItem(q);
718+ contentItem->setClip(true);
719 divider->init(q);
720 // content will be redirected to the contentItem, therefore we must report
721 // children changes as it would come from the main component
722@@ -410,6 +445,8 @@
723 void UCListItemPrivate::_q_updateThemedData()
724 {
725 Q_Q(UCListItem);
726+ // update the divider colors
727+ divider->paletteChanged();
728 // we reload the implicit style only if the custom style is not set, and
729 // the component is ready
730 if (!styleComponent && ready) {
731@@ -422,17 +459,17 @@
732 }
733 }
734
735-void UCListItemPrivate::_q_rebound()
736+// re-layouting the ListItem's contentItem
737+void UCListItemPrivate::_q_relayout()
738 {
739- setHighlighted(false);
740- // initiate rebinding only if there were actions tugged
741- if (!UCActionPanel::isConnected(leadingPanel) &&
742- !UCActionPanel::isConnected(trailingPanel)) {
743- return;
744+ QQuickAnchors *contentAnchors = QQuickItemPrivate::get(contentItem)->anchors();
745+ QQuickAnchorLine anchorLine;
746+ if (divider->isVisible()) {
747+ anchorLine = QQuickItemPrivate::get(divider)->top();
748+ } else {
749+ anchorLine = bottom();
750 }
751- setSwiped(false);
752- // rebound to zero
753- animator->snap(0);
754+ contentAnchors->setBottom(anchorLine);
755 }
756
757 void UCListItemPrivate::_q_updateIndex()
758@@ -465,7 +502,7 @@
759 return;
760 }
761 // make sure we're rebound before we change the panel component
762- promptRebound();
763+ snapOut();
764 bool reloadStyle = styleItem != 0;
765 if (styleItem) {
766 styleItem->deleteLater();
767@@ -489,7 +526,7 @@
768 styleComponent = 0;
769 // rebound as the current panels are not gonna be valid anymore
770 if (swiped) {
771- promptRebound();
772+ snapOut();
773 }
774 bool reloadStyle = styleItem != 0;
775 if (styleItem) {
776@@ -531,6 +568,7 @@
777 return;
778 }
779 QQmlContext *context = new QQmlContext(qmlContext(q));
780+ context->setContextProperty("styledItem", q);
781 QObject *object = delegate->beginCreate(context);
782 styleItem = qobject_cast<UCListItemStyle*>(object);
783 if (!styleItem) {
784@@ -560,20 +598,13 @@
785 return styleItem;
786 }
787
788-// rebound without animation
789-void UCListItemPrivate::promptRebound()
790-{
791- setHighlighted(false);
792- setSwiped(false);
793- if (animator) {
794- animator->snapOut();
795- }
796-}
797-
798 // called when units size changes
799 void UCListItemPrivate::_q_updateSize()
800 {
801 Q_Q(UCListItem);
802+
803+ // update divider thickness
804+ divider->setImplicitHeight(UCUnits::instance().dp(DIVIDER_THICKNESS_DP));
805 QQuickItem *owner = flickable ? flickable : parentItem;
806 q->setImplicitWidth(owner ? owner->width() : UCUnits::instance().gu(IMPLICIT_LISTITEM_WIDTH_GU));
807 q->setImplicitHeight(UCUnits::instance().gu(IMPLICIT_LISTITEM_HEIGHT_GU));
808@@ -666,17 +697,6 @@
809 }
810 }
811
812-// adjust contentItem height depending on teh divider's visibility
813-void UCListItemPrivate::adjustContentItemHeight()
814-{
815- QQuickAnchors *contentAnchors = QQuickItemPrivate::get(contentItem)->anchors();
816- if (divider->m_visible) {
817- contentAnchors->setBottomMargin(divider->m_thickness);
818- } else {
819- contentAnchors->resetBottomMargin();
820- }
821-}
822-
823 void UCListItemPrivate::update()
824 {
825 if (!ready) {
826@@ -781,7 +801,7 @@
827 * Being an Item, all properties can be accessed or altered. However, make sure you
828 * never change \c x, \c y, \c width, \c height or \c anchors properties as those are
829 * controlled by the ListItem itself when leading or trailing actions are revealed
830- * and thus might cause the component to misbehave.
831+ * and thus might cause the component to misbehave. Anchors margins are free to alter.
832 *
833 * Each ListItem has a thin divider shown on the bottom of the component. This
834 * divider can be configured through the \c divider grouped property, which can
835@@ -880,35 +900,19 @@
836
837 UCListItemAttached *UCListItem::qmlAttachedProperties(QObject *owner)
838 {
839- /*
840- * Detect the attachee, whether is it a child item of the panelItem. The panelItem
841- * itself cannot be detected, as the object can be attached during the call of
842- * component.beginCreate().
843- */
844- UCListItemAttached *attached = new UCListItemAttached(owner);
845- QQuickItem *item = qobject_cast<QQuickItem*>(owner);
846- while (item) {
847- // has item our attached property?
848- UCListItemAttached *itemAttached = static_cast<UCListItemAttached*>(
849- qmlAttachedPropertiesObject<UCListItem>(item, false));
850- if (itemAttached) {
851- attached->connectToAttached(itemAttached);
852- break;
853- }
854- item = item->parentItem();
855- }
856- return attached;
857+ return new UCListItemAttached(owner);
858 }
859
860 void UCListItem::componentComplete()
861 {
862 UCStyledItemBase::componentComplete();
863 Q_D(UCListItem);
864+ // set contentItem's context
865+ QQmlEngine::setContextForObject(d->contentItem, qmlContext(this));
866 // anchor contentItem prior doing anything else
867 QQuickAnchors *contentAnchors = QQuickItemPrivate::get(d->contentItem)->anchors();
868 contentAnchors->setTop(d->top());
869- contentAnchors->setBottom(d->bottom());
870- d->adjustContentItemHeight();
871+ d->_q_relayout();
872 d->lockContentItem(true);
873
874 d->ready = true;
875@@ -986,8 +990,12 @@
876 }
877 if (color.alphaF() >= (1.0f / 255.0f)) {
878 rectNode->setColor(color);
879- // cover only the area of the contentItem
880- rectNode->setRect(d->contentItem->boundingRect());
881+ // cover only the area of the contentItem, removing divider's thickness
882+ QRectF rect(boundingRect());
883+ if (d->divider->isVisible()) {
884+ rect -= QMarginsF(0, 0, 0, d->divider->height());
885+ }
886+ rectNode->setRect(rect);
887 rectNode->setGradientStops(QGradientStops());
888 rectNode->setAntialiasing(true);
889 rectNode->setAntialiasing(false);
890@@ -998,24 +1006,6 @@
891 rectNode = 0;
892 }
893 oldNode = rectNode;
894- QSGNode *dividerNode = oldNode ? oldNode->childAtIndex(0) : 0;
895- if (d->divider && d->divider->m_visible) {
896- QSGNode *newNode = d->divider->paint(dividerNode, boundingRect());
897- if (newNode != dividerNode && oldNode) {
898- if (dividerNode) {
899- oldNode->removeChildNode(dividerNode);
900- }
901- if (newNode) {
902- oldNode->appendChildNode(newNode);
903- }
904- }
905- if (!oldNode) {
906- oldNode = newNode;
907- }
908- } else if (dividerNode) {
909- // the divider painter node may be still added as child, so remove it
910- oldNode->removeChildNode(dividerNode);
911- }
912 return oldNode;
913 }
914
915@@ -1030,9 +1020,7 @@
916 if (d->canHighlight(event) && !d->suppressClick
917 && !d->highlighted && event->button() == Qt::LeftButton) {
918 // stop any ongoing animation!
919- if (d->animator) {
920- d->animator->stop();
921- }
922+ d->animator.stop();
923 d->setHighlighted(true);
924 d->lastPos = d->pressedPos = event->localPos();
925 // connect the Flickable to know when to rebound
926@@ -1062,11 +1050,14 @@
927 }
928
929 if (!d->suppressClick) {
930- Q_EMIT clicked();
931- if (d->mainAction) {
932- Q_EMIT d->mainAction->trigger(d->index());
933+ // emit clicked only if not swiped
934+ if (!d->swiped) {
935+ Q_EMIT clicked();
936+ if (d->mainAction) {
937+ Q_EMIT d->mainAction->trigger(d->index());
938+ }
939 }
940- d->_q_rebound();
941+ d->animator.snap(0);
942 } else {
943 d->suppressClick = false;
944 }
945@@ -1103,10 +1094,6 @@
946 if (d->parentAttached) {
947 d->parentAttached->disableInteractive(this, true);
948 }
949- // create animator if not created yet
950- if (!d->animator) {
951- d->animator = new UCListItemSnapAnimator(this);
952- }
953 }
954 }
955
956@@ -1125,14 +1112,15 @@
957 d->setSwiped(true);
958 d->contentItem->setX(x);
959 // decide which panel is visible by checking the contentItem's X coordinates
960- if (d->contentItem->x() > 0) {
961+ qreal margin = QQuickItemPrivate::get(d->contentItem)->anchors()->leftMargin();
962+ if (d->contentItem->x() > margin) {
963 if (d->leadingPanel) {
964 d->leadingPanel->panel()->setVisible(true);
965 }
966 if (d->trailingPanel) {
967 d->trailingPanel->panel()->setVisible(false);
968 }
969- } else if (d->contentItem->x() < 0) {
970+ } else if (d->contentItem->x() < margin) {
971 // trailing revealed
972 if (d->leadingPanel) {
973 d->leadingPanel->panel()->setVisible(false);
974@@ -1185,7 +1173,7 @@
975 }
976 if (!myPos.isNull() && !contains(myPos)) {
977 Q_D(UCListItem);
978- d->_q_rebound();
979+ d->animator.snap(0);
980 // only accept event, but let it be handled by the underlying or surrounding Flickables
981 event->accept();
982 }
983@@ -1195,7 +1183,7 @@
984 void UCListItem::timerEvent(QTimerEvent *event)
985 {
986 Q_D(UCListItem);
987- if (event->timerId() == d->pressAndHoldTimer.timerId() && d->highlighted) {
988+ if (event->timerId() == d->pressAndHoldTimer.timerId() && d->highlighted && !d->swiped) {
989 d->pressAndHoldTimer.stop();
990 if (isEnabled() && d->isPressAndHoldConnected()) {
991 d->suppressClick = true;
992@@ -1226,7 +1214,7 @@
993 return;
994 }
995 // snap out before we change the actions
996- d->promptRebound();
997+ d->snapOut();
998 // then delete panel
999 delete d->leadingPanel;
1000 d->leadingPanel = 0;
1001@@ -1254,7 +1242,7 @@
1002 return;
1003 }
1004 // snap out before we change the actions
1005- d->promptRebound();
1006+ d->snapOut();
1007 // then delete panel
1008 delete d->trailingPanel;
1009 d->trailingPanel = 0;
1010@@ -1265,7 +1253,22 @@
1011 /*!
1012 * \qmlproperty Item ListItem::contentItem
1013 *
1014- * contentItem holds the components placed on a ListItem.
1015+ * contentItem holds the components placed on a ListItem. It is anchored to the
1016+ * ListItem on left, top and right, and to the divider on the bottom, or to the
1017+ * ListItem's bottom in case the divider is not visible. The content is clipped
1018+ * by default. It is not recommended to change the anchors as the ListItem controls
1019+ * them, however any other property value is free to change.
1020+ * Example:
1021+ * \qml
1022+ * ListItem {
1023+ * contentItem.anchors {
1024+ * leftMargin: units.gu(2)
1025+ * rightMargin: units.gu(2)
1026+ * topMargin: units.gu(0.5)
1027+ * bottomMargin: units.gu(0.5)
1028+ * }
1029+ * }
1030+ * \endqml
1031 */
1032 QQuickItem* UCListItem::contentItem() const
1033 {
1034@@ -1275,27 +1278,18 @@
1035
1036 /*!
1037 * \qmlpropertygroup ::ListItem::divider
1038- * \qmlproperty bool ListItem::divider.visible
1039- * \qmlproperty real ListItem::divider.leftMargin
1040- * \qmlproperty real ListItem::divider.rightMargin
1041 * \qmlproperty real ListItem::divider.colorFrom
1042 * \qmlproperty real ListItem::divider.colorTo
1043 *
1044 * This grouped property configures the thin divider shown in the bottom of the
1045- * component. Configures the visibility and the margins from the left and right
1046- * of the ListItem. When swiped left or right to reveal the actions, it is not
1047- * moved together with the content. \c colorFrom and \c colorTo configure
1048- * the starting and ending colors of the divider.
1049+ * component. The divider is not moved together with the content when swiped left
1050+ * or right to reveal the actions. \c colorFrom and \c colorTo configure
1051+ * the starting and ending colors of the divider. Beside these properties all Item
1052+ * specific properties can be accessed.
1053 *
1054 * When \c visible is true, the ListItem's content size gets thinner with the
1055- * divider's \c thickness.
1056- *
1057- * The default values for the properties are:
1058- * \list
1059- * \li \c visible: true
1060- * \li \c leftMargin: 0
1061- * \li \c rightMargin: 0
1062- * \endlist
1063+ * divider's \c thickness. By default the divider is anchored to the bottom, left
1064+ * right of the ListItem, and has a 2dp height.
1065 */
1066 UCListItemDivider* UCListItem::divider() const
1067 {
1068
1069=== modified file 'modules/Ubuntu/Components/plugin/uclistitem.h'
1070--- modules/Ubuntu/Components/plugin/uclistitem.h 2015-01-15 10:39:27 +0000
1071+++ modules/Ubuntu/Components/plugin/uclistitem.h 2015-02-10 13:46:20 +0000
1072@@ -106,13 +106,40 @@
1073 private:
1074 Q_DECLARE_PRIVATE(UCListItem)
1075 Q_PRIVATE_SLOT(d_func(), void _q_updateThemedData())
1076- Q_PRIVATE_SLOT(d_func(), void _q_rebound())
1077+ Q_PRIVATE_SLOT(d_func(), void _q_relayout())
1078 Q_PRIVATE_SLOT(d_func(), void _q_updateSize())
1079 Q_PRIVATE_SLOT(d_func(), void _q_updateIndex())
1080 };
1081-
1082 QML_DECLARE_TYPEINFO(UCListItem, QML_HAS_ATTACHED_PROPERTIES)
1083
1084+class UCListItemDividerPrivate;
1085+class UCListItemDivider : public QQuickItem
1086+{
1087+ Q_OBJECT
1088+ Q_PROPERTY(QColor colorFrom READ colorFrom WRITE setColorFrom NOTIFY colorFromChanged)
1089+ Q_PROPERTY(QColor colorTo READ colorTo WRITE setColorTo NOTIFY colorToChanged)
1090+public:
1091+ explicit UCListItemDivider(UCListItem *parent = 0);
1092+ ~UCListItemDivider();
1093+ void init(UCListItem *listItem);
1094+ void paletteChanged();
1095+
1096+Q_SIGNALS:
1097+ void colorFromChanged();
1098+ void colorToChanged();
1099+
1100+protected:
1101+ QSGNode *updatePaintNode(QSGNode *node, UpdatePaintNodeData *data);
1102+
1103+private:
1104+ void updateGradient();
1105+ QColor colorFrom() const;
1106+ void setColorFrom(const QColor &color);
1107+ QColor colorTo() const;
1108+ void setColorTo(const QColor &color);
1109+ Q_DECLARE_PRIVATE(UCListItemDivider)
1110+};
1111+
1112 class UCAction;
1113 class UCListItemActions;
1114 class UCListItemAttachedPrivate;
1115@@ -121,14 +148,12 @@
1116 Q_OBJECT
1117 Q_PROPERTY(UCListItemActions *actions READ actions NOTIFY actionsChanged)
1118 Q_PROPERTY(QQmlListProperty<UCAction> visibleActions READ visibleActions NOTIFY visibleActionsChanged)
1119- Q_PROPERTY(UCListItem *item READ item NOTIFY itemChanged)
1120 Q_PROPERTY(int index READ index NOTIFY indexChanged)
1121 Q_PROPERTY(UCListItem::PanelStatus panelStatus READ panelStatus NOTIFY panelStatusChanged)
1122 public:
1123 UCListItemAttached(QObject *parent = 0);
1124 ~UCListItemAttached();
1125 void setList(UCListItem *list, bool leading, bool visualizeActions);
1126- void connectToAttached(UCListItemAttached *parentAttached);
1127
1128 UCListItemActions *actions() const;
1129 QQmlListProperty<UCAction> visibleActions();
1130@@ -142,7 +167,6 @@
1131 Q_SIGNALS:
1132 void actionsChanged();
1133 void visibleActionsChanged();
1134- void itemChanged();
1135 void indexChanged();
1136 void panelStatusChanged();
1137
1138
1139=== modified file 'modules/Ubuntu/Components/plugin/uclistitem_p.h'
1140--- modules/Ubuntu/Components/plugin/uclistitem_p.h 2015-02-05 14:11:27 +0000
1141+++ modules/Ubuntu/Components/plugin/uclistitem_p.h 2015-02-10 13:46:20 +0000
1142@@ -31,13 +31,45 @@
1143 #define IMPLICIT_LISTITEM_HEIGHT_GU 7
1144 #define DIVIDER_THICKNESS_DP 2
1145 #define DEFAULT_SWIPE_THRESHOLD_GU 1.5
1146+#define LAYOUT_HMARGIN_GU 2
1147+#define LAYOUT_VMARGIN_GU 0.5
1148+
1149+class QQuickAbstractAnimation;
1150+class QQuickBehavior;
1151+class ListItemAnimator : public QObject
1152+{
1153+ Q_OBJECT
1154+public:
1155+ enum Animation {
1156+ SnapInAnimation = 0x01,
1157+ SnapOutAnimation = 0x02
1158+ };
1159+
1160+ ListItemAnimator(QObject *parent = 0);
1161+ ~ListItemAnimator();
1162+ void init(UCListItem *listItem)
1163+ {
1164+ item = listItem;
1165+ }
1166+
1167+ bool snap(qreal to);
1168+ void stop();
1169+
1170+public Q_SLOTS:
1171+ void completeAnimation();
1172+
1173+private:
1174+ QQuickAbstractAnimation *getSnapBehavior();
1175+
1176+private:
1177+ int activeAnimations;
1178+ UCListItem *item;
1179+ QPointer<QQuickBehavior> snapBehavior;
1180+};
1181
1182 class QQuickFlickable;
1183-class QQuickPropertyAnimation;
1184-class UCListItemContent;
1185 class UCListItemDivider;
1186 class UCListItemActions;
1187-class UCListItemSnapAnimator;
1188 class UCListItemStyle;
1189 class UCActionPanel;
1190 class UCListItemPrivate : public UCStyledItemBasePrivate
1191@@ -57,8 +89,7 @@
1192 bool isClickedConnected();
1193 bool isPressAndHoldConnected();
1194 void _q_updateThemedData();
1195- void _q_rebound();
1196- void promptRebound();
1197+ void _q_relayout();
1198 void _q_updateSize();
1199 void _q_updateIndex();
1200 int index();
1201@@ -67,9 +98,10 @@
1202 void setSwiped(bool tugged);
1203 void listenToRebind(bool listen);
1204 void lockContentItem(bool lock);
1205- void adjustContentItemHeight();
1206 void update();
1207 void clampAndMoveX(qreal &x, qreal dx);
1208+ void snapOut();
1209+ void snapIn();
1210
1211 bool highlighted:1;
1212 bool contentMoved:1;
1213@@ -95,7 +127,7 @@
1214 UCListItemActions *trailingActions;
1215 UCActionPanel *leadingPanel;
1216 UCActionPanel *trailingPanel;
1217- UCListItemSnapAnimator *animator;
1218+ ListItemAnimator animator;
1219 UCAction *mainAction;
1220
1221 // FIXME move these to StyledItemBase togehther with subtheming.
1222@@ -192,80 +224,8 @@
1223 bool connected:1;
1224 };
1225
1226-class UCListItemDivider : public QObject
1227-{
1228- Q_OBJECT
1229- Q_PROPERTY(bool visible MEMBER m_visible WRITE setVisible NOTIFY visibleChanged)
1230- Q_PROPERTY(qreal leftMargin MEMBER m_leftMargin WRITE setLeftMargin NOTIFY leftMarginChanged)
1231- Q_PROPERTY(qreal rightMargin MEMBER m_rightMargin WRITE setRightMargin NOTIFY rightMarginChanged)
1232- Q_PROPERTY(QColor colorFrom MEMBER m_colorFrom WRITE setColorFrom NOTIFY colorFromChanged)
1233- Q_PROPERTY(QColor colorTo MEMBER m_colorTo WRITE setColorTo NOTIFY colorToChanged)
1234-public:
1235- explicit UCListItemDivider(QObject *parent = 0);
1236- ~UCListItemDivider();
1237- void init(UCListItem *listItem);
1238-
1239-Q_SIGNALS:
1240- void visibleChanged();
1241- void leftMarginChanged();
1242- void rightMarginChanged();
1243- void colorFromChanged();
1244- void colorToChanged();
1245-
1246-protected:
1247- QSGNode *paint(QSGNode *node, const QRectF &rect);
1248-
1249-private Q_SLOTS:
1250- void unitsChanged();
1251- void paletteChanged();
1252-
1253-private:
1254- void updateGradient();
1255- void setVisible(bool visible);
1256- void setLeftMargin(qreal leftMargin);
1257- void setRightMargin(qreal rightMargin);
1258- void setColorFrom(const QColor &color);
1259- void setColorTo(const QColor &color);
1260-
1261- bool m_visible:1;
1262- bool m_colorFromChanged:1;
1263- bool m_colorToChanged:1;
1264- qreal m_thickness;
1265- qreal m_leftMargin;
1266- qreal m_rightMargin;
1267- QColor m_colorFrom;
1268- QColor m_colorTo;
1269- QGradientStops m_gradient;
1270- UCListItemPrivate *m_listItem;
1271- friend class UCListItem;
1272- friend class UCListItemPrivate;
1273-};
1274-
1275 QColor getPaletteColor(const char *profile, const char *color);
1276
1277 QML_DECLARE_TYPE(UCListItemDivider)
1278
1279-class QQuickPropertyAnimation;
1280-class UCListItemSnapAnimator : public QObject
1281-{
1282- Q_OBJECT
1283-public:
1284- UCListItemSnapAnimator(UCListItem *item);
1285- ~UCListItemSnapAnimator();
1286-
1287- bool snap(qreal to);
1288- void stop();
1289- void complete();
1290-
1291-public Q_SLOTS:
1292- void snapOut();
1293- void snapIn();
1294-
1295- QQuickPropertyAnimation *getDefaultAnimation();
1296-
1297-private:
1298- bool active;
1299- UCListItem *item;
1300-};
1301-
1302 #endif // UCVIEWITEM_P_H
1303
1304=== modified file 'modules/Ubuntu/Components/plugin/uclistitemattached.cpp'
1305--- modules/Ubuntu/Components/plugin/uclistitemattached.cpp 2015-01-12 15:44:59 +0000
1306+++ modules/Ubuntu/Components/plugin/uclistitemattached.cpp 2015-02-10 13:46:20 +0000
1307@@ -30,13 +30,6 @@
1308 {
1309 }
1310
1311-void UCListItemAttached::connectToAttached(UCListItemAttached *parentAttached)
1312-{
1313- bool visualizeActions = UCListItemAttachedPrivate::get(parentAttached)->panel;
1314- bool isLeading = visualizeActions ? UCListItemAttachedPrivate::get(parentAttached)->panel->isLeading() : false;
1315- setList(parentAttached->item(), isLeading, visualizeActions);
1316-}
1317-
1318 void UCListItemAttached::setList(UCListItem *list, bool leading, bool visualizeActions)
1319 {
1320 Q_D(UCListItemAttached);
1321@@ -44,7 +37,6 @@
1322 return;
1323 }
1324 d->listItem = list;
1325- Q_EMIT itemChanged();
1326
1327 if (visualizeActions) {
1328 d->panel = leading ? UCListItemPrivate::get(d->listItem)->leadingPanel : UCListItemPrivate::get(d->listItem)->trailingPanel;
1329@@ -169,13 +161,5 @@
1330 }
1331 UCListItemPrivate *listItem = UCListItemPrivate::get(d->listItem);
1332 position *= (itemStatus == UCListItem::Leading) ? 1 : -1;
1333- if (position == 0.0) {
1334- listItem->_q_rebound();
1335- } else {
1336- if (listItem->animator) {
1337- listItem->animator->snap(position);
1338- } else {
1339- listItem->contentItem->setX(position);
1340- }
1341- }
1342+ listItem->animator.snap(position);
1343 }
1344
1345=== modified file 'modules/Ubuntu/Components/plugin/uclistitemstyle.cpp'
1346--- modules/Ubuntu/Components/plugin/uclistitemstyle.cpp 2014-12-01 11:13:10 +0000
1347+++ modules/Ubuntu/Components/plugin/uclistitemstyle.cpp 2015-02-10 13:46:20 +0000
1348@@ -15,6 +15,8 @@
1349 */
1350
1351 #include "uclistitemstyle.h"
1352+#include <QtQml/QQmlEngine>
1353+#include <QtQuick/private/qquickbehavior_p.h>
1354
1355 /*!
1356 * \qmltype ListItemStyle
1357@@ -60,8 +62,10 @@
1358 */
1359
1360 /*!
1361- * \qmlproperty PropertyAnimation ListItemStyle::snapAnimation
1362- * Holds the animation used in animating when snapped in or out.
1363+ * \qmlproperty Animation ListItemStyle::snapAnimation
1364+ * Holds the behavior used in animating when snapped in or out. It can hold many
1365+ * animations, and will be used in a Behavior on the \l ListItem::contentItem
1366+ * \c x property.
1367 */
1368
1369 /*!
1370
1371=== modified file 'modules/Ubuntu/Components/plugin/uclistitemstyle.h'
1372--- modules/Ubuntu/Components/plugin/uclistitemstyle.h 2015-01-07 07:41:29 +0000
1373+++ modules/Ubuntu/Components/plugin/uclistitemstyle.h 2015-02-10 13:46:20 +0000
1374@@ -19,14 +19,15 @@
1375 #include <QtQuick/QQuickItem>
1376
1377 class QQmlComponent;
1378-class QQuickPropertyAnimation;
1379+class QQuickAbstractAnimation;
1380+class QQuickBehavior;
1381 class UCListItemStyle : public QQuickItem
1382 {
1383 Q_OBJECT
1384 Q_PROPERTY(QQmlComponent *actionsDelegate MEMBER m_actionsDelegate NOTIFY actionsDelegateChanged)
1385 Q_PROPERTY(QQmlComponent *selectionDelegate MEMBER m_selectionDelegate NOTIFY selectionDelegateChanged)
1386 Q_PROPERTY(QQmlComponent *dragHandlerDelegate MEMBER m_dragHandlerDelegate NOTIFY dragHandlerDelegateChanged)
1387- Q_PROPERTY(QQuickPropertyAnimation *snapAnimation MEMBER m_snapAnimation NOTIFY snapAnimationChanged)
1388+ Q_PROPERTY(QQuickAbstractAnimation *snapAnimation MEMBER m_snapAnimation NOTIFY snapAnimationChanged)
1389 Q_PROPERTY(qreal swipeOvershoot MEMBER m_swipeOvershoot NOTIFY swipeOvershootChanged)
1390 public:
1391 explicit UCListItemStyle(QQuickItem *parent = 0);
1392@@ -42,12 +43,12 @@
1393 QQmlComponent *m_actionsDelegate;
1394 QQmlComponent *m_selectionDelegate;
1395 QQmlComponent *m_dragHandlerDelegate;
1396- QQuickPropertyAnimation *m_snapAnimation;
1397+ QQuickAbstractAnimation *m_snapAnimation;
1398 qreal m_swipeOvershoot;
1399
1400 friend class UCListItemPrivate;
1401 friend class UCActionPanel;
1402- friend class UCListItemSnapAnimator;
1403+ friend class ListItemAnimator;
1404 };
1405
1406 #endif // UCLISTITEMSTYLE_H
1407
1408=== modified file 'modules/Ubuntu/Components/plugin/ucviewitemsattached.cpp'
1409--- modules/Ubuntu/Components/plugin/ucviewitemsattached.cpp 2015-01-20 10:24:08 +0000
1410+++ modules/Ubuntu/Components/plugin/ucviewitemsattached.cpp 2015-02-10 13:46:20 +0000
1411@@ -196,14 +196,8 @@
1412 {
1413 Q_D(UCViewItemsAttached);
1414 if (d->boundItem) {
1415- // depending on content item's X coordinate, we either do animated or prompt rebind
1416- if (d->boundItem->contentItem()->x() != 0.0) {
1417- // content is not in origin, rebind
1418- UCListItemPrivate::get(d->boundItem.data())->_q_rebound();
1419- } else {
1420- // do some cleanup
1421- UCListItemPrivate::get(d->boundItem.data())->promptRebound();
1422- }
1423+ // snap out before we unbind
1424+ UCListItemPrivate::get(d->boundItem)->animator.snap(0);
1425 d->boundItem.clear();
1426 }
1427 // clear binding list
1428
1429=== modified file 'tests/resources/listitems/ListItemTest.qml'
1430--- tests/resources/listitems/ListItemTest.qml 2015-02-05 14:11:27 +0000
1431+++ tests/resources/listitems/ListItemTest.qml 2015-02-10 13:46:20 +0000
1432@@ -95,7 +95,7 @@
1433 color: "lime"
1434 onClicked: {
1435 print("click")
1436- main.override = !main.override
1437+ units.gridUnit += 2;
1438 }
1439 onPressAndHold: print("pressAndHold", objectName)
1440 Label {
1441@@ -154,7 +154,7 @@
1442 id: view
1443 clip: true
1444 width: parent.width
1445- height: units.gu(20)
1446+ height: units.gu(28)
1447 model: 25
1448 pressDelay: 0
1449 delegate: ListItem {
1450@@ -164,10 +164,16 @@
1451 onPressAndHold: print("pressAndHold")
1452 leadingActions: leading
1453 trailingActions: leadingActions
1454+
1455 Label {
1456- text: modelData + " item"
1457+ anchors.fill: parent
1458+ verticalAlignment: Text.AlignVCenter
1459+ text: "This is one Label split in two lines.\n" +
1460+ "The second line - item #" + modelData
1461 }
1462
1463+ onContentMovementEnded: print('end')
1464+
1465 states: State {
1466 name: "override"
1467 when: main.override
1468@@ -181,7 +187,7 @@
1469 Flickable {
1470 id: flicker
1471 width: parent.width
1472- height: units.gu(20)
1473+ height: units.gu(28)
1474 clip: true
1475 contentHeight: column.childrenRect.height
1476 ListItemActions {
1477@@ -198,7 +204,7 @@
1478 model: 10
1479 ListItem {
1480 objectName: "InFlickable"+index
1481- color: "red"
1482+ color: UbuntuColors.red
1483 highlightColor: "lime"
1484 divider.colorFrom: UbuntuColors.green
1485 swipeOvershoot: units.gu(10)
1486
1487=== modified file 'tests/unit_x11/tst_components/tst_listitem.qml'
1488--- tests/unit_x11/tst_components/tst_listitem.qml 2015-02-05 14:11:27 +0000
1489+++ tests/unit_x11/tst_components/tst_listitem.qml 2015-02-10 13:46:20 +0000
1490@@ -86,9 +86,10 @@
1491 color: "blue"
1492 leadingActions: leading
1493 trailingActions: trailing
1494- Item {
1495+ Label {
1496 id: bodyItem
1497 anchors.fill: parent
1498+ text: "Data"
1499 }
1500 }
1501 ListItem {
1502@@ -112,6 +113,9 @@
1503 width: parent.width
1504 leadingActions: leading
1505 trailingActions: trailing
1506+ Label {
1507+ text: "Data " + index
1508+ }
1509 }
1510 }
1511 Flickable {
1512@@ -140,7 +144,6 @@
1513 id: movingSpy
1514 signalName: "contentMovementEnded"
1515 }
1516-
1517 SignalSpy {
1518 id: highlightedSpy
1519 signalName: "highlightedChanged"
1520@@ -171,14 +174,15 @@
1521 watchTarget = item;
1522 }
1523
1524- movingSpy.target = null;
1525- movingSpy.target = watchTarget;
1526- movingSpy.clear();
1527- mouseClick(item, centerOf(item).x, centerOf(item).y);
1528- if (watchTarget.contentMoving) {
1529+ if (watchTarget.contentItem.x != watchTarget.contentItem.anchors.leftMargin) {
1530+ movingSpy.target = watchTarget;
1531+ movingSpy.clear();
1532+ mouseClick(item, centerOf(item).x, centerOf(item).y);
1533 movingSpy.wait();
1534+ tryCompareFunction(function() {
1535+ return watchTarget.contentItem.x == watchTarget.contentItem.anchors.leftMargin;
1536+ }, true, 500);
1537 }
1538- movingSpy.target = null;
1539 }
1540
1541 function initTestCase() {
1542@@ -188,6 +192,7 @@
1543
1544 function cleanup() {
1545 testItem.action = null;
1546+ testItem.contentItem.anchors.margins = 0;
1547 movingSpy.clear();
1548 highlightedSpy.clear();
1549 clickSpy.clear();
1550@@ -213,8 +218,8 @@
1551 compare(defaults.highlighted, false, "Not highlighted by default");
1552 compare(defaults.swipeOvershoot, 0.0, "No overshoot till the style is loaded!");
1553 compare(defaults.divider.visible, true, "divider is visible by default");
1554- compare(defaults.divider.leftMargin, 0, "divider's left margin is 0");
1555- compare(defaults.divider.rightMargin, 0, "divider's right margin is 0");
1556+ compare(defaults.divider.anchors.leftMargin, 0, "divider's left margin is 0");
1557+ compare(defaults.divider.anchors.rightMargin, 0, "divider's right margin is 0");
1558 compare(defaults.divider.colorFrom, "#000000", "colorFrom differs.");
1559 fuzzyCompare(defaults.divider.colorFrom.a, 0.14, 0.01, "colorFrom alpha differs");
1560 compare(defaults.divider.colorTo, "#ffffff", "colorTo differs.");
1561@@ -266,6 +271,38 @@
1562 clickSpy.wait();
1563 }
1564
1565+ function test_no_click_when_swiped() {
1566+ var item = findChild(listView, "listItem0");
1567+ clickSpy.target = item;
1568+ clickSpy.clear();
1569+ movingSpy.target = item;
1570+ flick(item, centerOf(item).x, centerOf(item).y, units.gu(20), 0);
1571+ movingSpy.wait();
1572+
1573+ // click over the contentItem
1574+ movingSpy.clear();
1575+ mouseClick(item.contentItem, 1, 1);
1576+ compare(clickSpy.count, 0, "No click() should be emitted on a swiped in ListItem.");
1577+ movingSpy.wait();
1578+ }
1579+
1580+ function test_no_pressAndHold_when_swiped() {
1581+ var item = findChild(listView, "listItem0");
1582+ pressAndHoldSpy.target = item;
1583+ pressAndHoldSpy.clear();
1584+ movingSpy.target = item;
1585+ flick(item, centerOf(item).x, centerOf(item).y, units.gu(20), 0);
1586+ movingSpy.wait();
1587+
1588+ // press and hold
1589+ movingSpy.clear();
1590+ mouseLongPress(item.contentItem, 1, 1);
1591+ mouseRelease(item.contentItem, 1, 1);
1592+ mouseRelease(item.contentItem, 1, 1);
1593+ compare(pressAndHoldSpy.count, 0, "No pressAndHold() should be emitted on a swiped in ListItem.");
1594+ movingSpy.wait();
1595+ }
1596+
1597 function test_mouse_click_on_listitem() {
1598 var listItem = findChild(listView, "listItem0");
1599 verify(listItem, "Cannot find listItem0");
1600@@ -279,9 +316,9 @@
1601 mouseMove(listItem, listItem.width / 2, dy);
1602 }
1603 compare(listItem.highlighted, false, "Item is highlighted still!");
1604- mouseRelease(listItem, listItem.width / 2, dy);
1605- // dismiss
1606- rebound(listItem);
1607+ // cleanup, simulate drop event
1608+ mouseRelease(listItem, listItem.width / 2, dy);
1609+ mouseRelease(listItem, listItem.width / 2, dy);
1610 }
1611 function test_touch_click_on_listitem() {
1612 var listItem = findChild(listView, "listItem0");
1613@@ -298,16 +335,18 @@
1614 compare(listItem.highlighted, false, "Item is highlighted still!");
1615 // cleanup, wait few milliseconds to avoid dbl-click collision
1616 TestExtras.touchRelease(0, listItem, Qt.point(listItem.width / 2, dy));
1617- // dismiss
1618- rebound(listItem);
1619+ TestExtras.touchRelease(0, listItem, Qt.point(listItem.width / 2, dy));
1620+ wait(400);
1621 }
1622
1623 function test_background_height_change_on_divider_visible() {
1624 // make sure the testItem's divider is shown
1625 testItem.divider.visible = true;
1626- verify(testItem.contentItem.height < testItem.height, "ListItem's background height must be less than the item itself.");
1627+ var margins = testItem.contentItem.anchors.topMargin + testItem.contentItem.anchors.bottomMargin;
1628+ compare(testItem.contentItem.height, testItem.height - margins - testItem.divider.height, "ListItem's background height must be less than the item itself.");
1629 testItem.divider.visible = false;
1630- compare(testItem.contentItem.height, testItem.height, "ListItem's background height must be the same as the item itself.");
1631+ waitForRendering(testItem.contentItem);
1632+ compare(testItem.contentItem.height, testItem.height - margins, "ListItem's background height must be the same as the item itself.");
1633 testItem.divider.visible = true;
1634 }
1635
1636@@ -386,16 +425,13 @@
1637 }
1638 movingSpy.wait();
1639 // animation should no longer be running!
1640- verify(!data.item.__styleInstance.snapAnimation.running, "Animation is still running!");
1641 compare(listView.interactive, true, "The ListView is still non-interactive!");
1642 compare(interactiveSpy.count, 2, "Less/more times changed!");
1643 // check if it snapped in
1644 verify(data.item.contentItem.x != 0.0, "Not snapped in!!");
1645 // dismiss
1646 rebound(data.clickOn, data.item);
1647- // animation should no longer be running!
1648- verify(!data.item.__styleInstance.snapAnimation.running, "Animation is still running!");
1649- fuzzyCompare(data.item.contentItem.x, 0.0, 0.1, "Not snapped out!!");
1650+ fuzzyCompare(data.item.contentItem.x, data.item.contentItem.anchors.leftMargin, 0.1, "Not snapped out!!");
1651 }
1652
1653 function test_visualized_actions_data() {
1654@@ -422,6 +458,26 @@
1655 rebound(data.item);
1656 }
1657
1658+ function test_listitem_margins_data() {
1659+ var item = findChild(listView, "listItem1");
1660+ return [
1661+ {tag: "leading", item: item, dx: units.gu(10), leading: true},
1662+ {tag: "trailing", item: item, dx: -units.gu(10), leading: false}
1663+ ];
1664+ }
1665+ function test_listitem_margins(data) {
1666+ data.item.contentItem.anchors.margins = units.gu(1);
1667+ movingSpy.target = data.item;
1668+ flick(data.item, centerOf(data.item).x, centerOf(data.item).y, data.dx, 0);
1669+ movingSpy.wait();
1670+ var panel = panelItem(data.item, data.leading);
1671+ verify(panel && panel.visible, "Panel not visible.");
1672+ // cleanup
1673+ rebound(data.item);
1674+ compare(data.item.contentItem.x, units.gu(1), "contentItem.x differs from margin");
1675+ data.item.contentItem.anchors.margins = 0;
1676+ }
1677+
1678 function test_selecting_action_rebounds_data() {
1679 var item0 = findChild(listView, "listItem0");
1680 return [
1681@@ -452,7 +508,7 @@
1682 TestExtras.touchClick(0, selectedAction, centerOf(selectedAction));
1683 }
1684 movingSpy.wait();
1685- fuzzyCompare(data.item.contentItem.x, 0.0, 0.1, "Content not snapped out");
1686+ fuzzyCompare(data.item.contentItem.x, data.item.contentItem.anchors.leftMargin, 0.1, "Content not snapped out");
1687 }
1688
1689 function test_custom_trailing_delegate() {
1690@@ -494,14 +550,14 @@
1691 // cleanup
1692 rebound(data.item);
1693 } else {
1694- tryCompareFunction(function() { return data.item.contentItem.x; }, 0.0, 1000, "Not snapped back");
1695+ tryCompareFunction(function() { return data.item.contentItem.x; }, data.item.contentItem.anchors.leftMargin, 1000, "Not snapped back");
1696 }
1697 }
1698
1699 function test_snap_gesture_data() {
1700 var listItem = findChild(listView, "listItem0");
1701- var front = Qt.point(units.gu(1), listItem.height / 2);
1702- var rear = Qt.point(listItem.width - units.gu(1), listItem.height / 2);
1703+ var front = Qt.point(listItem.contentItem.anchors.leftMargin + units.gu(1), listItem.height / 2);
1704+ var rear = Qt.point(listItem.width - (listItem.contentItem.anchors.rightMargin + units.gu(1)), listItem.height / 2);
1705 return [
1706 // the first dx must be big enough to drag the panel in, it is always the last dx value
1707 // which decides the snap direction
1708@@ -531,7 +587,7 @@
1709 // dismiss
1710 rebound(data.item);
1711 } else {
1712- fuzzyCompare(data.item.contentItem.x, 0.0, 0.1, "Not snapped out!");
1713+ fuzzyCompare(data.item.contentItem.x, data.item.contentItem.anchors.leftMargin, 0.1, "Not snapped out!");
1714 }
1715 }
1716
1717@@ -596,9 +652,9 @@
1718 function test_verify_action_value(data) {
1719 // tug actions in
1720 movingSpy.target = data.item;
1721- flick(data.item, centerOf(data.item).x, centerOf(data.item).y, units.gu(20), 0, 100, 10);
1722+ flick(data.item, centerOf(data.item).x, centerOf(data.item).y, units.gu(20), 0);
1723 movingSpy.wait();
1724- verify(data.item.contentItem.x != 0.0, "Not snapped in");
1725+ verify(data.item.contentItem.x != data.item.contentItem.anchors.leftMargin, "Not snapped in");
1726
1727 var panel = panelItem(data.item, "Leading");
1728 var action = findChild(panel, "leading_2");
1729@@ -607,9 +663,7 @@
1730 actionSpy.target = data.item.leadingActions.actions[1];
1731
1732 // select the action
1733- movingSpy.clear();
1734 mouseClick(action, centerOf(action).x, centerOf(action).y);
1735- movingSpy.wait();
1736
1737 // check the action param
1738 actionSpy.wait();

Subscribers

People subscribed via source and target branches