2020-09-29 08:37:08 +00:00
import QtQuick 2.13
import QtQuick . Controls 2.13
import QtQuick . Layouts 1.3
import QtGraphicalEffects 1.0
2021-09-28 15:04:06 +00:00
2021-10-25 11:48:23 +00:00
import StatusQ . Controls 0.1
2022-03-07 14:56:05 +00:00
import StatusQ . Core . Utils 0.1 as StatusQUtils
2021-10-25 11:48:23 +00:00
2021-09-28 15:04:06 +00:00
import utils 1.0
2021-10-25 11:48:23 +00:00
2021-10-27 21:27:49 +00:00
import shared . panels 1.0
import shared . controls 1.0
2020-09-29 08:37:08 +00:00
Popup {
2021-07-07 09:32:03 +00:00
id: popup
2020-09-29 08:37:08 +00:00
property var categories: [ ]
2020-11-19 20:45:18 +00:00
property alias searchString: searchBox . text
2021-07-07 09:32:03 +00:00
property var skinColors: [ "1f3fb" , "1f3fc" , "1f3fd" , "1f3fe" , "1f3ff" ]
2020-09-29 08:37:08 +00:00
2022-03-07 14:56:05 +00:00
signal emojiSelected ( string emoji , bool atCu )
2020-09-29 08:37:08 +00:00
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"
}
}
2021-07-07 09:32:03 +00:00
function containsSkinColor ( code ) {
return skinColors . some ( function ( color ) {
return code . includes ( color )
} ) ;
}
2020-09-29 08:37:08 +00:00
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 ( ` 0 x $ { codePoint } ` )
} )
const encodedIcon = String . fromCodePoint ( . . . codePointParts ) ;
// Add at the start of the list
2021-10-20 09:50:50 +00:00
let recentEmojis = localAccountSensitiveSettings . recentEmojis
2020-09-29 08:37:08 +00:00
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
2021-10-20 09:50:50 +00:00
localAccountSensitiveSettings . recentEmojis = recentEmojis
2020-09-29 08:37:08 +00:00
2022-03-07 14:56:05 +00:00
popup . emojiSelected ( StatusQUtils . Emoji . parse ( encodedIcon ) + ' ' , true ) // Adding a space because otherwise, some emojis would fuse since emoji is just a string
2020-09-29 08:37:08 +00:00
popup . close ( )
}
2021-02-28 15:33:25 +00:00
function populateCategories ( ) {
2020-09-29 08:37:08 +00:00
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 ( [ ] )
}
2021-07-07 09:32:03 +00:00
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"
]
2021-10-20 09:50:50 +00:00
if ( localAccountSensitiveSettings . skinColor !== "" ) {
if ( emoji . unicode . includes ( localAccountSensitiveSettings . skinColor ) ) {
2021-09-28 15:04:06 +00:00
newCategories [ categoryNames [ emoji . category ] ] . push ( Object . assign ( { } , emoji , { filename: emoji . unicode } ) ) ;
2021-07-07 09:32:03 +00:00
} else {
if ( ! emojisWithColors . includes ( emoji . unicode ) && ! containsSkinColor ( emoji . unicode ) ) {
2021-09-28 15:04:06 +00:00
newCategories [ categoryNames [ emoji . category ] ] . push ( Object . assign ( { } , emoji , { filename: emoji . unicode } ) ) ;
2021-07-07 09:32:03 +00:00
}
}
} else {
if ( ! containsSkinColor ( emoji . unicode ) ) {
2021-09-28 15:04:06 +00:00
newCategories [ categoryNames [ emoji . category ] ] . push ( Object . assign ( { } , emoji , { filename: emoji . unicode } ) ) ;
2021-07-07 09:32:03 +00:00
}
}
2020-09-29 08:37:08 +00:00
} )
if ( newCategories [ categoryNames . recent ] . length === 0 ) {
2021-07-07 09:32:03 +00:00
newCategories [ categoryNames . recent ] . push ( { category: "recent" , empty: true } )
2020-09-29 08:37:08 +00:00
}
2021-07-07 09:32:03 +00:00
categories = newCategories ;
2020-09-29 08:37:08 +00:00
}
2021-07-07 09:32:03 +00:00
2020-09-29 08:37:08 +00:00
Connections {
2020-11-19 20:45:18 +00:00
id: connectionSettings
2021-12-09 13:28:02 +00:00
target: Global
2020-09-29 08:37:08 +00:00
onSettingsLoaded: {
2020-11-19 20:45:18 +00:00
connectionSettings . enabled = false
2020-09-29 08:37:08 +00:00
// Add recent
2021-10-20 09:50:50 +00:00
if ( ! localAccountSensitiveSettings . recentEmojis || ! localAccountSensitiveSettings . recentEmojis . length ) {
2020-09-29 08:37:08 +00:00
return
}
2021-10-20 09:50:50 +00:00
categories [ 0 ] = localAccountSensitiveSettings . recentEmojis
emojiSectionsRepeater . itemAt ( 0 ) . allEmojis = localAccountSensitiveSettings . recentEmojis
2020-09-29 08:37:08 +00:00
}
}
onOpened: {
2020-11-19 20:45:18 +00:00
searchBox . text = ""
2020-09-29 08:37:08 +00:00
searchBox . forceActiveFocus ( Qt . MouseFocusReason )
2021-02-28 15:33:25 +00:00
Qt . callLater ( populateCategories ) ;
2020-09-29 08:37:08 +00:00
}
contentItem: ColumnLayout {
anchors.fill: parent
spacing: 0
Item {
property int headerMargin: 8
id: emojiHeader
Layout.fillWidth: true
height: searchBox . height + emojiHeader . headerMargin
SearchBox {
2021-07-07 09:32:03 +00:00
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
2020-09-29 08:37:08 +00:00
}
2021-07-07 09:32:03 +00:00
Row {
2020-09-29 08:37:08 +00:00
id: skinToneEmoji
2021-07-07 09:32:03 +00:00
property bool expandSkinColorOptions: false
width: expandSkinColorOptions ? ( 22 * skinColorEmojiRepeater . count ) : 22
2020-09-29 08:37:08 +00:00
height: 22
2021-07-07 09:32:03 +00:00
opacity: expandSkinColorOptions ? 1.0 : 0.0
Behavior on width { NumberAnimation { duration: 400 } }
Behavior on opacity { NumberAnimation { duration: 200 } }
visible: ( opacity > 0.1 )
2020-09-29 08:37:08 +00:00
anchors.verticalCenter: searchBox . verticalCenter
anchors.right: parent . right
anchors.rightMargin: emojiHeader . headerMargin
2021-07-07 09:32:03 +00:00
Repeater {
id: skinColorEmojiRepeater
model: [ "1f590-1f3fb" , "1f590-1f3fc" , "1f590-1f3fd" , "1f590-1f3fe" , "1f590-1f3ff" , "1f590" ]
delegate: SVGImage {
width: 22
height: 22
2021-09-28 15:04:06 +00:00
source: Style . emoji ( "72x72/" + modelData )
2021-07-07 09:32:03 +00:00
MouseArea {
cursorShape: Qt . PointingHandCursor
anchors.fill: parent
onClicked: {
2021-10-20 09:50:50 +00:00
localAccountSensitiveSettings . skinColor = ( index === 5 ) ? "" : modelData . split ( "-" ) [ 1 ] ;
2021-07-07 09:32:03 +00:00
popup . populateCategories ( ) ;
skinToneEmoji . expandSkinColorOptions = false ;
}
}
}
}
}
2020-09-29 08:37:08 +00:00
2021-07-07 09:32:03 +00:00
SVGImage {
width: 22
height: 22
anchors.verticalCenter: searchBox . verticalCenter
anchors.right: parent . right
anchors.rightMargin: emojiHeader . headerMargin
visible: ! skinToneEmoji . expandSkinColorOptions
2021-10-20 09:50:50 +00:00
source: Style . emoji ( "72x72/1f590" + ( ( localAccountSensitiveSettings . skinColor !== "" && visible ) ? ( "-" + localAccountSensitiveSettings . skinColor ) : "" ) )
2020-09-29 08:37:08 +00:00
MouseArea {
cursorShape: Qt . PointingHandCursor
anchors.fill: parent
2021-07-07 09:32:03 +00:00
onClicked: {
skinToneEmoji . expandSkinColorOptions = true ;
2020-09-29 08:37:08 +00:00
}
}
}
}
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
2021-10-25 11:48:23 +00:00
StatusTabBarIconButton {
icon.name: modelData
highlighted: index === scrollView . activeCategory
onClicked: {
2020-09-29 08:37:08 +00:00
scrollView . activeCategory = index
scrollView . scrollToCategory ( index )
}
}
}
}
}
}
/ * # # ^ # #
Designer {
D { i: 0 ; formeditorColor: "#ffffff" ; height: 440 ; width: 360 }
}
# # ^ # # * /