Add an image editor
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
191
imports/NeoChat/Page/ImageEditorPage.qml
Normal file
191
imports/NeoChat/Page/ImageEditorPage.qml
Normal 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
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
1
res.qrc
1
res.qrc
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user