Support emojis display.

This commit is contained in:
Julien Wadel 2023-02-07 15:40:02 +01:00
parent 27770fb3b7
commit 8168b7dde8
12 changed files with 209 additions and 110 deletions

View file

@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- VFS Encryption
- File viewer in chats (Image/Animated Image/Video/Texts) with the option to export the file.
- Accept/decline CLI commands.
- Colored Emojis with its own font family.
## 5.0.11 - udnefined

View file

@ -1468,6 +1468,26 @@ void SettingsModel::setTextMessageFontSize(const int& size){
mConfig->setInt(UiSection, "text_message_font_size", size);
emit textMessageFontSizeChanged(size);
}
QFont SettingsModel::getEmojiFont() const{
QString family = Utils::coreStringToAppString(mConfig->getString(UiSection, "emoji_font", Utils::appStringToCoreString(QFont(Constants::DefaultEmojiFont).family())));
int pointSize = getTextMessageFontSize();
return QFont(family,pointSize);
}
void SettingsModel::setEmojiFont(const QFont& font){
mConfig->setString(UiSection, "emoji_font", Utils::appStringToCoreString(font.family()));
emit emojiFontChanged(font);
}
int SettingsModel::getEmojiFontSize() const{
return mConfig->getInt(UiSection, "emoji_font_size", 10);
}
void SettingsModel::setEmojiFontSize(const int& size){
mConfig->setInt(UiSection, "emoji_font_size", size);
emit emojiFontSizeChanged(size);
}
QString SettingsModel::getSavedScreenshotsFolder () const {
return QDir::cleanPath(

View file

@ -198,6 +198,8 @@ class SettingsModel : public QObject {
Q_PROPERTY(QFont textMessageFont READ getTextMessageFont WRITE setTextMessageFont NOTIFY textMessageFontChanged)
Q_PROPERTY(int textMessageFontSize READ getTextMessageFontSize WRITE setTextMessageFontSize NOTIFY textMessageFontSizeChanged)
Q_PROPERTY(QFont emojiFont READ getEmojiFont WRITE setEmojiFont NOTIFY emojiFontChanged)
Q_PROPERTY(int emojiFontSize READ getEmojiFontSize WRITE setEmojiFontSize NOTIFY emojiFontSizeChanged)
Q_PROPERTY(QString remoteProvisioning READ getRemoteProvisioning WRITE setRemoteProvisioning NOTIFY remoteProvisioningChanged)
Q_PROPERTY(QString flexiAPIUrl READ getFlexiAPIUrl WRITE setFlexiAPIUrl NOTIFY flexiAPIUrlChanged)
@ -556,6 +558,12 @@ public:
int getTextMessageFontSize() const;
void setTextMessageFontSize(const int& size);
QFont getEmojiFont() const;
void setEmojiFont(const QFont& font);
int getEmojiFontSize() const;
void setEmojiFontSize(const int& size);
QString getSavedScreenshotsFolder () const;
void setSavedScreenshotsFolder (const QString &folder);
@ -785,6 +793,9 @@ signals:
void textMessageFontChanged(const QFont& font);
void textMessageFontSizeChanged(const int& size);
void emojiFontChanged(const QFont& font);
void emojiFontSizeChanged(const int& size);
void savedScreenshotsFolderChanged (const QString &folder);
void savedCallsFolderChanged (const QString &folder);
void downloadFolderChanged (const QString &folder);

View file

@ -23,6 +23,7 @@ constexpr char Constants::AssistantViewName[];
constexpr char Constants::ApplicationMinimalQtVersion[];
constexpr char Constants::DefaultFont[];
constexpr char Constants::DefaultEmojiFont[];
constexpr char Constants::QtDomain[];
constexpr size_t Constants::MaxLogsCollectionSize;

View file

@ -38,6 +38,7 @@ public:
static constexpr char DefaultLocale[] = "en";
static constexpr char DefaultFont[] = "Noto Sans";
static constexpr char DefaultEmojiFont[] = "Noto Color Emoji";
static constexpr size_t MaxLogsCollectionSize = 10485760*5; // 50MB.

View file

@ -659,6 +659,44 @@ QString Utils::getFileChecksum(const QString& filePath){
}
return QString();
}
bool Utils::codepointIsEmoji(uint code){
return (code >= 0x2600 && code <= 0x27bf) || (code >= 0x2b00 && code <= 0x2bff) ||
(code >= 0x1f000 && code <= 0x1faff) || code == 0x200d || code == 0xfe0f;
}
QString Utils::replaceEmoji(const QString &body){
QString fmtBody = "";
QVector<uint> utf32_string = body.toUcs4();
bool insideFontBlock = false;
for (auto &code : utf32_string) {
if (Utils::codepointIsEmoji(code)) {
if (!insideFontBlock) {
fmtBody += QString("<font face=\"" +
CoreManager::getInstance()->getSettingsModel()->getEmojiFont().family() + "\">");
insideFontBlock = true;
}
} else {
if (insideFontBlock) {
fmtBody += "</font>";
insideFontBlock = false;
}
}
fmtBody += QString::fromUcs4(&code, 1);
}
if (insideFontBlock) {
fmtBody += "</font>";
}
return fmtBody;
}
bool Utils::isOnlyEmojis(const QString& text){
QVector<uint> utf32_string = text.toUcs4();
for (auto &code : utf32_string)
if( !Utils::codepointIsEmoji(code))
return false;
return true;
}
QString Utils::encodeTextToQmlRichFormat(const QString& text, const QVariantMap& options){
@ -667,39 +705,43 @@ QString Utils::encodeTextToQmlRichFormat(const QString& text, const QVariantMap&
QStringList imageFormat;
for(auto format : QImageReader::supportedImageFormats())
imageFormat.append(QString::fromLatin1(format).toUpper());
auto iriParsed = UriTools::parseIri(text);
for(int i = 0 ; i < iriParsed.size() ; ++i){
QString iri = iriParsed[i].second.replace('&', "&amp;")
.replace('<', "\u2063&lt;")
.replace('>', "\u2063&gt;")
.replace('"', "&quot;")
.replace('\'', "&#039;");
if(!iriParsed[i].first)
formattedText.append(iri);
else{
QString uri = iriParsed[i].second.left(3) == "www" ? "http://"+iriParsed[i].second : iriParsed[i].second ;
int extIndex = iriParsed[i].second.lastIndexOf('.');
QString ext;
if( extIndex >= 0)
ext = iriParsed[i].second.mid(extIndex+1).toUpper();
if(imageFormat.contains(ext.toLatin1())){// imagesHeight is not used because of bugs on display (blank image if set without width)
images += "<a href=\"" + uri + "\"><img" + (
options.contains("imagesWidth")
? QString(" width='") + options["imagesWidth"].toString() + "'"
if(options.contains("noLink") && options["noLink"].toBool()){
formattedText.append(text);
}else{
auto iriParsed = UriTools::parseIri(text);
for(int i = 0 ; i < iriParsed.size() ; ++i){
QString iri = iriParsed[i].second.replace('&', "&amp;")
.replace('<', "\u2063&lt;")
.replace('>', "\u2063&gt;")
.replace('"', "&quot;")
.replace('\'', "&#039;");
if(!iriParsed[i].first)
formattedText.append(iri);
else{
QString uri = iriParsed[i].second.left(3) == "www" ? "http://"+iriParsed[i].second : iriParsed[i].second ;
int extIndex = iriParsed[i].second.lastIndexOf('.');
QString ext;
if( extIndex >= 0)
ext = iriParsed[i].second.mid(extIndex+1).toUpper();
if(imageFormat.contains(ext.toLatin1())){// imagesHeight is not used because of bugs on display (blank image if set without width)
images += "<a href=\"" + uri + "\"><img" + (
options.contains("imagesWidth")
? QString(" width='") + options["imagesWidth"].toString() + "'"
: ""
) + (
options.contains("imagesWidth")
? QString(" height='auto'")
: ""
) + (
options.contains("imagesWidth")
? QString(" height='auto'")
: ""
) + " src=\"" + iriParsed[i].second + "\" /></a>";
}else
formattedText.append( "<a href=\"" + uri + "\">" + iri + "</a>");
) + " src=\"" + iriParsed[i].second + "\" /></a>";
}else
formattedText.append( "<a href=\"" + uri + "\">" + iri + "</a>");
}
}
}
if(images != "")
images = "<div>" + images +"</div>";
return images + "<p style=\"white-space:pre-wrap;\">" + formattedText.join("") + "</p>";
return images + "<p style=\"white-space:pre-wrap;\">" + replaceEmoji(formattedText.join("")) + "</p>";
}
QString Utils::getFileContent(const QString& filePath){

View file

@ -70,7 +70,10 @@ public:
Q_INVOKABLE QSize getImageSize(const QString& url);
Q_INVOKABLE static QPoint getCursorPosition();
Q_INVOKABLE static QString getFileChecksum(const QString& filePath);
Q_INVOKABLE static QString encodeTextToQmlRichFormat(const QString& text, const QVariantMap& options);
static bool codepointIsEmoji(uint code);
static QString replaceEmoji(const QString &body);
Q_INVOKABLE static bool isOnlyEmojis(const QString& text);
Q_INVOKABLE static QString encodeTextToQmlRichFormat(const QString& text, const QVariantMap& options = QVariantMap());
Q_INVOKABLE static QString getFileContent(const QString& filePath);
//----------------------------------------------------------------------------------

View file

@ -6,6 +6,7 @@ import Common 1.0
import Linphone 1.0
import Linphone.Styles 1.0
import UtilsCpp 1.0
// =============================================================================
Item {
@ -72,7 +73,8 @@ Item {
elide: Text.ElideRight
font.bold: true
font.pointSize: AccountStatusStyle.username.pointSize
text: AccountSettingsModel.username
text: UtilsCpp.encodeTextToQmlRichFormat(AccountSettingsModel.username)
textFormat: Text.RichText
verticalAlignment: Text.AlignBottom
}
Item {

View file

@ -40,7 +40,7 @@ TextEdit {
readOnly: true
selectByMouse: true
font.family: customFont.family
font.pointSize: Units.dp * customFont.pointSize
font.pointSize: Units.dp * customFont.pointSize * (UtilsCpp.isOnlyEmojis(contentModel.text) ? 4 : 1 )
text: visible ? UtilsCpp.encodeTextToQmlRichFormat(contentModel.text, {
imagesHeight: ChatStyle.entry.message.images.height,

View file

@ -33,7 +33,7 @@ Item {
}
function _computeInitials () {
return UtilsCpp.getInitials(username);
return UtilsCpp.encodeTextToQmlRichFormat(UtilsCpp.getInitials(username));
}
// ---------------------------------------------------------------------------
@ -67,8 +67,8 @@ Item {
return AvatarStyle.initials.pointSize * (width || 1)
}
text: _computeInitials()
textFormat: Text.RichText
visible: roundedImage.status !== Image.Ready && !avatar.isPhoneNumber && avatar.isOneToOne
}

View file

@ -1,9 +1,11 @@
import QtQuick 2.7
import QtQuick.Layouts 1.3
import Linphone 1.0
import Linphone.Styles 1.0
import Common 1.0
import UtilsCpp 1.0
// =============================================================================
Column {
@ -31,53 +33,62 @@ Column {
signal titleClicked()
// ---------------------------------------------------------------------------
TextEdit {
id: title
property string fullText
anchors.horizontalCenter: (horizontalTextAlignment == Text.AlignHCenter ? parent.horizontalCenter : undefined)
color: titleColor
font.family: SettingsModel.textMessageFont.family
font.weight: contactDescriptionStyle.title.weight
font.pointSize: contactDescriptionStyle.title.pointSize
horizontalAlignment: horizontalTextAlignment
verticalAlignment: (subtitle.visible?Text.AlignBottom:Text.AlignVCenter)
width: Math.min(parent.width-statusWidth, titleImplicitWidthWorkaround.implicitWidth)
Item{
anchors.left: parent.left
anchors.right: parent.right
height: (parent.height-parent.topPadding-parent.bottomPadding)/parent.visibleChildren.length
text: metrics.elidedText
onActiveFocusChanged: deselect();
readOnly: true
selectByMouse: true
Text{// Workaround to get implicitWidth from text without eliding
id: titleImplicitWidthWorkaround
text: title.fullText
RowLayout{
anchors.fill: parent
TextEdit {
id: title
property string fullText
Layout.fillWidth: true
color: titleColor
font.family: SettingsModel.textMessageFont.family
font.weight: title.font.weight
font.pointSize: title.font.pointSize
visible: false
font.weight: contactDescriptionStyle.title.weight
font.pointSize: contactDescriptionStyle.title.pointSize
textFormat: Text.RichText
horizontalAlignment: horizontalTextAlignment
verticalAlignment: (subtitle.visible?Text.AlignBottom:Text.AlignVCenter)
text: UtilsCpp.encodeTextToQmlRichFormat(metrics.elidedText, {noLink:true})
onActiveFocusChanged: deselect();
readOnly: true
selectByMouse: true
Layout.preferredHeight: parent.height
Text{// Workaround to get implicitWidth from text without eliding
id: titleImplicitWidthWorkaround
text: title.fullText
font.family: SettingsModel.textMessageFont.family
font.weight: title.font.weight
font.pointSize: title.font.pointSize
textFormat: Text.RichText
visible: false
}
TextMetrics {
id: metrics
font: title.font
text: title.fullText
elideWidth: title.width
elide: Qt.ElideRight
}
}
Text{
id:status
Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter
//anchors.top:parent.top
//anchors.bottom : parent.bottom
//anchors.left:parent.right
//anchors.leftMargin:5
verticalAlignment: Text.AlignVCenter
visible: text != ''
text : ''
color: contactDescriptionStyle.title.status.colorModel.color
font.pointSize: contactDescriptionStyle.title.status.pointSize
font.italic : true
}
TextMetrics {
id: metrics
font: title.font
text: title.fullText
elideWidth: title.width
elide: Qt.ElideRight
}
Text{
id:status
anchors.top:parent.top
anchors.bottom : parent.bottom
anchors.left:parent.right
anchors.leftMargin:5
verticalAlignment: Text.AlignVCenter
visible: text != ''
text : ''
color: contactDescriptionStyle.title.status.colorModel.color
font.pointSize: contactDescriptionStyle.title.status.pointSize
font.italic : true
}
MouseArea{
anchors.fill:parent
@ -85,40 +96,45 @@ Column {
onClicked: titleClicked()
}
}
TextEdit {
id:subtitle
property string fullText
anchors.horizontalCenter: (horizontalTextAlignment == Text.AlignHCenter ? parent.horizontalCenter : undefined)
color: subtitleColor
font.family: SettingsModel.textMessageFont.family
font.weight: contactDescriptionStyle.subtitle.weight
font.pointSize: contactDescriptionStyle.subtitle.pointSize
horizontalAlignment: horizontalTextAlignment
verticalAlignment: (title.visible?Text.AlignTop:Text.AlignVCenter)
width: Math.min(parent.width-statusWidth, subtitleImplicitWidthWorkaround.implicitWidth)
Item{
anchors.left: parent.left
anchors.right: parent.right
height: (parent.height-parent.topPadding-parent.bottomPadding)/parent.visibleChildren.length
visible: text != ''
text: subtitleMetrics.elidedText
onActiveFocusChanged: deselect();
readOnly: true
selectByMouse: true
Text{// Workaround to get implicitWidth from text without eliding
id: subtitleImplicitWidthWorkaround
text: subtitle.fullText
font.weight: subtitle.font.weight
font.pointSize: subtitle.font.pointSize
visible: false
}
TextMetrics {
id: subtitleMetrics
font: subtitle.font
text: subtitle.fullText
elideWidth: subtitle.width
elide: Qt.ElideRight
visible: subtitle.fullText != ''
TextEdit {
id:subtitle
property string fullText
anchors.fill: parent
color: subtitleColor
font.family: SettingsModel.textMessageFont.family
font.weight: contactDescriptionStyle.subtitle.weight
font.pointSize: contactDescriptionStyle.subtitle.pointSize
textFormat: Text.RichText
horizontalAlignment: horizontalTextAlignment
verticalAlignment: (title.visible?Text.AlignTop:Text.AlignVCenter)
text: UtilsCpp.encodeTextToQmlRichFormat(subtitleMetrics.elidedText, {noLink:true})
onActiveFocusChanged: deselect();
readOnly: true
selectByMouse: true
Text{// Workaround to get implicitWidth from text without eliding
id: subtitleImplicitWidthWorkaround
text: subtitle.fullText
font.weight: subtitle.font.weight
font.pointSize: subtitle.font.pointSize
visible: false
textFormat: Text.RichText
}
TextMetrics {
id: subtitleMetrics
font: subtitle.font
text: subtitle.fullText
elideWidth: subtitle.width
elide: Qt.ElideRight
}
}
}
}

View file

@ -290,7 +290,9 @@ ColumnLayout {
pointSize: ContactsStyle.contact.username.pointSize
}
font.family: SettingsModel.textMessageFont.family
text: $modelData.vcard.username
text: UtilsCpp.encodeTextToQmlRichFormat($modelData.vcard.username)
textFormat: Text.RichText
verticalAlignment: Text.AlignVCenter
}