Colorful emoji in reaction

Use ICU to determine if the string contains only emojis
This commit is contained in:
Carl Schwan
2023-08-10 15:37:37 +02:00
committed by Carl Schwan
parent 3a4f71de7f
commit f22107c8ab
5 changed files with 67 additions and 21 deletions

View File

@@ -63,9 +63,9 @@ set_package_properties(KF6 PROPERTIES
PURPOSE "Basic application components"
)
set_package_properties(KF6Kirigami2 PROPERTIES
TYPE REQUIRED
PURPOSE "Kirigami application UI framework"
)
TYPE REQUIRED
PURPOSE "Kirigami application UI framework"
)
find_package(KF6KirigamiAddons 0.7.2 REQUIRED)
if(ANDROID)
@@ -81,6 +81,12 @@ else()
TYPE RUNTIME
)
ecm_find_qmlmodule(org.kde.syntaxhighlighting 1.0)
find_package(ICU 61.0 COMPONENTS uc)
set_package_properties(ICU PROPERTIES
TYPE REQUIRED
PURPOSE "Unicode library"
)
endif()
if (NOT ANDROID AND NOT WIN32 AND NOT APPLE)

View File

@@ -319,9 +319,10 @@ if(NOT ANDROID)
else()
target_sources(neochat PRIVATE trayicon.cpp trayicon.h)
endif()
target_link_libraries(neochat PUBLIC KF6::ConfigWidgets KF6::WindowSystem)
target_link_libraries(neochat PUBLIC KF6::ConfigWidgets KF6::WindowSystem ICU::uc)
target_compile_definitions(neochat PUBLIC -DHAVE_COLORSCHEME)
target_compile_definitions(neochat PUBLIC -DHAVE_WINDOWSYSTEM)
target_compile_definitions(neochat PUBLIC -DHAVE_ICU)
endif()
if (NOT ANDROID AND NOT WIN32 AND NOT APPLE)

View File

@@ -4,6 +4,12 @@
#include "reactionmodel.h"
#include <QDebug>
#ifdef HAVE_ICU
#include <QTextBoundaryFinder>
#include <QTextCharFormat>
#include <unicode/uchar.h>
#include <unicode/urename.h>
#endif
#include <KLocalizedString>
@@ -29,11 +35,38 @@ QVariant ReactionModel::data(const QModelIndex &index, int role) const
const auto &reaction = m_reactions.at(index.row());
if (role == TextRole) {
const auto isEmoji = [](const QString &text) {
#ifdef HAVE_ICU
QTextBoundaryFinder finder(QTextBoundaryFinder::Grapheme, text);
int from = 0;
while (finder.toNextBoundary() != -1) {
auto to = finder.position();
if (text[from].isSpace()) {
from = to;
continue;
}
auto first = text.mid(from, to - from).toUcs4()[0];
if (!u_hasBinaryProperty(first, UCHAR_EMOJI_PRESENTATION)) {
return false;
}
from = to;
}
return true;
#else
return false;
#endif
};
const auto reactionText = isEmoji(reaction.reaction)
? QStringLiteral("<span style=\"font-family: 'emoji';\">") + reaction.reaction + QStringLiteral("</span>")
: reaction.reaction;
if (role == TextContentRole) {
if (reaction.authors.count() > 1) {
return QStringLiteral("%1 %2").arg(reaction.reaction, QString::number(reaction.authors.count()));
return QStringLiteral("%1 %2").arg(reactionText, QString::number(reaction.authors.count()));
} else {
return reaction.reaction;
return reactionText;
}
}
@@ -64,7 +97,7 @@ QVariant ReactionModel::data(const QModelIndex &index, int role) const
"%2 reacted with %3",
reaction.authors.count(),
text,
reaction.reaction);
reactionText);
return text;
}
@@ -101,7 +134,7 @@ void ReactionModel::setReactions(QList<Reaction> reactions)
QHash<int, QByteArray> ReactionModel::roleNames() const
{
return {
{TextRole, "text"},
{TextContentRole, "textContent"},
{ReactionRole, "reaction"},
{ToolTipRole, "toolTip"},
{AuthorsRole, "authors"},

View File

@@ -34,7 +34,7 @@ public:
* @brief Defines the model roles.
*/
enum Roles {
TextRole = Qt::DisplayRole, /**< The text to show in the reaction. */
TextContentRole = Qt::DisplayRole, /**< The text to show in the reaction. */
ReactionRole, /**< The reaction emoji. */
ToolTipRole, /**< The tool tip to show for the reaction. */
AuthorsRole, /**< The list of authors who sent the given reaction. */

View File

@@ -28,39 +28,45 @@ Flow {
id: reactionRepeater
delegate: QQC2.AbstractButton {
width: Math.max(reactionTextMetrics.advanceWidth + Kirigami.Units.smallSpacing * 4, height)
id: reactionDelegate
required property string textContent
required property string reaction
required property string toolTip
required property bool hasLocalUser
width: Math.max(contentItem.implicitWidth + leftPadding + rightPadding, height)
height: Math.round(Kirigami.Units.gridUnit * 1.5)
contentItem: QQC2.Label {
id: reactionLabel
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
text: model.text
TextMetrics {
id: reactionTextMetrics
text: reactionLabel.text
}
text: reactionDelegate.textContent
background: null
wrapMode: TextEdit.NoWrap
textFormat: Text.RichText
}
padding: Kirigami.Units.smallSpacing
background: Kirigami.ShadowedRectangle {
color: model.hasLocalUser ? Kirigami.Theme.positiveBackgroundColor : Kirigami.Theme.backgroundColor
color: reactionDelegate.hasLocalUser ? Kirigami.Theme.positiveBackgroundColor : Kirigami.Theme.backgroundColor
Kirigami.Theme.inherit: false
Kirigami.Theme.colorSet: Kirigami.Theme.View
radius: height / 2
shadow {
size: Kirigami.Units.smallSpacing
color: !model.hasLocalUser ? Qt.rgba(0.0, 0.0, 0.0, 0.10) : Qt.rgba(Kirigami.Theme.textColor.r, Kirigami.Theme.textColor.g, Kirigami.Theme.textColor.b, 0.10)
color: !reactionDelegate.hasLocalUser ? Qt.rgba(0.0, 0.0, 0.0, 0.10) : Qt.rgba(Kirigami.Theme.textColor.r, Kirigami.Theme.textColor.g, Kirigami.Theme.textColor.b, 0.10)
}
}
onClicked: reactionClicked(model.reaction)
onClicked: reactionClicked(reactionDelegate.reaction)
hoverEnabled: true
QQC2.ToolTip.visible: hovered
QQC2.ToolTip.text: model.toolTip
QQC2.ToolTip.text: reactionDelegate.toolTip
}
}
}