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