lib_ui/ui/text/text_utilities.cpp

158 lines
4.3 KiB
C++

// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#include "ui/text/text_utilities.h"
#include "base/algorithm.h"
#include "base/qt/qt_string_view.h"
#include <QtCore/QRegularExpression>
namespace Ui {
namespace Text {
namespace {
TextWithEntities WithSingleEntity(
const QString &text,
EntityType type,
const QString &data = QString()) {
auto result = TextWithEntities{ text };
result.entities.push_back({ type, 0, int(text.size()), data });
return result;
}
} // namespace
TextWithEntities Bold(const QString &text) {
return WithSingleEntity(text, EntityType::Bold);
}
TextWithEntities Semibold(const QString &text) {
return WithSingleEntity(text, EntityType::Semibold);
}
TextWithEntities Italic(const QString &text) {
return WithSingleEntity(text, EntityType::Italic);
}
TextWithEntities Link(const QString &text, const QString &url) {
return WithSingleEntity(text, EntityType::CustomUrl, url);
}
TextWithEntities Link(const QString &text, int index) {
return Link(text, u"internal:index"_q + QChar(index));
}
TextWithEntities Link(TextWithEntities text, const QString &url) {
return Wrapped(std::move(text), EntityType::CustomUrl, url);
}
TextWithEntities Link(TextWithEntities text, int index) {
return Link(std::move(text), u"internal:index"_q + QChar(index));
}
TextWithEntities PlainLink(const QString &text) {
return WithSingleEntity(text, EntityType::PlainLink);
}
TextWithEntities PlainLink(TextWithEntities text) {
return Wrapped(std::move(text), EntityType::PlainLink, QString());
}
TextWithEntities Wrapped(
TextWithEntities text,
EntityType type,
const QString &data) {
text.entities.insert(
text.entities.begin(),
{ type, 0, int(text.text.size()), data });
return text;
}
TextWithEntities RichLangValue(const QString &text) {
static const auto kStart = QRegularExpression("(\\*\\*|__)");
auto result = TextWithEntities();
auto offset = 0;
while (offset < text.size()) {
const auto m = kStart.match(text, offset);
if (!m.hasMatch()) {
result.text.append(base::StringViewMid(text, offset));
break;
}
const auto position = m.capturedStart();
const auto from = m.capturedEnd();
const auto tag = m.capturedView();
const auto till = text.indexOf(tag, from + 1);
if (till <= from) {
offset = from;
continue;
}
if (position > offset) {
result.text.append(base::StringViewMid(text, offset, position - offset));
}
const auto type = (tag == qstr("__"))
? EntityType::Italic
: EntityType::Bold;
result.entities.push_back({ type, int(result.text.size()), int(till - from) });
result.text.append(base::StringViewMid(text, from, till - from));
offset = till + tag.size();
}
return result;
}
TextWithEntities SingleCustomEmoji(QString data) {
return {
u"@"_q,
{ EntityInText(EntityType::CustomEmoji, 0, 1, data) },
};
}
TextWithEntities Mid(const TextWithEntities &text, int position, int n) {
if (n == -1) {
n = int(text.text.size()) - position;
}
const auto midEnd = (position + n);
auto entities = ranges::views::all(
text.entities
) | ranges::views::filter([&](const EntityInText &entity) {
// Intersects of ranges.
const auto l1 = entity.offset();
const auto r1 = entity.offset() + entity.length() - 1;
const auto l2 = position;
const auto r2 = midEnd - 1;
return !(l1 > r2 || l2 > r1);
}) | ranges::views::transform([&](const EntityInText &entity) {
if ((entity.offset() == position) && (entity.length() == n)) {
return entity;
}
const auto start = std::max(entity.offset(), position);
const auto end = std::min(entity.offset() + entity.length(), midEnd);
return EntityInText(
entity.type(),
start - position,
end - start,
entity.data());
}) | ranges::to<EntitiesInText>();
return {
.text = text.text.mid(position, n),
.entities = std::move(entities),
};
}
TextWithEntities Filtered(
const TextWithEntities &text,
const std::vector<EntityType> &types) {
auto result = ranges::views::all(
text.entities
) | ranges::views::filter([&](const EntityInText &entity) {
return ranges::contains(types, entity.type());
}) | ranges::to<EntitiesInText>();
return { .text = text.text, .entities = std::move(result) };
}
} // namespace Text
} // namespace Ui