support as much emojis as possible for Qt < 6.9.0

This commit is contained in:
Gaelle Braud 2026-02-16 15:22:47 +01:00
parent 7c3bed2bcb
commit a5cb280c82
13 changed files with 58 additions and 13 deletions

View file

@ -98,6 +98,8 @@ endif()
if(${Qt6_VERSION} VERSION_LESS "6.8.0") if(${Qt6_VERSION} VERSION_LESS "6.8.0")
message( FATAL_ERROR "Linphone requires Qt 6.8.0 or newer. Exiting CMake." ) message( FATAL_ERROR "Linphone requires Qt 6.8.0 or newer. Exiting CMake." )
elseif(${Qt6_VERSION} VERSION_LESS "6.9.0")
message(WARNING "Using Qt version "${Qt6_VERSION} ". Some emojis could not be rendered well as Linphone requires Qt 6.9.0 minimum version for rendering them without safety issue.")
endif() endif()
qt6_standard_project_setup() qt6_standard_project_setup()

View file

@ -545,7 +545,13 @@ QStringList ChatMessageCore::getReactionsSingletonAsStrings() const {
auto map = reac.toMap(); auto map = reac.toMap();
auto count = map["count"].toInt(); auto count = map["count"].toInt();
totalCount += count; totalCount += count;
QString body = map["body"].toString();
#if QT_VERSION < QT_VERSION_CHECK(6, 9, 0)
reacStringList.append(
ToolModel::encodeTextToQmlRichFormat(QString("%1 %2").arg(map["body"].toString()).arg(count)));
#else
reacStringList.append(QString("%1 %2").arg(map["body"].toString()).arg(count)); reacStringList.append(QString("%1 %2").arg(map["body"].toString()).arg(count));
#endif
} }
reacStringList.prepend(QString("%1 %2").arg(mTotalReactionsLabel).arg(totalCount)); reacStringList.prepend(QString("%1 %2").arg(mTotalReactionsLabel).arg(totalCount));
return reacStringList; return reacStringList;

View file

