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  StyledItem {
128  styleName: "FocusShape"
129  anchors.fill: iconShape
130  activeFocusOnTab: true
131  StyleHints {
132  visible: root.highlighted
133  radius: units.gu(2.55)
134  }
135  }
136 
137  ProportionalShape {
138  id: iconShape
139  anchors.centerIn: parent
140  width: root.itemWidth
141  aspect: UbuntuShape.DropShadow
142  source: Image {
143  id: iconImage
144  sourceSize.width: iconShape.width
145  sourceSize.height: iconShape.height
146  source: root.iconName
147  cache: false // see lpbug#1543290 why no cache
148  }
149  }
150 
151  UbuntuShape {
152  id: countEmblem
153  objectName: "countEmblem"
154  anchors {
155  right: parent.right
156  bottom: parent.bottom
157  rightMargin: (iconItem.width - root.itemWidth) / 2 - units.dp(2)
158  margins: units.dp(5)
159  }
160  width: Math.min(root.itemWidth, Math.max(units.gu(2), countLabel.implicitWidth + units.gu(1)))
161  height: units.gu(2)
162  backgroundColor: theme.palette.normal.positive
163  visible: root.countVisible
164  aspect: UbuntuShape.Flat
165 
166  Label {
167  id: countLabel
168  objectName: "countLabel"
169  text: root.count
170  anchors.centerIn: parent
171  width: root.itemWidth - units.gu(1)
172  horizontalAlignment: Text.AlignHCenter
173  elide: Text.ElideRight
174  color: "white"
175  fontSize: "x-small"
176  }
177  }
178 
179  UbuntuShape {
180  id: progressOverlay
181  objectName: "progressOverlay"
182 
183  anchors.centerIn: parent
184  width: root.itemWidth * .8
185  height: units.dp(3)
186  visible: root.progress > -1
187  backgroundColor: "white"
188  borderSource: "none"
189 
190  Item {
191  anchors {
192  left: parent.left
193  top: parent.top
194  bottom: parent.bottom
195  }
196  width: Math.min(100, root.progress) / 100 * parent.width
197  clip: true
198 
199  UbuntuShape {
200  anchors {
201  left: parent.left
202  top: parent.top
203  bottom: parent.bottom
204  }
205  backgroundColor: theme.palette.normal.activity
206  borderSource: "none"
207  width: progressOverlay.width
208  }
209  }
210  }
211 
212  Column {
213  anchors {
214  left: parent.left
215  verticalCenter: parent.verticalCenter
216  }
217  spacing: units.gu(.5)
218  Repeater {
219  objectName: "surfacePipRepeater"
220  model: Math.min(3, root.surfaceCount)
221  Rectangle {
222  objectName: "runningHighlight" + index
223  width: units.gu(0.25)
224  height: units.gu(.5)
225  color: root.alerting ? theme.palette.normal.activity : "white"
226  visible: root.itemRunning
227  }
228  }
229  }
230 
231  Rectangle {
232  objectName: "focusedHighlight"
233  anchors {
234  right: parent.right
235  verticalCenter: parent.verticalCenter
236  }
237  width: units.gu(0.25)
238  height: units.gu(.5)
239  color: "white"
240  visible: root.itemFocused
241  }
242 
243  UbuntuShape {
244  objectName: "shortcutHint"
245  anchors.centerIn: parent
246  width: units.gu(2.5)
247  height: width
248  backgroundColor: "#F2111111"
249  visible: root.shortcutHintShown
250  aspect: UbuntuShape.Flat
251  Label {
252  anchors.centerIn: parent
253  text: (itemIndex + 1) % 10
254  color: "white"
255  font.weight: Font.Light
256  }
257  }
258  }
259 
260  ShaderEffect {
261  id: transformEffect
262  anchors.centerIn: parent
263  anchors.verticalCenterOffset: root.offset
264  width: iconItem.width
265  height: iconItem.height
266  property real itemOpacity: root.itemOpacity
267  property real brightness: Math.max(-1, root.brightness)
268  property real angle: root.angle
269  rotation: root.inverted ? 180 : 0
270 
271  property variant source: ShaderEffectSource {
272  id: shaderEffectSource
273  sourceItem: iconItem
274  hideSource: true
275  }
276 
277  transform: [
278  // The rotation about the icon's center/z-axis for the wiggle
279  // needs to happen here too, because there's no other way to
280  // align the wiggle with the icon-folding otherwise
281  Rotation {
282  axis { x: 0; y: 0; z: 1 }
283  origin { x: iconItem.width / 2; y: iconItem.height / 2; z: 0 }
284  angle: priv.wiggleAngle
285  },
286  // Rotating 3 times at top/bottom because that increases the perspective.
287  // This is a hack, but as QML does not support real 3D coordinates
288  // getting a higher perspective can only be done by a hack. This is the most
289  // readable/understandable one I could come up with.
290  Rotation {
291  axis { x: 1; y: 0; z: 0 }
292  origin { x: iconItem.width / 2; y: angle > 0 ? 0 : iconItem.height; z: 0 }
293  angle: root.angle * 0.7
294  },
295  Rotation {
296  axis { x: 1; y: 0; z: 0 }
297  origin { x: iconItem.width / 2; y: angle > 0 ? 0 : iconItem.height; z: 0 }
298  angle: root.angle * 0.7
299  },
300  Rotation {
301  axis { x: 1; y: 0; z: 0 }
302  origin { x: iconItem.width / 2; y: angle > 0 ? 0 : iconItem.height; z: 0 }
303  angle: root.angle * 0.7
304  },
305  // Because rotating it 3 times moves it more to the front/back, i.e. it gets
306  // bigger/smaller and we need a scale to compensate that again.
307  Scale {
308  xScale: 1 - (Math.abs(angle) / 500)
309  yScale: 1 - (Math.abs(angle) / 500)
310  origin { x: iconItem.width / 2; y: iconItem.height / 2}
311  }
312  ]
313 
314  // Using a fragment shader instead of QML's opacity and BrightnessContrast
315  // to be able to do both in one step which gives quite some better performance
316  fragmentShader: "
317  varying highp vec2 qt_TexCoord0;
318  uniform sampler2D source;
319  uniform lowp float brightness;
320  uniform lowp float itemOpacity;
321  void main(void)
322  {
323  highp vec4 sourceColor = texture2D(source, qt_TexCoord0);
324  sourceColor.rgb = mix(sourceColor.rgb, vec3(step(0.0, brightness)), abs(brightness));
325  sourceColor *= itemOpacity;
326  gl_FragColor = sourceColor;
327  }"
328  }
329 }