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.4
21 import Ubuntu.Components 1.3
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 asynchronous: image.asynchronous
35  property alias cache: image.cache
36  property alias sourceImage: image
37 
38  property bool useUbuntuShape: true
39  property bool pressed: false
40 
41  state: "default"
42 
43  onSourceChanged: {
44  if (state === "ready") {
45  state = "default";
46  image.nextSource = source;
47  } else {
48  image.source = source;
49  }
50  }
51 
52  Loader {
53  id: placeholder
54  objectName: "placeholder"
55  anchors.fill: shape
56  active: useUbuntuShape
57  visible: opacity != 0
58  sourceComponent: UbuntuShape {
59  backgroundColor: "#22FFFFFF"
60  }
61 
62  ActivityIndicator {
63  id: activity
64  anchors.centerIn: parent
65  opacity: 0
66  visible: opacity != 0
67 
68  running: visible
69  }
70 
71  Image {
72  id: errorImage
73  objectName: "errorImage"
74  anchors.centerIn: parent
75  opacity: 0
76  visible: opacity != 0
77 
78  source: "graphics/close.png"
79  sourceSize { width: units.gu(3); height: units.gu(3) }
80  }
81  }
82 
83  Loader {
84  id: shape
85  objectName: "shape"
86  height: root.initialHeight
87  width: root.initialWidth
88  anchors.centerIn: root.scaleTo == "fit" ? parent : undefined
89  active: useUbuntuShape
90  opacity: 0
91  visible: opacity != 0
92  sourceComponent: UbuntuShapeOverlay {
93  property bool pressed: false
94  overlayColor: Qt.rgba(0, 0, 0, pressed ? 0.1 : 0)
95  overlayRect: Qt.rect(0.0, 0.0, 1.0, 1.0)
96  }
97  onLoaded: {
98  item.source = image;
99  item.pressed = Qt.binding(function() { return root.pressed; });
100  }
101 
102  Image {
103  id: image
104  objectName: "image"
105 
106  property url nextSource
107  property string format: image.implicitWidth > image.implicitHeight ? "landscape" : "portrait"
108 
109  anchors.fill: parent
110  visible: !useUbuntuShape
111  fillMode: Image.PreserveAspectFit
112  asynchronous: true
113  cache: false
114  sourceSize.width: root.scaleTo == "width" ? root.width
115  : root.scaleTo == "fit" && root.width <= root.height ? root.width
116  : 0
117  sourceSize.height: root.scaleTo == "height" ? root.height
118  : root.scaleTo == "fit" && root.height <= root.width ? root.height
119  : 0
120  }
121  }
122 
123  states: [
124  State {
125  name: "default"
126  when: image.source == ""
127  PropertyChanges { target: root; implicitWidth: root.initialWidth; implicitHeight: root.initialHeight }
128  PropertyChanges { target: errorImage; opacity: 0 }
129  },
130  State {
131  name: "loading"
132  extend: "default"
133  when: image.status === Image.Loading
134  PropertyChanges { target: activity; opacity: 1 }
135  },
136  State {
137  name: "ready"
138  when: image.status === Image.Ready && image.source != ""
139  PropertyChanges { target: root; implicitWidth: shape.width; implicitHeight: shape.height }
140  PropertyChanges { target: placeholder; opacity: 0 }
141  PropertyChanges { target: shape; opacity: 1
142  width: root.scaleTo == "width" || (root.scaleTo == "fit" && image.format == "landscape") ? root.width
143  : root.scaleTo == "" ? image.implicitWidth : image.implicitWidth * height / image.implicitHeight
144  height: root.scaleTo == "height" || (root.scaleTo == "fit" && image.format == "portrait") ? root.height
145  : root.scaleTo == "" ? image.implicitHeight : image.implicitHeight * width / image.implicitWidth
146  }
147  },
148  State {
149  name: "error"
150  extend: "default"
151  when: image.status === Image.Error
152  PropertyChanges { target: errorImage; opacity: 1.0 }
153  }
154  ]
155 
156  transitions: [
157  Transition {
158  to: "ready"
159  objectName: "readyTransition"
160  SequentialAnimation {
161  PropertyAction { target: shape; property: "visible" }
162  ParallelAnimation {
163  NumberAnimation { target: shape; property: "opacity"; easing.type: Easing.Linear }
164  UbuntuNumberAnimation { target: root; properties: "implicitWidth,implicitHeight" }
165  UbuntuNumberAnimation { target: shape; properties: "width,height" }
166  NumberAnimation {
167  targets: [placeholder, activity, errorImage]; property: "opacity";
168  easing.type: Easing.Linear; duration: UbuntuAnimation.SnapDuration
169  }
170  }
171  }
172  },
173 
174  Transition {
175  to: "*"
176  objectName: "genericTransition"
177  SequentialAnimation {
178  ParallelAnimation {
179  NumberAnimation { target: shape; property: "opacity"; easing.type: Easing.Linear }
180  NumberAnimation {
181  targets: [placeholder, activity, errorImage]; property: "opacity";
182  easing.type: Easing.Linear; duration: UbuntuAnimation.SnapDuration
183  }
184  UbuntuNumberAnimation { target: root; properties: "implicitWidth,implicitHeight" }
185  UbuntuNumberAnimation { target: shape; properties: "width,height" }
186  }
187  PropertyAction { target: shape; property: "visible" }
188  }
189 
190  onRunningChanged: {
191  if (!running && state === "default" && image.nextSource !== "") {
192  image.source = image.nextSource;
193  image.nextSource = "";
194  }
195  }
196  }
197  ]
198 }