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.0
18 import Ubuntu.Components 1.1
19 
20 Item {
21  id: root
22 
23  property string iconName
24  property int count: 0
25  property bool countVisible: false
26  property int progress: -1
27  property bool itemRunning: false
28  property bool itemFocused: false
29  property real maxAngle: 0
30  property bool inverted: false
31  property bool alerting: false
32  readonly property alias wiggling: wiggleAnim.running
33 
34  readonly property int effectiveHeight: Math.cos(angle * Math.PI / 180) * itemHeight
35  readonly property real foldedHeight: Math.cos(maxAngle * Math.PI / 180) * itemHeight
36 
37  property int itemWidth
38  property int itemHeight
39  // The angle used for rotating
40  property real angle: 0
41  // This is the offset that keeps the items inside the panel
42  property real offset: 0
43  property real itemOpacity: 1
44  property real brightness: 0
45  property double maxWiggleAngle: 5.0
46 
47  QtObject {
48  id: priv
49 
50  readonly property int wiggleDuration: UbuntuAnimation.SnapDuration
51  property real wiggleAngle: 0
52  }
53 
54  SequentialAnimation {
55  id: wiggleAnim
56 
57  running: alerting
58  loops: 1
59  alwaysRunToEnd: true
60 
61  NumberAnimation {
62  target: priv
63  property: "wiggleAngle"
64  from: 0
65  to: maxWiggleAngle
66  duration: priv.wiggleDuration
67  easing.type: Easing.InQuad
68  }
69 
70  NumberAnimation {
71  target: priv
72  property: "wiggleAngle"
73  from: maxWiggleAngle
74  to: -maxWiggleAngle
75  duration: priv.wiggleDuration
76  easing.type: Easing.InOutQuad
77  }
78 
79  NumberAnimation {
80  target: priv
81  property: "wiggleAngle"
82  from: -maxWiggleAngle
83  to: maxWiggleAngle
84  duration: priv.wiggleDuration
85  easing.type: Easing.InOutQuad
86  }
87 
88  NumberAnimation {
89  target: priv
90  property: "wiggleAngle"
91  from: maxWiggleAngle
92  to: -maxWiggleAngle
93  duration: priv.wiggleDuration
94  easing.type: Easing.InOutQuad
95  }
96 
97  NumberAnimation {
98  target: priv
99  property: "wiggleAngle"
100  from: -maxWiggleAngle
101  to: maxWiggleAngle
102  duration: priv.wiggleDuration
103  easing.type: Easing.InOutQuad
104  }
105 
106  NumberAnimation {
107  target: priv
108  property: "wiggleAngle"
109  from: maxWiggleAngle
110  to: 0
111  duration: priv.wiggleDuration
112  easing.type: Easing.OutQuad
113  }
114 
115  UbuntuNumberAnimation {
116  target: root
117  property: "alerting"
118  to: 0
119  }
120  }
121 
122  Item {
123  id: iconItem
124  width: parent.itemWidth + units.gu(1)
125  height: parent.itemHeight + units.gu(1)
126  anchors.centerIn: parent
127 
128  UbuntuShape {
129  id: iconShape
130  anchors.fill: parent
131  anchors.margins: units.gu(1)
132  radius: "medium"
133  borderSource: "none"
134 
135  image: Image {
136  id: iconImage
137  sourceSize.width: iconShape.width
138  sourceSize.height: iconShape.height
139  fillMode: Image.PreserveAspectCrop
140  source: root.iconName
141  }
142  }
143 
144  BorderImage {
145  id: itemGlow
146  anchors.centerIn: iconItem
147  source: "graphics/icon-top-highlight.png"
148  width: root.itemWidth - units.gu(1)
149  height: root.itemHeight - units.gu(1)
150  }
151 
152  UbuntuShape {
153  id: countEmblem
154  objectName: "countEmblem"
155  anchors {
156  right: parent.right
157  bottom: parent.bottom
158  margins: units.dp(3)
159  }
160  width: Math.min(root.itemWidth, Math.max(units.gu(2), countLabel.implicitWidth + units.gu(1)))
161  height: units.gu(2)
162  color: UbuntuColors.orange
163  visible: root.countVisible
164  borderSource: "none"
165 
166  Label {
167  id: countLabel
168  objectName: "countLabel"
169  text: root.count
170  anchors.centerIn: parent
171  // FIXME: verticalCenter seems to be off wee bit and QML doesn't have a centerLine
172  // property for Text: https://bugreports.qt-project.org/browse/QTBUG-40479
173  anchors.verticalCenterOffset: -units.dp(.5)
174  width: root.itemWidth - units.gu(1)
175  horizontalAlignment: Text.AlignHCenter
176  elide: Text.ElideRight
177  color: "white"
178  fontSize: "x-small"
179  }
180  }
181 
182  UbuntuShape {
183  id: progressOverlay
184  objectName: "progressOverlay"
185 
186  anchors {
187  left: iconItem.left
188  right: iconItem.right
189  verticalCenter: parent.verticalCenter
190  leftMargin: units.gu(1.5)
191  rightMargin: units.gu(1.5)
192  }
193  height: units.gu(1)
194  visible: root.progress > -1
195  color: 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  color: "white"
214  borderSource: "none"
215  width: progressOverlay.width
216  }
217  }
218  }
219 
220  Image {
221  objectName: "runningHighlight"
222  anchors {
223  left: parent.left
224  verticalCenter: parent.verticalCenter
225  }
226  visible: root.itemRunning
227  rotation: 180
228  source: "graphics/focused_app_arrow.png"
229  }
230 
231  Image {
232  objectName: "focusedHighlight"
233  anchors {
234  right: parent.right
235  verticalCenter: parent.verticalCenter
236  }
237  visible: root.itemFocused
238  source: "graphics/focused_app_arrow.png"
239  }
240  }
241 
242  ShaderEffect {
243  id: transformEffect
244  anchors.centerIn: parent
245  anchors.verticalCenterOffset: root.offset
246  width: iconItem.width
247  height: iconItem.height
248  property real itemOpacity: root.itemOpacity
249  property real brightness: Math.max(-1, root.brightness)
250  property real angle: root.angle
251  rotation: root.inverted ? 180 : 0
252 
253  property variant source: ShaderEffectSource {
254  id: shaderEffectSource
255  sourceItem: iconItem
256  hideSource: true
257  }
258 
259  transform: [
260  // The rotation about the icon's center/z-axis for the wiggle
261  // needs to happen here too, because there's no other way to
262  // align the wiggle with the icon-folding otherwise
263  Rotation {
264  axis { x: 0; y: 0; z: 1 }
265  origin { x: iconItem.width / 2; y: iconItem.height / 2; z: 0 }
266  angle: priv.wiggleAngle
267  },
268  // Rotating 3 times at top/bottom because that increases the perspective.
269  // This is a hack, but as QML does not support real 3D coordinates
270  // getting a higher perspective can only be done by a hack. This is the most
271  // readable/understandable one I could come up with.
272  Rotation {
273  axis { x: 1; y: 0; z: 0 }
274  origin { x: iconItem.width / 2; y: angle > 0 ? 0 : iconItem.height; z: 0 }
275  angle: root.angle * 0.7
276  },
277  Rotation {
278  axis { x: 1; y: 0; z: 0 }
279  origin { x: iconItem.width / 2; y: angle > 0 ? 0 : iconItem.height; z: 0 }
280  angle: root.angle * 0.7
281  },
282  Rotation {
283  axis { x: 1; y: 0; z: 0 }
284  origin { x: iconItem.width / 2; y: angle > 0 ? 0 : iconItem.height; z: 0 }
285  angle: root.angle * 0.7
286  },
287  // Because rotating it 3 times moves it more to the front/back, i.e. it gets
288  // bigger/smaller and we need a scale to compensate that again.
289  Scale {
290  xScale: 1 - (Math.abs(angle) / 500)
291  yScale: 1 - (Math.abs(angle) / 500)
292  origin { x: iconItem.width / 2; y: iconItem.height / 2}
293  }
294  ]
295 
296  // Using a fragment shader instead of QML's opacity and BrightnessContrast
297  // to be able to do both in one step which gives quite some better performance
298  fragmentShader: "
299  varying highp vec2 qt_TexCoord0;
300  uniform sampler2D source;
301  uniform lowp float brightness;
302  uniform lowp float itemOpacity;
303  void main(void)
304  {
305  highp vec4 sourceColor = texture2D(source, qt_TexCoord0);
306  sourceColor.rgb = mix(sourceColor.rgb, vec3(step(0.0, brightness)), abs(brightness));
307  sourceColor *= itemOpacity;
308  gl_FragColor = sourceColor;
309  }"
310  }
311 }