Add an image editor

This commit is contained in:
Carl Schwan
2020-12-07 09:58:03 +00:00
parent a5adbc1339
commit 87833a8458
7 changed files with 291 additions and 36 deletions

View File

@@ -15,6 +15,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
include(FeatureSummary)
include(ECMSetupVersion)
include(KDEInstallDirs)
include(ECMQMLModules)
include(KDEClangFormat)
include(KDECMakeSettings)
include(KDECompilerSettings NO_POLICY_SCOPE)
@@ -53,6 +54,15 @@ set_package_properties(cmark PROPERTIES
PURPOSE "Convert markdown to html"
)
ecm_find_qmlmodule(org.kde.kquickimageeditor 1.0)
find_package(KQuickImageEditor COMPONENTS)
set_package_properties(KQuickImageEditor PROPERTIES
DESCRIPTION "Simple image editor for QtQuick applications"
URL "https://invent.kde.org/libraries/kquickimageeditor/"
PURPOSE "Add image editing capability to image attachments"
)
install(FILES org.kde.neochat.desktop DESTINATION ${KDE_INSTALL_APPDIR})
install(FILES org.kde.neochat.appdata.xml DESTINATION ${KDE_INSTALL_METAINFODIR})
install(FILES neochat.svg DESTINATION ${KDE_INSTALL_FULL_ICONDIR}/hicolor/scalable/apps)

View File

