Improve emojis & reactions
This commit is contained in:
@@ -11,6 +11,10 @@ struct CustomEmoji {
|
||||
QString name; // with :semicolons:
|
||||
QString url; // mxc://
|
||||
QRegularExpression regexp;
|
||||
|
||||
Q_GADGET
|
||||
Q_PROPERTY(QString unicode MEMBER url)
|
||||
Q_PROPERTY(QString name MEMBER name)
|
||||
};
|
||||
|
||||
class CustomEmojiModel : public QAbstractListModel
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "customemojimodel.h"
|
||||
#include <KLocalizedString>
|
||||
#include <qnamespace.h>
|
||||
|
||||
EmojiModel::EmojiModel(QObject *parent)
|
||||
: QAbstractListModel(parent)
|
||||
@@ -68,6 +68,13 @@ QVariantList EmojiModel::history() const
|
||||
}
|
||||
|
||||
QVariantList EmojiModel::filterModel(const QString &filter, bool limit)
|
||||
{
|
||||
auto emojis = CustomEmojiModel::instance().filterModel(filter);
|
||||
emojis += filterModelNoCustom(filter, limit);
|
||||
return emojis;
|
||||
}
|
||||
|
||||
QVariantList EmojiModel::filterModelNoCustom(const QString &filter, bool limit)
|
||||
{
|
||||
QVariantList result;
|
||||
|
||||
@@ -82,7 +89,6 @@ QVariantList EmojiModel::filterModel(const QString &filter, bool limit)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -110,11 +116,27 @@ QVariantList EmojiModel::emojis(Category category) const
|
||||
if (category == History) {
|
||||
return history();
|
||||
}
|
||||
if (category == HistoryNoCustom) {
|
||||
QVariantList list;
|
||||
for (const auto &e : history()) {
|
||||
auto emoji = qvariant_cast<Emoji>(e);
|
||||
if (!emoji.isCustom) {
|
||||
list.append(e);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
if (category == Custom) {
|
||||
return CustomEmojiModel::instance().filterModel({});
|
||||
}
|
||||
return _emojis[category];
|
||||
}
|
||||
|
||||
QVariantList EmojiModel::tones(const QString &baseEmoji) const
|
||||
{
|
||||
if (baseEmoji.endsWith("tone")) {
|
||||
return _tones.values(baseEmoji.split(":")[0]);
|
||||
}
|
||||
return _tones.values(baseEmoji);
|
||||
}
|
||||
|
||||
@@ -123,21 +145,11 @@ QHash<EmojiModel::Category, QVariantList> EmojiModel::_emojis;
|
||||
QVariantList EmojiModel::categories() const
|
||||
{
|
||||
return QVariantList{
|
||||
// {QVariantMap{
|
||||
// {"category", EmojiModel::Search},
|
||||
// {"name", i18nc("Search for emojis", "Search")},
|
||||
// {"emoji", QStringLiteral("🔎")},
|
||||
// }},
|
||||
{QVariantMap{
|
||||
{"category", EmojiModel::History},
|
||||
{"category", EmojiModel::HistoryNoCustom},
|
||||
{"name", i18nc("Previously used emojis", "History")},
|
||||
{"emoji", QStringLiteral("⌛️")},
|
||||
}},
|
||||
{QVariantMap{
|
||||
{"category", EmojiModel::Custom},
|
||||
{"name", i18nc("'Custom' is a category of emoji", "Custom")},
|
||||
{"emoji", QStringLiteral("😏")},
|
||||
}},
|
||||
{QVariantMap{
|
||||
{"category", EmojiModel::Smileys},
|
||||
{"name", i18nc("'Smileys' is a category of emoji", "Smileys")},
|
||||
@@ -185,3 +197,23 @@ QVariantList EmojiModel::categories() const
|
||||
}},
|
||||
};
|
||||
}
|
||||
|
||||
QVariantList EmojiModel::categoriesWithCustom() const
|
||||
{
|
||||
auto cats = categories();
|
||||
cats.removeAt(0);
|
||||
cats.insert(0,
|
||||
QVariantMap{
|
||||
{"category", EmojiModel::History},
|
||||
{"name", i18nc("Previously used emojis", "History")},
|
||||
{"emoji", QStringLiteral("⌛️")},
|
||||
});
|
||||
cats.insert(1,
|
||||
QVariantMap{
|
||||
{"category", EmojiModel::Custom},
|
||||
{"name", i18nc("'Custom' is a category of emoji", "Custom")},
|
||||
{"emoji", QStringLiteral("🖼️")},
|
||||
});
|
||||
;
|
||||
return cats;
|
||||
}
|
||||
|
||||
@@ -49,6 +49,7 @@ class EmojiModel : public QAbstractListModel
|
||||
|
||||
Q_PROPERTY(QVariantList history READ history NOTIFY historyChanged)
|
||||
Q_PROPERTY(QVariantList categories READ categories CONSTANT)
|
||||
Q_PROPERTY(QVariantList categoriesWithCustom READ categoriesWithCustom CONSTANT)
|
||||
|
||||
public:
|
||||
static EmojiModel &instance()
|
||||
@@ -69,7 +70,9 @@ public:
|
||||
enum Category {
|
||||
Custom,
|
||||
Search,
|
||||
SearchNoCustom,
|
||||
History,
|
||||
HistoryNoCustom,
|
||||
Smileys,
|
||||
People,
|
||||
Nature,
|
||||
@@ -89,11 +92,14 @@ public:
|
||||
|
||||
Q_INVOKABLE QVariantList history() const;
|
||||
Q_INVOKABLE static QVariantList filterModel(const QString &filter, bool limit = true);
|
||||
Q_INVOKABLE static QVariantList filterModelNoCustom(const QString &filter, bool limit = true);
|
||||
|
||||
Q_INVOKABLE QVariantList emojis(Category category) const;
|
||||
|
||||
Q_INVOKABLE QVariantList tones(const QString &baseEmoji) const;
|
||||
|
||||
QVariantList categories() const;
|
||||
QVariantList categoriesWithCustom() const;
|
||||
|
||||
Q_SIGNALS:
|
||||
void historyChanged();
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
53
src/qml/Component/Emoji/EmojiDelegate.qml
Normal file
53
src/qml/Component/Emoji/EmojiDelegate.qml
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
87
src/qml/Component/Emoji/EmojiGrid.qml
Normal file
87
src/qml/Component/Emoji/EmojiGrid.qml
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
66
src/qml/Component/Emoji/EmojiTonesPicker.qml
Normal file
66
src/qml/Component/Emoji/EmojiTonesPicker.qml
Normal 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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
86
src/qml/Component/Emoji/ReactionPicker.qml
Normal file
86
src/qml/Component/Emoji/ReactionPicker.qml
Normal 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
|
||||
}
|
||||
}
|
||||
@@ -9,29 +9,38 @@ import org.kde.kirigami 2.15 as Kirigami
|
||||
import org.kde.neochat 1.0
|
||||
|
||||
QQC2.Popup {
|
||||
id: root
|
||||
|
||||
id: emojiPopup
|
||||
signal react(string emoji)
|
||||
|
||||
Connections {
|
||||
target: RoomManager
|
||||
function onCurrentRoomChanged() {
|
||||
emojiPopup.close()
|
||||
}
|
||||
}
|
||||
|
||||
background: Kirigami.ShadowedRectangle {
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.View
|
||||
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: 2
|
||||
}
|
||||
|
||||
modal: true
|
||||
focus: true
|
||||
closePolicy: QQC2.Popup.CloseOnEscape | QQC2.Popup.CloseOnPressOutsideParent
|
||||
margins: 0
|
||||
padding: 1
|
||||
implicitWidth: Kirigami.Units.gridUnit * 16
|
||||
implicitHeight: Kirigami.Units.gridUnit * 20
|
||||
padding: 2
|
||||
|
||||
background: Rectangle {
|
||||
Kirigami.Theme.inherit: false
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.View
|
||||
color: Kirigami.Theme.backgroundColor
|
||||
border.width: 1
|
||||
border.color: Kirigami.ColorUtils.linearInterpolation(Kirigami.Theme.backgroundColor,
|
||||
Kirigami.Theme.textColor,
|
||||
0.15)
|
||||
}
|
||||
|
||||
contentItem: EmojiPicker {
|
||||
onChosen: react(emoji)
|
||||
implicitHeight: Kirigami.Units.gridUnit * 20 + 2 * padding
|
||||
width: Math.min(contentItem.categoryIconSize * contentItem.categoryCount + 2 * padding, QQC2.Overlay.overlay.width)
|
||||
contentItem: ReactionPicker {
|
||||
onChosen: {
|
||||
react(emoji)
|
||||
emojiPopup.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,5 +93,9 @@
|
||||
<file alias="ConfirmEncryptionDialog.qml">qml/Dialog/ConfirmEncryptionDialog.qml</file>
|
||||
<file alias="RemoveSheet.qml">qml/Menu/Timeline/RemoveSheet.qml</file>
|
||||
<file alias="BanSheet.qml">qml/Menu/Timeline/BanSheet.qml</file>
|
||||
<file alias="ReactionPicker.qml">qml/Component/Emoji/ReactionPicker.qml</file>
|
||||
<file alias="EmojiTonesPicker.qml">qml/Component/Emoji/EmojiTonesPicker.qml</file>
|
||||
<file alias="EmojiDelegate.qml">qml/Component/Emoji/EmojiDelegate.qml</file>
|
||||
<file alias="EmojiGrid.qml">qml/Component/Emoji/EmojiGrid.qml</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
Reference in New Issue
Block a user