Unity 8
LauncherDelegate.qml
1 /*
2  * Copyright (C) 2013 Canonical, Ltd.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; version 3.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program. If not, see <http://www.gnu.org/licenses/>.
15  */
16 
17 import QtQuick 2.4
18 import Ubuntu.Components 1.3
19 
20 Item {
21  id: root
22 
23  property int itemIndex: 0
24  property string iconName
25  property int count: 0
26  property bool countVisible: false
27  property int progress: -1
28  property bool itemRunning: false
29  property bool itemFocused: false
30  property real maxAngle: 0
31  property bool inverted: false
32  property bool alerting: false
33  property bool highlighted: false
34  property bool shortcutHintShown: false
35 
36  readonly property int effectiveHeight: Math.cos(angle * Math.PI / 180) * itemHeight
37  readonly property real foldedHeight: Math.cos(maxAngle * Math.PI / 180) * itemHeight
38  readonly property alias wiggling: wiggleAnim.running
39 
40  property int itemWidth
41  property int itemHeight
42  // The angle used for rotating
43  property real angle: 0
44  // This is the offset that keeps the items inside the panel
45  property real offset: 0
46  property real itemOpacity: 1
47  property real brightness: 0
48  property double maxWiggleAngle: 5.0
49 
50  QtObject {
51  id: priv
52 
53  readonly property int wiggleDuration: UbuntuAnimation.SnapDuration
54  property real wiggleAngle: 0
55  }
56 
57  SequentialAnimation {
58  id: wiggleAnim
59 
60  running: alerting
61  loops: 1
62  alwaysRunToEnd: true
63 
64  NumberAnimation {
65  target: priv
66  property: "wiggleAngle"
67  from: 0
68  to: maxWiggleAngle
69  duration: priv.wiggleDuration
70  easing.type: Easing.InQuad
71  }
72 
73  NumberAnimation {
74  target: priv
75  property: "wiggleAngle"
76  from: maxWiggleAngle
77  to: -maxWiggleAngle
78  duration: priv.wiggleDuration
79  easing.type: Easing.InOutQuad
80  }
81 
82  NumberAnimation {
83  target: priv
84  property: "wiggleAngle"
85  from: -maxWiggleAngle
86  to: maxWiggleAngle
87  duration: priv.wiggleDuration
88  easing.type: Easing.InOutQuad
89  }
90 
91  NumberAnimation {
92  target: priv
93  property: "wiggleAngle"
94  from: maxWiggleAngle
95  to: -maxWiggleAngle
96  duration: priv.wiggleDuration
97  easing.type: Easing.InOutQuad
98  }
99 
100  NumberAnimation {
101  target: priv
102  property: "wiggleAngle"
103  from: -maxWiggleAngle
104  to: maxWiggleAngle
105  duration: priv.wiggleDuration
106  easing.type: Easing.InOutQuad
107  }
108 
109  NumberAnimation {
110  target: priv
111  property: "wiggleAngle"
112  from: maxWiggleAngle
113  to: 0
114  duration: priv.wiggleDuration
115  easing.type: Easing.OutQuad
116  }
117 
118  UbuntuNumberAnimation {
119  target: root
120  property: "alerting"
121  to: 0
122  }
123  }
124 
125  Item {
126  id: iconItem
127  width: root.width
128  height: parent.itemHeight + units.gu(1)
129  anchors.centerIn: parent
130 
131  Image {
132  objectName: "focusRing"
133  anchors.centerIn: iconShape
134  height: width * 15 / 16
135  width: iconShape.width + units.gu(1)
136  source: "graphics/launcher-app-focus-ring.svg"
137  sourceSize.width: width
138  sourceSize.height: height
139  visible: root.highlighted
140  }
141 
142  ProportionalShape {
143  id: iconShape
144  anchors.centerIn: parent
145  width: root.itemWidth
146  aspect: UbuntuShape.DropShadow
147  source: Image {
148  id: iconImage
149  sourceSize.width: iconShape.width
150  sourceSize.height: iconShape.height
151  source: root.iconName
152  cache: false // see lpbug#1543290 why no cache
153  }
154  }
155 
156  UbuntuShape {
157  id: countEmblem
158  objectName: "countEmblem"
159  anchors {
160  right: parent.right
161  bottom: parent.bottom
162  rightMargin: (iconItem.width - root.itemWidth) / 2 - units.dp(2)
163  margins: units.dp(5)
164  }
165  width: Math.min(root.itemWidth, Math.max(units.gu(2), countLabel.implicitWidth + units.gu(1)))
166  height: units.gu(2)
167  backgroundColor: theme.palette.normal.positive
168  visible: root.countVisible
169  aspect: UbuntuShape.Flat
170 
171  Label {
172  id: countLabel
173  objectName: "countLabel"
174  text: root.count
175  anchors.centerIn: parent
176  width: root.itemWidth - units.gu(1)
177  horizontalAlignment: Text.AlignHCenter
178  elide: Text.ElideRight
179  color: "white"
180  fontSize: "x-small"
181  }
182  }
183 
184  UbuntuShape {
185  id: progressOverlay
186  objectName: "progressOverlay"
187 
188  anchors.centerIn: parent
189  width: root.itemWidth * .8
190  height: units.dp(3)
191  visible: root.progress > -1
192  backgroundColor: "white"
193  borderSource: "none"
194 
195  Item {
196  anchors {
197  left: parent.left
198  top: parent.top
199  bottom: parent.bottom
200  }
201  width: Math.min(100, root.progress) / 100 * parent.width
202  clip: true
203 
204  UbuntuShape {
205  anchors {
206  left: parent.left
207  top: parent.top
208  bottom: parent.bottom
209  }
210  backgroundColor: theme.palette.normal.activity
211  borderSource: "none"
212  width: progressOverlay.width
213  }
214  }
215  }
216 
217  Column {
218  anchors {
219  left: parent.left
220  verticalCenter: parent.verticalCenter
221  }
222  spacing: units.gu(.5)
223  Repeater {
224  model: 1 // TODO: This should be "Math.min(3, app.surfaceCount)" once we have multiple surfaces
225  Rectangle {
226  objectName: "runningHighlight" + index
227  width: units.gu(0.25)
228  height: units.gu(.5)
229  color: "white"
230  visible: root.itemRunning
231  }
232  }
233  }
234 
235  Rectangle {
236  objectName: "focusedHighlight"
237  anchors {
238  right: parent.right
239  verticalCenter: parent.verticalCenter
240  }
241  width: units.gu(0.25)
242  height: units.gu(.5)
243  color: "white"
244  visible: root.itemFocused
245  }
246 
247  UbuntuShape {
248  objectName: "shortcutHint"
249  anchors.centerIn: parent
250  width: units.gu(2.5)
251  height: width
252  backgroundColor: "#F2111111"
253  visible: root.shortcutHintShown
254  aspect: UbuntuShape.Flat
255  Label {
256  anchors.centerIn: parent
257  text: (itemIndex + 1) % 10
258  color: "white"
259  font.weight: Font.Light
260  }
261  }
262  }
263 
264  ShaderEffect {
265  id: transformEffect
266  anchors.centerIn: parent
267  anchors.verticalCenterOffset: root.offset
268  width: iconItem.width
269  height: iconItem.height
270  property real itemOpacity: root.itemOpacity
271  property real brightness: Math.max(-1, root.brightness)
272  property real angle: root.angle
273  rotation: root.inverted ? 180 : 0
274 
275  property variant source: ShaderEffectSource {
276  id: shaderEffectSource
277  sourceItem: iconItem
278  hideSource: true
279  }
280 
281  transform: [
282  // The rotation about the icon's center/z-axis for the wiggle
283  // needs to happen here too, because there's no other way to
284  // align the wiggle with the icon-folding otherwise
285  Rotation {
286  axis { x: 0; y: 0; z: 1 }
287  origin { x: iconItem.width / 2; y: iconItem.height / 2; z: 0 }
288  angle: priv.wiggleAngle
289  },
290  // Rotating 3 times at top/bottom because that increases the perspective.
291  // This is a hack, but as QML does not support real 3D coordinates
292  // getting a higher perspective can only be done by a hack. This is the most
293  // readable/understandable one I could come up with.
294  Rotation {
295  axis { x: 1; y: 0; z: 0 }
296  origin { x: iconItem.width / 2; y: angle > 0 ? 0 : iconItem.height; z: 0 }
297  angle: root.angle * 0.7
298  },
299  Rotation {
300  axis { x: 1; y: 0; z: 0 }
301  origin { x: iconItem.width / 2; y: angle > 0 ? 0 : iconItem.height; z: 0 }
302  angle: root.angle * 0.7
303  },
304  Rotation {
305  axis { x: 1; y: 0; z: 0 }
306  origin { x: iconItem.width / 2; y: angle > 0 ? 0 : iconItem.height; z: 0 }
307  angle: root.angle * 0.7
308  },
309  // Because rotating it 3 times moves it more to the front/back, i.e. it gets
310  // bigger/smaller and we need a scale to compensate that again.
311  Scale {
312  xScale: 1 - (Math.abs(angle) / 500)
313  yScale: 1 - (Math.abs(angle) / 500)
314  origin { x: iconItem.width / 2; y: iconItem.height / 2}
315  }
316  ]
317 
318  // Using a fragment shader instead of QML's opacity and BrightnessContrast
319  // to be able to do both in one step which gives quite some better performance
320  fragmentShader: "
321  varying highp vec2 qt_TexCoord0;
322  uniform sampler2D source;
323  uniform lowp float brightness;
324  uniform lowp float itemOpacity;
325  void main(void)
326  {
327  highp vec4 sourceColor = texture2D(source, qt_TexCoord0);
328  sourceColor.rgb = mix(sourceColor.rgb, vec3(step(0.0, brightness)), abs(brightness));
329  sourceColor *= itemOpacity;
330  gl_FragColor = sourceColor;
331  }"
332  }
333 }