on xenial help sign in
convert launcher to be a flickable.

add interactive folding areas and snapping

commit4166f7204cab0e51de5bacedb91f21a067348462 authorMichael Zanetti <michael.zanetti@canonical.com> dateMon 24 Jun 2013, 14:07 (13 years ago) parentc51562b
2 files changed+92−61
Launcher/LauncherDelegate.qml+17−19 Launcher/LauncherPanel.qml+75−42
Launcher/LauncherDelegate.qml+17−19
@@ -67,6 +67,7 @@ Item {
6767 onClicked: root.clicked()
6868 onCanceled: root.released()
6969 preventStealing: false
70+ enabled: root.angle < 5
7071
7172 onPressAndHold: {
7273 root.state = "moving"
@@ -153,18 +154,15 @@ Item {
153154 QtObject {
154155 id: priv
155156
156− // The height of the area where icons start getting folded
157− property int foldingAreaHeight: totalUnfoldedHeight * 0.75
158−
159− property real totalUnfoldedHeight: launcherFlickable.itemSize + launcherColumn.spacing
160− property real totalEffectiveHeight: effectiveHeight + launcherColumn.spacing
161− property real distanceFromTopEdge: -(launcherFlickable.contentY - itemsBeforeThis*totalUnfoldedHeight)
162− property real distanceFromBottomEdge: launcherFlickable.height - (y+height) + launcherFlickable.contentY
157+ property real totalUnfoldedHeight: launcherListView.itemSize + launcherListView.spacing
158+ property real totalEffectiveHeight: effectiveHeight + launcherListView.spacing
159+ property real distanceFromTopEdge: -(launcherListView.contentY + launcherListView.topMargin - itemsBeforeThis*totalUnfoldedHeight)
160+ property real distanceFromBottomEdge: launcherListView.height - launcherListView.bottomMargin - (y+height) + launcherListView.contentY
163161
164162 property real distanceFromEdge: Math.abs(distanceFromBottomEdge) < Math.abs(distanceFromTopEdge) ? distanceFromBottomEdge : distanceFromTopEdge
165163 property real orientationFlag: Math.abs(distanceFromBottomEdge) < Math.abs(distanceFromTopEdge) ? -1 : 1
166164
167− property real overlapWithFoldingArea: foldingAreaHeight - distanceFromEdge
165+ property real overlapWithFoldingArea: launcherListView.foldingAreaHeight - distanceFromEdge
168166
169167 }
170168
@@ -177,7 +175,7 @@ Item {
177175 // This is the offset that keeps the items inside the panel
178176 offset: {
179177 // First/last items are special
180− if (index == 0 || index == iconRepeater.count-1) {
178+ if (index == 0 || index == launcherListView.count-1) {
181179 // Just keep them bound to the edges in case they're outside of the visible area
182180 if (priv.distanceFromEdge < 0) {
183181 return (-priv.distanceFromEdge - (height - effectiveHeight)) * priv.orientationFlag;
@@ -203,10 +201,10 @@ Item {
203201 angle: {
204202 //return 0;
205203 // First item is special
206− if (index == 0 || index == iconRepeater.count-1) {
204+ if (index == 0 || index == launcherListView.count-1) {
207205 if (priv.distanceFromEdge < 0) {
208206 // distanceFromTopEdge : angle = totalUnfoldedHeight/2 : maxAngle
209− return Math.max(-maxAngle, priv.distanceFromEdge * maxAngle / (priv.foldingAreaHeight)) * priv.orientationFlag
207+ return Math.max(-maxAngle, priv.distanceFromEdge * maxAngle / (launcherListView.foldingAreaHeight)) * priv.orientationFlag
210208 }
211209 return 0; // Don't fold first item as long as inside the view
212210 }
@@ -214,13 +212,13 @@ Item {
214212 // Are we in the already completely outside the flickable? Fold for the last 5 degrees
215213 if (priv.distanceFromEdge < 0) {
216214 // -distanceFromTopEdge : angle = totalUnfoldedHeight : 5
217− return Math.max(-maxAngle, (priv.distanceFromEdge * 5 / priv.foldingAreaHeight) - (maxAngle-5)) * priv.orientationFlag
215+ return Math.max(-maxAngle, (priv.distanceFromEdge * 5 / launcherListView.foldingAreaHeight) - (maxAngle-5)) * priv.orientationFlag
218216 }
219217
220218 // We are overlapping with the folding area, fold the icon to maxAngle - 5 degrees
221219 if (priv.overlapWithFoldingArea > 0) {
222220 // overlap: totalHeight = angle : (maxAngle - 5)
223− return -priv.overlapWithFoldingArea * (maxAngle -5) / priv.foldingAreaHeight * priv.orientationFlag;
221+ return -priv.overlapWithFoldingArea * (maxAngle -5) / launcherListView.foldingAreaHeight * priv.orientationFlag;
224222
225223 }
226224
@@ -229,10 +227,10 @@ Item {
229227
230228 opacity: {
231229 // First item is special
232− if (index == 0 || index == iconRepeater.count-1) {
230+ if (index == 0 || index == launcherListView.count-1) {
233231 if (priv.distanceFromEdge < 0) {
234232 // Fade from 1 to 0 in the distance of 3 * foldingAreaHeight (which is when the next item reaches the edge)
235− return 1.0 - (-priv.distanceFromEdge / (priv.foldingAreaHeight * 3))
233+ return 1.0 - (-priv.distanceFromEdge / (launcherListView.foldingAreaHeight * 3))
236234 }
237235 return 1; // Don't make first/last item transparent as long as inside the view
238236 }
@@ -246,15 +244,15 @@ Item {
246244 // We are overlapping with the folding area, fade out to 0.75
247245 if (priv.overlapWithFoldingArea > 0) {
248246 // overlap : totalHeight = 1-opacity : 0.25
249− return 1 - (priv.overlapWithFoldingArea * 0.25 / priv.foldingAreaHeight)
247+ return 1 - (priv.overlapWithFoldingArea * 0.25 / launcherListView.foldingAreaHeight)
250248 }
251249 return 1;
252250 }
253251
254252 brightness: {
255− if (index == 0 || index == iconRepeater.count-1) {
253+ if (index == 0 || index == launcherListView.count-1) {
256254 if (priv.distanceFromEdge < 0) {
257− return -(-priv.distanceFromEdge / (priv.foldingAreaHeight * 3))
255+ return -(-priv.distanceFromEdge / (launcherListView.foldingAreaHeight * 3))
258256 }
259257 return 0;
260258 }
@@ -263,7 +261,7 @@ Item {
263261 }
264262
265263 if (priv.overlapWithFoldingArea > 0) {
266− return - (priv.overlapWithFoldingArea * 0.3 / priv.foldingAreaHeight)
264+ return - (priv.overlapWithFoldingArea * 0.3 / launcherListView.foldingAreaHeight)
267265 }
268266 return 0;
269267 }
Launcher/LauncherPanel.qml+75−42
@@ -27,7 +27,7 @@ Item {
2727 property var model
2828 property bool inverted: false
2929 property bool dragging: false
30− property bool moving: launcherFlickable.moving
30+ property bool moving: launcherListView.moving
3131 property int dragPosition: 0
3232 property int highlightIndex: -1
3333
@@ -35,23 +35,23 @@ Item {
3535 signal dashItemSelected(int index)
3636
3737 onDragPositionChanged: {
38− var effectiveDragPosition = root.inverted ? launcherFlickable.height - dragPosition : dragPosition - mainColumn.anchors.margins
38+ var effectiveDragPosition = root.inverted ? launcherListView.height - dragPosition : dragPosition - mainColumn.anchors.margins
3939
40− var hiddenContentHeight = launcherFlickable.contentHeight - launcherFlickable.height
40+ var hiddenContentHeight = launcherListView.contentHeight - launcherListView.height
4141 // Shortening scrollable height because the first/last item needs to be fully expanded before reaching the top/bottom
42− var scrollableHeight = launcherFlickable.height - (launcherFlickable.itemSize + launcherColumn.spacing) *2
42+ var scrollableHeight = launcherListView.height - (launcherListView.itemSize + launcherColumn.spacing) *2
4343 // As we shortened the scrollableHeight, lets move everything down by the itemSize
44− var shortenedEffectiveDragPosition = effectiveDragPosition - launcherFlickable.itemSize - launcherColumn.spacing
44+ var shortenedEffectiveDragPosition = effectiveDragPosition - launcherListView.itemSize - launcherColumn.spacing
4545 var newContentY = shortenedEffectiveDragPosition * hiddenContentHeight / scrollableHeight
4646
4747 // limit top/bottom to prevent overshooting
48− launcherFlickable.contentY = Math.min(hiddenContentHeight, Math.max(0, newContentY));
48+ launcherListView.contentY = Math.min(hiddenContentHeight, Math.max(0, newContentY));
4949
5050 // Now calculate the current index:
5151 // > the current mouse position + the hidden/scolled content on top is the mouse position in the averall view
5252 // > adjust that removing all the margins
5353 // > divide by itemSize to get index
54− highlightIndex = (effectiveDragPosition + launcherFlickable.contentY - mainColumn.anchors.margins*3 - launcherColumn.spacing/2) / (launcherFlickable.itemSize + launcherColumn.spacing)
54+ highlightIndex = (effectiveDragPosition + launcherListView.contentY - mainColumn.anchors.margins*3 - launcherColumn.spacing/2) / (launcherListView.itemSize + launcherColumn.spacing)
5555 }
5656
5757 BorderImage {
@@ -76,6 +76,7 @@ Item {
7676 width: parent.width
7777 height: units.gu(6.5)
7878 onClicked: root.dashItemSelected(0)
79+ z: 1
7980 Image {
8081 objectName: "dashItem"
8182 width: units.gu(5.5)
@@ -92,46 +93,78 @@ Item {
9293 }
9394 }
9495
95− Flickable {
96− id: launcherFlickable
96+ Item {
9797 anchors.left: parent.left
9898 anchors.right: parent.right
9999 height: parent.height - dashItem.height - parent.spacing*2
100− contentHeight: launcherColumn.height
101−
102− property int itemSize: width
103−
104− Column {
105− id: launcherColumn
106− width: parent.width
107− //spacing: units.gu(1)
108− anchors.horizontalCenter: parent.horizontalCenter
109−
110− Repeater {
111− id: iconRepeater
112− model: root.model
113−
114− LauncherDelegate {
115− id: launcherDelegate
116− objectName: "launcherDelegate" + index
117− width: launcherFlickable.itemSize
118− height: launcherFlickable.itemSize
119− iconName: model.icon
120− inverted: root.inverted
121− highlighted: root.dragging && index === root.highlightIndex
122− z: -Math.abs(offset)
123− state: "docked"
124−
125− maxAngle: 60
126−
127− itemsBeforeThis: index
128− itemsAfterThis: iconRepeater.count - (index+1)
129−
130− onClicked: {
131− root.applicationSelected(launcherModel.get(index).desktopFile);
132− }
100+ ListView {
101+ id: launcherListView
102+ anchors.fill: parent
103+ anchors.topMargin: -itemSize
104+ anchors.bottomMargin: -itemSize
105+ topMargin: itemSize
106+ bottomMargin: itemSize
107+ height: parent.height - dashItem.height - parent.spacing*2
108+ model: root.model
109+ cacheBuffer: itemSize * 3
110+ snapMode: ListView.SnapToItem
111+ highlightRangeMode: ListView.ApplyRange
112+ preferredHighlightBegin: (height - itemSize) / 2
113+ preferredHighlightEnd: (height + itemSize) / 2
114+ currentIndex: 0
115+
116+ property int itemSize: width
117+
118+ // The height of the area where icons start getting folded
119+ property int foldingAreaHeight: itemSize * 0.75
120+
121+
122+ delegate: LauncherDelegate {
123+ id: launcherDelegate
124+ objectName: "launcherDelegate" + index
125+ width: launcherListView.itemSize
126+ height: launcherListView.itemSize
127+ iconName: model.icon
128+ inverted: root.inverted
129+ highlighted: root.dragging && index === root.highlightIndex
130+ z: -Math.abs(offset)
131+ state: "docked"
132+
133+ maxAngle: 60
134+
135+ itemsBeforeThis: index
136+ itemsAfterThis: launcherListView.count - (index+1)
137+
138+ onClicked: {
139+ root.applicationSelected(launcherModel.get(index).desktopFile);
133140 }
134141 }
142+
143+ MouseArea {
144+ id: topFoldingArea
145+ anchors {
146+ left: parent.left
147+ right: parent.right
148+ top: parent.top
149+ topMargin: launcherListView.topMargin
150+ }
151+ height: launcherListView.itemSize
152+ enabled: launcherListView.contentY > -launcherListView.topMargin
153+ onClicked: launcherListView.flick(0, units.gu(40))
154+ }
155+
156+ MouseArea {
157+ id: bottomFoldingArea
158+ anchors {
159+ left: parent.left
160+ right: parent.right
161+ bottom: parent.bottom
162+ bottomMargin: launcherListView.bottomMargin
163+ }
164+ height: launcherListView.itemSize
165+ enabled: launcherListView.contentHeight - launcherListView.height - launcherListView.contentY > -launcherListView.bottomMargin
166+ onClicked: launcherListView.flick(0, -units.gu(40))
167+ }
135168 }
136169 }
137170 }