Unity 8
LazyImage.qml
1 /*
2  * Copyright (C) 2013 Canonical, Ltd.
3  *
4  * Authors:
5  * MichaƂ Sawicz <michal.sawicz@canonical.com>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; version 3.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program. If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 import QtQuick 2.0
21 import Ubuntu.Components 0.1
22 
23 Item {
24  id: root
25 
26  property url source
27  // TODO convert into enums when available in QML
28  property string scaleTo
29 
30  property real initialWidth: scaleTo == "width" || scaleTo == "fit" ? width : units.gu(10)
31  property real initialHeight: scaleTo == "height" || scaleTo == "fit" ? height : units.gu(10)
32 
33  property alias sourceSize: image.sourceSize
34  property alias fillMode: image.fillMode
35  property alias asynchronous: image.asynchronous
36  property alias cache: image.cache
37  property alias horizontalAlignment: image.horizontalAlignment
38  property alias verticalAlignment: image.verticalAlignment
39  property alias sourceImage: image
40  property alias borderSource: shape.borderSource
41 
42  state: "default"
43 
44  onSourceChanged: {
45  if (state === "ready") {
46  state = "default";
47  image.nextSource = source;
48  } else {
49  image.source = source;
50  }
51  }
52 
53  UbuntuShape {
54  id: placeholder
55  objectName: "placeholder"
56  color: "#22FFFFFF"
57  anchors.fill: shape
58  visible: opacity != 0
59 
60  ActivityIndicator {
61  id: activity
62  anchors.centerIn: parent
63  opacity: 0
64  visible: opacity != 0
65 
66  running: visible
67  }
68 
69  Image {
70  id: errorImage
71  objectName: "errorImage"
72  anchors.centerIn: parent
73  opacity: 0
74  visible: opacity != 0
75 
76  source: "graphics/close.png"
77  sourceSize { width: units.gu(3); height: units.gu(3) }
78  }
79  }
80 
81  UbuntuShape {
82  id: shape
83  objectName: "shape"
84  height: root.initialHeight
85  width: root.initialWidth
86  anchors.centerIn: root.scaleTo == "fit" ? parent : undefined
87 
88  opacity: 0
89  visible: opacity != 0
90 
91  image: Image {
92  id: image
93  objectName: "image"
94 
95  property url nextSource
96  property string format: image.implicitWidth > image.implicitHeight ? "landscape" : "portrait"
97 
98  fillMode: Image.PreserveAspectFit
99  asynchronous: true
100  cache: false
101  horizontalAlignment: Image.AlignHCenter
102  verticalAlignment: Image.AlignVCenter
103  sourceSize.width: root.scaleTo == "width" ? root.width
104  : root.scaleTo == "fit" && root.width <= root.height ? root.width
105  : 0
106  sourceSize.height: root.scaleTo == "height" ? root.height
107  : root.scaleTo == "fit" && root.height <= root.width ? root.height
108  : 0
109  }
110  }
111 
112  states: [
113  State {
114  name: "default"
115  when: image.source == ""
116  PropertyChanges { target: root; implicitWidth: root.initialWidth; implicitHeight: root.initialHeight }
117  PropertyChanges { target: errorImage; opacity: 0 }
118  },
119  State {
120  name: "loading"
121  extend: "default"
122  when: image.status === Image.Loading
123  PropertyChanges { target: activity; opacity: 1 }
124  },
125  State {
126  name: "ready"
127  when: image.status === Image.Ready && image.source != ""
128  PropertyChanges { target: root; implicitWidth: shape.width; implicitHeight: shape.height }
129  PropertyChanges { target: placeholder; opacity: 0 }
130  PropertyChanges { target: shape; opacity: 1
131  width: root.scaleTo == "width" || (root.scaleTo == "fit" && image.format == "landscape") ? root.width
132  : root.scaleTo == "" ? image.implicitWidth : image.implicitWidth * height / image.implicitHeight
133  height: root.scaleTo == "height" || (root.scaleTo == "fit" && image.format == "portrait") ? root.height
134  : root.scaleTo == "" ? image.implicitHeight : image.implicitHeight * width / image.implicitWidth
135  }
136  },
137  State {
138  name: "error"
139  extend: "default"
140  when: image.status === Image.Error
141  PropertyChanges { target: errorImage; opacity: 1.0 }
142  }
143  ]
144 
145  transitions: [
146  Transition {
147  to: "ready"
148  objectName: "readyTransition"
149  SequentialAnimation {
150  PropertyAction { target: shape; property: "visible" }
151  ParallelAnimation {
152  NumberAnimation { target: shape; property: "opacity"; easing.type: Easing.Linear }
153  UbuntuNumberAnimation { target: root; properties: "implicitWidth,implicitHeight" }
154  UbuntuNumberAnimation { target: shape; properties: "width,height" }
155  NumberAnimation {
156  targets: [placeholder, activity, errorImage]; property: "opacity";
157  easing.type: Easing.Linear; duration: UbuntuAnimation.SnapDuration
158  }
159  }
160  }
161  },
162 
163  Transition {
164  to: "*"
165  objectName: "genericTransition"
166  SequentialAnimation {
167  ParallelAnimation {
168  NumberAnimation { target: shape; property: "opacity"; easing.type: Easing.Linear }
169  NumberAnimation {
170  targets: [placeholder, activity, errorImage]; property: "opacity";
171  easing.type: Easing.Linear; duration: UbuntuAnimation.SnapDuration
172  }
173  UbuntuNumberAnimation { target: root; properties: "implicitWidth,implicitHeight" }
174  UbuntuNumberAnimation { target: shape; properties: "width,height" }
175  }
176  PropertyAction { target: shape; property: "visible" }
177  }
178 
179  onRunningChanged: {
180  if (!running && state === "default" && image.nextSource !== "") {
181  image.source = image.nextSource;
182  image.nextSource = "";
183  }
184  }
185  }
186  ]
187 }