mirror of
https://github.com/Nheko-Reborn/nheko.git
synced 2024-11-21 18:50:47 +03:00
Add rows to stickerpicker
This commit is contained in:
parent
4a060b59b2
commit
0dfdba4316
9 changed files with 238 additions and 28 deletions
|
@ -456,6 +456,8 @@ set(SRC_FILES
|
|||
src/ColorImageProvider.h
|
||||
src/CombinedImagePackModel.cpp
|
||||
src/CombinedImagePackModel.h
|
||||
src/GridImagePackModel.cpp
|
||||
src/GridImagePackModel.h
|
||||
src/CommandCompleter.cpp
|
||||
src/CommandCompleter.h
|
||||
src/CompletionModelRoles.h
|
||||
|
|
|
@ -433,7 +433,7 @@ Rectangle {
|
|||
ToolTip.visible: hovered
|
||||
ToolTip.text: qsTr("Stickers")
|
||||
onClicked: stickerPopup.visible ? stickerPopup.close() : stickerPopup.show(stickerButton, room.roomId, function(row) {
|
||||
room.input.sticker(stickerPopup.model.sourceModel, row);
|
||||
room.input.sticker(row);
|
||||
TimelineManager.focusMessageInput();
|
||||
})
|
||||
|
||||
|
|
|
@ -102,19 +102,41 @@ Menu {
|
|||
}
|
||||
}
|
||||
|
||||
// emoji grid
|
||||
GridView {
|
||||
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("stickers", roomid) : null
|
||||
model: roomid ? TimelineManager.completerFor("stickergrid", roomid) : null
|
||||
Layout.preferredHeight: cellHeight * 3.5
|
||||
Layout.preferredWidth: stickersPerRow * stickerDimPad + 20 - Nheko.paddingSmall
|
||||
cellWidth: stickerDimPad
|
||||
cellHeight: stickerDimPad
|
||||
property int cellHeight: stickerDimPad
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
clip: true
|
||||
currentIndex: -1 // prevent sorting from stealing focus
|
||||
cacheBuffer: 500
|
||||
|
||||
section.property: "packname"
|
||||
section.criteria: ViewSection.FullString
|
||||
section.delegate: sectionHeading
|
||||
section.labelPositioning: ViewSection.InlineLabels | ViewSection.CurrentLabelAtStart
|
||||
|
||||
ScrollHelper {
|
||||
flickable: parent
|
||||
|
@ -123,23 +145,29 @@ Menu {
|
|||
}
|
||||
|
||||
// Individual emoji
|
||||
delegate: AbstractButton {
|
||||
delegate: Row {
|
||||
required property var row;
|
||||
|
||||
Repeater {
|
||||
model: row
|
||||
|
||||
delegate: AbstractButton {
|
||||
width: stickerDim
|
||||
height: stickerDim
|
||||
hoverEnabled: true
|
||||
ToolTip.text: ":" + model.shortcode + ": - " + model.body
|
||||
ToolTip.text: ":" + modelData.shortcode + ": - " + modelData.body
|
||||
ToolTip.visible: hovered
|
||||
// TODO: maybe add favorites at some point?
|
||||
onClicked: {
|
||||
console.debug("Picked " + model.shortcode);
|
||||
console.debug("Picked " + modelData.descriptor);
|
||||
stickerPopup.close();
|
||||
callback(model.originalRow);
|
||||
callback(modelData.descriptor);
|
||||
}
|
||||
|
||||
contentItem: Image {
|
||||
height: stickerDim
|
||||
width: stickerDim
|
||||
source: model.url.replace("mxc://", "image://MxcImage/") + "?scale"
|
||||
source: modelData.url.replace("mxc://", "image://MxcImage/") + "?scale"
|
||||
fillMode: Image.PreserveAspectFit
|
||||
}
|
||||
|
||||
|
@ -150,6 +178,8 @@ Menu {
|
|||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ScrollBar.vertical: ScrollBar {
|
||||
id: emojiScroll
|
||||
|
|
88
src/GridImagePackModel.cpp
Normal file
88
src/GridImagePackModel.cpp
Normal file
|
@ -0,0 +1,88 @@
|
|||
// SPDX-FileCopyrightText: Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "GridImagePackModel.h"
|
||||
|
||||
#include "Cache_p.h"
|
||||
#include "CompletionModelRoles.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
Q_DECLARE_METATYPE(StickerImage)
|
||||
|
||||
GridImagePackModel::GridImagePackModel(const std::string &roomId, bool stickers, QObject *parent)
|
||||
: QAbstractListModel(parent)
|
||||
, room_id(roomId)
|
||||
{
|
||||
[[maybe_unused]] static auto id = qRegisterMetaType<StickerImage>();
|
||||
|
||||
auto originalPacks = cache::client()->getImagePacks(room_id, stickers);
|
||||
|
||||
for (auto &pack : originalPacks) {
|
||||
PackDesc newPack{};
|
||||
newPack.packname =
|
||||
pack.pack.pack ? QString::fromStdString(pack.pack.pack->display_name) : QString();
|
||||
newPack.room_id = pack.source_room;
|
||||
newPack.state_key = pack.state_key;
|
||||
|
||||
newPack.images.resize(pack.pack.images.size());
|
||||
std::ranges::transform(std::move(pack.pack.images), newPack.images.begin(), [](auto &&img) {
|
||||
return std::pair(std::move(img.second), QString::fromStdString(img.first));
|
||||
});
|
||||
|
||||
size_t packRowCount =
|
||||
(newPack.images.size() / columns) + (newPack.images.size() % columns ? 1 : 0);
|
||||
newPack.firstRow = rowToPack.size();
|
||||
for (size_t i = 0; i < packRowCount; i++)
|
||||
rowToPack.push_back(packs.size());
|
||||
packs.push_back(std::move(newPack));
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
GridImagePackModel::rowCount(const QModelIndex &) const
|
||||
{
|
||||
return (int)rowToPack.size();
|
||||
}
|
||||
|
||||
QHash<int, QByteArray>
|
||||
GridImagePackModel::roleNames() const
|
||||
{
|
||||
return {
|
||||
{Roles::PackName, "packname"},
|
||||
{Roles::Row, "row"},
|
||||
};
|
||||
}
|
||||
|
||||
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(),
|
||||
}});
|
||||
}
|
||||
return QVariant::fromValue(imgs);
|
||||
}
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
72
src/GridImagePackModel.h
Normal file
72
src/GridImagePackModel.h
Normal file
|
@ -0,0 +1,72 @@
|
|||
// SPDX-FileCopyrightText: Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QAbstractListModel>
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
|
||||
#include <mtx/events/mscs/image_packs.hpp>
|
||||
|
||||
struct StickerImage
|
||||
{
|
||||
Q_GADGET
|
||||
Q_PROPERTY(QString url MEMBER url CONSTANT)
|
||||
Q_PROPERTY(QString shortcode MEMBER shortcode CONSTANT)
|
||||
Q_PROPERTY(QString body MEMBER body CONSTANT)
|
||||
Q_PROPERTY(QStringList descriptor READ descriptor CONSTANT)
|
||||
|
||||
public:
|
||||
QStringList descriptor() const
|
||||
{
|
||||
if (descriptor_.size() == 3)
|
||||
return QStringList{
|
||||
QString::fromStdString(descriptor_[0]),
|
||||
QString::fromStdString(descriptor_[1]),
|
||||
QString::fromStdString(descriptor_[2]),
|
||||
};
|
||||
else
|
||||
return {};
|
||||
}
|
||||
|
||||
QString url;
|
||||
QString shortcode;
|
||||
QString body;
|
||||
|
||||
std::vector<std::string> descriptor_; // roomid, statekey, shortcode
|
||||
};
|
||||
|
||||
class GridImagePackModel final : public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum Roles
|
||||
{
|
||||
PackName = Qt::UserRole,
|
||||
Row,
|
||||
};
|
||||
|
||||
GridImagePackModel(const std::string &roomId, bool stickers, QObject *parent = nullptr);
|
||||
QHash<int, QByteArray> roleNames() const override;
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
QVariant data(const QModelIndex &index, int role) const override;
|
||||
|
||||
private:
|
||||
std::string room_id;
|
||||
|
||||
struct PackDesc
|
||||
{
|
||||
QString packname;
|
||||
QString packavatar;
|
||||
std::string room_id, state_key;
|
||||
|
||||
std::vector<std::pair<mtx::events::msc2545::PackImage, QString>> images;
|
||||
std::size_t firstRow;
|
||||
};
|
||||
|
||||
std::vector<PackDesc> packs;
|
||||
std::vector<size_t> rowToPack;
|
||||
int columns = 3;
|
||||
};
|
|
@ -19,6 +19,7 @@
|
|||
#include "CompletionProxyModel.h"
|
||||
#include "Config.h"
|
||||
#include "EventAccessors.h"
|
||||
#include "GridImagePackModel.h"
|
||||
#include "ImagePackListModel.h"
|
||||
#include "InviteesModel.h"
|
||||
#include "JdenticonProvider.h"
|
||||
|
@ -150,6 +151,7 @@ MainWindow::registerQmlTypes()
|
|||
qRegisterMetaType<mtx::responses::User>();
|
||||
qRegisterMetaType<mtx::responses::Profile>();
|
||||
qRegisterMetaType<CombinedImagePackModel *>();
|
||||
qRegisterMetaType<GridImagePackModel *>();
|
||||
qRegisterMetaType<RoomSettingsAllowedRoomsModel *>();
|
||||
qRegisterMetaType<mtx::events::collections::TimelineEvents>();
|
||||
qRegisterMetaType<std::vector<DeviceInfo>>();
|
||||
|
|
|
@ -836,28 +836,40 @@ InputBar::getCommandAndArgs(const QString ¤tText) const
|
|||
}
|
||||
|
||||
void
|
||||
InputBar::sticker(CombinedImagePackModel *model, int row)
|
||||
InputBar::sticker(QStringList descriptor)
|
||||
{
|
||||
if (!model || row < 0)
|
||||
if (descriptor.size() != 3)
|
||||
return;
|
||||
|
||||
auto img = model->imageAt(row);
|
||||
auto originalPacks = cache::client()->getImagePacks(room->roomId().toStdString(), true);
|
||||
|
||||
mtx::events::msg::StickerImage sticker{};
|
||||
sticker.info = img.info.value_or(mtx::common::ImageInfo{});
|
||||
sticker.url = img.url;
|
||||
sticker.body = img.body.empty() ? model->shortcodeAt(row).toStdString() : img.body;
|
||||
auto source_room = descriptor[0].toStdString();
|
||||
auto state_key = descriptor[1].toStdString();
|
||||
auto short_code = descriptor[2].toStdString();
|
||||
|
||||
// workaround for https://github.com/vector-im/element-ios/issues/2353
|
||||
sticker.info.thumbnail_url = sticker.url;
|
||||
sticker.info.thumbnail_info.mimetype = sticker.info.mimetype;
|
||||
sticker.info.thumbnail_info.size = sticker.info.size;
|
||||
sticker.info.thumbnail_info.h = sticker.info.h;
|
||||
sticker.info.thumbnail_info.w = sticker.info.w;
|
||||
for (auto &pack : originalPacks) {
|
||||
if (pack.source_room == source_room && pack.state_key == state_key &&
|
||||
pack.pack.images.contains(short_code)) {
|
||||
auto img = pack.pack.images.at(short_code);
|
||||
|
||||
sticker.relations = generateRelations();
|
||||
mtx::events::msg::StickerImage sticker{};
|
||||
sticker.info = img.info.value_or(mtx::common::ImageInfo{});
|
||||
sticker.url = img.url;
|
||||
sticker.body = img.body.empty() ? short_code : img.body;
|
||||
|
||||
room->sendMessageEvent(sticker, mtx::events::EventType::Sticker);
|
||||
// workaround for https://github.com/vector-im/element-ios/issues/2353
|
||||
sticker.info.thumbnail_url = sticker.url;
|
||||
sticker.info.thumbnail_info.mimetype = sticker.info.mimetype;
|
||||
sticker.info.thumbnail_info.size = sticker.info.size;
|
||||
sticker.info.thumbnail_info.h = sticker.info.h;
|
||||
sticker.info.thumbnail_info.w = sticker.info.w;
|
||||
|
||||
sticker.relations = generateRelations();
|
||||
|
||||
room->sendMessageEvent(sticker, mtx::events::EventType::Sticker);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -217,7 +217,7 @@ public slots:
|
|||
MarkdownOverride useMarkdown = MarkdownOverride::NOT_SPECIFIED,
|
||||
bool rainbowify = false);
|
||||
void reaction(const QString &reactedEvent, const QString &reactionKey);
|
||||
void sticker(CombinedImagePackModel *model, int row);
|
||||
void sticker(QStringList descriptor);
|
||||
|
||||
void acceptUploads();
|
||||
void declineUploads();
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "CommandCompleter.h"
|
||||
#include "CompletionProxyModel.h"
|
||||
#include "EventAccessors.h"
|
||||
#include "GridImagePackModel.h"
|
||||
#include "ImagePackListModel.h"
|
||||
#include "InviteesModel.h"
|
||||
#include "Logging.h"
|
||||
|
@ -477,6 +478,9 @@ TimelineViewManager::completerFor(const QString &completerName, const QString &r
|
|||
auto proxy = new CompletionProxyModel(stickerModel, 1, static_cast<size_t>(-1) / 4);
|
||||
stickerModel->setParent(proxy);
|
||||
return proxy;
|
||||
} else if (completerName == QLatin1String("stickergrid")) {
|
||||
auto stickerModel = new GridImagePackModel(roomId.toStdString(), true);
|
||||
return stickerModel;
|
||||
} else if (completerName == QLatin1String("customEmoji")) {
|
||||
auto stickerModel = new CombinedImagePackModel(roomId.toStdString(), false);
|
||||
auto proxy = new CompletionProxyModel(stickerModel);
|
||||
|
|
Loading…
Reference in a new issue