Improve emojis & reactions

This commit is contained in:
Tobias Fella
2022-12-05 16:46:55 +00:00
parent 1f83ab4450
commit 9060de1d60
11 changed files with 422 additions and 113 deletions

View File

@@ -57,15 +57,21 @@ ColumnLayout {
id: emojiPickerLoader
active: visible
visible: chatBar.emojiPaneOpened
onItemChanged: if (visible) {
emojiPickerLoader.item.forceActiveFocus()
}
Layout.fillWidth: true
sourceComponent: QQC2.Pane {
onActiveFocusChanged: if(activeFocus) {
emojiPicker.forceActiveFocus()
}
topPadding: 0
bottomPadding: 0
rightPadding: 0
leftPadding: 0
Kirigami.Theme.colorSet: Kirigami.Theme.View
contentItem: EmojiPicker {
textArea: chatBar.textField
id: emojiPicker
onChosen: insertText(emoji)
}
}

View File

@@ -0,0 +1,53 @@
// SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
// SPDX-License-Identifier: GPL-2.0-or-later
import QtQuick 2.15
import QtQuick.Controls 2.15 as QQC2
import org.kde.kirigami 2.20 as Kirigami
QQC2.ItemDelegate {
id: emojiDelegate
property string name
property string emoji
property bool showTones: false
QQC2.ToolTip.text: emojiDelegate.name
QQC2.ToolTip.visible: hovered
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
contentItem: Item {
Kirigami.Heading {
anchors.fill: parent
visible: !emojiDelegate.emoji.startsWith("image")
text: emojiDelegate.emoji
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
font.family: "emoji"
Kirigami.Icon {
width: Kirigami.Units.gridUnit * 0.5
height: Kirigami.Units.gridUnit * 0.5
source: "arrow-down"
anchors.bottom: parent.bottom
anchors.right: parent.right
visible: emojiDelegate.showTones
}
}
Image {
anchors.fill: parent
visible: emojiDelegate.emoji.startsWith("image")
source: visible ? emojiDelegate.emoji : ""
}
}
background: Rectangle {
color: emojiDelegate.checked ? Kirigami.Theme.highlightColor : Kirigami.Theme.backgroundColor
Rectangle {
anchors.fill: parent
color: Kirigami.Theme.highlightColor
opacity: emojiDelegate.hovered && !emojiDelegate.pressed ? 0.2 : 0
}
}
}

View File

@@ -0,0 +1,87 @@
// SPDX-FileCopyrightText: 2022 Tobias Fella
// SPDX-License-Identifier: GPL-2.0-or-later
import QtQuick 2.15
import QtQuick.Controls 2.15 as QQC2
import org.kde.kirigami 2.20 as Kirigami
import org.kde.neochat 1.0
QQC2.ScrollView {
id: emojiGrid
property alias model: emojis.model
property alias count: emojis.count
required property int targetIconSize
readonly property int emojisPerRow: emojis.width / targetIconSize
required property bool withCustom
readonly property var searchCategory: withCustom ? EmojiModel.Search : EmojiModel.SearchNoCustom
required property QtObject header
signal chosen(string unicode)
onActiveFocusChanged: if (activeFocus) {
emojis.forceActiveFocus()
}
GridView {
id: emojis
anchors.fill: parent
anchors.rightMargin: parent.QQC2.ScrollBar.vertical.visible ? parent.QQC2.ScrollBar.vertical.width : 0
currentIndex: -1
keyNavigationEnabled: true
onActiveFocusChanged: if (activeFocus && currentIndex === -1) {
currentIndex = 0
} else {
currentIndex = -1
}
onModelChanged: currentIndex = -1
cellWidth: emojis.width / emojiGrid.emojisPerRow
cellHeight: emojiGrid.targetIconSize
KeyNavigation.up: emojiGrid.header
clip: true
delegate: EmojiDelegate {
id: emojiDelegate
checked: emojis.currentIndex === model.index
emoji: modelData.unicode
name: modelData.shortName
width: emojis.cellWidth
height: emojis.cellHeight
Keys.onEnterPressed: clicked()
Keys.onReturnPressed: clicked()
onClicked: {
emojiGrid.chosen(modelData.isCustom ? modelData.shortName : modelData.unicode)
EmojiModel.emojiUsed(modelData)
}
Keys.onSpacePressed: pressAndHold()
onPressAndHold: {
if (EmojiModel.tones(modelData.shortName).length === 0) {
return;
}
let tones = tonesPopupComponent.createObject(emojiDelegate, {shortName: modelData.shortName, unicode: modelData.unicode, categoryIconSize: emojiGrid.targetIconSize})
tones.open()
tones.forceActiveFocus()
}
showTones: EmojiModel.tones(modelData.shortName).length > 0
}
Kirigami.PlaceholderMessage {
anchors.centerIn: parent
text: i18n("No emojis")
visible: emojis.count === 0
}
}
Component {
id: tonesPopupComponent
EmojiTonesPicker {
onChosen: emojiGrid.chosen(emoji)
}
}
}

View File

@@ -1,63 +1,54 @@
// SPDX-FileCopyrightText: 2018-2019 Black Hat <bhat@encom.eu.org>
// SPDX-License-Identifier: GPL-3.0-only
// SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
// SPDX-License-Identifier: GPL-2.0-or-later
import QtQuick 2.15
import QtQuick.Controls 2.15 as QQC2
import QtQuick.Layouts 1.15
import org.kde.kirigami 2.15 as Kirigami
import org.kde.neochat 1.0
ColumnLayout {
id: _picker
id: emojiPicker
property var emojiCategory: EmojiModel.History
property var textArea
readonly property var emojiModel: EmojiModel
readonly property int categoryIconSize: 45
readonly property var currentCategory: EmojiModel.categoriesWithCustom[categories.currentIndex].category
readonly property int categoryCount: categories.count
signal chosen(string emoji)
spacing: 0
onActiveFocusChanged: if (activeFocus) categories.forceActiveFocus()
QQC2.ScrollView {
Layout.fillWidth: true
Layout.preferredHeight: Kirigami.Units.gridUnit * 2 + QQC2.ScrollBar.horizontal.height + 2 // for the focus line
QQC2.ScrollBar.horizontal.height: QQC2.ScrollBar.horizontal.visible ? QQC2.ScrollBar.horizontal.implicitHeight : 0
Layout.preferredHeight: emojiPicker.categoryIconSize + QQC2.ScrollBar.horizontal.height
ListView {
id: categories
clip: true
orientation: ListView.Horizontal
model: EmojiModel.categories
delegate: QQC2.ItemDelegate {
id: del
keyNavigationEnabled: true
keyNavigationWraps: true
Keys.forwardTo: searchField
interactive: width !== contentWidth
width: contentItem.Layout.preferredWidth
height: Kirigami.Units.gridUnit * 2
model: EmojiModel.categoriesWithCustom
delegate: EmojiDelegate {
id: emojiDelegate
contentItem: Kirigami.Heading {
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
level: modelData.category === EmojiModel.Custom ? 4 : 1
width: emojiPicker.categoryIconSize
height: width
Layout.preferredWidth: modelData.category === EmojiModel.Custom ? implicitWidth + Kirigami.Units.largeSpacing : Kirigami.Units.gridUnit * 2
checked: categories.currentIndex === model.index
emoji: modelData.emoji
name: modelData.name
font.family: modelData.category === EmojiModel.Custom ? Kirigami.Theme.defaultFont.family : 'emoji'
text: modelData.category === EmojiModel.Custom ? i18n("Custom") : modelData.emoji
onClicked: {
categories.currentIndex = index
}
Rectangle {
anchors.bottom: parent.bottom
width: parent.width
height: 2
visible: _picker.emojiCategory === modelData.category
color: Kirigami.Theme.focusColor
}
onClicked: _picker.emojiCategory = modelData.category
}
}
}
@@ -67,57 +58,22 @@ ColumnLayout {
Layout.preferredHeight: 1
}
QQC2.ScrollView {
Kirigami.SearchField {
id: searchField
Layout.margins: Kirigami.Units.smallSpacing
Layout.fillWidth: true
Layout.preferredHeight: Kirigami.Units.gridUnit * 8
Layout.fillHeight: true
}
GridView {
cellWidth: Kirigami.Units.gridUnit * 2
cellHeight: Kirigami.Units.gridUnit * 2
EmojiGrid {
id: emojiGrid
targetIconSize: emojiPicker.categoryIconSize
model: searchField.text.length === 0 ? EmojiModel.emojis(emojiPicker.currentCategory) : EmojiModel.filterModel(searchField.text, false)
Layout.fillWidth: true
Layout.preferredHeight: 350
onChosen: emojiPicker.chosen(unicode)
withCustom: true
header: categories
clip: true
model: _picker.emojiCategory === EmojiModel.Custom ? CustomEmojiModel : EmojiModel.emojis(_picker.emojiCategory)
delegate: QQC2.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)
}
}
}
Keys.forwardTo: searchField
}
}

View File

@@ -0,0 +1,66 @@
// SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
// SPDX-License-Identifier: GPL-2.0-or-later
import QtQuick 2.15
import QtQuick.Controls 2.15 as QQC2
import org.kde.kirigami 2.20 as Kirigami
import org.kde.neochat 1.0
QQC2.Popup {
id: tones
signal chosen(string emoji)
Component.onCompleted: {
tonesList.currentIndex = 0;
tonesList.forceActiveFocus();
}
required property string shortName
required property string unicode
required property int categoryIconSize
width: tones.categoryIconSize * tonesList.count + 2 * padding
height: tones.categoryIconSize + 2 * padding
y: -height
padding: 2
modal: true
dim: true
onOpened: x = Math.min(parent.mapFromGlobal(QQC2.Overlay.overlay.width - tones.width, 0).x, -(width - parent.width) / 2)
background: Kirigami.ShadowedRectangle {
color: Kirigami.Theme.backgroundColor
radius: Kirigami.Units.smallSpacing
shadow.size: Kirigami.Units.smallSpacing
shadow.color: Qt.rgba(0.0, 0.0, 0.0, 0.10)
border.color: Kirigami.ColorUtils.tintWithAlpha(color, Kirigami.Theme.textColor, 0.15)
border.width: 1
}
ListView {
id: tonesList
width: parent.width
height: parent.height
orientation: Qt.Horizontal
model: EmojiModel.tones(tones.shortName)
keyNavigationEnabled: true
keyNavigationWraps: true
delegate: EmojiDelegate {
id: emojiDelegate
checked: tonesList.currentIndex === model.index
emoji: modelData.unicode
name: modelData.shortName
width: tones.categoryIconSize
height: width
Keys.onEnterPressed: clicked()
Keys.onReturnPressed: clicked()
onClicked: {
tones.chosen(modelData.unicode)
EmojiModel.emojiUsed(modelData)
tones.close()
}
}
}
}

View File

@@ -0,0 +1,86 @@
// SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
// SPDX-License-Identifier: GPL-2.0-or-later
import QtQuick 2.15
import QtQuick.Controls 2.15 as QQC2
import QtQuick.Layouts 1.15
import org.kde.kirigami 2.15 as Kirigami
import org.kde.neochat 1.0
ColumnLayout {
id: reactionPicker
height: 400
readonly property int categoryIconSize: 45
readonly property var currentCategory: EmojiModel.categories[categories.currentIndex].category
readonly property alias categoryCount: categories.count
signal chosen(string emoji)
spacing: 0
QQC2.ScrollView {
Layout.fillWidth: true
Layout.preferredHeight: reactionPicker.categoryIconSize + QQC2.ScrollBar.horizontal.height
QQC2.ScrollBar.horizontal.height: QQC2.ScrollBar.horizontal.visible ? QQC2.ScrollBar.horizontal.implicitHeight : 0
ListView {
id: categories
keyNavigationEnabled: true
focus: true
height: reactionPicker.categoryIconSize
Keys.onReturnPressed: if (emojiGrid.count > 0) emojiGrid.focus = true
Keys.onEnterPressed: if (emojiGrid.count > 0) emojiGrid.focus = true
currentIndex: 2
keyNavigationWraps: true
Keys.forwardTo: searchField
interactive: width !== contentWidth
model: EmojiModel.categories
Component.onCompleted: categories.forceActiveFocus()
delegate: EmojiDelegate {
checked: categories.currentIndex === model.index
emoji: modelData.emoji
name: modelData.name
height: reactionPicker.categoryIconSize
width: height
onClicked: {
categories.currentIndex = index
categories.focus = true
}
}
orientation: Qt.Horizontal
KeyNavigation.down: emojiGrid.count > 0 ? emojiGrid : categories
KeyNavigation.tab: emojiGrid.count > 0 ? emojiGrid : categories
}
}
Kirigami.Separator {
Layout.fillWidth: true
Layout.preferredHeight: 1
}
Kirigami.SearchField {
id: searchField
Layout.margins: Kirigami.Units.smallSpacing
Layout.fillWidth: true
}
EmojiGrid {
id: emojiGrid
targetIconSize: reactionPicker.categoryIconSize
model: searchField.text.length === 0 ? EmojiModel.emojis(reactionPicker.currentCategory) : EmojiModel.filterModelNoCustom(searchField.text, false)
Layout.fillWidth: true
Layout.fillHeight: true
withCustom: false
onChosen: reactionPicker.chosen(unicode)
header: categories
Keys.forwardTo: searchField
}
}