mirror of
https://github.com/Nheko-Reborn/nheko.git
synced 2024-11-23 19:38:48 +03:00
52346d972e
Previously, the image would not try to stay centered; furthermore, on mobile devices, it was impossible to zoom images without dropping them at a slightly off rotation. This bothered me enough that I've clamped images to the center of the screen (as long as they aren't zoomed larger than the screen) and snapped rotations to 45-degree increments (with a nice animation to boot).
223 lines
5.9 KiB
QML
223 lines
5.9 KiB
QML
// SPDX-FileCopyrightText: Nheko Contributors
|
|
//
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
import QtQuick 2.15
|
|
import QtQuick.Window 2.15
|
|
|
|
import ".."
|
|
|
|
import im.nheko 1.0
|
|
|
|
Window {
|
|
id: imageOverlay
|
|
|
|
required property string url
|
|
required property string eventId
|
|
required property Room room
|
|
required property int originalWidth
|
|
required property double proportionalHeight
|
|
|
|
flags: Qt.FramelessWindowHint
|
|
|
|
//visibility: Window.FullScreen
|
|
color: Qt.rgba(0.2,0.2,0.2,0.66)
|
|
Component.onCompleted: Nheko.setWindowRole(imageOverlay, "imageoverlay")
|
|
|
|
Shortcut {
|
|
sequences: [StandardKey.Cancel]
|
|
onActivated: imageOverlay.close()
|
|
}
|
|
|
|
Shortcut {
|
|
sequences: [StandardKey.Copy]
|
|
onActivated: {
|
|
if (room) {
|
|
room.copyMedia(eventId);
|
|
} else {
|
|
TimelineManager.copyImage(url);
|
|
}
|
|
}
|
|
}
|
|
|
|
TapHandler {
|
|
onSingleTapped: imageOverlay.close();
|
|
}
|
|
|
|
|
|
Item {
|
|
id: imgContainer
|
|
|
|
property int imgSrcWidth: (imageOverlay.originalWidth && imageOverlay.originalWidth > 100) ? imageOverlay.originalWidth : imageOverlay.width
|
|
property int imgSrcHeight: imageOverlay.proportionalHeight ? imgSrcWidth * imageOverlay.proportionalHeight : imageOverlay.height
|
|
readonly property int physicalWidth: width * scale
|
|
readonly property int physicalHeight: height * scale
|
|
|
|
property double initialScale: Math.min(Window.height/imgSrcHeight, Window.width/imgSrcWidth, 1.0)
|
|
|
|
height: imgSrcHeight * initialScale
|
|
width: imgSrcWidth * initialScale
|
|
|
|
x: (parent.width - width) / 2
|
|
y: (parent.height - height) / 2
|
|
|
|
onXChanged: {
|
|
if (physicalWidth < imageOverlay.width)
|
|
x = (parent.width - width) / 2;
|
|
}
|
|
onYChanged: {
|
|
if (physicalHeight < imageOverlay.height)
|
|
y = (parent.height - height) / 2;
|
|
}
|
|
|
|
Behavior on rotation {
|
|
NumberAnimation {
|
|
duration: 100
|
|
easing.type: Easing.InOutQuad
|
|
}
|
|
}
|
|
|
|
Image {
|
|
id: img
|
|
|
|
visible: !mxcimage.loaded
|
|
anchors.fill: parent
|
|
source: url.replace("mxc://", "image://MxcImage/")
|
|
asynchronous: true
|
|
fillMode: Image.PreserveAspectFit
|
|
smooth: true
|
|
mipmap: true
|
|
property bool loaded: status == Image.Ready
|
|
}
|
|
|
|
MxcAnimatedImage {
|
|
id: mxcimage
|
|
|
|
visible: loaded
|
|
anchors.fill: parent
|
|
roomm: imageOverlay.room
|
|
play: !Settings.animateImagesOnHover || mouseArea.hovered
|
|
eventId: imageOverlay.eventId
|
|
}
|
|
|
|
onScaleChanged: {
|
|
if (scale > 10) scale = 10;
|
|
if (scale < 0.1) scale = 0.1
|
|
}
|
|
}
|
|
|
|
Item {
|
|
id: handlerContainer
|
|
|
|
function snapImageRotation()
|
|
{
|
|
// snap to 15-degree angles
|
|
let rotationOffset = imgContainer.rotation % 15;
|
|
if (rotationOffset != 0)
|
|
{
|
|
if (rotationOffset < 7.5)
|
|
imgContainer.rotation -= rotationOffset;
|
|
else
|
|
imgContainer.rotation += rotationOffset;
|
|
|
|
}
|
|
}
|
|
|
|
anchors.fill: parent
|
|
|
|
PinchHandler {
|
|
target: imgContainer
|
|
maximumScale: 10
|
|
minimumScale: 0.1
|
|
|
|
onGrabChanged: handlerContainer.snapImageRotation()
|
|
}
|
|
|
|
WheelHandler {
|
|
property: "scale"
|
|
// workaround for QTBUG-87646 / QTBUG-112394 / QTBUG-112432:
|
|
// Magic Mouse pretends to be a trackpad but doesn't work with PinchHandler
|
|
// and we don't yet distinguish mice and trackpads on Wayland either
|
|
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
|
|
target: imgContainer
|
|
|
|
onWheel: handlerContainer.snapImageRotation()
|
|
}
|
|
|
|
DragHandler {
|
|
target: imgContainer
|
|
xAxis.enabled: imgContainer.physicalWidth > imageOverlay.width
|
|
yAxis.enabled: imgContainer.physicalHeight > imageOverlay.height
|
|
|
|
onGrabChanged: handlerContainer.snapImageRotation()
|
|
}
|
|
|
|
HoverHandler {
|
|
id: mouseArea
|
|
}
|
|
}
|
|
|
|
|
|
|
|
Row {
|
|
anchors.top: parent.top
|
|
anchors.right: parent.right
|
|
anchors.margins: Nheko.paddingLarge
|
|
spacing: Nheko.paddingMedium
|
|
|
|
ImageButton {
|
|
height: 48
|
|
width: 48
|
|
hoverEnabled: true
|
|
image: ":/icons/icons/ui/copy.svg"
|
|
|
|
//ToolTip.visible: hovered
|
|
//ToolTip.delay: Nheko.tooltipDelay
|
|
//ToolTip.text: qsTr("Copy to clipboard")
|
|
|
|
onClicked: {
|
|
imageOverlay.hide();
|
|
if (room) {
|
|
room.copyMedia(eventId);
|
|
} else {
|
|
TimelineManager.copyImage(url);
|
|
}
|
|
imageOverlay.close();
|
|
}
|
|
}
|
|
|
|
ImageButton {
|
|
height: 48
|
|
width: 48
|
|
hoverEnabled: true
|
|
image: ":/icons/icons/ui/download.svg"
|
|
|
|
//ToolTip.visible: hovered
|
|
//ToolTip.delay: Nheko.tooltipDelay
|
|
//ToolTip.text: qsTr("Download")
|
|
|
|
onClicked: {
|
|
imageOverlay.hide();
|
|
if (room) {
|
|
room.saveMedia(eventId);
|
|
} else {
|
|
TimelineManager.saveMedia(url);
|
|
}
|
|
imageOverlay.close();
|
|
}
|
|
}
|
|
ImageButton {
|
|
height: 48
|
|
width: 48
|
|
hoverEnabled: true
|
|
image: ":/icons/icons/ui/dismiss.svg"
|
|
|
|
//ToolTip.visible: hovered
|
|
//ToolTip.delay: Nheko.tooltipDelay
|
|
//ToolTip.text: qsTr("Close")
|
|
|
|
onClicked: imageOverlay.close()
|
|
}
|
|
}
|
|
|
|
}
|