mirror of
https://gitlab.linphone.org/BC/public/linphone-desktop.git
synced 2026-01-27 00:48:08 +00:00
Support emojis display.
This commit is contained in:
parent
27770fb3b7
commit
8168b7dde8
12 changed files with 209 additions and 110 deletions
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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('&', "&")
|
||||
.replace('<', "\u2063<")
|
||||
.replace('>', "\u2063>")
|
||||
.replace('"', """)
|
||||
.replace('\'', "'");
|
||||
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('&', "&")
|
||||
.replace('<', "\u2063<")
|
||||
.replace('>', "\u2063>")
|
||||
.replace('"', """)
|
||||
.replace('\'', "'");
|
||||
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){
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
//----------------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue