|
|
|
@ -4,11 +4,12 @@ |
|
|
|
|
|
|
|
|
|
#include "GridImagePackModel.h" |
|
|
|
|
|
|
|
|
|
#include "Cache_p.h" |
|
|
|
|
#include "CompletionModelRoles.h" |
|
|
|
|
#include <QTextBoundaryFinder> |
|
|
|
|
|
|
|
|
|
#include <algorithm> |
|
|
|
|
|
|
|
|
|
#include "Cache_p.h" |
|
|
|
|
|
|
|
|
|
Q_DECLARE_METATYPE(StickerImage) |
|
|
|
|
|
|
|
|
|
GridImagePackModel::GridImagePackModel(const std::string &roomId, bool stickers, QObject *parent) |
|
|
|
@ -38,12 +39,52 @@ GridImagePackModel::GridImagePackModel(const std::string &roomId, bool stickers, |
|
|
|
|
rowToPack.push_back(packs.size()); |
|
|
|
|
packs.push_back(std::move(newPack)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// prepare search index
|
|
|
|
|
|
|
|
|
|
auto insertParts = [this](const QString &str, std::pair<std::uint32_t, std::uint32_t> id) { |
|
|
|
|
QTextBoundaryFinder finder(QTextBoundaryFinder::BoundaryType::Word, str); |
|
|
|
|
finder.toStart(); |
|
|
|
|
do { |
|
|
|
|
auto start = finder.position(); |
|
|
|
|
finder.toNextBoundary(); |
|
|
|
|
auto end = finder.position(); |
|
|
|
|
|
|
|
|
|
auto ref = str.midRef(start, end - start).trimmed(); |
|
|
|
|
if (!ref.isEmpty()) |
|
|
|
|
trie_.insert<ElementRank::second>(ref.toUcs4(), id); |
|
|
|
|
} while (finder.position() < str.size()); |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
std::uint32_t packIndex = 0; |
|
|
|
|
for (const auto &pack : packs) { |
|
|
|
|
std::uint32_t imgIndex = 0; |
|
|
|
|
for (const auto &img : pack.images) { |
|
|
|
|
std::pair<std::uint32_t, std::uint32_t> key{packIndex, imgIndex}; |
|
|
|
|
|
|
|
|
|
QString string1 = img.second.toCaseFolded(); |
|
|
|
|
QString string2 = QString::fromStdString(img.first.body).toCaseFolded(); |
|
|
|
|
|
|
|
|
|
if (!string1.isEmpty()) { |
|
|
|
|
trie_.insert<ElementRank::first>(string1.toUcs4(), key); |
|
|
|
|
insertParts(string1, key); |
|
|
|
|
} |
|
|
|
|
if (!string2.isEmpty()) { |
|
|
|
|
trie_.insert<ElementRank::first>(string2.toUcs4(), key); |
|
|
|
|
insertParts(string2, key); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
imgIndex++; |
|
|
|
|
} |
|
|
|
|
packIndex++; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
int |
|
|
|
|
GridImagePackModel::rowCount(const QModelIndex &) const |
|
|
|
|
{ |
|
|
|
|
return (int)rowToPack.size(); |
|
|
|
|
return static_cast<int>(searchString_.isEmpty() ? rowToPack.size() |
|
|
|
|
: rowToFirstRowEntryFromSearch.size()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
QHash<int, QByteArray> |
|
|
|
@ -59,30 +100,96 @@ QVariant |
|
|
|
|
GridImagePackModel::data(const QModelIndex &index, int role) const |
|
|
|
|
{ |
|
|
|
|
if (index.row() < rowCount() && index.row() >= 0) { |
|
|
|
|
const auto &pack = packs[rowToPack[index.row()]]; |
|
|
|
|
switch (role) { |
|
|
|
|
case Roles::PackName: |
|
|
|
|
return pack.packname; |
|
|
|
|
case Roles::Row: { |
|
|
|
|
std::size_t offset = static_cast<std::size_t>(index.row()) - pack.firstRow; |
|
|
|
|
QList<StickerImage> imgs; |
|
|
|
|
auto endOffset = std::min((offset + 1) * 3, pack.images.size()); |
|
|
|
|
for (std::size_t img = offset * 3; img < endOffset; img++) { |
|
|
|
|
const auto &data = pack.images.at(img); |
|
|
|
|
imgs.push_back({.url = QString::fromStdString(data.first.url), |
|
|
|
|
.shortcode = data.second, |
|
|
|
|
.body = QString::fromStdString(data.first.body), |
|
|
|
|
.descriptor_ = std::vector{ |
|
|
|
|
pack.room_id, |
|
|
|
|
pack.state_key, |
|
|
|
|
data.second.toStdString(), |
|
|
|
|
}}); |
|
|
|
|
if (searchString_.isEmpty()) { |
|
|
|
|
const auto &pack = packs[rowToPack[index.row()]]; |
|
|
|
|
switch (role) { |
|
|
|
|
case Roles::PackName: |
|
|
|
|
return pack.packname; |
|
|
|
|
case Roles::Row: { |
|
|
|
|
std::size_t offset = static_cast<std::size_t>(index.row()) - pack.firstRow; |
|
|
|
|
QList<StickerImage> imgs; |
|
|
|
|
auto endOffset = std::min((offset + 1) * columns, pack.images.size()); |
|
|
|
|
for (std::size_t img = offset * columns; img < endOffset; img++) { |
|
|
|
|
const auto &data = pack.images.at(img); |
|
|
|
|
imgs.push_back({.url = QString::fromStdString(data.first.url), |
|
|
|
|
.shortcode = data.second, |
|
|
|
|
.body = QString::fromStdString(data.first.body), |
|
|
|
|
.descriptor_ = std::vector{ |
|
|
|
|
pack.room_id, |
|
|
|
|
pack.state_key, |
|
|
|
|
data.second.toStdString(), |
|
|
|
|
}}); |
|
|
|
|
} |
|
|
|
|
return QVariant::fromValue(imgs); |
|
|
|
|
} |
|
|
|
|
default: |
|
|
|
|
return {}; |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
if (static_cast<size_t>(index.row()) >= rowToFirstRowEntryFromSearch.size()) |
|
|
|
|
return {}; |
|
|
|
|
|
|
|
|
|
const auto firstIndex = rowToFirstRowEntryFromSearch[index.row()]; |
|
|
|
|
const auto firstEntry = currentSearchResult[firstIndex]; |
|
|
|
|
const auto &pack = packs[firstEntry.first]; |
|
|
|
|
|
|
|
|
|
switch (role) { |
|
|
|
|
case Roles::PackName: |
|
|
|
|
return pack.packname; |
|
|
|
|
case Roles::Row: { |
|
|
|
|
QList<StickerImage> imgs; |
|
|
|
|
for (auto img = firstIndex; |
|
|
|
|
imgs.size() < columns && img < currentSearchResult.size() && |
|
|
|
|
currentSearchResult[img].first == firstEntry.first; |
|
|
|
|
img++) { |
|
|
|
|
const auto &data = pack.images.at(currentSearchResult[img].second); |
|
|
|
|
imgs.push_back({.url = QString::fromStdString(data.first.url), |
|
|
|
|
.shortcode = data.second, |
|
|
|
|
.body = QString::fromStdString(data.first.body), |
|
|
|
|
.descriptor_ = std::vector{ |
|
|
|
|
pack.room_id, |
|
|
|
|
pack.state_key, |
|
|
|
|
data.second.toStdString(), |
|
|
|
|
}}); |
|
|
|
|
} |
|
|
|
|
return QVariant::fromValue(imgs); |
|
|
|
|
} |
|
|
|
|
default: |
|
|
|
|
return {}; |
|
|
|
|
} |
|
|
|
|
return QVariant::fromValue(imgs); |
|
|
|
|
} |
|
|
|
|
default: |
|
|
|
|
return {}; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return {}; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void |
|
|
|
|
GridImagePackModel::setSearchString(QString key) |
|
|
|
|
{ |
|
|
|
|
beginResetModel(); |
|
|
|
|
currentSearchResult.clear(); |
|
|
|
|
rowToFirstRowEntryFromSearch.clear(); |
|
|
|
|
searchString_ = key; |
|
|
|
|
|
|
|
|
|
if (!key.isEmpty()) { |
|
|
|
|
auto searchParts = key.toCaseFolded().toUcs4(); |
|
|
|
|
auto tempResults = |
|
|
|
|
trie_.search(searchParts, static_cast<std::size_t>(columns * columns * 4)); |
|
|
|
|
std::ranges::sort(tempResults); |
|
|
|
|
currentSearchResult = std::move(tempResults); |
|
|
|
|
|
|
|
|
|
std::size_t lastPack = -1; |
|
|
|
|
int columnIndex = 0; |
|
|
|
|
for (std::size_t i = 0; i < currentSearchResult.size(); i++) { |
|
|
|
|
auto elem = currentSearchResult[i]; |
|
|
|
|
if (elem.first != lastPack || columnIndex == columns) { |
|
|
|
|
columnIndex = 0; |
|
|
|
|
lastPack = elem.first; |
|
|
|
|
rowToFirstRowEntryFromSearch.push_back(i); |
|
|
|
|
} |
|
|
|
|
columnIndex++; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
endResetModel(); |
|
|
|
|
emit newSearchString(); |
|
|
|
|
} |
|
|
|
|