@ -124,7 +124,7 @@ inline std::string u32_to_ascii(std::u32string const &s) {
return out; return out;
} }
QString Utils::getInitials(const QString &username) { QString Utils::getInitials(const QString &username, int letterCount) {
if (username.isEmpty()) return ""; if (username.isEmpty()) return "";
QRegularExpression regex("[\\s\\.]+"); QRegularExpression regex("[\\s\\.]+");
@ -136,7 +136,13 @@ QString Utils::getInitials(const QString &username) {
// if name starts by an emoji, only return this one // if name starts by an emoji, only return this one
QVector<uint> utf32_string = username.toUcs4(); QVector<uint> utf32_string = username.toUcs4();
auto code = utf32_string[0]; auto code = utf32_string[0];
if (Utils::codepointIsEmoji(code)) return QString::fromStdU32String(char32); if (Utils::codepointIsEmoji(code)) {
#if QT_VERSION < QT_VERSION_CHECK(6, 9, 0)
return Utils::encodeEmojiToQmlRichFormat(QString::fromStdU32String(char32));
#else
QString::fromStdU32String(char32);
#endif
}
QStringList initials; QStringList initials;
initials << QString::fromStdU32String(char32); initials << QString::fromStdU32String(char32);
@ -145,6 +151,7 @@ QString Utils::getInitials(const QString &username) {
str32 = words[i].toStdU32String(); str32 = words[i].toStdU32String();
char32[0] = str32[0]; char32[0] = str32[0];
initials << QString::fromStdU32String(char32); initials << QString::fromStdU32String(char32);
if (initials.count() >= letterCount) break;
std::string converted = u32_to_ascii(char32); std::string converted = u32_to_ascii(char32);
if (Utils::codepointIsEmoji(atoi(converted.c_str()))) { if (Utils::codepointIsEmoji(atoi(converted.c_str()))) {
break; break;
@ -1894,7 +1901,7 @@ VariantObject *Utils::encodeTextToQmlRichFormat(const QString &text,
QString Utils::encodeEmojiToQmlRichFormat(const QString &body) { QString Utils::encodeEmojiToQmlRichFormat(const QString &body) {
QString fmtBody = ""; QString fmtBody = "";
QVector<uint> utf32_string = body.toUcs4(); QVector<uint> utf32_string = body.toUcs4();
#if QT_VERSION < QT_VERSION_CHECK(6, 9, 0)
bool insideFontBlock = false; bool insideFontBlock = false;
for (auto &code : utf32_string) { for (auto &code : utf32_string) {
if (Utils::codepointIsEmoji(code)) { if (Utils::codepointIsEmoji(code)) {
@ -1914,6 +1921,9 @@ QString Utils::encodeEmojiToQmlRichFormat(const QString &body) {
if (insideFontBlock) { if (insideFontBlock) {
fmtBody += "</font>"; fmtBody += "</font>";
} }
#else
fmtBody = body;
#endif
return fmtBody; return fmtBody;
} }

View file

@ -65,7 +65,7 @@ public:
Q_INVOKABLE static QString getUsername(const QString &address); Q_INVOKABLE static QString getUsername(const QString &address);
Q_INVOKABLE static QString getGivenNameFromFullName(const QString &fullName); Q_INVOKABLE static QString getGivenNameFromFullName(const QString &fullName);
Q_INVOKABLE static QString getFamilyNameFromFullName(const QString &fullName); Q_INVOKABLE static QString getFamilyNameFromFullName(const QString &fullName);
Q_INVOKABLE static QString getInitials(const QString &username); // Support UTF32 Q_INVOKABLE static QString getInitials(const QString &username, int letterCount = 2); // Support UTF32
Q_INVOKABLE static VariantObject *findLocalAccountByAddress(const QString &address); Q_INVOKABLE static VariantObject *findLocalAccountByAddress(const QString &address);
Q_INVOKABLE static void Q_INVOKABLE static void

View file

@ -103,6 +103,7 @@ Control.TabBar {
contentItem: Text { contentItem: Text {
id: tabText id: tabText
width: implicitWidth width: implicitWidth
textFormat: Text.RichText
font { font {
pixelSize: mainItem.pixelSize pixelSize: mainItem.pixelSize
weight: mainItem.textWeight weight: mainItem.textWeight

View file

@ -180,7 +180,8 @@ ListView {
id: friendAddress id: friendAddress
Layout.fillWidth: true Layout.fillWidth: true
maximumLineCount: 1 maximumLineCount: 1
text: modelData? modelData.core.title : "" text: modelData? UtilsCpp.encodeEmojiToQmlRichFormat(modelData.core.title) : ""
textFormat: Text.RichText
color: DefaultStyle.main2_800 color: DefaultStyle.main2_800
font { font {
pixelSize: Typography.p1.pixelSize pixelSize: Typography.p1.pixelSize

View file

@ -536,6 +536,7 @@ Control.Control {
model: ConstantsCpp.reactionsList model: ConstantsCpp.reactionsList
delegate: Button { delegate: Button {
text: UtilsCpp.encodeEmojiToQmlRichFormat(modelData) text: UtilsCpp.encodeEmojiToQmlRichFormat(modelData)
textFormat: Text.RichText
background: Rectangle { background: Rectangle {
anchors.fill: parent anchors.fill: parent
color: DefaultStyle.grey_200 color: DefaultStyle.grey_200

View file

@ -157,6 +157,7 @@ Loader{
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
text: initialItem.initials text: initialItem.initials
textFormat: Text.RichText
font { font {
pixelSize: initialItem.height * 36 / 120 pixelSize: initialItem.height * 36 / 120
weight: Typography.h4.weight weight: Typography.h4.weight

View file

@ -36,7 +36,7 @@ FocusScope {
property real itemsRightMargin: Utils.getSizeWithScreenRatio(39) property real itemsRightMargin: Utils.getSizeWithScreenRatio(39)
property var displayName: searchResultItem? searchResultItem.core.fullName : "" property var displayName: searchResultItem? searchResultItem.core.fullName : ""
property var initial: displayName.length > 0 ? displayName[0].toLocaleLowerCase(AppCpp.localeAsString) : '' property var initial: displayName.length > 0 ? UtilsCpp.getInitials(displayName, 1).toLocaleLowerCase(AppCpp.localeAsString) : ''
signal clicked(var mouse) signal clicked(var mouse)
signal contactDeletionRequested(FriendGui contact) signal contactDeletionRequested(FriendGui contact)
@ -45,7 +45,7 @@ FocusScope {
MouseArea { MouseArea {
Text { Text {
id: initial id: initialText
anchors.left: parent.left anchors.left: parent.left
visible: mainItem.showInitials visible: mainItem.showInitials
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
@ -53,7 +53,8 @@ FocusScope {
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
width: Utils.getSizeWithScreenRatio(20) width: Utils.getSizeWithScreenRatio(20)
opacity: previousInitial != mainItem.initial ? 1 : 0 opacity: previousInitial != mainItem.initial ? 1 : 0
text: mainItem.initial || "" text: mainItem.initial
textFormat: Text.RichText
color: DefaultStyle.main2_400 color: DefaultStyle.main2_400
font { font {
pixelSize: Utils.getSizeWithScreenRatio(20) pixelSize: Utils.getSizeWithScreenRatio(20)
@ -63,7 +64,7 @@ FocusScope {
} }
RowLayout { RowLayout {
id: contactDelegate id: contactDelegate
anchors.left: initial.visible ? initial.right : parent.left anchors.left: initialText.visible ? initialText.right : parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.rightMargin: mainItem.itemsRightMargin anchors.rightMargin: mainItem.itemsRightMargin
anchors.top: parent.top anchors.top: parent.top
@ -84,8 +85,9 @@ FocusScope {
visible: mainItem.showDisplayName visible: mainItem.showDisplayName
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: visible ? implicitHeight: 0 Layout.preferredHeight: visible ? implicitHeight: 0
text: UtilsCpp.boldTextPart(mainItem.displayName, text: UtilsCpp.boldTextPart(UtilsCpp.encodeEmojiToQmlRichFormat((mainItem.displayName)),
mainItem.highlightText) mainItem.highlightText)
textFormat: Text.RichText
font { font {
pixelSize: mainItem.showDefaultAddress ? Typography.h4.pixelSize : Typography.p1.pixelSize pixelSize: mainItem.showDefaultAddress ? Typography.h4.pixelSize : Typography.p1.pixelSize
capitalization: mainItem.displayNameCapitalization ? Font.Capitalize : Font.MixedCase capitalization: mainItem.displayNameCapitalization ? Font.Capitalize : Font.MixedCase

View file

@ -108,9 +108,10 @@ FocusScope {
} }
ColumnLayout { ColumnLayout {
Text { Text {
text: mainItem.chat?.core.title || "" text: UtilsCpp.encodeEmojiToQmlRichFormat(mainItem.chat?.core.title) || ""
color: DefaultStyle.main2_600 color: DefaultStyle.main2_600
maximumLineCount: 1 maximumLineCount: 1
textFormat: Text.RichText
font { font {
pixelSize: Typography.h4.pixelSize pixelSize: Typography.h4.pixelSize
weight: Utils.getSizeWithScreenRatio(400) weight: Utils.getSizeWithScreenRatio(400)

View file

@ -89,6 +89,18 @@ ColumnLayout {
Layout.preferredHeight: title.contentHeight Layout.preferredHeight: title.contentHeight
anchors.margins: Utils.getSizeWithScreenRatio(2) anchors.margins: Utils.getSizeWithScreenRatio(2)
Text {
anchors.fill: parent
anchors.margins: 6
visible: !titleMainItem.isEditingSubject
text: UtilsCpp.encodeEmojiToQmlRichFormat(mainItem.chatGui.core.title) || ""
textFormat: Text.RichText
color: DefaultStyle.main2_700
wrapMode: Text.Wrap
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
font: Typography.p1
}
TextEdit { TextEdit {
id: title id: title
anchors.fill: parent anchors.fill: parent
@ -96,6 +108,7 @@ ColumnLayout {
font: Typography.p1 font: Typography.p1
color: DefaultStyle.main2_700 color: DefaultStyle.main2_700
text: mainItem.chatGui.core.title || "" text: mainItem.chatGui.core.title || ""
visible: titleMainItem.isEditingSubject
enabled: titleMainItem.isEditingSubject enabled: titleMainItem.isEditingSubject
wrapMode: TextEdit.Wrap wrapMode: TextEdit.Wrap
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
@ -105,6 +118,11 @@ ColumnLayout {
titleMainItem.saveSubject() titleMainItem.saveSubject()
titleMainItem.isEditingSubject = !titleMainItem.isEditingSubject titleMainItem.isEditingSubject = !titleMainItem.isEditingSubject
} }
Keys.onEnterPressed: {
if (titleMainItem.isEditingSubject)
titleMainItem.saveSubject()
titleMainItem.isEditingSubject = !titleMainItem.isEditingSubject
}
} }
} }

View file

@ -64,6 +64,7 @@ MessageInfosLayout {
Item{Layout.fillWidth: true} Item{Layout.fillWidth: true}
Text { Text {
text: UtilsCpp.encodeEmojiToQmlRichFormat(modelData.body) text: UtilsCpp.encodeEmojiToQmlRichFormat(modelData.body)
textFormat: Text.RichText
font { font {
pixelSize: Typography.h3.pixelSize pixelSize: Typography.h3.pixelSize
weight: Typography.p3.weight weight: Typography.p3.weight

View file

@ -55,10 +55,10 @@ AbstractMainPage {
} }
} }
onSelectedContactChanged: { onSelectedContactChanged: {
console.log("selected contact changed, go to contact details")
// if we are editing a contact, force staying on edition page // if we are editing a contact, force staying on edition page
if (!rightPanelStackView.currentItem if (!rightPanelStackView.currentItem
|| rightPanelStackView.currentItem.objectName != "contactEdition") { || rightPanelStackView.currentItem.objectName != "contactEdition") {
console.log("selected contact changed, go to contact details")
goToContactDetails() goToContactDetails()
} }
} }
@ -435,7 +435,8 @@ AbstractMainPage {
ColumnLayout { ColumnLayout {
spacing: 0 spacing: 0
Text { Text {
text: contactDetail.contactName text: UtilsCpp.encodeEmojiToQmlRichFormat(contactDetail.contactName)
textFormat: Text.RichText
Layout.fillWidth: true Layout.fillWidth: true
maximumLineCount: 1 maximumLineCount: 1
font { font {