As discussed in network/neochat#161, when clicking the image it now only covers the neochat window. A modal popup that covers the neochat window is now used. The app behind get dimmed. Left clicking anywhere closes the preview as well as the using the close button. Right clicking on the image itself still gives the image's context menu. Before  After  Latest  Closes network/neochat#161
309 lines
11 KiB
QML
309 lines
11 KiB
QML
// SPDX-FileCopyrightText: 2019 Black Hat <bhat@encom.eu.org>
|
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
|
|
import QtQuick 2.15
|
|
import QtQuick.Controls 2.15
|
|
import QtQuick.Layouts 1.15
|
|
import Qt.labs.platform 1.1
|
|
|
|
import org.kde.kirigami 2.15 as Kirigami
|
|
|
|
Popup {
|
|
id: root
|
|
|
|
property alias source: image.source
|
|
property string filename
|
|
property string blurhash: ""
|
|
property int imageWidth: -1
|
|
property int imageHeight: -1
|
|
property var modelData
|
|
|
|
parent: Overlay.overlay
|
|
closePolicy: Popup.CloseOnEscape
|
|
width: parent.width
|
|
height: parent.height
|
|
modal: true
|
|
padding: 0
|
|
background: null
|
|
|
|
ColumnLayout {
|
|
anchors.fill: parent
|
|
spacing: Kirigami.Units.largeSpacing
|
|
|
|
Control {
|
|
Layout.fillWidth: true
|
|
|
|
contentItem: RowLayout {
|
|
spacing: Kirigami.Units.largeSpacing
|
|
|
|
Kirigami.Avatar {
|
|
id: avatar
|
|
|
|
Layout.preferredWidth: Kirigami.Units.iconSizes.medium
|
|
Layout.preferredHeight: Kirigami.Units.iconSizes.medium
|
|
|
|
name: modelData.author.name ?? modelData.author.displayName
|
|
source: modelData.author.avatarMediaId ? ("image://mxc/" + modelData.author.avatarMediaId) : ""
|
|
color: modelData.author.color
|
|
}
|
|
ColumnLayout {
|
|
Layout.fillWidth: true
|
|
spacing: 0
|
|
|
|
Label {
|
|
id: nameLabel
|
|
|
|
text: modelData.author.displayName
|
|
textFormat: Text.PlainText
|
|
font.weight: Font.Bold
|
|
color: author.color
|
|
}
|
|
Label {
|
|
id: timeLabel
|
|
|
|
text: time.toLocaleString(Qt.locale(), Locale.ShortFormat)
|
|
}
|
|
}
|
|
Label {
|
|
id: imageLabel
|
|
Layout.fillWidth: true
|
|
Layout.leftMargin: Kirigami.Units.largeSpacing
|
|
|
|
text: modelData.display
|
|
font.weight: Font.Bold
|
|
elide: Text.ElideRight
|
|
}
|
|
ToolButton {
|
|
Layout.preferredWidth: Kirigami.Units.gridUnit * 2
|
|
Layout.preferredHeight: Kirigami.Units.gridUnit * 2
|
|
|
|
text: i18n("Zoom in")
|
|
Accessible.name: text
|
|
icon.name: "zoom-in"
|
|
display: AbstractButton.IconOnly
|
|
onClicked: {
|
|
image.scaleFactor = image.scaleFactor + 0.25
|
|
if (image.scaleFactor > 3) {
|
|
image.scaleFactor = 3
|
|
}
|
|
}
|
|
|
|
ToolTip.text: text
|
|
ToolTip.delay: Kirigami.Units.toolTipDelay
|
|
ToolTip.visible: hovered
|
|
}
|
|
ToolButton {
|
|
Layout.preferredWidth: Kirigami.Units.gridUnit * 2
|
|
Layout.preferredHeight: Kirigami.Units.gridUnit * 2
|
|
|
|
text: i18n("Zoom out")
|
|
Accessible.name: text
|
|
icon.name: "zoom-out"
|
|
display: AbstractButton.IconOnly
|
|
onClicked: {
|
|
image.scaleFactor = image.scaleFactor - 0.25
|
|
if (image.scaleFactor < 0.25) {
|
|
image.scaleFactor = 0.25
|
|
}
|
|
}
|
|
|
|
ToolTip.text: text
|
|
ToolTip.delay: Kirigami.Units.toolTipDelay
|
|
ToolTip.visible: hovered
|
|
}
|
|
ToolButton {
|
|
Layout.preferredWidth: Kirigami.Units.gridUnit * 2
|
|
Layout.preferredHeight: Kirigami.Units.gridUnit * 2
|
|
|
|
text: i18n("Rotate left")
|
|
Accessible.name: text
|
|
icon.name: "image-rotate-left-symbolic"
|
|
display: AbstractButton.IconOnly
|
|
onClicked: image.rotationAngle = image.rotationAngle - 90
|
|
|
|
ToolTip.text: text
|
|
ToolTip.delay: Kirigami.Units.toolTipDelay
|
|
ToolTip.visible: hovered
|
|
}
|
|
ToolButton {
|
|
Layout.preferredWidth: Kirigami.Units.gridUnit * 2
|
|
Layout.preferredHeight: Kirigami.Units.gridUnit * 2
|
|
|
|
text: i18n("Rotate right")
|
|
Accessible.name: text
|
|
icon.name: "image-rotate-right-symbolic"
|
|
display: AbstractButton.IconOnly
|
|
onClicked: image.rotationAngle = image.rotationAngle + 90
|
|
|
|
ToolTip.text: text
|
|
ToolTip.delay: Kirigami.Units.toolTipDelay
|
|
ToolTip.visible: hovered
|
|
}
|
|
ToolButton {
|
|
Layout.preferredWidth: Kirigami.Units.gridUnit * 2
|
|
Layout.preferredHeight: Kirigami.Units.gridUnit * 2
|
|
|
|
text: i18n("Save as")
|
|
Accessible.name: text
|
|
icon.name: "document-save"
|
|
display: AbstractButton.IconOnly
|
|
onClicked: {
|
|
var dialog = saveAsDialog.createObject(ApplicationWindow.overlay)
|
|
dialog.open()
|
|
dialog.currentFile = dialog.folder + "/" + currentRoom.fileNameToDownload(eventId)
|
|
}
|
|
|
|
ToolTip.text: text
|
|
ToolTip.delay: Kirigami.Units.toolTipDelay
|
|
ToolTip.visible: hovered
|
|
}
|
|
ToolButton {
|
|
Layout.preferredWidth: Kirigami.Units.gridUnit * 2
|
|
Layout.preferredHeight: Kirigami.Units.gridUnit * 2
|
|
|
|
text: i18n("Close")
|
|
Accessible.name: text
|
|
icon.name: "dialog-close"
|
|
display: AbstractButton.IconOnly
|
|
onClicked: {
|
|
root.close()
|
|
}
|
|
|
|
ToolTip.text: text
|
|
ToolTip.delay: Kirigami.Units.toolTipDelay
|
|
ToolTip.visible: hovered
|
|
}
|
|
}
|
|
|
|
background: Rectangle {
|
|
color: Kirigami.Theme.alternateBackgroundColor
|
|
}
|
|
|
|
Kirigami.Separator {
|
|
anchors {
|
|
left: parent.left
|
|
right: parent.right
|
|
bottom: parent.bottom
|
|
}
|
|
height: 1
|
|
}
|
|
}
|
|
|
|
BusyIndicator {
|
|
Layout.fillWidth: true
|
|
visible: image.status !== Image.Ready && root.blurhash === ""
|
|
running: visible
|
|
}
|
|
// Provides container to fill the space that isn't taken up by the top controls and clips the image when zooming makes it larger than the available area.
|
|
Item {
|
|
id: imageContainer
|
|
Layout.fillWidth: true
|
|
Layout.fillHeight: true
|
|
Layout.leftMargin: Kirigami.Units.largeSpacing
|
|
Layout.rightMargin: Kirigami.Units.largeSpacing
|
|
Layout.bottomMargin: Kirigami.Units.largeSpacing
|
|
clip: true
|
|
|
|
Image {
|
|
id: image
|
|
|
|
property var scaleFactor: 1
|
|
property int rotationAngle: 0
|
|
property var rotationInsensitiveWidth: Math.min(root.imageWidth !== -1 ? root.imageWidth : sourceSize.width, imageContainer.width - Kirigami.Units.largeSpacing * 2)
|
|
property var rotationInsensitiveHeight: Math.min(root.imageHeight !== -1 ? root.imageHeight : sourceSize.height, imageContainer.height - Kirigami.Units.largeSpacing * 2)
|
|
|
|
anchors.centerIn: parent
|
|
width: rotationAngle % 180 === 0 ? rotationInsensitiveWidth : rotationInsensitiveHeight
|
|
height: rotationAngle % 180 === 0 ? rotationInsensitiveHeight : rotationInsensitiveWidth
|
|
fillMode: Image.PreserveAspectFit
|
|
clip: true
|
|
|
|
Behavior on width {
|
|
NumberAnimation {duration: Kirigami.Units.longDuration; easing.type: Easing.InOutCubic}
|
|
}
|
|
Behavior on height {
|
|
NumberAnimation {duration: Kirigami.Units.longDuration; easing.type: Easing.InOutCubic}
|
|
}
|
|
|
|
Image {
|
|
anchors.centerIn: parent
|
|
width: image.width
|
|
height: image.height
|
|
source: root.blurhash !== "" ? ("image://blurhash/" + root.blurhash) : ""
|
|
visible: root.blurhash !== "" && parent.status !== Image.Ready
|
|
}
|
|
|
|
transform: [
|
|
Rotation {
|
|
origin.x: image.width / 2
|
|
origin.y: image.height / 2
|
|
angle: image.rotationAngle
|
|
|
|
Behavior on angle {
|
|
RotationAnimation {duration: Kirigami.Units.longDuration; easing.type: Easing.InOutCubic}
|
|
}
|
|
},
|
|
Scale {
|
|
origin.x: image.width / 2
|
|
origin.y: image.height / 2
|
|
xScale: image.scaleFactor
|
|
yScale: image.scaleFactor
|
|
|
|
Behavior on xScale {
|
|
NumberAnimation {duration: Kirigami.Units.longDuration; easing.type: Easing.InOutCubic}
|
|
}
|
|
Behavior on yScale {
|
|
NumberAnimation {duration: Kirigami.Units.longDuration; easing.type: Easing.InOutCubic}
|
|
}
|
|
}
|
|
]
|
|
|
|
MouseArea {
|
|
anchors.fill: parent
|
|
acceptedButtons: Qt.RightButton
|
|
onClicked: {
|
|
const contextMenu = fileDelegateContextMenu.createObject(parent, {
|
|
author: modelData.author,
|
|
message: modelData.message,
|
|
eventId: modelData.eventId,
|
|
source: modelData.source,
|
|
file: root.parent,
|
|
mimeType: modelData.mimeType,
|
|
progressInfo: modelData.progressInfo,
|
|
plainMessage: modelData.message,
|
|
});
|
|
contextMenu.closeFullscreen.connect(root.close)
|
|
contextMenu.open();
|
|
}
|
|
}
|
|
}
|
|
MouseArea {
|
|
anchors.fill: parent
|
|
acceptedButtons: Qt.LeftButton
|
|
onClicked: {
|
|
root.close()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Component {
|
|
id: saveAsDialog
|
|
FileDialog {
|
|
fileMode: FileDialog.SaveFile
|
|
folder: StandardPaths.writableLocation(StandardPaths.DownloadLocation)
|
|
onAccepted: {
|
|
if (!currentFile) {
|
|
return;
|
|
}
|
|
currentRoom.downloadFile(eventId, currentFile)
|
|
}
|
|
}
|
|
}
|
|
|
|
onClosed: {
|
|
image.scaleFactor = 1
|
|
image.rotationAngle = 0
|
|
}
|
|
}
|