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