369 lines
13 KiB
QML
369 lines
13 KiB
QML
import QtQuick 2.13
|
|
import QtQuick.Controls 2.13
|
|
import QtQuick.Layouts 1.3
|
|
import QtGraphicalEffects 1.0
|
|
|
|
import StatusQ.Core 0.1
|
|
import StatusQ.Controls 0.1
|
|
import StatusQ.Components 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"]
|
|
property string emojiSize: ""
|
|
|
|
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
|
|
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
|
|
if (recentEmojis === undefined) {
|
|
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
|
|
|
|
// Adding a space because otherwise, some emojis would fuse since emoji is just a string
|
|
popup.emojiSelected(StatusQUtils.Emoji.parse(encodedIcon, popup.emojiSize || undefined) + ' ', true)
|
|
popup.close()
|
|
}
|
|
|
|
function populateCategories() {
|
|
var categoryNames = {"recent": 0}
|
|
var newCategories = [[]]
|
|
|
|
StatusQUtils.Emoji.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.input.edit.forceActiveFocus()
|
|
Qt.callLater(populateCategories);
|
|
}
|
|
|
|
onClosed: {
|
|
popup.emojiSize = ""
|
|
}
|
|
|
|
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: StatusEmoji {
|
|
width: 22
|
|
height: 22
|
|
emojiId: modelData
|
|
MouseArea {
|
|
cursorShape: Qt.PointingHandCursor
|
|
anchors.fill: parent
|
|
onClicked: {
|
|
localAccountSensitiveSettings.skinColor = (index === 5) ? "" : modelData.split("-")[1];
|
|
popup.populateCategories();
|
|
skinToneEmoji.expandSkinColorOptions = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
StatusEmoji {
|
|
width: 22
|
|
height: 22
|
|
anchors.verticalCenter: searchBox.verticalCenter
|
|
anchors.right: parent.right
|
|
anchors.rightMargin: emojiHeader.headerMargin
|
|
visible: !skinToneEmoji.expandSkinColorOptions
|
|
emojiId: "1f590" + ((localAccountSensitiveSettings.skinColor !== "" && visible) ? ("-" + localAccountSensitiveSettings.skinColor) : "")
|
|
MouseArea {
|
|
cursorShape: Qt.PointingHandCursor
|
|
anchors.fill: parent
|
|
onClicked: {
|
|
skinToneEmoji.expandSkinColorOptions = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
StatusScrollView {
|
|
readonly property ScrollBar vScrollBar: ScrollBar.vertical
|
|
property var categrorySectionHeightRatios: []
|
|
property int activeCategory: 0
|
|
|
|
id: scrollView
|
|
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
|
|
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
|
|
categoryHeights.push(totalHeight)
|
|
}
|
|
var ratios = []
|
|
categoryHeights.forEach(function (catHeight) {
|
|
ratios.push(catHeight / totalHeight)
|
|
})
|
|
|
|
categrorySectionHeightRatios = ratios
|
|
return totalHeight
|
|
}
|
|
|
|
Repeater {
|
|
id: emojiSectionsRepeater
|
|
model: popup.categories
|
|
|
|
StatusEmojiSection {
|
|
width: scrollView.availableWidth
|
|
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}
|
|
}
|
|
##^##*/
|