@@ -12,7 +12,7 @@ import org.kde.kirigami 2.13 as Kirigami
import NeoChat.Component 1.0
import NeoChat.Component.Emoji 1.0
import NeoChat.Dialog 1.0
import NeoChat.Effect 1.0
import NeoChat.Page 1.0
import org.kde.neochat 1.0
@@ -188,6 +188,86 @@ ToolBar {
visible: emojiPicker.visible || replyItem.visible || autoCompleteListView.visible
}
Image {
Layout.preferredHeight: Kirigami.Units.gridUnit * 10
source: attachmentPath
visible: hasAttachment && (attachmentPath.toString().endsWith('.png') || attachmentPath.toString().endsWith('.jpg'))
fillMode: Image.PreserveAspectFit
Layout.preferredWidth: paintedWidth
RowLayout {
anchors.right: parent.right
Button {
visible: isImage
icon.name: "document-edit"
// HACK: Use a component because an url doesn't work
Component {
id: imageEditorPage
ImageEditorPage {
imagePath: attachmentPath
}
}
onClicked: {
let imageEditor = applicationWindow().pageStack.layers.push(imageEditorPage, {
imagePath: attachmentPath
});
imageEditor.newPathChanged.connect(function(newPath) {
applicationWindow().pageStack.layers.pop();
attachmentPath = newPath;
});
}
ToolTip {
text: i18n("Edit")
}
}
Button {
icon.name: "dialog-cancel"
onClicked: {
hasAttachment = false;
attachmentPath = "";
}
ToolTip {
text: i18n("Cancel")
}
}
}
Rectangle {
color: rgba(255, 255, 255, 40)
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
implicitHeight: fileLabel.implicitHeight
Label {
id: fileLabel
Layout.alignment: Qt.AlignVCenter
text: attachmentPath !== "" ? attachmentPath.toString().substring(attachmentPath.toString().lastIndexOf('/') + 1, attachmentPath.length) : ""
}
}
}
RowLayout {
visible: hasAttachment && !(attachmentPath.toString().endsWith('.png') || attachmentPath.toString().endsWith('.jpg'))
ToolButton {
icon.name: "dialog-cancel"
onClicked: {
hasAttachment = false;
attachmentPath = "";
}
}
Label {
Layout.alignment: Qt.AlignVCenter
text: attachmentPath !== "" ? attachmentPath.toString().substring(attachmentPath.toString().lastIndexOf('/') + 1, attachmentPath.length) : ""
}
}
Kirigami.Separator {
Layout.fillWidth: true
Layout.preferredHeight: 1
visible: hasAttachment
}
RowLayout {
Layout.fillWidth: true
@@ -203,39 +283,6 @@ ToolBar {
onClicked: clearReply()
}
Control {
Layout.margins: 6
Layout.preferredHeight: 36
Layout.alignment: Qt.AlignVCenter
visible: hasAttachment
rightPadding: 8
contentItem: RowLayout {
spacing: 0
ToolButton {
Layout.preferredWidth: height
Layout.fillHeight: true
id: cancelAttachmentButton
icon.name: "dialog-cancel"
onClicked: {
hasAttachment = false;
attachmentPath = "";
}
}
Label {
Layout.alignment: Qt.AlignVCenter
text: attachmentPath !== "" ? attachmentPath.toString().substring(attachmentPath.toString().lastIndexOf('/') + 1, attachmentPath.length) : ""
}
}
}
TextArea {
id: inputField

View File

@@ -0,0 +1,191 @@
/*
* SPDX-FileCopyrightText: (C) 2020 Carl Schwan <carl@carlschwan.eu>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
import QtQuick 2.10
import QtQuick.Controls 2.1 as QQC2
import QtQuick.Layouts 1.12
import org.kde.kirigami 2.12 as Kirigami
import QtQuick.Dialogs 1.2
import org.kde.kquickimageeditor 1.0 as KQuickImageEditor
import QtGraphicalEffects 1.12
import Qt.labs.platform 1.0 as Platform
Kirigami.Page {
id: rootEditorView
property bool resizing: false;
required property string imagePath
signal newPathChanged(string newPath);
title: i18n("Edit")
leftPadding: 0
rightPadding: 0
function crop() {
const ratioX = editImage.paintedWidth / editImage.nativeWidth;
const ratioY = editImage.paintedHeight / editImage.nativeHeight;
rootEditorView.resizing = false
imageDoc.crop(resizeRectangle.insideX / ratioX, resizeRectangle.insideY / ratioY, resizeRectangle.insideWidth / ratioX, resizeRectangle.insideHeight / ratioY);
}
actions {
left: Kirigami.Action {
id: undoAction
text: i18nc("@action:button Undo modification", "Undo")
iconName: "edit-undo"
onTriggered: imageDoc.undo();
visible: imageDoc.edited
}
main: Kirigami.Action {
id: okAction
text: i18nc("@action:button Accept image modification", "Accept")
iconName: "dialog-ok"
onTriggered: {
let newPath = Platform.StandardPaths.writableLocation(Platform.StandardPaths.CacheLocation) + "/" + (new Date()).getTime() + "." + imagePath.split('.').pop();
if (imageDoc.saveAs(newPath)) {;
newPathChanged(newPath);
} else {
msg.type = Kirigami.MessageType.Error
msg.text = i18n("Unable to save file. Check if you have the correct permission to edit the cache directory.")
msg.visible = true;
}
}
}
}
contentItem: KQuickImageEditor.ImageItem {
id: editImage
fillMode: KQuickImageEditor.ImageItem.PreserveAspectFit
image: imageDoc.image
Shortcut {
sequence: StandardKey.Undo
onActivated: undoAction.trigger();
}
Shortcut {
sequences: [StandardKey.Save, "Enter"]
onActivated: saveAction.trigger();
}
Shortcut {
sequence: StandardKey.SaveAs
onActivated: saveAsAction.trigger();
} anchors.fill: parent
FileDialog {
id: fileDialog
title: i18n("Save As")
folder: shortcuts.home
selectMultiple: false
selectExisting: false
onAccepted: {
fileDialog.close()
}
onRejected: {
fileDialog.close()
}
Component.onCompleted: visible = false
}
KQuickImageEditor.ImageDocument {
id: imageDoc
path: rootEditorView.imagePath
}
}
header: QQC2.ToolBar {
contentItem: Kirigami.ActionToolBar {
id: actionToolBar
display: QQC2.Button.TextBesideIcon
actions: [
Kirigami.Action {
iconName: rootEditorView.resizing ? "dialog-cancel" : "transform-crop"
text: rootEditorView.resizing ? i18n("Cancel") : i18nc("@action:button Crop an image", "Crop");
onTriggered: rootEditorView.resizing = !rootEditorView.resizing;
},
Kirigami.Action {
iconName: "dialog-ok"
visible: rootEditorView.resizing
text: i18nc("@action:button Rotate an image to the right", "Crop");
onTriggered: rootEditorView.crop();
},
Kirigami.Action {
iconName: "object-rotate-left"
text: i18nc("@action:button Rotate an image to the left", "Rotate left");
onTriggered: imageDoc.rotate(-90);
visible: !rootEditorView.resizing
},
Kirigami.Action {
iconName: "object-rotate-right"
text: i18nc("@action:button Rotate an image to the right", "Rotate right");
onTriggered: imageDoc.rotate(90);
visible: !rootEditorView.resizing
},
Kirigami.Action {
iconName: "object-flip-vertical"
text: i18nc("@action:button Mirror an image vertically", "Flip");
onTriggered: imageDoc.mirror(false, true);
visible: !rootEditorView.resizing
},
Kirigami.Action {
iconName: "object-flip-horizontal"
text: i18nc("@action:button Mirror an image horizontally", "Mirror");
onTriggered: imageDoc.mirror(true, false);
visible: !rootEditorView.resizing
}
]
}
}
footer: Kirigami.InlineMessage {
id: msg
type: Kirigami.MessageType.Error
showCloseButton: true
visible: false
}
KQuickImageEditor.ResizeRectangle {
id: resizeRectangle
visible: rootEditorView.resizing
width: editImage.paintedWidth
height: editImage.paintedHeight
x: 0
y: editImage.verticalPadding
insideX: 100
insideY: 100
insideWidth: 100
insideHeight: 100
onAcceptSize: rootEditorView.crop();
//resizeHandle: KQuickImageEditor.BasicResizeHandle { }
/*Rectangle {
radius: 2
width: Kirigami.Units.gridUnit * 8
height: Kirigami.Units.gridUnit * 3
anchors.centerIn: parent
Kirigami.Theme.colorSet: Kirigami.Theme.View
color: Kirigami.Theme.backgroundColor
QQC2.Label {
anchors.centerIn: parent
text: "x: " + (resizeRectangle.x - rootEditorView.contentItem.width + editImage.paintedWidth)
+ " y: " + (resizeRectangle.y - rootEditorView.contentItem.height + editImage.paintedHeight)
+ "\nwidth: " + resizeRectangle.width
+ " height: " + resizeRectangle.height
}
}*/
}
}

