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: UbuntuColors.orange
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  // FIXME: verticalCenter seems to be off wee bit and QML doesn't have a centerLine
177  // property for Text: https://bugreports.qt-project.org/browse/QTBUG-40479
178  anchors.verticalCenterOffset: -units.dp(.5)
179  width: root.itemWidth - units.gu(1)
180  horizontalAlignment: Text.AlignHCenter
181  elide: Text.ElideRight
182  color: "white"
183  fontSize: "x-small"
184  }
185  }
186 
187  UbuntuShape {
188  id: progressOverlay
189  objectName: "progressOverlay"
190 
191  anchors.centerIn: parent
192  width: root.itemWidth * .8
193  height: units.gu(1)
194  visible: root.progress > -1
195  backgroundColor: UbuntuColors.darkGrey
196  borderSource: "none"
197 
198  Item {
199  anchors {
200  left: parent.left
201  top: parent.top
202  bottom: parent.bottom
203  }
204  width: Math.min(100, root.progress) / 100 * parent.width
205  clip: true
206 
207  UbuntuShape {
208  anchors {
209  left: parent.left
210  top: parent.top
211  bottom: parent.bottom
212  }
213  backgroundColor: "white"
214  borderSource: "none"
215  width: progressOverlay.width
216  }
217  }
218  }
219 
220  Column {
221  anchors {
222  left: parent.left
223  verticalCenter: parent.verticalCenter
224  }
225  spacing: units.gu(.5)
226  Repeater {
227  model: 1 // TODO: This should be "Math.min(3, app.surfaceCount)" once we have multiple surfaces
228  Rectangle {
229  objectName: "runningHighlight" + index
230  width: units.gu(0.25)
231  height: units.gu(.5)
232  color: "white"
233  visible: root.itemRunning
234  }
235  }
236  }
237 
238  Rectangle {
239  objectName: "focusedHighlight"
240  anchors {
241  right: parent.right
242  verticalCenter: parent.verticalCenter
243  }
244  width: units.gu(0.25)
245  height: units.gu(.5)
246  color: "white"
247  visible: root.itemFocused
248  }
249 
250  Rectangle {
251  objectName: "shortcutHint"
252  anchors.centerIn: parent
253  width: units.gu(3)
254  height: width
255  color: "#E0292929"
256  visible: root.shortcutHintShown
257  Label {
258  anchors.centerIn: parent
259  text: (itemIndex + 1) % 10
260  color: "white"
261  font.weight: Font.DemiBold
262  }
263  }
264  }
265 
266  ShaderEffect {
267  id: transformEffect
268  anchors.centerIn: parent
269  anchors.verticalCenterOffset: root.offset
270  width: iconItem.width
271  height: iconItem.height
272  property real itemOpacity: root.itemOpacity
273  property real brightness: Math.max(-1, root.brightness)
274  property real angle: root.angle
275  rotation: root.inverted ? 180 : 0
276 
277  property variant source: ShaderEffectSource {
278  id: shaderEffectSource
279  sourceItem: iconItem
280  hideSource: true
281  }
282 
283  transform: [
284  // The rotation about the icon's center/z-axis for the wiggle
285  // needs to happen here too, because there's no other way to
286  // align the wiggle with the icon-folding otherwise
287  Rotation {
288  axis { x: 0; y: 0; z: 1 }
289  origin { x: iconItem.width / 2; y: iconItem.height / 2; z: 0 }
290  angle: priv.wiggleAngle
291  },
292  // Rotating 3 times at top/bottom because that increases the perspective.
293  // This is a hack, but as QML does not support real 3D coordinates
294  // getting a higher perspective can only be done by a hack. This is the most
295  // readable/understandable one I could come up with.
296  Rotation {
297  axis { x: 1; y: 0; z: 0 }
298  origin { x: iconItem.width / 2; y: angle > 0 ? 0 : iconItem.height; z: 0 }
299  angle: root.angle * 0.7
300  },
301  Rotation {
302  axis { x: 1; y: 0; z: 0 }
303  origin { x: iconItem.width / 2; y: angle > 0 ? 0 : iconItem.height; z: 0 }
304  angle: root.angle * 0.7
305  },
306  Rotation {
307  axis { x: 1; y: 0; z: 0 }
308  origin { x: iconItem.width / 2; y: angle > 0 ? 0 : iconItem.height; z: 0 }
309  angle: root.angle * 0.7
310  },
311  // Because rotating it 3 times moves it more to the front/back, i.e. it gets
312  // bigger/smaller and we need a scale to compensate that again.
313  Scale {
314  xScale: 1 - (Math.abs(angle) / 500)
315  yScale: 1 - (Math.abs(angle) / 500)
316  origin { x: iconItem.width / 2; y: iconItem.height / 2}
317  }
318  ]
319 
320  // Using a fragment shader instead of QML's opacity and BrightnessContrast
321  // to be able to do both in one step which gives quite some better performance
322  fragmentShader: "
323  varying highp vec2 qt_TexCoord0;
324  uniform sampler2D source;
325  uniform lowp float brightness;
326  uniform lowp float itemOpacity;
327  void main(void)
328  {
329  highp vec4 sourceColor = texture2D(source, qt_TexCoord0);
330  sourceColor.rgb = mix(sourceColor.rgb, vec3(step(0.0, brightness)), abs(brightness));
331  sourceColor *= itemOpacity;
332  gl_FragColor = sourceColor;
333  }"
334  }
335 }