From 144dc1f8f4fd1760f000307b4ebe947b2375e6d9 Mon Sep 17 00:00:00 2001 From: James Graham Date: Wed, 19 Apr 2023 21:10:30 +0000 Subject: [PATCH] Use kirigami-addons maximise components Use new kirigami-addons MaximizeImage.qml to replace local FullScreenImage.qml depends upon libraries/kirigami-addons!88 TODO - [x] Remove the old `fullscreenimage.qml` before merging as no longer needed Implements #571 --- src/qml/Component/FullScreenImage.qml | 312 ------------------ .../Component/NeochatMaximizeComponent.qml | 93 ++++++ src/qml/Component/Timeline/ImageDelegate.qml | 39 +-- src/qml/Component/Timeline/VideoDelegate.qml | 26 ++ src/qml/Page/RoomPage.qml | 6 - src/res.qrc | 2 +- 6 files changed, 135 insertions(+), 343 deletions(-) delete mode 100644 src/qml/Component/FullScreenImage.qml create mode 100644 src/qml/Component/NeochatMaximizeComponent.qml diff --git a/src/qml/Component/FullScreenImage.qml b/src/qml/Component/FullScreenImage.qml deleted file mode 100644 index 873d28753..000000000 --- a/src/qml/Component/FullScreenImage.qml +++ /dev/null @@ -1,312 +0,0 @@ -// SPDX-FileCopyrightText: 2019 Black Hat -// SPDX-License-Identifier: GPL-3.0-only - -import QtQuick 2.15 -import QtQuick.Controls 2.15 as QQC2 -import QtQuick.Layouts 1.15 -import Qt.labs.platform 1.1 - -import org.kde.kirigami 2.15 as Kirigami - -import org.kde.neochat 1.0 - -QQC2.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: QQC2.Overlay.overlay - closePolicy: QQC2.Popup.CloseOnEscape - width: parent.width - height: parent.height - modal: true - padding: 0 - background: null - - ColumnLayout { - anchors.fill: parent - spacing: Kirigami.Units.largeSpacing - - QQC2.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 - - QQC2.Label { - id: nameLabel - - text: modelData.author.displayName - textFormat: Text.PlainText - font.weight: Font.Bold - color: author.color - } - QQC2.Label { - id: timeLabel - - text: time.toLocaleString(Qt.locale(), Locale.ShortFormat) - } - } - QQC2.Label { - id: imageLabel - Layout.fillWidth: true - Layout.leftMargin: Kirigami.Units.largeSpacing - - text: modelData.display - font.weight: Font.Bold - elide: Text.ElideRight - } - QQC2.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: QQC2.AbstractButton.IconOnly - onClicked: { - image.scaleFactor = image.scaleFactor + 0.25 - if (image.scaleFactor > 3) { - image.scaleFactor = 3 - } - } - - QQC2.ToolTip.text: text - QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay - QQC2.ToolTip.visible: hovered - } - QQC2.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: QQC2.AbstractButton.IconOnly - onClicked: { - image.scaleFactor = image.scaleFactor - 0.25 - if (image.scaleFactor < 0.25) { - image.scaleFactor = 0.25 - } - } - - QQC2.ToolTip.text: text - QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay - QQC2.ToolTip.visible: hovered - } - QQC2.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: QQC2.AbstractButton.IconOnly - onClicked: image.rotationAngle = image.rotationAngle - 90 - - QQC2.ToolTip.text: text - QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay - QQC2.ToolTip.visible: hovered - } - QQC2.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: QQC2.AbstractButton.IconOnly - onClicked: image.rotationAngle = image.rotationAngle + 90 - - QQC2.ToolTip.text: text - QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay - QQC2.ToolTip.visible: hovered - } - QQC2.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: QQC2.AbstractButton.IconOnly - onClicked: { - var dialog = saveAsDialog.createObject(QQC2.ApplicationWindow.overlay) - dialog.open() - dialog.currentFile = dialog.folder + "/" + currentRoom.fileNameToDownload(eventId) - } - - QQC2.ToolTip.text: text - QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay - QQC2.ToolTip.visible: hovered - } - QQC2.ToolButton { - Layout.preferredWidth: Kirigami.Units.gridUnit * 2 - Layout.preferredHeight: Kirigami.Units.gridUnit * 2 - - text: i18n("Close") - Accessible.name: text - icon.name: "dialog-close" - display: QQC2.AbstractButton.IconOnly - onClicked: { - root.close() - } - - QQC2.ToolTip.text: text - QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay - QQC2.ToolTip.visible: hovered - } - } - - background: Rectangle { - color: Kirigami.Theme.alternateBackgroundColor - } - - Kirigami.Separator { - anchors { - left: parent.left - right: parent.right - bottom: parent.bottom - } - height: 1 - } - } - - QQC2.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 - - AnimatedImage { - id: image - - property var scaleFactor: 1 - property int rotationAngle: 0 - property var rotationInsensitiveWidth: Math.min(root.imageWidth > 0 ? root.imageWidth : sourceSize.width, imageContainer.width - Kirigami.Units.largeSpacing * 2) - property var rotationInsensitiveHeight: Math.min(root.imageHeight > 0 ? 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: Config.lastSaveDirectory.length > 0 ? Config.lastSaveDirectory : StandardPaths.writableLocation(StandardPaths.DownloadLocation) - onAccepted: { - Config.lastSaveDirectory = folder - Config.save() - if (!currentFile) { - return; - } - currentRoom.downloadFile(eventId, currentFile) - } - } - } - - onClosed: { - image.scaleFactor = 1 - image.rotationAngle = 0 - } -} diff --git a/src/qml/Component/NeochatMaximizeComponent.qml b/src/qml/Component/NeochatMaximizeComponent.qml new file mode 100644 index 000000000..943e3d390 --- /dev/null +++ b/src/qml/Component/NeochatMaximizeComponent.qml @@ -0,0 +1,93 @@ +// SPDX-FileCopyrightText: 2023 James Graham +// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL + +import QtQuick 2.15 +import QtQuick.Controls 2.15 as QQC2 +import QtQuick.Layouts 1.15 +import Qt.labs.platform 1.1 + +import org.kde.kirigami 2.13 as Kirigami +import org.kde.kirigamiaddons.labs.components 1.0 as Components + +import org.kde.neochat 1.0 + +Components.AlbumMaximizeComponent { + id: root + + property var modelData + + property list items: [ + Components.AlbumModelItem { + type: root.modelData.delegateType === MessageEventModel.Image ? Components.AlbumModelItem.Image : Components.AlbumModelItem.Video + source: root.modelData.delegateType === MessageEventModel.Video ? modelData.progressInfo.localPath : modelData.mediaUrl + tempSource: modelData.content.info["xyz.amorgan.blurhash"] ? ("image://blurhash/" + modelData.content.info["xyz.amorgan.blurhash"]) : "" + caption: modelData.display + } + ] + + model: items + initialIndex: 0 + + leading: RowLayout { + Kirigami.Avatar { + id: userAvatar + implicitWidth: Kirigami.Units.iconSizes.medium + implicitHeight: 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 { + spacing: 0 + QQC2.Label { + id: userLabel + text: modelData.author.name ?? modelData.author.displayName + color: modelData.author.color + font.weight: Font.Bold + elide: Text.ElideRight + } + QQC2.Label { + id: dateTimeLabel + text: modelData.time.toLocaleString(Qt.locale(), Locale.ShortFormat) + color: Kirigami.Theme.disabledTextColor + elide: Text.ElideRight + } + } + } + onItemRightClicked: { + const contextMenu = fileDelegateContextMenu.createObject(parent, { + author: modelData.author, + message: modelData.message, + eventId: modelData.eventId, + source: modelData.source, + file: parent, + mimeType: modelData.mimeType, + progressInfo: modelData.progressInfo, + plainMessage: modelData.message, + }); + contextMenu.closeFullscreen.connect(root.close) + contextMenu.open(); + } + onSaveItem: { + var dialog = saveAsDialog.createObject(QQC2.ApplicationWindow.overlay) + dialog.open() + dialog.currentFile = dialog.folder + "/" + currentRoom.fileNameToDownload(modelData.eventId) + } + + Component { + id: saveAsDialog + FileDialog { + fileMode: FileDialog.SaveFile + folder: root.saveFolder + onAccepted: { + Config.lastSaveDirectory = folder + Config.save() + if (!currentFile) { + return; + } + currentRoom.downloadFile(eventId, currentFile) + } + } + } +} diff --git a/src/qml/Component/Timeline/ImageDelegate.qml b/src/qml/Component/Timeline/ImageDelegate.qml index 049da98e1..1b4b0ced1 100644 --- a/src/qml/Component/Timeline/ImageDelegate.qml +++ b/src/qml/Component/Timeline/ImageDelegate.qml @@ -4,9 +4,10 @@ import QtQuick 2.15 import QtQuick.Controls 2.15 as QQC2 import QtQuick.Layouts 1.15 -import Qt.labs.platform 1.1 +import QtQml.Models 2.15 import org.kde.kirigami 2.15 as Kirigami +import org.kde.kirigamiaddons.labs.components 1.0 as Components import org.kde.neochat 1.0 @@ -115,37 +116,27 @@ TimelineContainer { } } - Component { - id: fileDialog - - FileDialog { - fileMode: FileDialog.SaveFile - folder: StandardPaths.writableLocation(StandardPaths.DownloadLocation) - onAccepted: { - currentRoom.downloadFile(eventId, file) - } - } - } - TapHandler { acceptedButtons: Qt.LeftButton onTapped: { img.QQC2.ToolTip.hide() img.paused = true - fullScreenImage.open() + imageDelegate.ListView.view.interactive = false + var popup = maximizeImageComponent.createObject(QQC2.ApplicationWindow.overlay, { + modelData: model, + }) + popup.closed.connect(() => { + imageDelegate.ListView.view.interactive = true + img.paused = false + popup.destroy() + }) + popup.open() } } - FullScreenImage { - id: fullScreenImage - filename: eventId - source: mediaUrl - blurhash: model.content.info["xyz.amorgan.blurhash"] - imageWidth: content.info.w - imageHeight: content.info.h - modelData: model - - onClosed: img.paused = false + Component { + id: maximizeImageComponent + NeochatMaximizeComponent {} } function downloadAndOpen() { diff --git a/src/qml/Component/Timeline/VideoDelegate.qml b/src/qml/Component/Timeline/VideoDelegate.qml index d6aa3db3f..8f64860b4 100644 --- a/src/qml/Component/Timeline/VideoDelegate.qml +++ b/src/qml/Component/Timeline/VideoDelegate.qml @@ -8,6 +8,7 @@ import QtMultimedia 5.15 import Qt.labs.platform 1.1 as Platform import org.kde.kirigami 2.13 as Kirigami +import org.kde.kirigamiaddons.labs.components 1.0 as Components import org.kde.neochat 1.0 @@ -310,6 +311,31 @@ TimelineContainer { } } } + QQC2.ToolButton { + id: maximizeButton + display: QQC2.AbstractButton.IconOnly + + action: Kirigami.Action { + text: i18n("Maximize") + icon.name: "view-fullscreen" + onTriggered: { + videoDelegate.ListView.view.interactive = false + vid.pause() + var popup = maximizeVideoComponent.createObject(QQC2.ApplicationWindow.overlay, { + modelData: model, + }) + popup.closed.connect(() => { + videoDelegate.ListView.view.interactive = true + popup.destroy() + }) + popup.open() + } + } + } + Component { + id: maximizeVideoComponent + NeochatMaximizeComponent {} + } } background: Kirigami.ShadowedRectangle { radius: 4 diff --git a/src/qml/Page/RoomPage.qml b/src/qml/Page/RoomPage.qml index 75b6e3ab4..da8460a37 100644 --- a/src/qml/Page/RoomPage.qml +++ b/src/qml/Page/RoomPage.qml @@ -433,12 +433,6 @@ Kirigami.ScrollablePage { FileDelegateContextMenu {} } - Component { - id: fullScreenImage - - FullScreenImage {} - } - Component { id: userDetailDialog diff --git a/src/res.qrc b/src/res.qrc index f711b7c40..c4f0150cc 100644 --- a/src/res.qrc +++ b/src/res.qrc @@ -29,7 +29,7 @@ qml/RoomSettings/PushNotification.qml qml/RoomSettings/Categories.qml qml/RoomSettings/Permissions.qml - qml/Component/FullScreenImage.qml + qml/Component/NeochatMaximizeComponent.qml qml/Component/FancyEffectsContainer.qml qml/Component/TypingPane.qml qml/Component/ShimmerGradient.qml