status-desktop/ui/imports/shared/status/StatusEmojiPopup.qml

360 lines
13 KiB
QML
Raw Normal View History

import QtQuick 2.13
import QtQuick.Controls 2.13
import QtQuick.Layouts 1.3
import QtGraphicalEffects 1.0
import StatusQ.Controls 0.1
import StatusQ.Core.Utils 0.1 as StatusQUtils
import utils 1.0
import shared.panels 1.0
import shared.controls 1.0
Popup {
id: popup
property var categories: []
property alias searchString: searchBox.text
property var skinColors: ["1f3fb", "1f3fc", "1f3fd", "1f3fe", "1f3ff"]
signal emojiSelected(string emoji, bool atCu)
modal: false
width: 360
background: Rectangle {
radius: Style.current.radius
color: Style.current.background
border.color: Style.current.border
layer.enabled: true
layer.effect: DropShadow{
verticalOffset: 3
radius: 8
samples: 15
fast: true
cached: true
color: "#22000000"
}
}
function containsSkinColor(code) {
return skinColors.some(function (color) {
return code.includes(color)
});
}
function addEmoji(emoji) {
const MAX_EMOJI_NUMBER = 36
const extenstionIndex = emoji.filename.lastIndexOf('.');
let iconCodePoint = emoji.filename
if (extenstionIndex > -1) {
iconCodePoint = iconCodePoint.substring(0, extenstionIndex)
}
// Split the filename to get all the parts and then encode them from hex to utf8
const splitCodePoint = iconCodePoint.split('-')
let codePointParts = []
splitCodePoint.forEach(function (codePoint) {
codePointParts.push(`0x${codePoint}`)
})
const encodedIcon = String.fromCodePoint(...codePointParts);
// Add at the start of the list
let recentEmojis = localAccountSensitiveSettings.recentEmojis
recentEmojis.unshift(emoji)
// Remove duplicates
recentEmojis = recentEmojis.filter(function (e, index) {
return !recentEmojis.some(function (e2, index2) {
return index2 < index && e2.filename === e.filename
})
})
if (recentEmojis.length > MAX_EMOJI_NUMBER) {
// remove last one
recentEmojis.splice(MAX_EMOJI_NUMBER - 1)
}
emojiSectionsRepeater.itemAt(0).allEmojis = recentEmojis
localAccountSensitiveSettings.recentEmojis = recentEmojis
popup.emojiSelected(StatusQUtils.Emoji.parse(encodedIcon) + ' ', true) // Adding a space because otherwise, some emojis would fuse since emoji is just a string
popup.close()
}
2021-02-28 15:33:25 +00:00
function populateCategories() {
var categoryNames = {"recent": 0}
var newCategories = [[]]
EmojiJSON.emoji_json.forEach(function (emoji) {
if (!categoryNames[emoji.category] && categoryNames[emoji.category] !== 0) {
categoryNames[emoji.category] = newCategories.length
newCategories.push([])
}
var emojisWithColors = [
"1f64c",
"1f44f",
"1f44b",
"1f44d",
"1f44e",
"1f44a",
"270a",
"270c",
"1f44c",
"270b",
"1f450",
"1f4aa",
"1f64f",
"261d",
"1f446",
"1f447",
"1f448",
"1f449",
"1f595",
"1f590",
"1f918",
"1f596",
"270d",
"1f485",
"1f442",
"1f443",
"1f476",
"1f466",
"1f467",
"1f468",
"1f469",
"1f471",
"1f474",
"1f475",
"1f472",
"1f473",
"1f46e",
"1f477",
"1f482",
"1f385",
"1f47c",
"1f478",
"1f470",
"1f6b6",
"1f3c3",
"1f483",
"1f647",
"1f481",
"1f645",
"1f646",
"1f64b",
"1f64e",
"1f64d",
"1f487",
"1f486",
"1f6a3",
"1f3ca",
"1f3c4",
"1f6c0",
"26f9",
"1f3cb",
"1f6b4",
"1f6b5",
"1f3c7",
"1f575"
]
if (localAccountSensitiveSettings.skinColor !== "") {
if (emoji.unicode.includes(localAccountSensitiveSettings.skinColor)) {
newCategories[categoryNames[emoji.category]].push(Object.assign({}, emoji, {filename: emoji.unicode}));
} else {
if (!emojisWithColors.includes(emoji.unicode) && !containsSkinColor(emoji.unicode)) {
newCategories[categoryNames[emoji.category]].push(Object.assign({}, emoji, {filename: emoji.unicode}));
}
}
} else {
if (!containsSkinColor(emoji.unicode)) {
newCategories[categoryNames[emoji.category]].push(Object.assign({}, emoji, {filename: emoji.unicode}));
}
}
})
if (newCategories[categoryNames.recent].length === 0) {
newCategories[categoryNames.recent].push({category: "recent", empty: true })
}
categories = newCategories;
}
Connections {
id: connectionSettings
target: Global
onSettingsLoaded: {
connectionSettings.enabled = false
// Add recent
if (!localAccountSensitiveSettings.recentEmojis || !localAccountSensitiveSettings.recentEmojis.length) {
return
}
categories[0] = localAccountSensitiveSettings.recentEmojis
emojiSectionsRepeater.itemAt(0).allEmojis = localAccountSensitiveSettings.recentEmojis
}
}
onOpened: {
searchBox.text = ""
searchBox.forceActiveFocus(Qt.MouseFocusReason)
2021-02-28 15:33:25 +00:00
Qt.callLater(populateCategories);
}
contentItem: ColumnLayout {
anchors.fill: parent
spacing: 0
Item {
property int headerMargin: 8
id: emojiHeader
Layout.fillWidth: true
height: searchBox.height + emojiHeader.headerMargin
SearchBox {
id: searchBox
anchors.right: skinToneEmoji.left
anchors.rightMargin: emojiHeader.headerMargin
anchors.top: parent.top
anchors.topMargin: emojiHeader.headerMargin
anchors.left: parent.left
anchors.leftMargin: emojiHeader.headerMargin
}
Row {
id: skinToneEmoji
property bool expandSkinColorOptions: false
width: expandSkinColorOptions ? (22 * skinColorEmojiRepeater.count) : 22
height: 22
opacity: expandSkinColorOptions ? 1.0 : 0.0
Behavior on width { NumberAnimation { duration: 400 } }
Behavior on opacity { NumberAnimation { duration: 200 } }
visible: (opacity > 0.1)
anchors.verticalCenter: searchBox.verticalCenter
anchors.right: parent.right
anchors.rightMargin: emojiHeader.headerMargin
Repeater {
id: skinColorEmojiRepeater
model: ["1f590-1f3fb", "1f590-1f3fc", "1f590-1f3fd", "1f590-1f3fe", "1f590-1f3ff", "1f590"]
delegate: SVGImage {
width: 22
height: 22
source: Style.emoji("72x72/" + modelData)
MouseArea {
cursorShape: Qt.PointingHandCursor
anchors.fill: parent
onClicked: {
localAccountSensitiveSettings.skinColor = (index === 5) ? "" : modelData.split("-")[1];
popup.populateCategories();
skinToneEmoji.expandSkinColorOptions = false;
}
}
}
}
}
SVGImage {
width: 22
height: 22
anchors.verticalCenter: searchBox.verticalCenter
anchors.right: parent.right
anchors.rightMargin: emojiHeader.headerMargin
visible: !skinToneEmoji.expandSkinColorOptions
source: Style.emoji("72x72/1f590" + ((localAccountSensitiveSettings.skinColor !== "" && visible) ? ("-" + localAccountSensitiveSettings.skinColor) : ""))
MouseArea {
cursorShape: Qt.PointingHandCursor
anchors.fill: parent
onClicked: {
skinToneEmoji.expandSkinColorOptions = true;
}
}
}
}
ScrollView {
property ScrollBar vScrollBar: ScrollBar.vertical
property var categrorySectionHeightRatios: []
property int activeCategory: 0
id: scrollView
topPadding: Style.current.smallPadding
leftPadding: Style.current.smallPadding
rightPadding: Style.current.smallPadding / 2
Layout.fillWidth: true
Layout.rightMargin: Style.current.smallPadding / 2
Layout.topMargin: Style.current.smallPadding
Layout.alignment: Qt.AlignTop | Qt.AlignLeft
Layout.preferredHeight: 400 - Style.current.smallPadding - emojiHeader.height
clip: true
ScrollBar.vertical.policy: ScrollBar.AlwaysOn
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
ScrollBar.vertical.onPositionChanged: function () {
if (vScrollBar.position < categrorySectionHeightRatios[scrollView.activeCategory - 1]) {
scrollView.activeCategory--
} else if (vScrollBar.position > categrorySectionHeightRatios[scrollView.activeCategory]) {
scrollView.activeCategory++
}
}
function scrollToCategory(category) {
if (category === 0) {
return vScrollBar.setPosition(0)
}
vScrollBar.setPosition(categrorySectionHeightRatios[category - 1])
}
contentHeight: {
var totalHeight = 0
var categoryHeights = []
for (let i = 0; i < emojiSectionsRepeater.count; i++) {
totalHeight += emojiSectionsRepeater.itemAt(i).height + Style.current.padding
categoryHeights.push(totalHeight)
}
var ratios = []
categoryHeights.forEach(function (catHeight) {
ratios.push(catHeight / totalHeight)
})
categrorySectionHeightRatios = ratios
return totalHeight + Style.current.padding
}
Repeater {
id: emojiSectionsRepeater
model: popup.categories
StatusEmojiSection {
searchString: popup.searchString
addEmoji: popup.addEmoji
}
}
}
Row {
Layout.fillWidth: true
height: 40
leftPadding: Style.current.smallPadding / 2
rightPadding: Style.current.smallPadding / 2
spacing: 0
Repeater {
model: EmojiJSON.emojiCategories
StatusTabBarIconButton {
icon.name: modelData
highlighted: index === scrollView.activeCategory
onClicked: {
scrollView.activeCategory = index
scrollView.scrollToCategory(index)
}
}
}
}
}
}
/*##^##
Designer {
D{i:0;formeditorColor:"#ffffff";height:440;width:360}
}
##^##*/