Commit b0f6d487 authored by Tobias Fella's avatar Tobias Fella
Browse files

Implement sending stickers

MSC2545 image packs are used as source. Currently, only room image pack
events are supported.
parent 24644887
Pipeline #124423 failed with stage
in 1 minute and 20 seconds
......@@ -106,6 +106,7 @@ Item {
onChosen: addText(emoji)
}
}
Behavior on height {
NumberAnimation {
property: "height"
......@@ -113,7 +114,6 @@ Item {
easing.type: Easing.OutCubic
}
}
}
Kirigami.Separator {
......
......@@ -24,48 +24,45 @@ ColumnLayout {
spacing: 0
ListView {
Layout.fillWidth: true
Layout.preferredHeight: Kirigami.Units.gridUnit * 2 + 2 // for the focus line
boundsBehavior: Flickable.DragOverBounds
clip: true
orientation: ListView.Horizontal
model: ListModel {
ListElement { label: "custom"; category: "custom" }
ListElement { label: "⌛️"; category: "history" }
ListElement { label: "😏"; category: "people" }
ListElement { label: "🌲"; category: "nature" }
ListElement { label: "🍛"; category: "food"}
ListElement { label: "🚁"; category: "activity" }
ListElement { label: "🚅"; category: "travel" }
ListElement { label: "💡"; category: "objects" }
ListElement { label: "🔣"; category: "symbols" }
ListElement { label: "🏁"; category: "flags" }
}
RowLayout {
Layout.alignment: Qt.AlignHCenter
Layout.preferredHeight: Kirigami.Units.gridUnit * 2
delegate: ItemDelegate {
id: del
ItemDelegate {
id: emojis
required property string label
required property string category
property bool selected: true
Layout.fillHeight: true
contentItem: Kirigami.Icon {
source: "preferences-desktop-emoticons"
width: parent.width
height: parent.height
}
onClicked: emojis.selected = true
width: contentItem.Layout.preferredWidth
height: Kirigami.Units.gridUnit * 2
Rectangle {
anchors.bottom: parent.bottom
contentItem: Kirigami.Heading {
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
level: del.label === "custom" ? 4 : 1
width: parent.width
height: 2
Layout.preferredWidth: del.label === "custom" ? implicitWidth + Kirigami.Units.largeSpacing : Kirigami.Units.gridUnit * 2
visible: emojis.selected
font.family: del.label === "custom" ? Kirigami.Theme.defaultFont.family : 'emoji'
text: del.label === "custom" ? i18n("Custom") : del.label
color: Kirigami.Theme.focusColor
}
}
ItemDelegate {
id: stickers
property bool selected: !emojis.selected
Layout.fillHeight: true
contentItem: Kirigami.Icon {
source: "stickers"
width: parent.width
height: parent.height
}
onClicked: emojis.selected = false
Rectangle {
anchors.bottom: parent.bottom
......@@ -73,12 +70,10 @@ ColumnLayout {
width: parent.width
height: 2
visible: emojiCategory === category
visible: stickers.selected
color: Kirigami.Theme.focusColor
}
onClicked: emojiCategory = category
}
}
......@@ -87,83 +82,162 @@ ColumnLayout {
Layout.preferredHeight: 1
}
GridView {
Loader {
id: gridLoader
sourceComponent: emojis.selected ? emojiComponent : stickerComponent
Layout.fillWidth: true
Layout.preferredHeight: Kirigami.Units.gridUnit * 8
Layout.fillHeight: true
}
cellWidth: Kirigami.Units.gridUnit * 2
cellHeight: Kirigami.Units.gridUnit * 2
boundsBehavior: Flickable.DragOverBounds
clip: true
model: {
switch (emojiCategory) {
case "custom":
return _picker.customModel
case "history":
return emojiModel.history
case "people":
return emojiModel.people
case "nature":
return emojiModel.nature
case "food":
return emojiModel.food
case "activity":
return emojiModel.activity
case "travel":
return emojiModel.travel
case "objects":
return emojiModel.objects
case "symbols":
return emojiModel.symbols
case "flags":
return emojiModel.flags
}
return null
}
delegate: ItemDelegate {
width: Kirigami.Units.gridUnit * 2
height: Kirigami.Units.gridUnit * 2
Component {
id: emojiComponent
contentItem: Kirigami.Heading {
level: 1
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
font.family: 'emoji'
text: modelData.isCustom ? "" : modelData.unicode
}
ColumnLayout {
spacing: 0
ListView {
Layout.fillWidth: true
Layout.preferredHeight: Kirigami.Units.gridUnit * 2
boundsBehavior: Flickable.DragOverBounds
Image {
visible: modelData.isCustom
source: visible ? modelData.unicode : ""
anchors.fill: parent
anchors.margins: 2
clip: true
sourceSize.width: width
sourceSize.height: height
orientation: ListView.Horizontal
Rectangle {
anchors.fill: parent
visible: parent.status === Image.Loading
radius: height/2
gradient: ShimmerGradient { }
model: ListModel {
ListElement { label: "custom"; category: "custom" }
ListElement { label: "⌛️"; category: "history" }
ListElement { label: "😏"; category: "people" }
ListElement { label: "🌲"; category: "nature" }
ListElement { label: "🍛"; category: "food"}
ListElement { label: "🚁"; category: "activity" }
ListElement { label: "🚅"; category: "travel" }
ListElement { label: "💡"; category: "objects" }
ListElement { label: "🔣"; category: "symbols" }
ListElement { label: "🏁"; category: "flags" }
}
delegate: ItemDelegate {
id: del
required property string label
required property string category
width: contentItem.Layout.preferredWidth
height: Kirigami.Units.gridUnit * 2
contentItem: Kirigami.Heading {
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
level: del.label === "custom" ? 4 : 1
Layout.preferredWidth: del.label === "custom" ? implicitWidth + Kirigami.Units.largeSpacing : Kirigami.Units.gridUnit * 2
font.family: del.label === "custom" ? Kirigami.Theme.defaultFont.family : 'emoji'
text: del.label === "custom" ? i18n("Custom") : del.label
}
Rectangle {
anchors.bottom: parent.bottom
width: parent.width
height: 2
visible: emojiCategory === category
color: Kirigami.Theme.focusColor
}
onClicked: emojiCategory = category
}
}
onClicked: {
if (modelData.isCustom) {
chosen(modelData.shortname)
} else {
chosen(modelData.unicode)
GridView {
Layout.fillWidth: true
Layout.preferredHeight: Kirigami.Units.gridUnit * 8
Layout.fillHeight: true
cellWidth: Kirigami.Units.gridUnit * 2
cellHeight: Kirigami.Units.gridUnit * 2
boundsBehavior: Flickable.DragOverBounds
clip: true
model: {
switch (emojiCategory) {
case "custom":
return _picker.customModel
case "history":
return emojiModel.history
case "people":
return emojiModel.people
case "nature":
return emojiModel.nature
case "food":
return emojiModel.food
case "activity":
return emojiModel.activity
case "travel":
return emojiModel.travel
case "objects":
return emojiModel.objects
case "symbols":
return emojiModel.symbols
case "flags":
return emojiModel.flags
}
return null
}
emojiModel.emojiUsed(modelData)
delegate: ItemDelegate {
width: Kirigami.Units.gridUnit * 2
height: Kirigami.Units.gridUnit * 2
contentItem: Kirigami.Heading {
level: 1
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
font.family: 'emoji'
text: modelData.isCustom ? "" : modelData.unicode
}
Image {
visible: modelData.isCustom
source: visible ? modelData.unicode : ""
anchors.fill: parent
anchors.margins: 2
sourceSize.width: width
sourceSize.height: height
Rectangle {
anchors.fill: parent
visible: parent.status === Image.Loading
radius: height/2
gradient: ShimmerGradient { }
}
}
onClicked: {
if (modelData.isCustom) {
chosen(modelData.shortname)
} else {
chosen(modelData.unicode)
}
emojiModel.emojiUsed(modelData)
}
}
ScrollBar.vertical: ScrollBar {}
}
}
}
ScrollBar.vertical: ScrollBar {}
Component {
id: stickerComponent
StickerPicker { }
}
}
// SPDX-FileCopyrightText: 2021 Tobias Fella <fella@posteo.de>
// SPDX-License-Identifier: GPL-2.0-or-later
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import org.kde.kirigami 2.15 as Kirigami
import org.kde.neochat 1.0
import NeoChat.Component 1.0
ColumnLayout {
id: stickerPicker
spacing: 0
ListView {
id: pack
property string selectedPack
Layout.fillWidth: true
Layout.preferredHeight: Kirigami.Units.gridUnit * 2
boundsBehavior: Flickable.DragOverBounds
clip: true
orientation: ListView.Horizontal
model: ImagePacksModel {
room: currentRoom
showStickers: true
showEmoticons: false
}
delegate: ItemDelegate {
id: del
required property string displayName
required property string avatarUrl
required property string id
height: Kirigami.Units.gridUnit * 2
width: height
contentItem: Image {
source: "image://mxc/" + del.avatarUrl
}
Rectangle {
anchors.bottom: parent.bottom
width: parent.width
height: 2
visible: pack.selectedPack === del.id
color: Kirigami.Theme.focusColor
}
onClicked: pack.selectedPack = id
}
}
GridView {
id: grid
Layout.fillWidth: true
Layout.preferredHeight: Kirigami.Units.gridUnit * 8
Layout.fillHeight: true
cellWidth: width / 5
cellHeight: cellWidth
boundsBehavior: Flickable.DragOverBounds
clip: true
model: StickerModel {
id: stickerModel
pack: pack.selectedPack
room: currentRoom
}
delegate: ItemDelegate {
width: grid.cellWidth
height: width
Image {
source: "image://mxc:/" + model.url
anchors.fill: parent
anchors.margins: 2
sourceSize.width: width
sourceSize.height: height
Rectangle {
anchors.fill: parent
visible: parent.status === Image.Loading
radius: height/2
gradient: ShimmerGradient { }
}
}
onClicked: stickerModel.postSticker(model.index)
}
ScrollBar.vertical: ScrollBar {}
}
}
module NeoChat.Component.Emoji
EmojiPicker 1.0 EmojiPicker.qml
StickerPicker 1.0 StickerPicker.qml
......@@ -30,6 +30,16 @@ Kirigami.CategorizedSettings {
room: root.room
}
}
},
Kirigami.SettingAction {
text: i18n("Stickers")
icon.name: "stickers"
page: Qt.resolvedUrl("RoomStickers.qml")
initialProperties: {
return {
room: root.room
}
}
}
]
}
// SPDX-FileCopyrightText: 2021 Tobias Fella <fella@posteo.de>
// SPDX-License-Identifier: GPL-2.0-or-later
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import org.kde.kirigami 2.15 as Kirigami
import org.kde.neochat 1.0
import NeoChat.Component 1.0 as Components
import NeoChat.Dialog 1.0
Kirigami.ScrollablePage {
id: root
property var room
property string packId
title: i18nc('@title:window', 'Edit image pack')
ColumnLayout {
anchors.fill: parent
ScrollView {
id: scroll
Layout.fillWidth: true
Layout.fillHeight: true
ListView {
clip: true
model: StickerModel {
id: stickerModel
room: root.room
pack: root.packId
}
Kirigami.PlaceholderMessage {
anchors.centerIn: parent
text: i18n("This sticker pack is empty")
visible: parent.count === 0
}
delegate: Kirigami.BasicListItem {
text: model.body
reserveSpaceForSubtitle: true
leading: Image {
width: height
sourceSize.width: width
sourceSize.height: height
source: "image://mxc:/" + model.url
Rectangle {
anchors.fill: parent
visible: parent.status === Image.Loading
radius: height/2
gradient: Components.ShimmerGradient { }
}
}
trailing: ToolButton {
width: height
icon.name: "delete"
onClicked: stickerModel.removeEmoji(del.name)
}
}
}
}
Loader {
active: pageSettingStack.wideMode
sourceComponent: addEmojiComponent
Layout.fillWidth: true
}
}
footer: ToolBar {
position: ToolBar.Footer
RowLayout {
anchors.fill: parent
Item {
Layout.fillWidth: Qt.application.layoutDirection == Qt.LeftToRight
}
TextField {
id: stickerField
placeholderText: i18n("Sticker name")
}
Button {
text: i18n("Add Sticker…")
enabled: stickerField.text != ""
property var fileDialog: null
onClicked: {
if (this.fileDialog != null) {
return;
}
this.fileDialog = openFileDialog.createObject(Overlay.overlay)
this.fileDialog.chosen.connect((url) => {
stickerModel.addEmoji(emojiField.text, url)
this.fileDialog = null
})
this.fileDialog.onRejected.connect(() => {
this.fileDialog = null
})
this.fileDialog.open()
}
}
Item {
Layout.fillWidth: Qt.application.layoutDirection == Qt.RightToLeft
}
}
}
Component {
id: openFileDialog
OpenFileDialog {
folder: StandardPaths.writableLocation(StandardPaths.PicturesLocation)
}
}