mirror of
https://github.com/Nheko-Reborn/nheko.git
synced 2024-11-24 20:18:53 +03:00
Allow scrolling to specific sections and order packs in sticker search by match quality
This commit is contained in:
parent
58cfc39ac4
commit
62844facf7
3 changed files with 130 additions and 26 deletions
|
@ -24,6 +24,7 @@ Menu {
|
||||||
readonly property int stickerDim: 128
|
readonly property int stickerDim: 128
|
||||||
readonly property int stickerDimPad: 128 + Nheko.paddingSmall
|
readonly property int stickerDimPad: 128 + Nheko.paddingSmall
|
||||||
readonly property int stickersPerRow: 3
|
readonly property int stickersPerRow: 3
|
||||||
|
readonly property int sidebarAvatarSize: 24
|
||||||
|
|
||||||
function show(showAt, roomid_, callback) {
|
function show(showAt, roomid_, callback) {
|
||||||
console.debug("Showing sticker picker");
|
console.debug("Showing sticker picker");
|
||||||
|
@ -40,28 +41,31 @@ Menu {
|
||||||
modal: true
|
modal: true
|
||||||
focus: true
|
focus: true
|
||||||
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
|
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
|
||||||
width: stickersPerRow * stickerDimPad + 20
|
width: sidebarAvatarSize + Nheko.paddingSmall + stickersPerRow * stickerDimPad + 20
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
color: Nheko.colors.window
|
color: Nheko.colors.window
|
||||||
height: columnView.implicitHeight + Nheko.paddingSmall*2
|
height: columnView.implicitHeight + Nheko.paddingSmall*2
|
||||||
width: stickersPerRow * stickerDimPad + 20
|
width: sidebarAvatarSize + Nheko.paddingSmall + stickersPerRow * stickerDimPad + 20
|
||||||
|
|
||||||
ColumnLayout {
|
GridLayout {
|
||||||
id: columnView
|
id: columnView
|
||||||
|
|
||||||
spacing: Nheko.paddingSmall
|
|
||||||
anchors.leftMargin: Nheko.paddingSmall
|
anchors.leftMargin: Nheko.paddingSmall
|
||||||
anchors.rightMargin: Nheko.paddingSmall
|
anchors.rightMargin: Nheko.paddingSmall
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
|
columns: 2
|
||||||
|
rows: 2
|
||||||
|
|
||||||
// Search field
|
// Search field
|
||||||
TextField {
|
TextField {
|
||||||
id: emojiSearch
|
id: emojiSearch
|
||||||
|
|
||||||
Layout.preferredWidth: stickersPerRow * stickerDimPad + 20 - Nheko.paddingSmall
|
Layout.preferredWidth: stickersPerRow * stickerDimPad + 20 - Nheko.paddingSmall
|
||||||
|
Layout.row: 0
|
||||||
|
Layout.column: 1
|
||||||
palette: Nheko.colors
|
palette: Nheko.colors
|
||||||
background: null
|
background: null
|
||||||
placeholderTextColor: Nheko.colors.buttonText
|
placeholderTextColor: Nheko.colors.buttonText
|
||||||
|
@ -102,9 +106,23 @@ Menu {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
// sticker grid
|
||||||
id: sectionHeading
|
ListView {
|
||||||
Rectangle {
|
id: gridView
|
||||||
|
|
||||||
|
model: roomid ? TimelineManager.completerFor("stickergrid", roomid) : null
|
||||||
|
Layout.row: 1
|
||||||
|
Layout.column: 1
|
||||||
|
Layout.preferredHeight: cellHeight * 3.5
|
||||||
|
Layout.preferredWidth: stickersPerRow * stickerDimPad + 20 - Nheko.paddingSmall
|
||||||
|
property int cellHeight: stickerDimPad
|
||||||
|
boundsBehavior: Flickable.StopAtBounds
|
||||||
|
clip: true
|
||||||
|
currentIndex: -1 // prevent sorting from stealing focus
|
||||||
|
|
||||||
|
section.property: "packname"
|
||||||
|
section.criteria: ViewSection.FullString
|
||||||
|
section.delegate: Rectangle {
|
||||||
width: gridView.width
|
width: gridView.width
|
||||||
height: childrenRect.height
|
height: childrenRect.height
|
||||||
color: Nheko.colors.alternateBase
|
color: Nheko.colors.alternateBase
|
||||||
|
@ -119,23 +137,6 @@ Menu {
|
||||||
font.bold: true
|
font.bold: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// sticker grid
|
|
||||||
ListView {
|
|
||||||
id: gridView
|
|
||||||
|
|
||||||
model: roomid ? TimelineManager.completerFor("stickergrid", roomid) : null
|
|
||||||
Layout.preferredHeight: cellHeight * 3.5
|
|
||||||
Layout.preferredWidth: stickersPerRow * stickerDimPad + 20 - Nheko.paddingSmall
|
|
||||||
property int cellHeight: stickerDimPad
|
|
||||||
boundsBehavior: Flickable.StopAtBounds
|
|
||||||
clip: true
|
|
||||||
currentIndex: -1 // prevent sorting from stealing focus
|
|
||||||
|
|
||||||
section.property: "packname"
|
|
||||||
section.criteria: ViewSection.FullString
|
|
||||||
section.delegate: sectionHeading
|
|
||||||
section.labelPositioning: ViewSection.InlineLabels | ViewSection.CurrentLabelAtStart
|
section.labelPositioning: ViewSection.InlineLabels | ViewSection.CurrentLabelAtStart
|
||||||
|
|
||||||
spacing: Nheko.paddingSmall
|
spacing: Nheko.paddingSmall
|
||||||
|
@ -191,6 +192,29 @@ Menu {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ListView {
|
||||||
|
Layout.row: 1
|
||||||
|
Layout.column: 0
|
||||||
|
Layout.preferredWidth: sidebarAvatarSize
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.rightMargin: Nheko.paddingSmall
|
||||||
|
|
||||||
|
model: gridView.model ? gridView.model.sections : null
|
||||||
|
spacing: Nheko.paddingSmall
|
||||||
|
|
||||||
|
delegate: Avatar {
|
||||||
|
height: sidebarAvatarSize
|
||||||
|
width: sidebarAvatarSize
|
||||||
|
url: modelData.url.replace("mxc://", "image://MxcImage/")
|
||||||
|
displayName: modelData.name
|
||||||
|
roomid: modelData.name
|
||||||
|
|
||||||
|
hoverEnabled: true
|
||||||
|
ToolTip.visible: hovered
|
||||||
|
ToolTip.text: modelData.name
|
||||||
|
onClicked: gridView.positionViewAtIndex(modelData.firstRowWith, ListView.Beginning)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,12 +12,16 @@
|
||||||
#include "Cache_p.h"
|
#include "Cache_p.h"
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(StickerImage)
|
Q_DECLARE_METATYPE(StickerImage)
|
||||||
|
Q_DECLARE_METATYPE(SectionDescription)
|
||||||
|
Q_DECLARE_METATYPE(QList<SectionDescription>)
|
||||||
|
|
||||||
GridImagePackModel::GridImagePackModel(const std::string &roomId, bool stickers, QObject *parent)
|
GridImagePackModel::GridImagePackModel(const std::string &roomId, bool stickers, QObject *parent)
|
||||||
: QAbstractListModel(parent)
|
: QAbstractListModel(parent)
|
||||||
, room_id(roomId)
|
, room_id(roomId)
|
||||||
{
|
{
|
||||||
[[maybe_unused]] static auto id = qRegisterMetaType<StickerImage>();
|
[[maybe_unused]] static auto id = qRegisterMetaType<StickerImage>();
|
||||||
|
[[maybe_unused]] static auto id2 = qRegisterMetaType<SectionDescription>();
|
||||||
|
[[maybe_unused]] static auto id3 = qRegisterMetaType<QList<SectionDescription>>();
|
||||||
|
|
||||||
auto originalPacks = cache::client()->getImagePacks(room_id, stickers);
|
auto originalPacks = cache::client()->getImagePacks(room_id, stickers);
|
||||||
|
|
||||||
|
@ -182,6 +186,58 @@ GridImagePackModel::nameFromPack(const PackDesc &pack) const
|
||||||
return tr("Account Pack");
|
return tr("Account Pack");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString
|
||||||
|
GridImagePackModel::avatarFromPack(const PackDesc &pack) const
|
||||||
|
{
|
||||||
|
if (!pack.packavatar.isEmpty()) {
|
||||||
|
return pack.packavatar;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pack.images.empty()) {
|
||||||
|
return QString::fromStdString(pack.images.begin()->first.url);
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<SectionDescription>
|
||||||
|
GridImagePackModel::sections() const
|
||||||
|
{
|
||||||
|
QList<SectionDescription> sectionNames;
|
||||||
|
if (searchString_.isEmpty()) {
|
||||||
|
std::size_t packIdx = -1;
|
||||||
|
for (std::size_t i = 0; i < rowToPack.size(); i++) {
|
||||||
|
if (rowToPack[i] != packIdx) {
|
||||||
|
const auto &pack = packs[rowToPack[i]];
|
||||||
|
sectionNames.push_back({
|
||||||
|
.name = nameFromPack(pack),
|
||||||
|
.url = avatarFromPack(pack),
|
||||||
|
.firstRowWith = static_cast<int>(i),
|
||||||
|
});
|
||||||
|
packIdx = rowToPack[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
std::uint32_t packIdx = -1;
|
||||||
|
int row = 0;
|
||||||
|
for (const auto &i : rowToFirstRowEntryFromSearch) {
|
||||||
|
const auto res = currentSearchResult[i];
|
||||||
|
if (res.first != packIdx) {
|
||||||
|
packIdx = res.first;
|
||||||
|
const auto &pack = packs[packIdx];
|
||||||
|
sectionNames.push_back({
|
||||||
|
.name = nameFromPack(pack),
|
||||||
|
.url = avatarFromPack(pack),
|
||||||
|
.firstRowWith = row,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
row++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sectionNames;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
GridImagePackModel::setSearchString(QString key)
|
GridImagePackModel::setSearchString(QString key)
|
||||||
{
|
{
|
||||||
|
@ -194,7 +250,14 @@ GridImagePackModel::setSearchString(QString key)
|
||||||
auto searchParts = key.toCaseFolded().toUcs4();
|
auto searchParts = key.toCaseFolded().toUcs4();
|
||||||
auto tempResults =
|
auto tempResults =
|
||||||
trie_.search(searchParts, static_cast<std::size_t>(columns * columns * 4));
|
trie_.search(searchParts, static_cast<std::size_t>(columns * columns * 4));
|
||||||
std::ranges::sort(tempResults);
|
|
||||||
|
std::map<std::uint32_t, std::size_t> firstPositionOfPack;
|
||||||
|
for (const auto &e : tempResults)
|
||||||
|
firstPositionOfPack.emplace(e.first, firstPositionOfPack.size());
|
||||||
|
|
||||||
|
std::ranges::stable_sort(tempResults, [&firstPositionOfPack](auto a, auto b) {
|
||||||
|
return firstPositionOfPack[a.first] < firstPositionOfPack[b.first];
|
||||||
|
});
|
||||||
currentSearchResult = std::move(tempResults);
|
currentSearchResult = std::move(tempResults);
|
||||||
|
|
||||||
std::size_t lastPack = -1;
|
std::size_t lastPack = -1;
|
||||||
|
|
|
@ -41,10 +41,24 @@ public:
|
||||||
std::vector<std::string> descriptor_; // roomid, statekey, shortcode
|
std::vector<std::string> descriptor_; // roomid, statekey, shortcode
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct SectionDescription
|
||||||
|
{
|
||||||
|
Q_GADGET
|
||||||
|
Q_PROPERTY(QString url MEMBER url CONSTANT)
|
||||||
|
Q_PROPERTY(QString name MEMBER name CONSTANT)
|
||||||
|
Q_PROPERTY(int firstRowWith MEMBER firstRowWith CONSTANT)
|
||||||
|
|
||||||
|
public:
|
||||||
|
QString name;
|
||||||
|
QString url;
|
||||||
|
int firstRowWith = 0;
|
||||||
|
};
|
||||||
|
|
||||||
class GridImagePackModel final : public QAbstractListModel
|
class GridImagePackModel final : public QAbstractListModel
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_PROPERTY(QString searchString READ searchString WRITE setSearchString NOTIFY newSearchString)
|
Q_PROPERTY(QString searchString READ searchString WRITE setSearchString NOTIFY newSearchString)
|
||||||
|
Q_PROPERTY(QList<SectionDescription> sections READ sections NOTIFY newSearchString)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum Roles
|
enum Roles
|
||||||
|
@ -61,6 +75,8 @@ public:
|
||||||
QString searchString() const { return searchString_; }
|
QString searchString() const { return searchString_; }
|
||||||
void setSearchString(QString newValue);
|
void setSearchString(QString newValue);
|
||||||
|
|
||||||
|
QList<SectionDescription> sections() const;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void newSearchString();
|
void newSearchString();
|
||||||
|
|
||||||
|
@ -87,4 +103,5 @@ private:
|
||||||
std::vector<std::size_t> rowToFirstRowEntryFromSearch;
|
std::vector<std::size_t> rowToFirstRowEntryFromSearch;
|
||||||
|
|
||||||
QString nameFromPack(const PackDesc &pack) const;
|
QString nameFromPack(const PackDesc &pack) const;
|
||||||
|
QString avatarFromPack(const PackDesc &pack) const;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue