2019-12-19 22:46:16 +11:00
|
|
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
|
|
|
|
2019-07-13 19:39:01 +10:00
|
|
|
import QtQuick 2.12
|
2020-03-16 05:14:05 +11:00
|
|
|
import QtGraphicalEffects 1.12
|
2019-04-27 11:16:45 +10:00
|
|
|
|
|
|
|
Image {
|
2019-08-18 03:01:43 +10:00
|
|
|
id: image
|
2020-03-16 05:14:05 +11:00
|
|
|
|
|
|
|
property bool circle: radius === circleRadius
|
2020-07-20 04:49:41 +10:00
|
|
|
property bool broken: image.status === Image.Error
|
2019-09-13 06:16:35 +10:00
|
|
|
property bool animate: true
|
2020-03-10 02:00:48 +11:00
|
|
|
property bool animated:
|
|
|
|
utils.urlExtension(image.source).toLowerCase() === "gif"
|
2019-12-16 04:02:40 +11:00
|
|
|
|
2020-07-17 21:26:31 +10:00
|
|
|
property int animatedFillMode: AnimatedImage.PreserveAspectFit
|
|
|
|
|
2020-03-16 05:14:05 +11:00
|
|
|
property alias radius: roundMask.radius
|
2019-12-16 04:02:40 +11:00
|
|
|
property alias showProgressBar: progressBarLoader.active
|
2019-09-13 06:23:30 +10:00
|
|
|
|
2020-03-16 05:14:05 +11:00
|
|
|
readonly property int circleRadius:
|
|
|
|
Math.ceil(Math.max(image.width, image.height))
|
|
|
|
|
2020-07-20 04:49:41 +10:00
|
|
|
function reload() {
|
|
|
|
// Can be reimplemented in components inheriting HImage
|
|
|
|
const oldSource = source
|
|
|
|
source = ""
|
|
|
|
source = oldSource
|
|
|
|
}
|
|
|
|
|
2019-09-13 06:16:35 +10:00
|
|
|
|
2020-07-12 14:25:57 +10:00
|
|
|
autoTransform: true
|
|
|
|
asynchronous: true
|
|
|
|
fillMode: Image.PreserveAspectFit
|
|
|
|
|
|
|
|
cache: ! (animate && animated) &&
|
|
|
|
(sourceSize.width + sourceSize.height) <= 512
|
|
|
|
|
|
|
|
layer.enabled: radius !== 0
|
|
|
|
layer.effect: OpacityMask { maskSource: roundMask }
|
|
|
|
|
2019-09-13 06:16:35 +10:00
|
|
|
Component {
|
2019-09-13 07:27:26 +10:00
|
|
|
id: animatedImageComponent
|
2019-09-13 06:16:35 +10:00
|
|
|
|
|
|
|
AnimatedImage {
|
2019-09-13 07:27:26 +10:00
|
|
|
id: animatedImage
|
2020-07-12 14:25:57 +10:00
|
|
|
|
|
|
|
property bool userPaused: ! window.settings.media.autoPlayGIF
|
|
|
|
|
2019-09-13 06:16:35 +10:00
|
|
|
source: image.source
|
|
|
|
autoTransform: image.autoTransform
|
|
|
|
asynchronous: image.asynchronous
|
2020-07-17 21:26:31 +10:00
|
|
|
fillMode: image.animatedFillMode
|
2020-07-17 20:13:50 +10:00
|
|
|
|
2019-09-13 06:16:35 +10:00
|
|
|
mirror: image.mirror
|
|
|
|
mipmap: image.mipmap
|
|
|
|
smooth: image.smooth
|
|
|
|
horizontalAlignment: image.horizontalAlignment
|
|
|
|
verticalAlignment: image.verticalAlignment
|
|
|
|
|
2020-07-12 14:25:57 +10:00
|
|
|
// Online GIFs won't be able to loop if cache is set to false,
|
|
|
|
// but caching GIFs is expansive.
|
|
|
|
cache: ! Qt.resolvedUrl(source).startsWith("file://")
|
|
|
|
paused: ! visible || window.hidden || userPaused
|
|
|
|
|
|
|
|
layer.enabled: image.radius !== 0
|
|
|
|
layer.effect: OpacityMask { maskSource: roundMask }
|
|
|
|
|
2020-03-16 15:15:47 +11:00
|
|
|
// Hack to make the non-animated image behind this one
|
|
|
|
// basically invisible
|
2020-07-17 21:26:31 +10:00
|
|
|
Binding {
|
|
|
|
target: image
|
|
|
|
property: "fillMode"
|
|
|
|
value: Image.Pad
|
|
|
|
}
|
2020-03-16 15:15:47 +11:00
|
|
|
Binding {
|
|
|
|
target: image
|
|
|
|
property: "sourceSize.width"
|
|
|
|
value: 1
|
|
|
|
}
|
|
|
|
Binding {
|
|
|
|
target: image
|
|
|
|
property: "sourceSize.height"
|
|
|
|
value: 1
|
|
|
|
}
|
|
|
|
|
2020-07-17 19:58:23 +10:00
|
|
|
HButton {
|
|
|
|
anchors.left: parent.left
|
|
|
|
anchors.bottom: parent.bottom
|
|
|
|
anchors.leftMargin: theme.spacing / 2
|
|
|
|
anchors.bottomMargin: theme.spacing / 2
|
|
|
|
|
|
|
|
enableRadius: true
|
|
|
|
icon.name: parent.userPaused ? "player-play" : "player-pause"
|
|
|
|
iconItem.small: true
|
|
|
|
visible: parent.width > width * 2 && parent.height > height * 2
|
|
|
|
onClicked: parent.userPaused = ! parent.userPaused
|
2019-09-13 07:27:26 +10:00
|
|
|
}
|
2019-09-13 06:16:35 +10:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
HLoader {
|
|
|
|
anchors.fill: parent
|
2019-09-13 07:27:26 +10:00
|
|
|
sourceComponent: animate && animated ? animatedImageComponent : null
|
2019-09-13 06:16:35 +10:00
|
|
|
}
|
2019-10-28 07:35:58 +11:00
|
|
|
|
2019-12-16 04:02:40 +11:00
|
|
|
HLoader {
|
|
|
|
id: progressBarLoader
|
2020-07-17 07:15:33 +10:00
|
|
|
|
|
|
|
readonly property alias progress: image.progress
|
|
|
|
readonly property Component determinate: HCircleProgressBar {
|
|
|
|
progress: image.progress
|
|
|
|
}
|
|
|
|
|
2019-10-28 07:35:58 +11:00
|
|
|
anchors.centerIn: parent
|
2020-07-19 13:37:55 +10:00
|
|
|
width: Math.min(
|
|
|
|
96 * theme.uiScale, Math.min(parent.width, parent.height) * 0.5,
|
|
|
|
)
|
2019-11-07 06:47:18 +11:00
|
|
|
height: width
|
2020-07-19 13:44:24 +10:00
|
|
|
active:
|
|
|
|
image.visible &&
|
|
|
|
image.opacity > 0.01 &&
|
|
|
|
image.status === Image.Loading
|
|
|
|
|
2020-07-17 07:15:33 +10:00
|
|
|
sourceComponent: HBusyIndicator {}
|
2019-10-28 07:35:58 +11:00
|
|
|
|
2020-07-17 07:15:33 +10:00
|
|
|
onProgressChanged:
|
|
|
|
if (progress > 0 && progress < 1) sourceComponent = determinate
|
2019-10-28 07:35:58 +11:00
|
|
|
}
|
2019-11-07 07:09:07 +11:00
|
|
|
|
|
|
|
HIcon {
|
|
|
|
anchors.centerIn: parent
|
2020-07-20 04:49:41 +10:00
|
|
|
visible: image.broken
|
2019-11-07 07:09:07 +11:00
|
|
|
svgName: "broken-image"
|
|
|
|
colorize: theme.colors.negativeBackground
|
|
|
|
}
|
2020-03-16 05:14:05 +11:00
|
|
|
|
|
|
|
Rectangle {
|
|
|
|
id: roundMask
|
|
|
|
anchors.fill: parent
|
|
|
|
visible: false
|
|
|
|
}
|
2020-07-20 04:49:41 +10:00
|
|
|
|
|
|
|
Timer {
|
|
|
|
property int retries: 0
|
|
|
|
|
|
|
|
running: image.broken
|
|
|
|
repeat: true
|
|
|
|
interval:
|
|
|
|
Math.min(60, 0.2 * Math.pow(2, Math.min(1000, retries) - 1)) * 1000
|
|
|
|
|
|
|
|
onTriggered: {
|
|
|
|
image.reload()
|
|
|
|
retries += 1
|
|
|
|
}
|
|
|
|
}
|
2019-04-27 11:16:45 +10:00
|
|
|
}
|