View File

@@ -6,4 +6,5 @@ RoomPage 1.0 RoomPage.qml
JoinRoomPage 1.0 JoinRoomPage.qml
InviteUserPage 1.0 InviteUserPage.qml
SettingsPage 1.0 SettingsPage.qml
ImageEditorPage 1.0 ImageEditorPage.qml

View File

@@ -121,9 +121,10 @@ Kirigami.ApplicationWindow {
modal: !root.wideScreen
onEnabledChanged: drawerOpen = enabled && !modal
onModalChanged: drawerOpen = !modal
enabled: roomManager.hasOpenRoom
enabled: roomManager.hasOpenRoom && pageStack.layers.depth < 2 && pageStack.depth < 3
visible: enabled
room: roomManager.currentRoom
handleVisible: enabled && pageStack.layers.depth < 2
handleVisible: enabled && pageStack.layers.depth < 2 && pageStack.depth < 3
}
globalDrawer: Kirigami.GlobalDrawer {

View File

@@ -14,6 +14,7 @@
<file>imports/NeoChat/Page/SettingsPage.qml</file>
<file>imports/NeoChat/Page/InvitationPage.qml</file>
<file>imports/NeoChat/Page/StartChatPage.qml</file>
<file>imports/NeoChat/Page/ImageEditorPage.qml</file>
<file>imports/NeoChat/Component/qmldir</file>
<file>imports/NeoChat/Component/ChatTextInput.qml</file>
<file>imports/NeoChat/Component/AutoMouseArea.qml</file>

View File

@@ -27,6 +27,10 @@ target_include_directories(neochat PRIVATE ${CMAKE_BINARY_DIR})
target_link_libraries(neochat PRIVATE Qt5::Quick Qt5::Qml Qt5::Gui Qt5::Network Qt5::QuickControls2 KF5::I18n KF5::Kirigami2 KF5::Notifications KF5::ConfigCore KF5::ConfigGui KF5::CoreAddons Quotient cmark::cmark)
kconfig_add_kcfg_files(neochat GENERATE_MOC neochatconfig.kcfgc)
if (KQuickImageEditor_FOUND)
target_compile_definitions(neochat PRIVATE HAS_KQUICKIMAGEEDITOR)
endif()
if(ANDROID)
target_link_libraries(neochat PRIVATE Qt5::Svg OpenSSL::SSL)
kirigami_package_breeze_icons(ICONS