summary refs log tree commit diff
diff options
context:
space:
mode:
authorNicolas Werner <nicolas.werner@hotmail.de>2023-05-20 00:31:47 +0200
committerNicolas Werner <nicolas.werner@hotmail.de>2023-05-20 00:57:52 +0200
commit62844facf7995f8d730a0f94a6d8a8559ac5410b (patch)
treeed513a8f2bd82c9368592fb3fb9563fd3173f7b8
parentFix StickerPicker padding and names of unnamed packs (diff)
downloadnheko-62844facf7995f8d730a0f94a6d8a8559ac5410b.tar.xz
Allow scrolling to specific sections and order packs in sticker search by match quality
-rw-r--r--resources/qml/emoji/StickerPicker.qml72
-rw-r--r--src/GridImagePackModel.cpp67
-rw-r--r--src/GridImagePackModel.h17
3 files changed, 130 insertions, 26 deletions
diff --git a/resources/qml/emoji/StickerPicker.qml b/resources/qml/emoji/StickerPicker.qml
index ce4d5200..2e1956b1 100644
--- a/resources/qml/emoji/StickerPicker.qml
+++ b/resources/qml/emoji/StickerPicker.qml
@@ -24,6 +24,7 @@ Menu {
     readonly property int stickerDim: 128
     readonly property int stickerDimPad: 128 + Nheko.paddingSmall
     readonly property int stickersPerRow: 3
+    readonly property int sidebarAvatarSize: 24
 
     function show(showAt, roomid_, callback) {
         console.debug("Showing sticker picker");
@@ -40,28 +41,31 @@ Menu {
     modal: true
     focus: true
     closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
-    width: stickersPerRow * stickerDimPad + 20
+    width: sidebarAvatarSize + Nheko.paddingSmall + stickersPerRow * stickerDimPad + 20
 
     Rectangle {
         color: Nheko.colors.window
         height: columnView.implicitHeight + Nheko.paddingSmall*2
-        width: stickersPerRow * stickerDimPad + 20
+        width: sidebarAvatarSize + Nheko.paddingSmall + stickersPerRow * stickerDimPad + 20
 
-        ColumnLayout {
+        GridLayout {
             id: columnView
 
-            spacing: Nheko.paddingSmall
             anchors.leftMargin: Nheko.paddingSmall
             anchors.rightMargin: Nheko.paddingSmall
             anchors.bottom: parent.bottom
             anchors.left: parent.left
             anchors.right: parent.right
+            columns: 2
+            rows: 2
 
             // Search field
             TextField {
                 id: emojiSearch
 
                 Layout.preferredWidth: stickersPerRow * stickerDimPad + 20 - Nheko.paddingSmall
+                Layout.row: 0
+                Layout.column: 1
                 palette: Nheko.colors
                 background: null
                 placeholderTextColor: Nheko.colors.buttonText
@@ -102,30 +106,13 @@ Menu {
                 }
             }
 
-            Component {
-                id: sectionHeading
-                Rectangle {
-                    width: gridView.width
-                    height: childrenRect.height
-                    color: Nheko.colors.alternateBase
-
-                    required property string section
-
-                    Text {
-                        anchors.left: parent.left
-                        anchors.right: parent.right
-                        text: parent.section
-                        color: Nheko.colors.text
-                        font.bold: true
-                    }
-                }
-            }
-
             // sticker grid
             ListView {
                 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
@@ -135,7 +122,21 @@ Menu {
 
                 section.property: "packname"
                 section.criteria: ViewSection.FullString
-                section.delegate: sectionHeading
+                section.delegate: Rectangle {
+                    width: gridView.width
+                    height: childrenRect.height
+                    color: Nheko.colors.alternateBase
+
+                    required property string section
+
+                    Text {
+                        anchors.left: parent.left
+                        anchors.right: parent.right
+                        text: parent.section
+                        color: Nheko.colors.text
+                        font.bold: true
+                    }
+                }
                 section.labelPositioning: ViewSection.InlineLabels | ViewSection.CurrentLabelAtStart
 
                 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)
+                }
+            }
         }
 
     }
diff --git a/src/GridImagePackModel.cpp b/src/GridImagePackModel.cpp
index 59b1725a..5db8c0cc 100644
--- a/src/GridImagePackModel.cpp
+++ b/src/GridImagePackModel.cpp
@@ -12,12 +12,16 @@
 #include "Cache_p.h"
 
 Q_DECLARE_METATYPE(StickerImage)
+Q_DECLARE_METATYPE(SectionDescription)
+Q_DECLARE_METATYPE(QList<SectionDescription>)
 
 GridImagePackModel::GridImagePackModel(const std::string &roomId, bool stickers, QObject *parent)
   : QAbstractListModel(parent)
   , 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);
 
@@ -182,6 +186,58 @@ GridImagePackModel::nameFromPack(const PackDesc &pack) const
     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
 GridImagePackModel::setSearchString(QString key)
 {
@@ -194,7 +250,14 @@ GridImagePackModel::setSearchString(QString key)
         auto searchParts = key.toCaseFolded().toUcs4();
         auto tempResults =
           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);
 
         std::size_t lastPack = -1;
diff --git a/src/GridImagePackModel.h b/src/GridImagePackModel.h
index 8da61b8e..c6be3346 100644
--- a/src/GridImagePackModel.h
+++ b/src/GridImagePackModel.h
@@ -41,10 +41,24 @@ public:
     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
 {
     Q_OBJECT
     Q_PROPERTY(QString searchString READ searchString WRITE setSearchString NOTIFY newSearchString)
+    Q_PROPERTY(QList<SectionDescription> sections READ sections NOTIFY newSearchString)
 
 public:
     enum Roles
@@ -61,6 +75,8 @@ public:
     QString searchString() const { return searchString_; }
     void setSearchString(QString newValue);
 
+    QList<SectionDescription> sections() const;
+
 signals:
     void newSearchString();
 
@@ -87,4 +103,5 @@ private:
     std::vector<std::size_t> rowToFirstRowEntryFromSearch;
 
     QString nameFromPack(const PackDesc &pack) const;
+    QString avatarFromPack(const PackDesc &pack) const;
 };